textual changes
[gnupg.git] / g10 / armor.c
index 1b31f62..82b240f 100644 (file)
@@ -1,14 +1,14 @@
 /* armor.c - Armor filter
- *     Copyright (c) 1997 by Werner Koch (dd9jn)
+ *     Copyright (C) 1998 Free Software Foundation, Inc.
  *
- * This file is part of G10.
+ * This file is part of GNUPG.
  *
- * G10 is free software; you can redistribute it and/or modify
+ * GNUPG is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
  * the Free Software Foundation; either version 2 of the License, or
  * (at your option) any later version.
  *
- * G10 is distributed in the hope that it will be useful,
+ * GNUPG is distributed in the hope that it will be useful,
  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  * GNU General Public License for more details.
@@ -36,8 +36,6 @@
 #include "status.h"
 
 
-
-
 #define CRCINIT 0xB704CE
 #define CRCPOLY 0X864CFB
 #define CRCUPDATE(a,c) do {                                                \
@@ -82,25 +80,32 @@ typedef enum {
 
 
 /* if we encounter this armor string with this index, go
- * into a mode, which fakes packets and wait for the next armor */
+ * into a mode which fakes packets and wait for the next armor */
 #define BEGIN_SIGNED_MSG_IDX 3
 static char *head_strings[] = {
     "BEGIN PGP MESSAGE",
     "BEGIN PGP PUBLIC KEY BLOCK",
     "BEGIN PGP SIGNATURE",
     "BEGIN PGP SIGNED MESSAGE",
+    "BEGIN PGP ARMORED FILE",
+    "BEGIN PGP SECRET KEY BLOCK",
     NULL
 };
 static char *tail_strings[] = {
     "END PGP MESSAGE",
     "END PGP PUBLIC KEY BLOCK",
     "END PGP SIGNATURE",
+    "END dummy",
+    "END PGP ARMORED FILE",
+    "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 );
 
 
 static void
@@ -135,7 +140,7 @@ initialize(void)
 }
 
 /****************
- * Check wether this is a armored file or not
+ * Check whether this is an armored file or not
  * See also parse-packet.c for details on this code
  * Returns: True if it seems to be armored
  */
@@ -149,8 +154,9 @@ is_armored( byte *buf )
        return 1; /* invalid packet: assume it is armored */
     pkttype =  ctb & 0x40 ? (ctb & 0x3f) : ((ctb>>2)&0xf);
     switch( pkttype ) {
-      case PKT_PUBLIC_CERT:
-      case PKT_SECRET_CERT:
+      case PKT_MARKER:
+      case PKT_PUBLIC_KEY:
+      case PKT_SECRET_KEY:
       case PKT_PUBKEY_ENC:
       case PKT_SIGNATURE:
       case PKT_COMMENT:
@@ -163,6 +169,29 @@ is_armored( byte *buf )
     return 1;
 }
 
