bug fixes
[gnupg.git] / g10 / armor.c
1 /* armor.c - Armor filter
2  *      Copyright (c) 1997 by Werner Koch (dd9jn)
3  *
4  * This file is part of G10.
5  *
6  * G10 is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version.
10  *
11  * G10 is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
19  */
20
21 #include <config.h>
22 #include <stdio.h>
23 #include <stdlib.h>
24 #include <string.h>
25 #include <errno.h>
26 #include <assert.h>
27
28 #include "errors.h"
29 #include "iobuf.h"
30 #include "memory.h"
31 #include "util.h"
32 #include "filter.h"
33 #include "packet.h"
34 #include "options.h"
35 #include "main.h"
36 #include "status.h"
37
38
39
40
41 #define CRCINIT 0xB704CE
42 #define CRCPOLY 0X864CFB
43 #define CRCUPDATE(a,c) do {                                                 \
44                         a = ((a) << 8) ^ crc_table[((a)&0xff >> 16) ^ (c)]; \
45                         a &= 0x00ffffff;                                    \
46                     } while(0)
47 static u32 crc_table[256];
48 static byte bintoasc[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
49                          "abcdefghijklmnopqrstuvwxyz"
50                          "0123456789+/";
51 static byte asctobin[256]; /* runtime initialized */
52 static int is_initialized;
53
54
55 typedef enum {
56     fhdrHASArmor,
57     fhdrNOArmor,
58     fhdrINIT,
59     fhdrINITCont,
60     fhdrINITSkip,
61     fhdrCHECKBegin,
62     fhdrWAITHeader,
63     fhdrWAITClearsig,
64     fhdrSKIPHeader,
65     fhdrCLEARSIG,
66     fhdrREADClearsig,
67     fhdrEMPTYClearsig,
68     fhdrCHECKClearsig,
69     fhdrCHECKClearsig2,
70     fhdrCHECKDashEscaped,
71     fhdrCHECKDashEscaped2,
72     fhdrCHECKDashEscaped3,
73     fhdrREADClearsigNext,
74     fhdrENDClearsig,
75     fhdrENDClearsigHelp,
76     fhdrTESTSpaces,
77     fhdrTEXT,
78     fhdrERROR,
79     fhdrERRORShow,
80     fhdrEOF
81 } fhdr_state_t;
82
83
84 /* if we encounter this armor string with this index, go
85  * into a mode, which fakes packets and wait for the next armor */
86 #define BEGIN_SIGNED_MSG_IDX 3
87 static char *head_strings[] = {
88     "BEGIN PGP MESSAGE",
89     "BEGIN PGP PUBLIC KEY BLOCK",
90     "BEGIN PGP SIGNATURE",
91     "BEGIN PGP SIGNED MESSAGE",
92     NULL
93 };
94 static char *tail_strings[] = {
95     "END PGP MESSAGE",
96     "END PGP PUBLIC KEY BLOCK",
97     "END PGP SIGNATURE",
98     NULL
99 };
100
101
102 static fhdr_state_t find_header( fhdr_state_t state, byte *buf,
103                      size_t *r_buflen, IOBUF a, size_t n, unsigned *r_empty);
104
105
106 static void
107 initialize(void)
108 {
109     int i, j;
110     u32 t;
111     byte *s;
112
113     /* init the crc lookup table */
114     crc_table[0] = 0;
115     for(i=j=0; j < 128; j++ ) {
116         t = crc_table[j];
117         if( t & 0x00800000 ) {
118             t <<= 1;
119             crc_table[i++] = t ^ CRCPOLY;
120             crc_table[i++] = t;
121         }
122         else {
123             t <<= 1;
124             crc_table[i++] = t;
125             crc_table[i++] = t ^ CRCPOLY;
126         }
127     }
128     /* build the helptable for radix64 to bin conversion */
129     for(i=0; i < 256; i++ )
130         asctobin[i] = 255; /* used to detect invalid characters */
131     for(s=bintoasc,i=0; *s; s++,i++ )
132         asctobin[*s] = i;
133
134     is_initialized=1;
135 }
136
137 /****************
138  * Check wether this is a armored file or not
139  * See also parse-packet.c for details on this code
140  * Returns: True if it seems to be armored
141  */
142 static int
143 is_armored( byte *buf )
144 {
145     int ctb, pkttype;
146
147     ctb = *buf;
148     if( !(ctb & 0x80) )
149         return 1; /* invalid packet: assume it is armored */
150     pkttype =  ctb & 0x40 ? (ctb & 0x3f) : ((ctb>>2)&0xf);
151     switch( pkttype ) {
152       case PKT_PUBLIC_CERT:
153       case PKT_SECRET_CERT:
154       case PKT_PUBKEY_ENC:
155       case PKT_SIGNATURE:
156       case PKT_COMMENT:
157       case PKT_PLAINTEXT:
158       case PKT_COMPRESSED:
159       case PKT_ENCRYPTED:
160         return 0; /* seems to be a regular packet: not armored */
161     }
162
163     return 1;
164 }
165
166 static void
167 invalid_armor(void)
168 {
169     write_status(STATUS_BADARMOR);
170     g10_exit(1); /* stop here */
171 }
172
173
174 /****************
175  * check wether the armor header is valid on a signed message.
176  * this is for security reasons: the header lines are not included in the
177  * hash and by using some creative formatting rules, Mallory could fake
178  * any text at the beginning of a document; assuming it is read with
179  * a simple viewer. We do only allow the Hash Header.
180  */
181 static int
182 parse_hash_header( const char *line )
183 {
184     const char *s, *s2;
185     unsigned found = 0;
186
187     if( strlen(line) < 6  || strlen(line) > 60 )
188         return 0; /* too short or too long */
189     if( memcmp( line, "Hash:", 5 ) )
190         return 0; /* invalid header */
191     s = line+5;
192     for(s=line+5;;s=s2) {
193         for(; *s && (*s==' ' || *s == '\t'); s++ )
194             ;
195         if( !*s )
196             break;
197         for(s2=s+1; *s2 && *s2!=' ' && *s2 != '\t' && *s2 != ','; s2++ )
198             ;
199         if( !strncmp( s, "RIPEMD160", s2-s ) )
200             found |= 1;
201         else if( !strncmp( s, "SHA1", s2-s ) )
202             found |= 2;
203         else if( !strncmp( s, "MD5", s2-s ) )
204             found |= 4;
205         else if( !strncmp( s, "MD2", s2-s ) )
206             found |= 8;
207         else
208             return 0;
209         for(; *s2 && (*s2==' ' || *s2 == '\t'); s2++ )
210             ;
211         if( *s2 && *s2 != ',' )
212             return 0;
213         if( *s2 )
214             s2++;
215     }
216     return found;
217 }
218
219
220 /****************
221  * parse an ascii armor.
222  * Returns: the state,
223  *          the remaining bytes in BUF are returned in RBUFLEN.
224  *          r_empty return the # of empty lines before the buffer
225  */
226 static fhdr_state_t
227 find_header( fhdr_state_t state, byte *buf, size_t *r_buflen,
228                                         IOBUF a, size_t n, unsigned *r_empty)
229 {
230     int c, i;
231     const char *s;
232     byte *p;
233     size_t buflen;
234     int cont;
235     int clearsig=0;
236     int hdr_line=0;
237     unsigned empty = 0;
238
239     buflen = *r_buflen;
240     assert(buflen >= 100 );
241     buflen -= 3; /* reserved room for CR,LF and one extra */
242
243     do {
244         switch( state ) {
245           case fhdrHASArmor:
246             /* read at least the first byte to check wether it is armored
247              * or not */
248             c = 0;
249             for(n=0; n < 28 && (c=iobuf_get2(a)) != -1 && c != '\n'; )
250                 buf[n++] = c;
251             if( !n  || c == -1 )
252                 state = fhdrNOArmor; /* too short */
253             else if( !is_armored( buf ) )
254                 state = fhdrNOArmor;
255             else if( c == '\n' )
256                 state = fhdrCHECKBegin;
257             else
258                 state = fhdrINITCont;
259             break;
260
261           case fhdrINIT: /* read some stuff into buffer */
262             n = 0;
263           case fhdrINITCont: /* read more stuff into buffer */
264             c = 0;
265             for(; n < buflen && (c=iobuf_get2(a)) != -1 && c != '\n'; )
266                 buf[n++] = c;
267             state = c == '\n' ? fhdrCHECKBegin :
268                      c == -1  ? fhdrEOF : fhdrINITSkip;
269             break;
270
271           case fhdrINITSkip:
272             while( (c=iobuf_get2(a)) != -1 && c != '\n' )
273                 ;
274             state =  c == -1? fhdrEOF : fhdrINIT;
275             break;
276
277           case fhdrSKIPHeader:
278             while( (c=iobuf_get2(a)) != -1 && c != '\n' )
279                 ;
280             state =  c == -1? fhdrEOF : fhdrWAITHeader;
281             break;
282
283           case fhdrWAITHeader: /* wait for Header lines */
284             c = 0;
285             for(n=0; n < buflen && (c=iobuf_get2(a)) != -1 && c != '\n'; )
286                 buf[n++] = c;
287             buf[n] = 0;
288             if( n < buflen || c == '\n' ) {
289                 if( n && buf[0] != '\r') { /* maybe a header */
290                     if( strchr( buf, ':') ) { /* yes */
291                         if( opt.verbose ) {
292                             log_info("armor header: ");
293                             print_string( stderr, buf, n );
294                             putc('\n', stderr);
295                         }
296                         if( clearsig && !parse_hash_header( buf ) ) {
297                             log_error("invalid clearsig header\n");
298                             state = fhdrERROR;
299                         }
300                         else
301                             state = fhdrWAITHeader;
302                     }
303                     else
304                         state = fhdrCHECKDashEscaped3;
305                 }
306                 else if( !n || (buf[0] == '\r' && !buf[1]) ) { /* empty line */
307                     if( clearsig )
308                         state = fhdrWAITClearsig;
309                     else {
310                         /* this is not really correct: if we do not have
311                          * a clearsig and no armor lines we are not allowed
312                          * to have an empty line */
313                         n = 0;
314                         state = fhdrTEXT;
315                     }
316                 }
317                 else {
318                     log_error("invalid armor header: ");
319                     print_string( stderr, buf, n );
320                     putc('\n', stderr);
321                     state = fhdrERROR;
322                 }
323             }
324             else if( c != -1 ) {
325                 if( strchr( buf, ':') ) { /* buffer to short, but this is okay*/
326                     if( opt.verbose ) {
327                         log_info("armor header: ");
328                         print_string( stderr, buf, n );
329                         fputs("[...]\n", stderr);  /* indicate it is truncated */
330                     }
331                     state = fhdrSKIPHeader;  /* skip rest of line */
332                 }
333                 else /* line too long */
334                     state = fhdrERROR;
335             }
336             else
337                 state = fhdrEOF;
338             break;
339
340           case fhdrWAITClearsig: /* skip all empty lines (for clearsig) */
341             c = 0;
342             for(n=0; n < buflen && (c=iobuf_get2(a)) != -1 && c != '\n'; )
343                 buf[n++] = c;
344             if( n < buflen || c == '\n' ) {
345                 buf[n] = 0;
346                 if( !n || (buf[0]=='\r' && !buf[1]) ) /* empty line */
347                     ;
348                 else
349                     state = fhdrCHECKDashEscaped3;
350             }
351             else {
352                 /* fixme: we should check wether this line continues
353                  *   it is poosible that we have only read ws until here
354                  *   and more stuff is to come */
355                 state = fhdrEOF;
356             }
357             break;
358
359           case fhdrENDClearsig:
360           case fhdrCHECKBegin:
361             state = state == fhdrCHECKBegin ? fhdrINITSkip : fhdrERRORShow;
362             if( n < 15 )
363                 break;  /* too short */
364             if( memcmp( buf, "-----", 5 ) )
365                 break;
366             buf[n] = 0;
367             p = strstr(buf+5, "-----");
368             if( !p )
369                 break;
370             *p = 0;
371             p += 5;
372             if( *p == '\r' )
373                 p++;
374             if( *p )
375                 break; /* garbage after dashes */
376             p = buf+5;
377             for(i=0; (s=head_strings[i]); i++ )
378                 if( !strcmp(s, p) )
379                     break;
380             if( !s )
381                 break; /* unknown begin line */
382             /* found the begin line */
383             hdr_line = i;
384             state = fhdrWAITHeader;
385             if( hdr_line == BEGIN_SIGNED_MSG_IDX )
386                 clearsig = 1;
387             if( opt.verbose > 1 )
388                 log_info("armor: %s\n", head_strings[hdr_line]);
389             break;
390
391           case fhdrCLEARSIG:
392           case fhdrEMPTYClearsig:
393           case fhdrREADClearsig:
394             /* we are at the start of a line: read a clearsig into the buffer
395              * we have to look for a the header line or dashed escaped text*/
396             n = 0;
397             c = 0;
398             while( n < buflen && (c=iobuf_get2(a)) != -1 && c != '\n' )
399                 buf[n++] = c;
400             buf[n] = 0;
401             if( c == -1 )
402                 state = fhdrEOF;
403             else if( !n || ( buf[0]=='\r' && !buf[1] ) ) {
404                 state = fhdrEMPTYClearsig;
405                 empty++;
406             }
407             else if( c == '\n' )
408                 state = fhdrCHECKClearsig2;
409             else
410                 state = fhdrCHECKClearsig;
411             break;
412
413           case fhdrCHECKDashEscaped3:
414             if( !(n > 1 && buf[0] == '-' && buf[1] == ' ' ) ) {
415                 state = fhdrTEXT;
416                 break;
417             }
418             /* fall through */
419           case fhdrCHECKDashEscaped2:
420           case fhdrCHECKDashEscaped:
421             /* check dash escaped line */
422             if( buf[2] == '-' || ( n > 6 && !memcmp(buf+2, "From ", 5))) {
423                 for(i=2; i < n; i++ )
424                     buf[i-2] = buf[i];
425                 n -= 2;
426                 buf[n] = 0; /* not really needed */
427                 state = state == fhdrCHECKDashEscaped3 ? fhdrTEXT :
428                         state == fhdrCHECKDashEscaped2 ?
429                                  fhdrREADClearsig : fhdrTESTSpaces;
430             }
431             else {
432                 log_error("invalid dash escaped line: ");
433                 print_string( stderr, buf, n );
434                 putc('\n', stderr);
435                 state = fhdrERROR;
436             }
437             break;
438
439           case fhdrCHECKClearsig:
440             /* check the clearsig line */
441             if( n > 15 && !memcmp(buf, "-----", 5 ) )
442                 state = fhdrENDClearsig;
443             else if( buf[0] == '-' && buf[1] == ' ' )
444                 state = fhdrCHECKDashEscaped;
445             else {
446                 state = fhdrTESTSpaces;
447             }
448             break;
449
450           case fhdrCHECKClearsig2:
451             /* check the clearsig line */
452             if( n > 15 && !memcmp(buf, "-----", 5 ) )
453                 state = fhdrENDClearsig;
454             else if( buf[0] == '-' && buf[1] == ' ' )
455                 state = fhdrCHECKDashEscaped2;
456             else {
457                 state = fhdrREADClearsig;
458             }
459             break;
460
461           case fhdrREADClearsigNext:
462             /* Read to the end of the line, do not care about checking
463              * for dashed escaped text of headers */
464             c = 0;
465             n = 0;
466             while( n < buflen && (c=iobuf_get2(a)) != -1 && c != '\n' )
467                 buf[n++] = c;
468             buf[n] = 0;
469             if( c == -1 )
470                 state = fhdrEOF;
471             else if( c == '\n' )
472                 state = fhdrREADClearsig;
473             else
474                 state = fhdrTESTSpaces;
475             break;
476
477           case fhdrTESTSpaces: {
478             /* but must check wether the rest of the line
479              * does only contain white spaces; this is problematic
480              * since we may have to restore the stuffs. simply
481              * counting spaces is not enough, because it may be a
482              * mix of different white space chacters */
483             IOBUF b = iobuf_temp();
484             while( (c=iobuf_get2(a)) != -1 && c != '\n' ) {
485                 iobuf_put(b,c);
486                 if( c != ' ' && c != '\t' && c != '\r' )
487                     break;
488             }
489             if( c == '\n' ) {
490                 /* okay we can skip the rest of the line */
491                 iobuf_close(b);
492                 state = fhdrREADClearsig;
493             }
494             else {
495                 iobuf_unget_and_close_temp(a,b);
496                 state = fhdrREADClearsigNext;
497             }
498           } break;
499
500           case fhdrERRORShow:
501             log_error("invalid clear text header: ");
502             print_string( stderr, buf, n );
503             putc('\n', stderr);
504             state = fhdrERROR;
505             break;
506
507           default: BUG();
508         }
509         switch( state ) {
510           case fhdrINIT:
511           case fhdrINITCont:
512           case fhdrINITSkip:
513           case fhdrCHECKBegin:
514           case fhdrWAITHeader:
515           case fhdrWAITClearsig:
516           case fhdrSKIPHeader:
517           case fhdrEMPTYClearsig:
518           case fhdrCHECKClearsig:
519           case fhdrCHECKClearsig2:
520           case fhdrCHECKDashEscaped:
521           case fhdrCHECKDashEscaped2:
522           case fhdrCHECKDashEscaped3:
523           case fhdrTESTSpaces:
524           case fhdrERRORShow:
525             cont = 1;
526             break;
527           default: cont = 0;
528         }
529     } while( cont );
530
531     if( clearsig && state == fhdrTEXT )
532         state = fhdrCLEARSIG;
533
534     if( state == fhdrCLEARSIG || state == fhdrREADClearsig ) {
535         /* append CR,LF after removing trailing wspaces */
536         for(p=buf+n-1; n; n--, p-- ) {
537             assert( *p != '\n' );
538             if( *p != ' ' && *p != '\t' && *p != '\r' ) {
539                 p[1] = '\r';
540                 p[2] = '\n';
541                 n += 2;
542                 break;
543             }
544         }
545         if( !n ) {
546             buf[0] = '\r';
547             buf[1] = '\n';
548             n = 2;
549         }
550     }
551
552
553     *r_buflen = n;
554     *r_empty = empty;
555     return state;
556 }
557
558
559 /* figure out wether the data is armored or not */
560 static int
561 check_input( armor_filter_context_t *afx, IOBUF a )
562 {
563     int rc = 0;
564     size_t n;
565     fhdr_state_t state = afx->parse_state;
566     unsigned emplines;
567
568     if( state != fhdrENDClearsig )
569         state = fhdrHASArmor;
570
571     n = DIM(afx->helpbuf);
572     state = find_header( state, afx->helpbuf, &n, a, afx->helplen, &emplines);
573     switch( state ) {
574       case fhdrNOArmor:
575         afx->inp_checked = 1;
576         afx->inp_bypass = 1;
577         afx->helplen = n;
578         break;
579
580       case fhdrERROR:
581         invalid_armor();
582         break;
583
584       case fhdrEOF:
585         rc = -1;
586         break;
587
588       case fhdrCLEARSIG: /* start fake package mode (for clear signatures) */
589         afx->helplen = n;
590         afx->helpidx = 0;
591         afx->faked = 1;
592         break;
593
594       case fhdrTEXT:
595         afx->helplen = n;
596         afx->helpidx = 0;
597         afx->inp_checked = 1;
598         afx->crc = CRCINIT;
599         afx->idx = 0;
600         afx->radbuf[0] = 0;
601         break;
602
603       default: BUG();
604     }
605
606     afx->parse_state = state;
607     return rc;
608 }
609
610
611
612 /* fake a literal data packet and wait for an armor line */
613 static int
614 fake_packet( armor_filter_context_t *afx, IOBUF a,
615              size_t *retn, byte *buf, size_t size  )
616 {
617     int rc = 0;
618     size_t len = 0;
619     size_t n, nn;
620     fhdr_state_t state = afx->parse_state;
621     unsigned emplines = afx->empty;
622
623     size = 100; /* FIXME: only used for testing (remove it)  */
624
625     len = 2;    /* reserve 2 bytes for the length header */
626     size -= 3;  /* and 1 for empline handling and 2 for the term header */
627     while( !rc && len < size ) {
628         if( emplines ) {
629             while( emplines && len < size ) {
630                 buf[len++] = '\r';
631                 buf[len++] = '\n';
632                 emplines--;
633             }
634             continue;
635         }
636         if( state == fhdrENDClearsigHelp ) {
637             state = fhdrENDClearsig;
638             afx->faked = 0;
639             rc = -1;
640             continue;
641         }
642         if( afx->helpidx < afx->helplen ) { /* flush the last buffer */
643             n = afx->helplen;
644             for(nn=afx->helpidx; len < size && nn < n ; nn++ )
645                 buf[len++] = afx->helpbuf[nn];
646             afx->helpidx = nn;
647             continue;
648         }
649         if( state == fhdrEOF ) {
650             rc = -1;
651             continue;
652         }
653         /* read a new one */
654         n = DIM(afx->helpbuf);
655         afx->helpidx = 0;
656         state = find_header( state, afx->helpbuf, &n, a, 0, &emplines );
657         switch( state) {
658           case fhdrERROR:
659             invalid_armor();
660             break;
661
662           case fhdrEOF:
663             rc = -1;
664             break;
665
666           case fhdrCLEARSIG:
667             BUG();
668
669           case fhdrREADClearsig:
670           case fhdrREADClearsigNext:
671             afx->helplen = n;
672             break;
673
674           case fhdrENDClearsig:
675             assert( emplines );
676             emplines--; /* don't count the last one */
677             state = fhdrENDClearsigHelp;
678             afx->helplen = n;
679             break;
680
681           default: BUG();
682         }
683     }
684     buf[0] = (len-2) >> 8;
685     buf[1] = (len-2);
686     if( state == fhdrENDClearsig ) { /* write last (ending) length header */
687         buf[len++] = 0;
688         buf[len++] = 0;
689         rc = 0;
690     }
691
692     afx->parse_state = state;
693     afx->empty = emplines;
694     *retn = len;
695     return rc;
696 }
697
698
699
700 static int
701 radix64_read( armor_filter_context_t *afx, IOBUF a, size_t *retn,
702               byte *buf, size_t size )
703 {
704     byte val;
705     int c, c2;
706     int checkcrc=0;
707     int rc = 0;
708     size_t n = 0;
709     int  idx, i;
710     u32 crc;
711
712     crc = afx->crc;
713     idx = afx->idx;
714     val = afx->radbuf[0];
715     for( n=0; n < size; ) {
716         if( afx->helpidx < afx->helplen )
717             c = afx->helpbuf[afx->helpidx++];
718         else if( (c=iobuf_get(a)) == -1 )
719             break;
720         if( c == '\n' || c == ' ' || c == '\r' || c == '\t' )
721             continue;
722         else if( c == '=' ) { /* pad character: stop */
723             if( idx == 1 )
724                 buf[n++] = val;
725             checkcrc++;
726             break;
727         }
728         else if( (c = asctobin[(c2=c)]) == 255 ) {
729             log_error("invalid radix64 character %02x skipped\n", c2);
730             continue;
731         }
732         switch(idx) {
733           case 0: val =  c << 2; break;
734           case 1: val |= (c>>4)&3; buf[n++]=val;val=(c<<4)&0xf0;break;
735           case 2: val |= (c>>2)&15; buf[n++]=val;val=(c<<6)&0xc0;break;
736           case 3: val |= c&0x3f; buf[n++] = val; break;
737         }
738         idx = (idx+1) % 4;
739     }
740     for(i=0; i < n; i++ )
741         crc = (crc << 8) ^ crc_table[((crc >> 16)&0xff) ^ buf[i]];
742     crc &= 0x00ffffff;
743     afx->crc = crc;
744     afx->idx = idx;
745     afx->radbuf[0] = val;
746     if( checkcrc ) {
747         afx->inp_eof = 1; /*assume eof */
748         for(;;) { /* skip lf and pad characters */
749             if( afx->helpidx < afx->helplen )
750                 c = afx->helpbuf[afx->helpidx++];
751             else if( (c=iobuf_get(a)) == -1 )
752                 break;
753             if( c == '\n' || c == ' ' || c == '\r'
754                 || c == '\t' || c == '=' )
755                 continue;
756             break;
757         }
758         if( c == -1 )
759             log_error("premature eof (no CRC)\n");
760         else {
761             u32 mycrc = 0;
762             idx = 0;
763             do {
764                 if( (c = asctobin[c]) == 255 )
765                     break;
766                 switch(idx) {
767                   case 0: val =  c << 2; break;
768                   case 1: val |= (c>>4)&3; mycrc |= val << 16;val=(c<<4)&0xf0;break;
769                   case 2: val |= (c>>2)&15; mycrc |= val << 8;val=(c<<6)&0xc0;break;
770                   case 3: val |= c&0x3f; mycrc |= val; break;
771                 }
772                 if( afx->helpidx < afx->helplen )
773                     c = afx->helpbuf[afx->helpidx++];
774                 else if( (c=iobuf_get(a)) == -1 )
775                     break;
776             } while( ++idx < 4 );
777             if( c == -1 )
778                 log_error("premature eof (in CRC)\n");
779             else if( idx != 4 )
780                 log_error("malformed CRC\n");
781             else if( mycrc != afx->crc )
782                 log_error("CRC error; %06lx - %06lx\n",
783                                     (ulong)afx->crc, (ulong)mycrc);
784             else {
785                 rc = 0;
786               #if 0
787                 for(rc=0;!rc;) {
788                     rc = 0 /*check_trailer( &fhdr, c )*/;
789                     if( !rc ) {
790                         if( afx->helpidx < afx->helplen )
791                             c = afx->helpbuf[afx->helpidx++];
792                         else if( (c=iobuf_get(a)) == -1 )
793                             rc = 2;
794                     }
795                 }
796                 if( rc == -1 )
797                     rc = 0;
798                 else if( rc == 2 )
799                     log_error("premature eof (in Trailer)\n");
800                 else
801                     log_error("error in trailer line\n");
802               #endif
803             }
804         }
805     }
806
807     if( !n )
808         rc = -1;
809
810     *retn = n;
811     return rc;
812 }
813
814
815 /****************
816  * The filter is used to handle the armor stuff
817  */
818 int
819 armor_filter( void *opaque, int control,
820              IOBUF a, byte *buf, size_t *ret_len)
821 {
822     size_t size = *ret_len;
823     armor_filter_context_t *afx = opaque;
824     int rc=0, i, c;
825     byte radbuf[3];
826     int  idx, idx2;
827     size_t n=0;
828     u32 crc;
829   #if 0
830     static FILE *fp ;
831
832     if( !fp ) {
833         fp = fopen("armor.out", "w");
834         assert(fp);
835     }
836   #endif
837
838     if( DBG_FILTER )
839         log_debug("armor-filter: control: %d\n", control );
840     if( control == IOBUFCTRL_UNDERFLOW && afx->inp_bypass ) {
841         for( n=0; n < size; n++ ) {
842             if( (c=iobuf_get(a)) == -1 )
843                 break;
844             buf[n] = c & 0xff;
845         }
846         if( !n )
847             rc = -1;
848         *ret_len = n;
849     }
850     else if( control == IOBUFCTRL_UNDERFLOW ) {
851         if( size < 30 )
852             BUG(); /* supplied buffer too short */
853
854         if( afx->inp_eof ) {
855             *ret_len = 0;
856             if( DBG_FILTER )
857                 log_debug("armor-filter: eof due to inp_eof flag\n" );
858             return -1;
859         }
860
861         if( afx->faked )
862             rc = fake_packet( afx, a, &n, buf, size );
863         else if( !afx->inp_checked ) {
864             rc = check_input( afx, a );
865             if( afx->inp_bypass ) {
866                 for( n=0; n < size && n < afx->helplen; n++ )
867                     buf[n] = afx->helpbuf[n];
868                 if( !n )
869                     rc = -1;
870                 assert( n == afx->helplen );
871                 afx->helplen = 0;
872             }
873             else if( afx->faked ) {
874                 /* the buffer is at least 30 bytes long, so it
875                  * is easy to construct the packets */
876
877                 /* first a onepass signature packet */
878                 buf[0] = 0x90; /* old packet forma, type 4, 1 length byte */
879                 buf[1] = 13;   /* length */
880                 buf[2] = 3;    /* version */
881                 buf[3] = 0x01; /* sigclass 0x01 (data in canonical text mode)*/
882                 buf[4] = 0;    /* digest algo (don't know) */
883                 buf[5] = 0;    /* public key algo (don't know) */
884                 memset(buf+6, 0, 8); /* don't know the keyid */
885                 buf[14] = 1;   /* this is the last one */
886
887                 /* followed by a plaintext packet */
888                 buf[15] = 0xaf; /* old packet format, type 11, var length */
889                 buf[16] = 0;    /* set the length header */
890                 buf[17] = 6;
891                 buf[18] = 't';  /* canonical text mode */
892                 buf[19] = 0;    /* namelength */
893                 memset(buf+20, 0, 4); /* timestamp */
894                 n = 24;
895             }
896             else if( !rc )
897                 rc = radix64_read( afx, a, &n, buf, size );
898         }
899         else
900             rc = radix64_read( afx, a, &n, buf, size );
901       #if 0
902         if( n )
903             if( fwrite(buf, n, 1, fp ) != 1 )
904                 BUG();
905       #endif
906         *ret_len = n;
907     }
908     else if( control == IOBUFCTRL_FLUSH ) {
909         if( !afx->status ) { /* write the header line */
910             if( afx->what >= DIM(head_strings) )
911                 log_bug("afx->what=%d", afx->what);
912             iobuf_writestr(a, "-----");
913             iobuf_writestr(a, head_strings[afx->what] );
914             iobuf_writestr(a, "-----\n");
915             iobuf_writestr(a, "Version: G10 pre-release "  VERSION "\n");
916             iobuf_writestr(a, "Comment: This is an alpha test version!\n\n");
917             afx->status++;
918             afx->idx = 0;
919             afx->idx2 = 0;
920             afx->crc = CRCINIT;
921         }
922         crc = afx->crc;
923         idx = afx->idx;
924         idx2 = afx->idx2;
925         for(i=0; i < idx; i++ )
926             radbuf[i] = afx->radbuf[i];
927
928         for(i=0; i < size; i++ )
929             crc = (crc << 8) ^ crc_table[((crc >> 16)&0xff) ^ buf[i]];
930         crc &= 0x00ffffff;
931
932         for( ; size; buf++, size-- ) {
933             radbuf[idx++] = *buf;
934             if( idx > 2 ) {
935                 idx = 0;
936                 c = bintoasc[(*radbuf >> 2) & 077];
937                 iobuf_put(a, c);
938                 c = bintoasc[(((*radbuf<<4)&060)|((radbuf[1] >> 4)&017))&077];
939                 iobuf_put(a, c);
940                 c = bintoasc[(((radbuf[1]<<2)&074)|((radbuf[2]>>6)&03))&077];
941                 iobuf_put(a, c);
942                 c = bintoasc[radbuf[2]&077];
943                 iobuf_put(a, c);
944                 if( ++idx2 > (72/4) ) {
945                     iobuf_put(a, '\n');
946                     idx2=0;
947                 }
948             }
949         }
950         for(i=0; i < idx; i++ )
951             afx->radbuf[i] = radbuf[i];
952         afx->idx = idx;
953         afx->idx2 = idx2;
954         afx->crc  = crc;
955     }
956     else if( control == IOBUFCTRL_INIT ) {
957         if( !is_initialized )
958             initialize();
959     }
960     else if( control == IOBUFCTRL_FREE ) {
961         if( afx->status ) { /* pad, write cecksum, and bottom line */
962             crc = afx->crc;
963             idx = afx->idx;
964             idx2 = afx->idx2;
965             for(i=0; i < idx; i++ )
966                 radbuf[i] = afx->radbuf[i];
967             if( idx ) {
968                 c = bintoasc[(*radbuf>>2)&077];
969                 iobuf_put(a, c);
970                 if( idx == 1 ) {
971                     c = bintoasc[((*radbuf << 4) & 060) & 077];
972                     iobuf_put(a, c);
973                     iobuf_put(a, '=');
974                     iobuf_put(a, '=');
975                 }
976                 else { /* 2 */
977                     c = bintoasc[(((*radbuf<<4)&060)|((radbuf[1]>>4)&017))&077];
978                     iobuf_put(a, c);
979                     c = bintoasc[((radbuf[1] << 2) & 074) & 077];
980                     iobuf_put(a, c);
981                     iobuf_put(a, '=');
982                 }
983                 ++idx2;
984             }
985             /* may need a linefeed */
986             if( idx2 < (72/4) )
987                 iobuf_put(a, '\n');
988             /* write the CRC */
989             iobuf_put(a, '=');
990             radbuf[0] = crc >>16;
991             radbuf[1] = crc >> 8;
992             radbuf[2] = crc;
993             c = bintoasc[(*radbuf >> 2) & 077];
994             iobuf_put(a, c);
995             c = bintoasc[(((*radbuf<<4)&060)|((radbuf[1] >> 4)&017))&077];
996             iobuf_put(a, c);
997             c = bintoasc[(((radbuf[1]<<2)&074)|((radbuf[2]>>6)&03))&077];
998             iobuf_put(a, c);
999             c = bintoasc[radbuf[2]&077];
1000             iobuf_put(a, c);
1001             iobuf_put(a, '\n');
1002             /* and the the trailer */
1003             if( afx->what >= DIM(tail_strings) )
1004                 log_bug("afx->what=%d", afx->what);
1005             iobuf_writestr(a, "-----");
1006             iobuf_writestr(a, tail_strings[afx->what] );
1007             iobuf_writestr(a, "-----\n");
1008         }
1009     }
1010     else if( control == IOBUFCTRL_DESC )
1011         *(char**)buf = "armor_filter";
1012     return rc;
1013 }
1014