last local commit
[gnupg.git] / g10 / armor.c
index 6d39dd9..aa56090 100644 (file)
@@ -34,6 +34,7 @@
 #include "options.h"
 #include "main.h"
 #include "status.h"
+#include "i18n.h"
 
 
 #define CRCINIT 0xB704CE
@@ -51,7 +52,7 @@ static int is_initialized;
 
 
 typedef enum {
-    fhdrHASArmor,
+    fhdrHASArmor = 0,
     fhdrNOArmor,
     fhdrINIT,
     fhdrINITCont,
@@ -62,6 +63,7 @@ typedef enum {
     fhdrSKIPHeader,
     fhdrCLEARSIG,
     fhdrREADClearsig,
+    fhdrNullClearsig,
     fhdrEMPTYClearsig,
     fhdrCHECKClearsig,
     fhdrCHECKClearsig2,
@@ -87,7 +89,9 @@ static char *head_strings[] = {
     "BEGIN PGP PUBLIC KEY BLOCK",
     "BEGIN PGP SIGNATURE",
     "BEGIN PGP SIGNED MESSAGE",
-    "BEGIN PGP ARMORED FILE",
+    "BEGIN PGP ARMORED FILE",       /* gnupg extension */
+    "BEGIN PGP PRIVATE KEY BLOCK",
+    "BEGIN PGP SECRET KEY BLOCK",   /* only used by pgp2 */
     NULL
 };
 static char *tail_strings[] = {
@@ -96,12 +100,17 @@ static char *tail_strings[] = {
     "END PGP SIGNATURE",
     "END dummy",
     "END PGP ARMORED FILE",
+    "END PGP PRIVATE KEY BLOCK",
+    "END PGP SECRET KEY BLOCK",
     NULL
 };
 
 
-static fhdr_state_t find_header( fhdr_state_t state, byte *buf,
-                    size_t *r_buflen, IOBUF a, size_t n, unsigned *r_empty);
+static fhdr_state_t find_header( fhdr_state_t state,
+                                byte *buf, size_t *r_buflen,
+                                IOBUF a, size_t n,
+                                unsigned *r_empty, int *r_hashes,
+                                int only_keyblocks );
 
 
 static void
@@ -151,11 +160,13 @@ is_armored( byte *buf )
     pkttype =  ctb & 0x40 ? (ctb & 0x3f) : ((ctb>>2)&0xf);
     switch( pkttype ) {
       case PKT_MARKER:
-      case PKT_PUBLIC_CERT:
-      case PKT_SECRET_CERT:
+      case PKT_SYMKEY_ENC:
+      case PKT_PUBLIC_KEY:
+      case PKT_SECRET_KEY:
       case PKT_PUBKEY_ENC:
       case PKT_SIGNATURE:
       case PKT_COMMENT:
+      case PKT_OLD_COMMENT:
       case PKT_PLAINTEXT:
       case PKT_COMPRESSED:
       case PKT_ENCRYPTED:
@@ -227,7 +238,7 @@ parse_hash_header( const char *line )
            found |= 2;
        else if( !strncmp( s, "MD5", s2-s ) )
            found |= 4;
-       else if( !strncmp( s, "MD2", s2-s ) )
+       else if( !strncmp( s, "TIGER", s2-s ) )
            found |= 8;
        else
            return 0;
@@ -250,7 +261,8 @@ parse_hash_header( const char *line )
  */
 static fhdr_state_t
 find_header( fhdr_state_t state, byte *buf, size_t *r_buflen,
-                                       IOBUF a, size_t n, unsigned *r_empty)
+            IOBUF a, size_t n, unsigned *r_empty, int *r_hashes,
+            int only_keyblocks )
 {
     int c=0, i;
     const char *s;
@@ -263,7 +275,7 @@ find_header( fhdr_state_t state, byte *buf, size_t *r_buflen,
 
     buflen = *r_buflen;
     assert(buflen >= 100 );
-    buflen -= 3; /* reserved room for CR,LF and one extra */
+    buflen -= 4; /* reserved room for CR,LF, and two extra */
 
     do {
        switch( state ) {
@@ -271,14 +283,17 @@ find_header( fhdr_state_t state, byte *buf, size_t *r_buflen,
            /* read at least the first byte to check whether it is armored
             * or not */
            c = 0;
-           for(n=0; n < 28 && (c=iobuf_get2(a)) != -1 && c != '\n'; )
+           for(n=0; n < 28 && (c=iobuf_get(a)) != -1 && c != '\n'; )
                buf[n++] = c;
            if( !n && c == '\n' )
                state = fhdrCHECKBegin;
            else if( !n  || c == -1 )
                state = fhdrNOArmor; /* too short */
-           else if( !is_armored( buf ) )
+           else if( !is_armored( buf ) ) {
                state = fhdrNOArmor;
+               if( c == '\n' )
+                   buf[n++] = c;
+           }
            else if( c == '\n' )
                state = fhdrCHECKBegin;
            else
@@ -289,7 +304,7 @@ find_header( fhdr_state_t state, byte *buf, size_t *r_buflen,
            n = 0;
          case fhdrINITCont: /* read more stuff into buffer */
            c = 0;
-           for(; n < buflen && (c=iobuf_get2(a)) != -1 && c != '\n'; )
+           for(; n < buflen && (c=iobuf_get(a)) != -1 && c != '\n'; )
                buf[n++] = c;
            state = c == '\n' ? fhdrCHECKBegin :
                     c == -1  ? fhdrEOF : fhdrINITSkip;
@@ -299,40 +314,46 @@ find_header( fhdr_state_t state, byte *buf, size_t *r_buflen,
            if( c == '\n' )
                n = 0;
            else {
-               while( (c=iobuf_get2(a)) != -1 && c != '\n' )
+               while( (c=iobuf_get(a)) != -1 && c != '\n' )
                    ;
            }
            state =  c == -1? fhdrEOF : fhdrINIT;
            break;
 
          case fhdrSKIPHeader:
-           while( (c=iobuf_get2(a)) != -1 && c != '\n' )
+           while( (c=iobuf_get(a)) != -1 && c != '\n' )
                ;
            state =  c == -1? fhdrEOF : fhdrWAITHeader;
            break;
 
          case fhdrWAITHeader: /* wait for Header lines */
            c = 0;
-           for(n=0; n < buflen && (c=iobuf_get2(a)) != -1 && c != '\n'; )
+           for(n=0; n < buflen && (c=iobuf_get(a)) != -1 && c != '\n'; )
                buf[n++] = c;
            buf[n] = 0;
            if( n < buflen || c == '\n' ) {
                if( n && buf[0] != '\r') { /* maybe a header */
                    if( strchr( buf, ':') ) { /* yes */
+                       int hashes=0;
                        if( buf[n-1] == '\r' )
                            buf[--n] = 0;
                        if( opt.verbose ) {
-                           log_info("armor header: ");
+                           log_info(_("armor header: "));
                            print_string( stderr, buf, n, 0 );
                            putc('\n', stderr);
                        }
-                       if( clearsig && !parse_hash_header( buf ) ) {
-                           log_error("invalid clearsig header\n");
+                       if( clearsig && !(hashes=parse_hash_header( buf )) ) {
+                           log_error(_("invalid clearsig header\n"));
                            state = fhdrERROR;
                        }
-                       else
+                       else {
                            state = fhdrWAITHeader;
+                           if( r_hashes )
+                               *r_hashes |= hashes;
+                       }
                    }
+                   else if( clearsig && n > 15 && !memcmp(buf, "-----", 5 ) )
+                       state = fhdrNullClearsig;
                    else
                        state = fhdrCHECKDashEscaped3;
                }
@@ -348,7 +369,7 @@ find_header( fhdr_state_t state, byte *buf, size_t *r_buflen,
                    }
                }
                else {
-                   log_error("invalid armor header: ");
+                   log_error(_("invalid armor header: "));
                    print_string( stderr, buf, n, 0 );
                    putc('\n', stderr);
                    state = fhdrERROR;
@@ -357,7 +378,7 @@ find_header( fhdr_state_t state, byte *buf, size_t *r_buflen,
            else if( c != -1 ) {
                if( strchr( buf, ':') ) { /* buffer to short, but this is okay*/
                    if( opt.verbose ) {
-                       log_info("armor header: ");
+                       log_info(_("armor header: "));
                        print_string( stderr, buf, n, 0 );
                        fputs("[...]\n", stderr);  /* indicate it is truncated */
                    }
@@ -370,14 +391,14 @@ find_header( fhdr_state_t state, byte *buf, size_t *r_buflen,
                state = fhdrEOF;
            break;
 
-         case fhdrWAITClearsig: /* skip all empty lines (for clearsig) */
+         case fhdrWAITClearsig: /* skip the empty line (for clearsig) */
            c = 0;
-           for(n=0; n < buflen && (c=iobuf_get2(a)) != -1 && c != '\n'; )
+           for(n=0; n < buflen && (c=iobuf_get(a)) != -1 && c != '\n'; )
                buf[n++] = c;
            if( n < buflen || c == '\n' ) {
                buf[n] = 0;
-               if( !n || (buf[0]=='\r' && !buf[1]) ) /* empty line */
-                   ;
+               if( n > 15 && !memcmp(buf, "-----", 5 ) )
+                   state = fhdrNullClearsig;
                else
                    state = fhdrCHECKDashEscaped3;
            }
@@ -389,6 +410,10 @@ find_header( fhdr_state_t state, byte *buf, size_t *r_buflen,
            }
            break;
 
+         case fhdrNullClearsig: /* zero length cleartext */
+           state = fhdrENDClearsig;
+           break;
+
          case fhdrENDClearsig:
          case fhdrCHECKBegin:
            state = state == fhdrCHECKBegin ? fhdrINITSkip : fhdrERRORShow;
@@ -412,13 +437,16 @@ find_header( fhdr_state_t state, byte *buf, size_t *r_buflen,
                    break;
            if( !s )
                break; /* unknown begin line */
+           if( only_keyblocks && i != 1 && i != 5 && i != 6 )
+               break; /* not a keyblock armor */
+
            /* found the begin line */
            hdr_line = i;
            state = fhdrWAITHeader;
            if( hdr_line == BEGIN_SIGNED_MSG_IDX )
                clearsig = 1;
            if( opt.verbose > 1 )
-               log_info("armor: %s\n", head_strings[hdr_line]);
+               log_info(_("armor: %s\n"), head_strings[hdr_line]);
            break;
 
          case fhdrCLEARSIG:
@@ -428,7 +456,7 @@ find_header( fhdr_state_t state, byte *buf, size_t *r_buflen,
             * we have to look for a header line or dashed escaped text*/
            n = 0;
            c = 0;
-           while( n < buflen && (c=iobuf_get2(a)) != -1 && c != '\n' )
+           while( n < buflen && (c=iobuf_get(a)) != -1 && c != '\n' )
                buf[n++] = c;
            buf[n] = 0;
            if( c == -1 )
@@ -462,7 +490,7 @@ find_header( fhdr_state_t state, byte *buf, size_t *r_buflen,
                                 fhdrREADClearsig : fhdrTESTSpaces;
            }
            else {
-               log_error("invalid dash escaped line: ");
+               log_error(_("invalid dash escaped line: "));
                print_string( stderr, buf, n, 0 );
                putc('\n', stderr);
                state = fhdrERROR;
@@ -496,7 +524,7 @@ find_header( fhdr_state_t state, byte *buf, size_t *r_buflen,
             * for dashed escaped text of headers */
            c = 0;
            n = 0;
-           while( n < buflen && (c=iobuf_get2(a)) != -1 && c != '\n' )
+           while( n < buflen && (c=iobuf_get(a)) != -1 && c != '\n' )
                buf[n++] = c;
            buf[n] = 0;
            if( c == -1 )
@@ -510,11 +538,11 @@ find_header( fhdr_state_t state, byte *buf, size_t *r_buflen,
          case fhdrTESTSpaces: {
            /* but must check whether the rest of the line
             * only contains white spaces; this is problematic
-            * since we may have to restore the stuff.  simply
+            * since we may have to restore the stuff.  simply
             * counting spaces is not enough, because it may be a
             * mix of different white space characters */
            IOBUF b = iobuf_temp();
-           while( (c=iobuf_get2(a)) != -1 && c != '\n' ) {
+           while( (c=iobuf_get(a)) != -1 && c != '\n' ) {
                iobuf_put(b,c);
                if( c != ' ' && c != '\t' && c != '\r' )
                    break;
@@ -531,7 +559,7 @@ find_header( fhdr_state_t state, byte *buf, size_t *r_buflen,
          } break;
 
          case fhdrERRORShow:
-           log_error("invalid clear text header: ");
+           log_error(_("invalid clear text header: "));
            print_string( stderr, buf, n, 0 );
            putc('\n', stderr);
            state = fhdrERROR;
@@ -582,7 +610,6 @@ find_header( fhdr_state_t state, byte *buf, size_t *r_buflen,
        }
     }
 
-
     *r_buflen = n;
     *r_empty = empty;
     return state;
@@ -602,7 +629,9 @@ check_input( armor_filter_context_t *afx, IOBUF a )
        state = fhdrHASArmor;
 
     n = DIM(afx->helpbuf);
-    state = find_header( state, afx->helpbuf, &n, a, afx->helplen, &emplines);
+    state = find_header( state, afx->helpbuf, &n, a,
+                               afx->helplen, &emplines, &afx->hashes,
+                               afx->only_keyblocks );
     switch( state ) {
       case fhdrNOArmor:
        afx->inp_checked = 1;
@@ -618,6 +647,7 @@ check_input( armor_filter_context_t *afx, IOBUF a )
        rc = -1;
        break;
 
+      case fhdrNullClearsig:
       case fhdrCLEARSIG: /* start fake package mode (for clear signatures) */
        afx->helplen = n;
        afx->helpidx = 0;
@@ -670,7 +700,8 @@ fake_packet( armor_filter_context_t *afx, IOBUF a,
            rc = -1;
            continue;
        }
-       if( afx->helpidx < afx->helplen ) { /* flush the last buffer */
+       if( state != fhdrNullClearsig
+           && afx->helpidx < afx->helplen ) { /* flush the last buffer */
            n = afx->helplen;
            for(nn=afx->helpidx; len < size && nn < n ; nn++ )
                buf[len++] = afx->helpbuf[nn];
@@ -684,7 +715,10 @@ fake_packet( armor_filter_context_t *afx, IOBUF a,
        /* read a new one */
        n = DIM(afx->helpbuf);
        afx->helpidx = 0;
-       state = find_header( state, afx->helpbuf, &n, a, 0, &emplines );
+       state = find_header( state, afx->helpbuf, &n, a,
+                             state == fhdrNullClearsig? afx->helplen:0,
+                                               &emplines, &afx->hashes,
+                                               afx->only_keyblocks );
        switch( state) {
          case fhdrERROR:
            invalid_armor();
@@ -703,8 +737,6 @@ fake_packet( armor_filter_context_t *afx, IOBUF a,
            break;
 
          case fhdrENDClearsig:
-           assert( emplines );
-           emplines--; /* don't count the last one */
            state = fhdrENDClearsigHelp;
            afx->helplen = n;
            break;
@@ -715,8 +747,10 @@ fake_packet( armor_filter_context_t *afx, IOBUF a,
     buf[0] = (len-2) >> 8;
     buf[1] = (len-2);
     if( state == fhdrENDClearsig ) { /* write last (ending) length header */
-       buf[len++] = 0;
-       buf[len++] = 0;
+       if( buf[0] || buf[1] ) { /* write only if length of text is > 0 */
+           buf[len++] = 0;
+           buf[len++] = 0;
+       }
        rc = 0;
     }
 
@@ -757,7 +791,7 @@ radix64_read( armor_filter_context_t *afx, IOBUF a, size_t *retn,
            break;
        }
        else if( (c = asctobin[(c2=c)]) == 255 ) {
-           log_error("invalid radix64 character %02x skipped\n", c2);
+           log_error(_("invalid radix64 character %02x skipped\n"), c2);
            continue;
        }
        switch(idx) {
@@ -775,7 +809,10 @@ radix64_read( armor_filter_context_t *afx, IOBUF a, size_t *retn,
     afx->idx = idx;
     afx->radbuf[0] = val;
     if( checkcrc ) {
-       afx->inp_eof = 1; /*assume eof */
+       afx->any_data = 1;
+       afx->inp_checked=0;
+       afx->faked = 0;
+       afx->parse_state = 0;
        for(;;) { /* skip lf and pad characters */
            if( afx->helpidx < afx->helplen )
                c = afx->helpbuf[afx->helpidx++];
@@ -787,7 +824,7 @@ radix64_read( armor_filter_context_t *afx, IOBUF a, size_t *retn,
            break;
        }
        if( c == -1 )
-           log_error("premature eof (no CRC)\n");
+           log_error(_("premature eof (no CRC)\n"));
        else {
            u32 mycrc = 0;
            idx = 0;
@@ -805,13 +842,19 @@ radix64_read( armor_filter_context_t *afx, IOBUF a, size_t *retn,
                else if( (c=iobuf_get(a)) == -1 )
                    break;
            } while( ++idx < 4 );
-           if( c == -1 )
-               log_error("premature eof (in CRC)\n");
-           else if( idx != 4 )
-               log_error("malformed CRC\n");
-           else if( mycrc != afx->crc )
-               log_error("CRC error; %06lx - %06lx\n",
+           if( c == -1 ) {
+               log_error(_("premature eof (in CRC)\n"));
+               rc = G10ERR_INVALID_ARMOR;
+           }
+           else if( idx != 4 ) {
+               log_error(_("malformed CRC\n"));
+               rc = G10ERR_INVALID_ARMOR;
+           }
+           else if( mycrc != afx->crc ) {
+               log_error(_("CRC error; %06lx - %06lx\n"),
                                    (ulong)afx->crc, (ulong)mycrc);
+               rc = G10ERR_INVALID_ARMOR;
+           }
            else {
                rc = 0;
              #if 0
@@ -826,10 +869,14 @@ radix64_read( armor_filter_context_t *afx, IOBUF a, size_t *retn,
                }
                if( rc == -1 )
                    rc = 0;
-               else if( rc == 2 )
-                   log_error("premature eof (in Trailer)\n");
-               else
-                   log_error("error in trailer line\n");
+               else if( rc == 2 ) {
+                   log_error(_("premature eof (in Trailer)\n"));
+                   rc = G10ERR_INVALID_ARMOR;
+               }
+               else {
+                   log_error(_("error in trailer line\n"));
+                   rc = G10ERR_INVALID_ARMOR;
+               }
              #endif
            }
        }
@@ -842,7 +889,6 @@ radix64_read( armor_filter_context_t *afx, IOBUF a, size_t *retn,
     return rc;
 }
 
-
 /****************
  * This filter is used to handle the armor stuff
  */
@@ -879,16 +925,9 @@ armor_filter( void *opaque, int control,
        *ret_len = n;
     }
     else if( control == IOBUFCTRL_UNDERFLOW ) {
-       if( size < 30 )
+       if( size < 15+(4*15) )  /* need space for up to 4 onepass_sigs */
            BUG(); /* supplied buffer too short */
 
-       if( afx->inp_eof ) {
-           *ret_len = 0;
-           if( DBG_FILTER )
-               log_debug("armor-filter: eof due to inp_eof flag\n" );
-           return -1;
-       }
-
        if( afx->faked )
            rc = fake_packet( afx, a, &n, buf, size );
        else if( !afx->inp_checked ) {
@@ -902,27 +941,53 @@ armor_filter( void *opaque, int control,
                afx->helplen = 0;
            }
            else if( afx->faked ) {
-               /* the buffer is at least 30 bytes long, so it
+               unsigned hashes = afx->hashes;
+               /* the buffer is at least 15+n*15 bytes long, so it
                 * is easy to construct the packets */
 
-               /* first a onepass signature packet */
-               buf[0] = 0x90; /* old packet format, type 4, 1 length byte */
-               buf[1] = 13;   /* length */
-               buf[2] = 3;    /* version */
-               buf[3] = 0x01; /* sigclass 0x01 (data in canonical text mode)*/
-               buf[4] = 0;    /* digest algo (don't know) */
-               buf[5] = 0;    /* public key algo (don't know) */
-               memset(buf+6, 0, 8); /* don't know the keyid */
-               buf[14] = 1;   /* this is the last one */
+               hashes &= 1|2|4|8;
+               if( !hashes )
+                   hashes |= 4;  /* default to MD 5 */
+               n=0;
+               do {
+                   /* first some onepass signature packets */
+                   buf[n++] = 0x90; /* old format, type 4, 1 length byte */
+                   buf[n++] = 13;   /* length */
+                   buf[n++] = 3;    /* version */
+                   buf[n++] = 0x01; /* sigclass 0x01 (canonical text mode)*/
+                   if( hashes & 1 ) {
+                       hashes &= ~1;
+                       buf[n++] = DIGEST_ALGO_RMD160;
+                   }
+                   else if( hashes & 2 ) {
+                       hashes &= ~2;
+                       buf[n++] = DIGEST_ALGO_SHA1;
+                   }
+                   else if( hashes & 4 ) {
+                       hashes &= ~4;
+                       buf[n++] = DIGEST_ALGO_MD5;
+                   }
+                   else if( hashes & 8 ) {
+                       hashes &= ~8;
+                       buf[n++] = DIGEST_ALGO_TIGER;
+                   }
+                   else
+                       buf[n++] = 0;    /* (don't know) */
+
+                   buf[n++] = 0;    /* public key algo (don't know) */
+                   memset(buf+n, 0, 8); /* don't know the keyid */
+                   n += 8;
+                   buf[n++] = !hashes;   /* last one */
+               } while( hashes );
 
                /* followed by a plaintext packet */
-               buf[15] = 0xaf; /* old packet format, type 11, var length */
-               buf[16] = 0;    /* set the length header */
-               buf[17] = 6;
-               buf[18] = 't';  /* canonical text mode */
-               buf[19] = 0;    /* namelength */
-               memset(buf+20, 0, 4); /* timestamp */
-               n = 24;
+               buf[n++] = 0xaf; /* old packet format, type 11, var length */
+               buf[n++] = 0;    /* set the length header */
+               buf[n++] = 6;
+               buf[n++] = 't';  /* canonical text mode */
+               buf[n++] = 0;    /* namelength */
+               memset(buf+n, 0, 4); /* timestamp */
+               n += 4;
            }
            else if( !rc )
                rc = radix64_read( afx, a, &n, buf, size );
@@ -945,7 +1010,25 @@ armor_filter( void *opaque, int control,
            iobuf_writestr(a, "-----\n");
            iobuf_writestr(a, "Version: GNUPG v"  VERSION " ("
                                            PRINTABLE_OS_NAME ")\n");
-           iobuf_writestr(a, "Comment: This is an alpha version!\n");
+
+           if( opt.comment_string ) {
+               const char *s = opt.comment_string;
+               iobuf_writestr(a, "Comment: " );
+               for( ; *s; s++ ) {
+                   if( *s == '\n' )
+                       iobuf_writestr(a, "\\n" );
+                   else if( *s == '\r' )
+                       iobuf_writestr(a, "\\r" );
+                   else if( *s == '\v' )
+                       iobuf_writestr(a, "\\v" );
+                   else
+                       iobuf_put(a, *s );
+               }
+               iobuf_put(a, '\n' );
+           }
+           else
+               iobuf_writestr(a,
+                   "Comment: For info finger gcrypt@ftp.guug.de\n");
            if( afx->hdrlines )
                iobuf_writestr(a, afx->hdrlines);
            iobuf_put(a, '\n');
@@ -976,7 +1059,7 @@ armor_filter( void *opaque, int control,
                iobuf_put(a, c);
                c = bintoasc[radbuf[2]&077];
                iobuf_put(a, c);
-               if( ++idx2 > (72/4) ) {
+               if( ++idx2 >= (72/4) ) {
                    iobuf_put(a, '\n');
                    idx2=0;
                }
@@ -1015,10 +1098,13 @@ armor_filter( void *opaque, int control,
                    iobuf_put(a, c);
                    iobuf_put(a, '=');
                }
-               ++idx2;
+               if( ++idx2 >= (72/4) ) {
+                   iobuf_put(a, '\n');
+                   idx2=0;
+               }
            }
            /* may need a linefeed */
-           if( idx2 < (72/4) )
+           if( idx2 )
                iobuf_put(a, '\n');
            /* write the CRC */
            iobuf_put(a, '=');
@@ -1041,6 +1127,8 @@ armor_filter( void *opaque, int control,
            iobuf_writestr(a, tail_strings[afx->what] );
            iobuf_writestr(a, "-----\n");
        }
+       else if( !afx->any_data && !afx->inp_bypass )
+           log_error(_("no valid RFC1991 or OpenPGP data found.\n"));
     }
     else if( control == IOBUFCTRL_DESC )
        *(char**)buf = "armor_filter";