+
+/****************
+ * Try to check whether the iobuf is armored
+ * Returns true if this may be the case; the caller should use the
+ *        filter to do further processing.
+ */
+int
+use_armor_filter( IOBUF a )
+{
+    byte buf[1];
+    int n;
+
+    n = iobuf_peek(a, buf, 1 );
+    if( n == -1 )
+       return 0; /* EOF, doesn't matter whether armored or not */
+    if( !n )
+       return 1; /* can't check it: try armored */
+    return is_armored(buf);
+}
+
+
+
+
 static void
 invalid_armor(void)
 {
@@ -172,11 +201,11 @@ invalid_armor(void)
 
 
 /****************
- * check wether the armor header is valid on a signed message.
+ * check whether the armor header is valid on a signed message.
  * this is for security reasons: the header lines are not included in the
  * hash and by using some creative formatting rules, Mallory could fake
  * any text at the beginning of a document; assuming it is read with
- * a simple viewer. We do only allow the Hash Header.
+ * a simple viewer. We only allow the Hash Header.
  */
 static int
 parse_hash_header( const char *line )
@@ -202,7 +231,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;
@@ -225,9 +254,9 @@ 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 c, i;
+    int c=0, i;
     const char *s;
     byte *p;
     size_t buflen;
@@ -243,12 +272,14 @@ find_header( fhdr_state_t state, byte *buf, size_t *r_buflen,
     do {
        switch( state ) {
          case fhdrHASArmor:
-           /* read at least the first byte to check wether it is armored
+           /* 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'; )
                buf[n++] = c;
-           if( !n  || c == -1 )
+           if( !n && c == '\n' )
+               state = fhdrCHECKBegin;
+           else if( !n  || c == -1 )
                state = fhdrNOArmor; /* too short */
            else if( !is_armored( buf ) )
                state = fhdrNOArmor;
@@ -269,8 +300,12 @@ find_header( fhdr_state_t state, byte *buf, size_t *r_buflen,
            break;
 
          case fhdrINITSkip:
-           while( (c=iobuf_get2(a)) != -1 && c != '\n' )
-               ;
+           if( c == '\n' )
+               n = 0;
+           else {
+               while( (c=iobuf_get2(a)) != -1 && c != '\n' )
+                   ;
+           }
            state =  c == -1? fhdrEOF : fhdrINIT;
            break;
 
@@ -288,17 +323,23 @@ find_header( fhdr_state_t state, byte *buf, size_t *r_buflen,
            if( n < buflen || c == '\n' ) {
                if( n && buf[0] != '\r') { /* maybe a header */
                    if( strchr( buf, ':') ) { /* yes */
+                       int hashes;
+                       if( buf[n-1] == '\r' )
+                           buf[--n] = 0;
                        if( opt.verbose ) {
                            log_info("armor header: ");
-                           print_string( stderr, buf, n );
+                           print_string( stderr, buf, n, 0 );
                            putc('\n', stderr);
                        }
-                       if( clearsig && !parse_hash_header( buf ) ) {
+                       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
                        state = fhdrCHECKDashEscaped3;
@@ -316,7 +357,7 @@ find_header( fhdr_state_t state, byte *buf, size_t *r_buflen,
                }
                else {
                    log_error("invalid armor header: ");
-                   print_string( stderr, buf, n );
+                   print_string( stderr, buf, n, 0 );
                    putc('\n', stderr);
                    state = fhdrERROR;
                }
@@ -325,7 +366,7 @@ find_header( fhdr_state_t state, byte *buf, size_t *r_buflen,
                if( strchr( buf, ':') ) { /* buffer to short, but this is okay*/
                    if( opt.verbose ) {
                        log_info("armor header: ");
-                       print_string( stderr, buf, n );
+                       print_string( stderr, buf, n, 0 );
                        fputs("[...]\n", stderr);  /* indicate it is truncated */
                    }
                    state = fhdrSKIPHeader;  /* skip rest of line */
@@ -349,8 +390,8 @@ find_header( fhdr_state_t state, byte *buf, size_t *r_buflen,
                    state = fhdrCHECKDashEscaped3;
            }
            else {
-               /* fixme: we should check wether this line continues
-                *   it is poosible that we have only read ws until here
+               /* fixme: we should check whether this line continues
+                *   it is possible that we have only read ws until here
                 *   and more stuff is to come */
                state = fhdrEOF;
            }
@@ -392,7 +433,7 @@ find_header( fhdr_state_t state, byte *buf, size_t *r_buflen,
          case fhdrEMPTYClearsig:
          case fhdrREADClearsig:
            /* we are at the start of a line: read a clearsig into the buffer
-            * we have to look for a the header line or dashed escaped text*/
+            * 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' )
@@ -430,7 +471,7 @@ find_header( fhdr_state_t state, byte *buf, size_t *r_buflen,
            }
            else {
                log_error("invalid dash escaped line: ");
-               print_string( stderr, buf, n );
+               print_string( stderr, buf, n, 0 );
                putc('\n', stderr);
                state = fhdrERROR;
            }
@@ -475,11 +516,11 @@ find_header( fhdr_state_t state, byte *buf, size_t *r_buflen,
            break;
 
          case fhdrTESTSpaces: {
-           /* but must check wether the rest of the line
-            * does only contain white spaces; this is problematic
-            * since we may have to restore the stuffs. simply
+           /* 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
             * counting spaces is not enough, because it may be a
-            * mix of different white space chacters */
+            * mix of different white space characters */
            IOBUF b = iobuf_temp();
            while( (c=iobuf_get2(a)) != -1 && c != '\n' ) {
                iobuf_put(b,c);
@@ -499,7 +540,7 @@ find_header( fhdr_state_t state, byte *buf, size_t *r_buflen,
 
          case fhdrERRORShow:
            log_error("invalid clear text header: ");
-           print_string( stderr, buf, n );
+           print_string( stderr, buf, n, 0 );
            putc('\n', stderr);
            state = fhdrERROR;
            break;
@@ -556,7 +597,7 @@ find_header( fhdr_state_t state, byte *buf, size_t *r_buflen,
 }
 
 
-/* figure out wether the data is armored or not */
+/* figure out whether the data is armored or not */
 static int
 check_input( armor_filter_context_t *afx, IOBUF a )
 {
@@ -569,7 +610,8 @@ 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);
     switch( state ) {
       case fhdrNOArmor:
        afx->inp_checked = 1;
@@ -620,8 +662,6 @@ fake_packet( armor_filter_context_t *afx, IOBUF a,
     fhdr_state_t state = afx->parse_state;
     unsigned emplines = afx->empty;
 
-    size = 100; /* FIXME: only used for testing (remove it)  */
-
     len = 2;   /* reserve 2 bytes for the length header */
     size -= 3; /* and 1 for empline handling and 2 for the term header */
     while( !rc && len < size ) {
@@ -653,7 +693,8 @@ 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, 0,
+                                               &emplines, &afx->hashes );
        switch( state) {
          case fhdrERROR:
            invalid_armor();
@@ -672,8 +713,13 @@ fake_packet( armor_filter_context_t *afx, IOBUF a,
            break;
 
          case fhdrENDClearsig:
-           assert( emplines );
-           emplines--; /* don't count the last one */
+           /* FIXME: this is wrong: Only the last CRLF should
+            * not be included in the hash, muts rewrite the FSM again
+            * This proble does only occur if the last line does not end
+            * in with a LF?
+            */
+           if( emplines )
+               emplines--; /* don't count the last one */
            state = fhdrENDClearsigHelp;
            afx->helplen = n;
            break;
@@ -813,7 +859,7 @@ radix64_read( armor_filter_context_t *afx, IOBUF a, size_t *retn,
 
 
 /****************
- * The filter is used to handle the armor stuff
+ * This filter is used to handle the armor stuff
  */
 int
 armor_filter( void *opaque, int control,
@@ -848,7 +894,7 @@ 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 ) {
@@ -871,27 +917,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 forma, 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 );
@@ -912,8 +984,12 @@ armor_filter( void *opaque, int control,
            iobuf_writestr(a, "-----");
            iobuf_writestr(a, head_strings[afx->what] );
            iobuf_writestr(a, "-----\n");
-           iobuf_writestr(a, "Version: G10 pre-release "  VERSION "\n");
-           iobuf_writestr(a, "Comment: This is an alpha test version!\n\n");
+           iobuf_writestr(a, "Version: GNUPG v"  VERSION " ("
+                                           PRINTABLE_OS_NAME ")\n");
+           iobuf_writestr(a, "Comment: This is an alpha version!\n");
+           if( afx->hdrlines )
+               iobuf_writestr(a, afx->hdrlines);
+           iobuf_put(a, '\n');
            afx->status++;
            afx->idx = 0;
            afx->idx2 = 0;
@@ -941,7 +1017,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;
                }
@@ -980,10 +1056,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, '=');
@@ -1012,3 +1091,5 @@ armor_filter( void *opaque, int control,
     return rc;
 }
 
+
+