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