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