See ChangeLog: Tue Aug 31 17:20:44 CEST 1999 Werner Koch
[gnupg.git] / g10 / build-packet.c
index 1ddb420..4049f37 100644 (file)
@@ -1,14 +1,14 @@
 /* build-packet.c - assemble packets and write them
  *     Copyright (C) 1998 Free Software Foundation, Inc.
  *
- * This file is part of GNUPG.
+ * This file is part of GnuPG.
  *
- * GNUPG 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.
  *
- * GNUPG 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.
@@ -43,11 +43,12 @@ static int do_pubkey_enc( IOBUF out, int ctb, PKT_pubkey_enc *enc );
 static u32 calc_plaintext( PKT_plaintext *pt );
 static int do_plaintext( IOBUF out, int ctb, PKT_plaintext *pt );
 static int do_encrypted( IOBUF out, int ctb, PKT_encrypted *ed );
+static int do_encrypted_mdc( IOBUF out, int ctb, PKT_encrypted *ed );
 static int do_compressed( IOBUF out, int ctb, PKT_compressed *cd );
 static int do_signature( IOBUF out, int ctb, PKT_signature *sig );
 static int do_onepass_sig( IOBUF out, int ctb, PKT_onepass_sig *ops );
 
-static int calc_header_length( u32 len );
+static int calc_header_length( u32 len, int new_ctb );
 static int write_16(IOBUF inp, u16 a);
 static int write_32(IOBUF inp, u32 a);
 static int write_header( IOBUF out, int ctb, u32 len );
@@ -74,7 +75,8 @@ build_packet( IOBUF out, PACKET *pkt )
     switch( pkt->pkttype ) {
       case PKT_OLD_COMMENT: pkt->pkttype = PKT_COMMENT; break;
       case PKT_PLAINTEXT: new_ctb = pkt->pkt.plaintext->new_ctb; break;
-      case PKT_ENCRYPTED: new_ctb = pkt->pkt.encrypted->new_ctb; break;
+      case PKT_ENCRYPTED:
+      case PKT_ENCRYPTED_MDC: new_ctb = pkt->pkt.encrypted->new_ctb; break;
       case PKT_COMPRESSED:new_ctb = pkt->pkt.compressed->new_ctb; break;
       default: break;
     }
@@ -110,6 +112,9 @@ build_packet( IOBUF out, PACKET *pkt )
       case PKT_ENCRYPTED:
        rc = do_encrypted( out, ctb, pkt->pkt.encrypted );
        break;
+      case PKT_ENCRYPTED_MDC:
+       rc = do_encrypted_mdc( out, ctb, pkt->pkt.encrypted );
+       break;
       case PKT_COMPRESSED:
        rc = do_compressed( out, ctb, pkt->pkt.compressed );
        break;
@@ -120,6 +125,7 @@ build_packet( IOBUF out, PACKET *pkt )
        rc = do_onepass_sig( out, ctb, pkt->pkt.onepass_sig );
        break;
       case PKT_RING_TRUST:
+       break; /* ignore it */
       default:
        log_bug("invalid packet type in build_packet()\n");
        break;
@@ -135,11 +141,13 @@ u32
 calc_packet_length( PACKET *pkt )
 {
     u32 n=0;
+    int new_ctb = 0;
 
     assert( pkt->pkt.generic );
     switch( pkt->pkttype ) {
       case PKT_PLAINTEXT:
        n = calc_plaintext( pkt->pkt.plaintext );
+       new_ctb = pkt->pkt.plaintext->new_ctb;
        break;
       case PKT_USER_ID:
       case PKT_COMMENT:
@@ -156,20 +164,20 @@ calc_packet_length( PACKET *pkt )
        log_bug("invalid packet type in calc_packet_length()");
        break;
     }
-    n += calc_header_length(n);
+
+    n += calc_header_length(n, new_ctb);
     return n;
 }
 
 static void
 write_fake_data( IOBUF out, MPI a )
 {
-    byte *s;
-    u16 len;
-
     if( a ) {
-       s = (byte*)a;
-       len = (s[0] << 8) | s[1];
-       iobuf_write( out, s+2, len );
+       int i;
+       void *p;
+
+       p = mpi_get_opaque( a, &i );
+       iobuf_write( out, p, i );
     }
 }
 
@@ -206,8 +214,14 @@ do_public_key( IOBUF out, int ctb, PKT_public_key *pk )
     else
        iobuf_put( a, pk->version );
     write_32(a, pk->timestamp );
-    if( pk->version < 4 )
-       write_16(a, pk->valid_days );
+    if( pk->version < 4 ) {
+       u16 ndays;
+       if( pk->expiredate )
+           ndays = (u16)((pk->expiredate - pk->timestamp) / 86400L);
+       else
+           ndays = 0;
+       write_16(a, ndays );
+    }
     iobuf_put(a, pk->pubkey_algo );
     n = pubkey_get_npkey( pk->pubkey_algo );
     if( !n )
