release 0.2.3
[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                         log_debug("armor header: ");
292                         print_string( stderr, buf, n );
293                         putc('\n', stderr);
294                         if( clearsig && !parse_hash_header( buf ) ) {
295                             log_error("invalid clearsig header\n");
296                             state = fhdrERROR;
297                         }
298                         else
299                             state = fhdrWAITHeader;
300                     }
301                     else
302                         state = fhdrCHECKDashEscaped3;
303                 }
304                 else if( !n || (buf[0] == '\r' && !buf[1]) ) { /* empty line */
305                     if( clearsig )
306                         state = fhdrWAITClearsig;
307                     else {
308                         /* this is not really correct: if we do not have
309                          * a clearsig and no armor lines we are not allowed
310                          * to have an empty line */
311                         n = 0;
312                         state = fhdrTEXT;
313                     }
314                 }
315                 else {
316                     log_error("invalid armor header: ");
317                     print_string( stderr, buf, n );
318                     putc('\n', stderr);
319                     state = fhdrERROR;
320                 }
321             }
322             else if( c != -1 ) {
323                 if( strchr( buf, ':') ) { /* buffer to short, but this is okay*/
324                     log_debug("armor header: ");
325                     print_string( stderr, buf, n );
326                     fputs("[...]\n", stderr);  /* indicate it is truncated */
327                     state = fhdrSKIPHeader;  /* skip rest of line */
328                 }
329                 else /* line too long */
330                     state = fhdrERROR;
331             }
332             else
333                 state = fhdrEOF;
334             break;
335
336           case fhdrWAITClearsig: /* skip all empty lines (for clearsig) */
337             c = 0;
338             for(n=0; n < buflen && (c=iobuf_get2(a)) != -1 && c != '\n'; )
339                 buf[n++] = c;
340             if( n < buflen || c == '\n' ) {
341                 buf[n] = 0;
342                 if( !n || (buf[0]=='\r' && !buf[1]) ) /* empty line */
343                     ;
344                 else
345                     state = fhdrCHECKDashEscaped3;
346             }
347             else {
348                 /* fixme: we should check wether this line continues
349                  *   it is poosible that we have only read ws until here
350                  *   and more stuff is to come */
351                 state = fhdrEOF;
352             }
353             break;
354
355           case fhdrENDClearsig:
356           case fhdrCHECKBegin:
357             state = state == fhdrCHECKBegin ? fhdrINITSkip : fhdrERRORShow;
358             if( n < 15 )
359                 break;  /* too short */
360             if( memcmp( buf, "-----", 5 ) )
361                 break;
362             buf[n] = 0;
363             p = strstr(buf+5, "-----");
364             if( !p )
365                 break;
366             *p = 0;
367             p += 5;
368             if( *p == '\r' )
369                 p++;
370             if( *p )
371                 break; /* garbage after dashes */
372             p = buf+5;
373             for(i=0; (s=head_strings[i]); i++ )
374                 if( !strcmp(s, p) )
375                     break;
376             if( !s )
377                 break; /* unknown begin line */
378             /* found the begin line */
379             hdr_line = i;
380             state = fhdrWAITHeader;
381             if( hdr_line == BEGIN_SIGNED_MSG_IDX )
382                 clearsig = 1;
383             log_debug("armor: %s\n", head_strings[hdr_line]);
384             break;
385
386           case fhdrCLEARSIG:
387           case fhdrEMPTYClearsig:
388           case fhdrREADClearsig:
389             /* we are at the start of a line: read a clearsig into the buffer
390              * we have to look for a the header line or dashed escaped text*/
391             n = 0;
392             c = 0;
393             while( n < buflen && (c=iobuf_get2(a)) != -1 && c != '\n' )
394                 buf[n++] = c;
395             buf[n] = 0;
396             if( c == -1 )
397                 state = fhdrEOF;
398             else if( !n || ( buf[0]=='\r' && !buf[1] ) ) {
399                 state = fhdrEMPTYClearsig;
400                 empty++;
401             }
402             else if( c == '\n' )
403                 state = fhdrCHECKClearsig2;
404             else
405                 state = fhdrCHECKClearsig;
406             break;
407
408           case fhdrCHECKDashEscaped3:
409             if( !(n > 1 && buf[0] == '-' && buf[1] == ' ' ) ) {
410                 state = fhdrTEXT;
411                 break;
412             }
413             /* fall through */
414           case fhdrCHECKDashEscaped2:
415           case fhdrCHECKDashEscaped:
416             /* check dash escaped line */
417             if( buf[2] == '-' || ( n > 6 && !memcmp(buf+2, "From ", 5))) {
418                 for(i=2; i < n; i++ )
419                     buf[i-2] = buf[i];
420                 n -= 2;
421                 buf[n] = 0; /* not really needed */
422                 state = state == fhdrCHECKDashEscaped3 ? fhdrTEXT :
423                         state == fhdrCHECKDashEscaped2 ?
424                                  fhdrREADClearsig : fhdrTESTSpaces;
425             }
426             else {
427                 log_error("invalid dash escaped line: ");
428                 print_string( stderr, buf, n );
429                 putc('\n', stderr);
430                 state = fhdrERROR;
431             }
432             break;
433
434           case fhdrCHECKClearsig:
435           case fhdrCHECKClearsig2:
436             /* check the clearsig line */
437             if( n > 15 && !memcmp(buf, "-----", 5 ) )
438                 state = fhdrENDClearsig;
439             else if( buf[0] == '-' && buf[1] == ' ' )
440                 state = fhdrCHECKDashEscaped;
441             else {
442                 state = state == fhdrCHECKClearsig2 ?
443                                   fhdrREADClearsig : fhdrTESTSpaces;
444             }
445             break;
446
447           case fhdrREADClearsigNext:
448             /* Read to the end of the line, do not care about checking
449              * for dashed escaped text of headers */
450             c = 0;
451             n = 0;
452             while( n < buflen && (c=iobuf_get2(a)) != -1 && c != '\n' )
453                 buf[n++] = c;
454             buf[n] = 0;
455             if( c == -1 )
456                 state = fhdrEOF;
457             else if( c == '\n' )
458                 state = fhdrREADClearsig;
459             else
460                 state = fhdrTESTSpaces;
461             break;
462
463           case fhdrTESTSpaces: {
464             /* but must check wether the rest of the line
465              * does only contain white spaces; this is problematic
466              * since we may have to restore the stuffs. simply
467              * counting spaces is not enough, because it may be a
468              * mix of different white space chacters */
469             IOBUF b = iobuf_temp();
470             while( (c=iobuf_get2(a)) != -1 && c != '\n' ) {
471                 iobuf_put(b,c);
472                 if( c != ' ' && c != '\t' && c != '\r' )
473                     break;
474             }
475             if( c == '\n' ) {
476                 /* okay we can skip the rest of the line */
477                 iobuf_close(b);
478                 state = fhdrREADClearsig;
479             }
480             else {
481                 iobuf_unget_and_close_temp(a,b);
482                 state = fhdrREADClearsigNext;
483             }
484           } break;
485
486           case fhdrERRORShow:
487             log_error("invalid clear text header: ");
488             print_string( stderr, buf, n );
489             putc('\n', stderr);
490             state = fhdrERROR;
491             break;
492
493           default: BUG();
494         }
495         switch( state ) {
496           case fhdrINIT:
497           case fhdrINITCont:
498           case fhdrINITSkip:
499           case fhdrCHECKBegin:
500           case fhdrWAITHeader:
501           case fhdrWAITClearsig:
502           case fhdrSKIPHeader:
503           case fhdrEMPTYClearsig:
504           case fhdrCHECKClearsig:
505           case fhdrCHECKClearsig2:
506           case fhdrCHECKDashEscaped:
507           case fhdrCHECKDashEscaped2:
508           case fhdrCHECKDashEscaped3:
509           case fhdrTESTSpaces:
510           case fhdrERRORShow:
511             cont = 1;
512             break;
513           default: cont = 0;
514         }
515     } while( cont );
516
517     if( clearsig && state == fhdrTEXT )
518         state = fhdrCLEARSIG;
519
520     if( state == fhdrCLEARSIG || state == fhdrREADClearsig ) {
521         /* append CR,LF after removing trailing wspaces */
522         for(p=buf+n-1; n; n--, p-- ) {
523             assert( *p != '\n' );
524             if( *p != ' ' && *p != '\t' && *p != '\r' ) {
525                 p[1] = '\r';
526                 p[2] = '\n';
527                 n += 2;
528                 break;
529             }
530         }
531         if( !n ) {
532             buf[0] = '\r';
533             buf[1] = '\n';
534             n = 2;
535         }
536     }
537
538
539     *r_buflen = n;
540     *r_empty = empty;
541     return state;
542 }
543
544
545 /* figure out wether the data is armored or not */
546 static int
547 check_input( armor_filter_context_t *afx, IOBUF a )
548 {
549     int rc = 0;
550     size_t n;
551     fhdr_state_t state = afx->parse_state;
552     unsigned emplines;
553
554     if( state != fhdrENDClearsig )
555         state = fhdrHASArmor;
556
557     n = DIM(afx->helpbuf);
558     state = find_header( state, afx->helpbuf, &n, a, afx->helplen, &emplines);
559     switch( state ) {
560       case fhdrNOArmor:
561         afx->inp_checked = 1;
562         afx->inp_bypass = 1;
563         afx->helplen = n;
564         break;
565
566       case fhdrERROR:
567         invalid_armor();
568         break;
569
570       case fhdrEOF:
571         rc = -1;
572         break;
573
574       case fhdrCLEARSIG: /* start fake package mode (for clear signatures) */
575         afx->helplen = n;
576         afx->helpidx = 0;
577         afx->faked = 1;
578         break;
579
580       case fhdrTEXT:
581         afx->helplen = n;
582         afx->helpidx = 0;
583         afx->inp_checked = 1;
584         afx->crc = CRCINIT;
585         afx->idx = 0;
586         afx->radbuf[0] = 0;
587         break;
588
589       default: BUG();
590     }
591
592     afx->parse_state = state;
593     return rc;
594 }
595
596
597
598 /* fake a literal data packet and wait for an armor line */
599 static int
600 fake_packet( armor_filter_context_t *afx, IOBUF a,
601              size_t *retn, byte *buf, size_t size  )
602 {
603     int rc = 0;
604     size_t len = 0;
605     size_t n, nn;
606     fhdr_state_t state = afx->parse_state;
607     unsigned emplines = afx->empty;
608
609     size = 100; /* FIXME: only used for testing (remove it)  */
610
611     len = 2;    /* reserve 2 bytes for the length header */
612     size -= 3;  /* and 1 for empline handling and 2 for the term header */
613     while( !rc && len < size ) {
614         if( emplines ) {
615             while( emplines && len < size ) {
616                 buf[len++] = '\r';
617                 buf[len++] = '\n';
618                 emplines--;
619             }
620             continue;
621         }
622         if( state == fhdrENDClearsigHelp ) {
623             state = fhdrENDClearsig;
624             afx->faked = 0;
625             rc = -1;
626             continue;
627         }
628         if( afx->helpidx < afx->helplen ) { /* flush the last buffer */
629             n = afx->helplen;
630             for(nn=afx->helpidx; len < size && nn < n ; nn++ )
631                 buf[len++] = afx->helpbuf[nn];
632             afx->helpidx = nn;
633             continue;
634         }
635         if( state == fhdrEOF ) {
636             rc = -1;
637             continue;
638         }
639         /* read a new one */
640         n = DIM(afx->helpbuf);
641         afx->helpidx = 0;
642         state = find_header( state, afx->helpbuf, &n, a, 0, &emplines );
643         switch( state) {
644           case fhdrERROR:
645             invalid_armor();
646             break;
647
648           case fhdrEOF:
649             rc = -1;
650             break;
651
652           case fhdrCLEARSIG:
653             BUG();
654
655           case fhdrREADClearsig:
656           case fhdrREADClearsigNext:
657             afx->helplen = n;
658             break;
659
660           case fhdrENDClearsig:
661             assert( emplines );
662             emplines--; /* don't count the last one */
663             state = fhdrENDClearsigHelp;
664             afx->helplen = n;
665             break;
666
667           default: BUG();
668         }
669     }
670     buf[0] = (len-2) >> 8;
671     buf[1] = (len-2);
672     if( state == fhdrENDClearsig ) { /* write last (ending) length header */
673         buf[len++] = 0;
674         buf[len++] = 0;
675         rc = 0;
676     }
677
678     afx->parse_state = state;
679     afx->empty = emplines;
680     *retn = len;
681     return rc;
682 }
683
684
685
686 static int
687 radix64_read( armor_filter_context_t *afx, IOBUF a, size_t *retn,
688               byte *buf, size_t size )
689 {
690     byte val;
691     int c, c2;
692     int checkcrc=0;
693     int rc = 0;
694     size_t n = 0;
695     int  idx, i;
696     u32 crc;
697
698     crc = afx->crc;
699     idx = afx->idx;
700     val = afx->radbuf[0];
701     for( n=0; n < size; ) {
702         if( afx->helpidx < afx->helplen )
703             c = afx->helpbuf[afx->helpidx++];
704         else if( (c=iobuf_get(a)) == -1 )
705             break;
706         if( c == '\n' || c == ' ' || c == '\r' || c == '\t' )
707             continue;
708         else if( c == '=' ) { /* pad character: stop */
709             if( idx == 1 )
710                 buf[n++] = val;
711             checkcrc++;
712             break;
713         }
714         else if( (c = asctobin[(c2=c)]) == 255 ) {
715             log_error("invalid radix64 character %02x skipped\n", c2);
716             continue;
717         }
718         switch(idx) {
719           case 0: val =  c << 2; break;
720           case 1: val |= (c>>4)&3; buf[n++]=val;val=(c<<4)&0xf0;break;
721           case 2: val |= (c>>2)&15; buf[n++]=val;val=(c<<6)&0xc0;break;
722           case 3: val |= c&0x3f; buf[n++] = val; break;
723         }
724         idx = (idx+1) % 4;
725     }
726     for(i=0; i < n; i++ )
727         crc = (crc << 8) ^ crc_table[((crc >> 16)&0xff) ^ buf[i]];
728     crc &= 0x00ffffff;
729     afx->crc = crc;
730     afx->idx = idx;
731     afx->radbuf[0] = val;
732     if( checkcrc ) {
733         afx->inp_eof = 1; /*assume eof */
734         for(;;) { /* skip lf and pad characters */
735             if( afx->helpidx < afx->helplen )
736                 c = afx->helpbuf[afx->helpidx++];
737             else if( (c=iobuf_get(a)) == -1 )
738                 break;
739             if( c == '\n' || c == ' ' || c == '\r'
740                 || c == '\t' || c == '=' )
741                 continue;
742             break;
743         }
744         if( c == -1 )
745             log_error("premature eof (no CRC)\n");
746         else {
747             u32 mycrc = 0;
748             idx = 0;
749             do {
750                 if( (c = asctobin[c]) == 255 )
751                     break;
752                 switch(idx) {
753                   case 0: val =  c << 2; break;
754                   case 1: val |= (c>>4)&3; mycrc |= val << 16;val=(c<<4)&0xf0;break;
755                   case 2: val |= (c>>2)&15; mycrc |= val << 8;val=(c<<6)&0xc0;break;
756                   case 3: val |= c&0x3f; mycrc |= val; break;
757                 }
758                 if( afx->helpidx < afx->helplen )
759                     c = afx->helpbuf[afx->helpidx++];
760                 else if( (c=iobuf_get(a)) == -1 )
761                     break;
762             } while( ++idx < 4 );
763             if( c == -1 )
764                 log_error("premature eof (in CRC)\n");
765             else if( idx != 4 )
766                 log_error("malformed CRC\n");
767             else if( mycrc != afx->crc )
768                 log_error("CRC error; %06lx - %06lx\n",
769                                     (ulong)afx->crc, (ulong)mycrc);
770             else {
771                 rc = 0;
772               #if 0
773                 for(rc=0;!rc;) {
774                     rc = 0 /*check_trailer( &fhdr, c )*/;
775                     if( !rc ) {
776                         if( afx->helpidx < afx->helplen )
777                             c = afx->helpbuf[afx->helpidx++];
778                         else if( (c=iobuf_get(a)) == -1 )
779                             rc = 2;
780                     }
781                 }
782                 if( rc == -1 )
783                     rc = 0;
784                 else if( rc == 2 )
785                     log_error("premature eof (in Trailer)\n");
786                 else
787                     log_error("error in trailer line\n");
788               #endif
789             }
790         }
791     }
792
793     if( !n )
794         rc = -1;
795
796     *retn = n;
797     return rc;
798 }
799
800
801 /****************
802  * The filter is used to handle the armor stuff
803  */
804 int
805 armor_filter( void *opaque, int control,
806              IOBUF a, byte *buf, size_t *ret_len)
807 {
808     size_t size = *ret_len;
809     armor_filter_context_t *afx = opaque;
810     int rc=0, i, c;
811     byte radbuf[3];
812     int  idx, idx2;
813     size_t n=0;
814     u32 crc;
815   #if 1
816     static FILE *fp ;
817
818     if( !fp ) {
819         fp = fopen("armor.out", "w");
820         assert(fp);
821     }
822   #endif
823
824     if( DBG_FILTER )
825         log_debug("armor-filter: control: %d\n", control );
826     if( control == IOBUFCTRL_UNDERFLOW && afx->inp_bypass ) {
827         for( n=0; n < size; n++ ) {
828             if( (c=iobuf_get(a)) == -1 )
829                 break;
830             buf[n] = c & 0xff;
831         }
832         if( !n )
833             rc = -1;
834         *ret_len = n;
835     }
836     else if( control == IOBUFCTRL_UNDERFLOW ) {
837         if( size < 30 )
838             BUG(); /* supplied buffer too short */
839
840         if( afx->inp_eof ) {
841             *ret_len = 0;
842             if( DBG_FILTER )
843                 log_debug("armor-filter: eof due to inp_eof flag\n" );
844             return -1;
845         }
846
847         if( afx->faked )
848             rc = fake_packet( afx, a, &n, buf, size );
849         else if( !afx->inp_checked ) {
850             rc = check_input( afx, a );
851             if( afx->inp_bypass ) {
852                 for( n=0; n < size && n < afx->helplen; n++ )
853                     buf[n] = afx->helpbuf[n];
854                 if( !n )
855                     rc = -1;
856                 assert( n == afx->helplen );
857                 afx->helplen = 0;
858             }
859             else if( afx->faked ) {
860                 /* the buffer is at least 30 bytes long, so it
861                  * is easy to construct the packets */
862
863                 /* first a onepass signature packet */
864                 buf[0] = 0x90; /* old packet forma, type 4, 1 length byte */
865                 buf[1] = 13;   /* length */
866                 buf[2] = 3;    /* version */
867                 buf[3] = 0x01; /* sigclass 0x01 (data in canonical text mode)*/
868                 buf[4] = 0;    /* digest algo (don't know) */
869                 buf[5] = 0;    /* public key algo (don't know) */
870                 memset(buf+6, 0, 8); /* don't know the keyid */
871                 buf[14] = 1;   /* this is the last one */
872
873                 /* followed by a plaintext packet */
874                 buf[15] = 0xaf; /* old packet format, type 11, var length */
875                 buf[16] = 0;    /* set the length header */
876                 buf[17] = 6;
877                 buf[18] = 't';  /* canonical text mode */
878                 buf[19] = 0;    /* namelength */
879                 memset(buf+20, 0, 4); /* timestamp */
880                 n = 24;
881             }
882             else if( !rc )
883                 rc = radix64_read( afx, a, &n, buf, size );
884         }
885         else
886             rc = radix64_read( afx, a, &n, buf, size );
887       #if 1
888         if( n )
889             if( fwrite(buf, n, 1, fp ) != 1 )
890                 BUG();
891       #endif
892         *ret_len = n;
893     }
894     else if( control == IOBUFCTRL_FLUSH ) {
895         if( !afx->status ) { /* write the header line */
896             if( afx->what >= DIM(head_strings) )
897                 log_bug("afx->what=%d", afx->what);
898             iobuf_writestr(a, "-----");
899             iobuf_writestr(a, head_strings[afx->what] );
900             iobuf_writestr(a, "-----\n");
901             iobuf_writestr(a, "Version: G10 pre-release "  VERSION "\n");
902             iobuf_writestr(a, "Comment: This is an alpha test version!\n\n");
903             afx->status++;
904             afx->idx = 0;
905             afx->idx2 = 0;
906             afx->crc = CRCINIT;
907         }
908         crc = afx->crc;
909         idx = afx->idx;
910         idx2 = afx->idx2;
911         for(i=0; i < idx; i++ )
912             radbuf[i] = afx->radbuf[i];
913
914         for(i=0; i < size; i++ )
915             crc = (crc << 8) ^ crc_table[((crc >> 16)&0xff) ^ buf[i]];
916         crc &= 0x00ffffff;
917
918         for( ; size; buf++, size-- ) {
919             radbuf[idx++] = *buf;
920             if( idx > 2 ) {
921                 idx = 0;
922                 c = bintoasc[(*radbuf >> 2) & 077];
923                 iobuf_put(a, c);
924                 c = bintoasc[(((*radbuf<<4)&060)|((radbuf[1] >> 4)&017))&077];
925                 iobuf_put(a, c);
926                 c = bintoasc[(((radbuf[1]<<2)&074)|((radbuf[2]>>6)&03))&077];
927                 iobuf_put(a, c);
928                 c = bintoasc[radbuf[2]&077];
929                 iobuf_put(a, c);
930                 if( ++idx2 > (72/4) ) {
931                     iobuf_put(a, '\n');
932                     idx2=0;
933                 }
934             }
935         }
936         for(i=0; i < idx; i++ )
937             afx->radbuf[i] = radbuf[i];
938         afx->idx = idx;
939         afx->idx2 = idx2;
940         afx->crc  = crc;
941     }
942     else if( control == IOBUFCTRL_INIT ) {
943         if( !is_initialized )
944             initialize();
945     }
946     else if( control == IOBUFCTRL_FREE ) {
947         if( afx->status ) { /* pad, write cecksum, and bottom line */
948             crc = afx->crc;
949             idx = afx->idx;
950             idx2 = afx->idx2;
951             for(i=0; i < idx; i++ )
952                 radbuf[i] = afx->radbuf[i];
953             if( idx ) {
954                 c = bintoasc[(*radbuf>>2)&077];
955                 iobuf_put(a, c);
956                 if( idx == 1 ) {
957                     c = bintoasc[((*radbuf << 4) & 060) & 077];
958                     iobuf_put(a, c);
959                     iobuf_put(a, '=');
960                     iobuf_put(a, '=');
961                 }
962                 else { /* 2 */
963                     c = bintoasc[(((*radbuf<<4)&060)|((radbuf[1]>>4)&017))&077];
964                     iobuf_put(a, c);
965                     c = bintoasc[((radbuf[1] << 2) & 074) & 077];
966                     iobuf_put(a, c);
967                     iobuf_put(a, '=');
968                 }
969                 ++idx2;
970             }
971             /* may need a linefeed */
972             if( idx2 < (72/4) )
973                 iobuf_put(a, '\n');
974             /* write the CRC */
975             iobuf_put(a, '=');
976             radbuf[0] = crc >>16;
977             radbuf[1] = crc >> 8;
978             radbuf[2] = crc;
979             c = bintoasc[(*radbuf >> 2) & 077];
980             iobuf_put(a, c);
981             c = bintoasc[(((*radbuf<<4)&060)|((radbuf[1] >> 4)&017))&077];
982             iobuf_put(a, c);
983             c = bintoasc[(((radbuf[1]<<2)&074)|((radbuf[2]>>6)&03))&077];
984             iobuf_put(a, c);
985             c = bintoasc[radbuf[2]&077];
986             iobuf_put(a, c);
987             iobuf_put(a, '\n');
988             /* and the the trailer */
989             if( afx->what >= DIM(tail_strings) )
990                 log_bug("afx->what=%d", afx->what);
991             iobuf_writestr(a, "-----");
992             iobuf_writestr(a, tail_strings[afx->what] );
993             iobuf_writestr(a, "-----\n");
994         }
995     }
996     else if( control == IOBUFCTRL_DESC )
997         *(char**)buf = "armor_filter";
998     return rc;
999 }
1000