@@ -232,6 +246,8 @@ hash_public_key( MD_HANDLE md, PKT_public_key *pk )
 {
     PACKET pkt;
     int rc = 0;
+    int ctb;
+    ulong pktlen;
     int c;
     IOBUF a = iobuf_temp();
   #if 0
@@ -247,6 +263,42 @@ hash_public_key( MD_HANDLE md, PKT_public_key *pk )
     pkt.pkt.public_key = pk;
     if( (rc = build_packet( a, &pkt )) )
        log_fatal("build public_key for hashing failed: %s\n", g10_errstr(rc));
+
+    if( !(pk->version == 3 && pk->pubkey_algo == 16) ) {
+       /* skip the constructed header but don't do this for our very old
+        * v3 ElG keys */
+       ctb = iobuf_get_noeof(a);
+       pktlen = 0;
+       if( (ctb & 0x40) ) {
+           c = iobuf_get_noeof(a);
+           if( c < 192 )
+               pktlen = c;
+           else if( c < 224 ) {
+               pktlen = (c - 192) * 256;
+               c = iobuf_get_noeof(a);
+               pktlen += c + 192;
+           }
+           else if( c == 255 ) {
+               pktlen  = iobuf_get_noeof(a) << 24;
+               pktlen |= iobuf_get_noeof(a) << 16;
+               pktlen |= iobuf_get_noeof(a) << 8;
+               pktlen |= iobuf_get_noeof(a);
+           }
+       }
+       else {
+           int lenbytes = ((ctb&3)==3)? 0 : (1<<(ctb & 3));
+           for( ; lenbytes; lenbytes-- ) {
+               pktlen <<= 8;
+               pktlen |= iobuf_get_noeof(a);
+           }
+       }
+       /* hash a header */
+       md_putc( md, 0x99 );
+       pktlen &= 0xffff; /* can't handle longer packets */
+       md_putc( md, pktlen >> 8 );
+       md_putc( md, pktlen & 0xff );
+    }
+    /* hash the packet body */
     while( (c=iobuf_get(a)) != -1 ) {
       #if 0
        fprintf( fp," %02x", c );
@@ -277,8 +329,14 @@ do_secret_key( IOBUF out, int ctb, PKT_secret_key *sk )
     else
        iobuf_put( a, sk->version );
     write_32(a, sk->timestamp );
-    if( sk->version < 4 )
-       write_16(a, sk->valid_days );
+    if( sk->version < 4 ) {
+       u16 ndays;
+       if( sk->expiredate )
+           ndays = (u16)((sk->expiredate - sk->timestamp) / 86400L);
+       else
+           ndays = 0;
+       write_16(a, 0 );
+    }
     iobuf_put(a, sk->pubkey_algo );
     nskey = pubkey_get_nskey( sk->pubkey_algo );
     npkey = pubkey_get_npkey( sk->pubkey_algo );
@@ -291,9 +349,10 @@ do_secret_key( IOBUF out, int ctb, PKT_secret_key *sk )
     for(i=0; i < npkey; i++ )
        mpi_write(a, sk->skey[i] );
     if( sk->is_protected ) {
-       if( is_RSA(sk->pubkey_algo) && sk->version < 4 ) {
+       if( is_RSA(sk->pubkey_algo) && sk->version < 4
+                                   && !sk->protect.s2k.mode ) {
            iobuf_put(a, sk->protect.algo );
-           iobuf_write(a, sk->protect.iv, 8 );
+           iobuf_write(a, sk->protect.iv, sk->protect.ivlen );
        }
        else {
            iobuf_put(a, 0xff );
@@ -305,14 +364,16 @@ do_secret_key( IOBUF out, int ctb, PKT_secret_key *sk )
                iobuf_write(a, sk->protect.s2k.salt, 8 );
            if( sk->protect.s2k.mode == 3 )
                iobuf_put(a, sk->protect.s2k.count );
-           iobuf_write(a, sk->protect.iv, 8 );
+           iobuf_write(a, sk->protect.iv, sk->protect.ivlen );
        }
     }
     else
        iobuf_put(a, 0 );
-    if( sk->is_protected && sk->version >= 4
-                        && !(opt.emulate_bugs & EMUBUG_ENCR_MPI) ) {
-       BUG();
+    if( sk->is_protected && sk->version >= 4 ) {
+       byte *p;
+       assert( mpi_is_opaque( sk->skey[npkey] ) );
+       p = mpi_get_opaque( sk->skey[npkey], &i );
+       iobuf_write(a, p, i );
     }
     else {
        for(   ; i < nskey; i++ )
@@ -371,8 +432,14 @@ do_pubkey_enc( IOBUF out, int ctb, PKT_pubkey_enc *enc )
     IOBUF a = iobuf_temp();
 
     write_version( a, ctb );
-    write_32(a, enc->keyid[0] );
-    write_32(a, enc->keyid[1] );
+    if( enc->throw_keyid ) {
+       write_32(a, 0 );  /* don't tell Eve who can decrypt the message */
+       write_32(a, 0 );
+    }
+    else {
+       write_32(a, enc->keyid[0] );
+       write_32(a, enc->keyid[1] );
+    }
     iobuf_put(a,enc->pubkey_algo );
     n = pubkey_get_nenc( enc->pubkey_algo );
     if( !n )
@@ -402,7 +469,7 @@ do_plaintext( IOBUF out, int ctb, PKT_plaintext *pt )
 {
     int i, rc = 0;
     u32 n;
-    byte buf[1000]; /* FIXME: this buffer has the plaintext! */
+    byte buf[1000]; /* this buffer has the plaintext! */
     int nbytes;
 
     write_header(out, ctb, calc_plaintext( pt ) );
@@ -448,6 +515,24 @@ do_encrypted( IOBUF out, int ctb, PKT_encrypted *ed )
 }
 
 static int
+do_encrypted_mdc( IOBUF out, int ctb, PKT_encrypted *ed )
+{
+    int rc = 0;
+    u32 n;
+
+    assert( ed->mdc_method );
+
+    n = ed->len ? (ed->len + 10) : 0;
+    write_header(out, ctb, n );
+    iobuf_put(out, 1 );  /* version */
+    iobuf_put(out, ed->mdc_method );
+
+    /* This is all. The caller has to write the real data */
+
+    return rc;
+}
+
+static int
 do_compressed( IOBUF out, int ctb, PKT_compressed *cd )
 {
     int rc = 0;
@@ -499,6 +584,7 @@ find_subpkt( byte *buffer, sigsubpkttype_t reqtype,
            if( buflen < 2 )
                break;
            n = (( n - 192 ) << 8) + *buffer + 192;
+           buffer++;
            buflen--;
        }
        if( buflen < n )
@@ -525,7 +611,7 @@ find_subpkt( byte *buffer, sigsubpkttype_t reqtype,
 
 /****************
  * Create or update a signature subpacket for SIG of TYPE.
- * This functions know, where to put the data (hashed or unhashed).
+ * This functions knows where to put the data (hashed or unhashed).
  * The function may move data from the unhased part to the hashed one.
  * Note: All pointers into sig->[un]hashed are not valid after a call
  * to this function.  The data to but into the subpaket should be
@@ -537,21 +623,29 @@ build_sig_subpkt( PKT_signature *sig, sigsubpkttype_t type,
 {
 
     byte *data;
-    size_t hlen, dlen;
-    int found, hashed, realloced;
+    size_t hlen, dlen, nlen;
+    int found=0;
+    int critical, hashed, realloced;
     size_t n, n0;
 
-    if( (data = find_subpkt( sig->hashed_data, type, &hlen, &dlen )) )
+    critical = (type & SIGSUBPKT_FLAG_CRITICAL);
+    type &= ~SIGSUBPKT_FLAG_CRITICAL;
+
+    if( type == SIGSUBPKT_NOTATION )
+       ; /* we allow multiple packets */
+    else if( (data = find_subpkt( sig->hashed_data, type, &hlen, &dlen )) )
        found = 1;
     else if( (data = find_subpkt( sig->unhashed_data, type, &hlen, &dlen )))
        found = 2;
-    else
-       found = 0;
 
     if( found )
        log_bug("build_sig_packet: update nyi\n");
-    if( buflen+1 >= 192 )
-       log_bug("build_sig_packet: long subpackets are nyi\n");
+    if( (buflen+1) >= 8384 )
+       nlen = 5;
+    else if( (buflen+1) >= 192 )
+       nlen = 2;
+    else
+       nlen = 1;
 
     switch( type ) {
       case SIGSUBPKT_SIG_CREATED:
@@ -561,6 +655,8 @@ build_sig_subpkt( PKT_signature *sig, sigsubpkttype_t type,
       case SIGSUBPKT_PREF_COMPR:
       case SIGSUBPKT_KS_FLAGS:
       case SIGSUBPKT_KEY_EXPIRE:
+      case SIGSUBPKT_NOTATION:
+      case SIGSUBPKT_POLICY:
               hashed = 1; break;
       default: hashed = 0; break;
     }
@@ -568,7 +664,7 @@ build_sig_subpkt( PKT_signature *sig, sigsubpkttype_t type,
     if( hashed ) {
        n0 = sig->hashed_data ? ((*sig->hashed_data << 8)
                                    | sig->hashed_data[1]) : 0;
-       n = n0 + 1 + 1 + buflen; /* length, type, buffer */
+       n = n0 + nlen + 1 + buflen; /* length, type, buffer */
        realloced = !!sig->hashed_data;
        data = sig->hashed_data ? m_realloc( sig->hashed_data, n+2 )
                                : m_alloc( n+2 );
@@ -576,17 +672,37 @@ build_sig_subpkt( PKT_signature *sig, sigsubpkttype_t type,
     else {
        n0 = sig->unhashed_data ? ((*sig->unhashed_data << 8)
                                      | sig->unhashed_data[1]) : 0;
-       n = n0 + 1 + 1 + buflen; /* length, type, buffer */
+       n = n0 + nlen + 1 + buflen; /* length, type, buffer */
        realloced = !!sig->unhashed_data;
        data = sig->unhashed_data ? m_realloc( sig->unhashed_data, n+2 )
                                  : m_alloc( n+2 );
     }
 
+    if( critical )
+       type |= SIGSUBPKT_FLAG_CRITICAL;
+
     data[0] = (n >> 8) & 0xff;
     data[1] = n & 0xff;
-    data[n0+2] = buflen+1;
-    data[n0+3] = type;
-    memcpy(data+n0+4, buffer, buflen );
+    if( nlen == 5 ) {
+       data[n0+2] = 255;
+       data[n0+3] = (buflen+1) >> 24;
+       data[n0+4] = (buflen+1) >> 16;
+       data[n0+5] = (buflen+1) >>  8;
+       data[n0+6] = (buflen+1);
+       data[n0+7] = type;
+       memcpy(data+n0+8, buffer, buflen );
+    }
+    else if( nlen == 2 ) {
+       data[n0+2] = (buflen+1-192) / 256 + 192;
+       data[n0+3] = (buflen+1-192) & 256;
+       data[n0+4] = type;
+       memcpy(data+n0+5, buffer, buflen );
+    }
+    else {
+       data[n0+2] = buflen+1;
+       data[n0+3] = type;
+       memcpy(data+n0+4, buffer, buflen );
+    }
 
     if( hashed ) {
        if( !realloced )
@@ -600,7 +716,6 @@ build_sig_subpkt( PKT_signature *sig, sigsubpkttype_t type,
     }
 }
 
-
 /****************
  * Put all the required stuff from SIG into subpackets of sig.
  */
@@ -653,20 +768,20 @@ do_signature( IOBUF out, int ctb, PKT_signature *sig )
     iobuf_put(a, sig->pubkey_algo );
     iobuf_put(a, sig->digest_algo );
     if( sig->version >= 4 ) {
-       size_t n;
+       size_t nn;
        /* timestamp and keyid must have been packed into the
         * subpackets prior to the call of this function, because
         * these subpackets are hashed */
-       n = sig->hashed_data?((sig->hashed_data[0]<<8)
-                             |sig->hashed_data[1])   :0;
-       write_16(a, n);
-       if( n )
-           iobuf_write( a, sig->hashed_data+2, n );
-       n = sig->unhashed_data?((sig->unhashed_data[0]<<8)
-                               |sig->unhashed_data[1])   :0;
-       write_16(a, n);
-       if( n )
-           iobuf_write( a, sig->unhashed_data+2, n );
+       nn = sig->hashed_data?((sig->hashed_data[0]<<8)
+                               |sig->hashed_data[1])   :0;
+       write_16(a, nn);
+       if( nn )
+           iobuf_write( a, sig->hashed_data+2, nn );
+       nn = sig->unhashed_data?((sig->unhashed_data[0]<<8)
+                                 |sig->unhashed_data[1])   :0;
+       write_16(a, nn);
+       if( nn )
+           iobuf_write( a, sig->unhashed_data+2, nn );
     }
     iobuf_put(a, sig->digest_start[0] );
     iobuf_put(a, sig->digest_start[1] );
@@ -736,16 +851,25 @@ write_32(IOBUF out, u32 a)
  * calculate the length of a header
  */
 static int
-calc_header_length( u32 len )
+calc_header_length( u32 len, int new_ctb )
 {
     if( !len )
        return 1; /* only the ctb */
-    else if( len < 256 )
+
+    if( new_ctb ) {
+       if( len < 192 )
+           return 2;
+       if( len < 8384 )
+           return 3;
+       else
+           return 6;
+    }
+    if( len < 256 )
        return 2;
-    else if( len < 65536 )
+    if( len < 65536 )
        return 3;
-    else
-       return 5;
+
+    return 5;
 }
 
 /****************