See ChangeLog: Wed Dec 23 13:34:22 CET 1998 Werner Koch
[gnupg.git] / g10 / build-packet.c
index 3849575..5b952f1 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.
@@ -36,8 +36,8 @@
 
 static int do_comment( IOBUF out, int ctb, PKT_comment *rem );
 static int do_user_id( IOBUF out, int ctb, PKT_user_id *uid );
-static int do_public_cert( IOBUF out, int ctb, PKT_public_cert *pk );
-static int do_secret_cert( IOBUF out, int ctb, PKT_secret_cert *pk );
+static int do_public_key( IOBUF out, int ctb, PKT_public_key *pk );
+static int do_secret_key( IOBUF out, int ctb, PKT_secret_key *pk );
 static int do_symkey_enc( IOBUF out, int ctb, PKT_symkey_enc *enc );
 static int do_pubkey_enc( IOBUF out, int ctb, PKT_pubkey_enc *enc );
 static u32 calc_plaintext( PKT_plaintext *pt );
@@ -47,10 +47,11 @@ 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 );
+static int write_sign_packet_header( IOBUF out, int ctb, u32 len );
 static int write_header2( IOBUF out, int ctb, u32 len, int hdrlen, int blkmode );
 static int write_new_header( IOBUF out, int ctb, u32 len, int hdrlen );
 static int write_version( IOBUF out, int ctb );
@@ -64,12 +65,21 @@ static int write_version( IOBUF out, int ctb );
 int
 build_packet( IOBUF out, PACKET *pkt )
 {
-    int rc=0, ctb;
+    int new_ctb=0, rc=0, ctb;
 
     if( DBG_PACKET )
        log_debug("build_packet() type=%d\n", pkt->pkttype );
     assert( pkt->pkt.generic );
-    if( pkt->pkttype > 15 ) /* new format */
+
+    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_COMPRESSED:new_ctb = pkt->pkt.compressed->new_ctb; break;
+      default: break;
+    }
+
+    if( new_ctb || pkt->pkttype > 15 ) /* new format */
        ctb = 0xc0 | (pkt->pkttype & 0x3f);
     else
        ctb = 0x80 | ((pkt->pkttype & 15)<<2);
@@ -80,11 +90,13 @@ build_packet( IOBUF out, PACKET *pkt )
       case PKT_COMMENT:
        rc = do_comment( out, ctb, pkt->pkt.comment );
        break;
-      case PKT_PUBLIC_CERT:
-       rc = do_public_cert( out, ctb, pkt->pkt.public_cert );
+      case PKT_PUBLIC_SUBKEY:
+      case PKT_PUBLIC_KEY:
+       rc = do_public_key( out, ctb, pkt->pkt.public_key );
        break;
-      case PKT_SECRET_CERT:
-       rc = do_secret_cert( out, ctb, pkt->pkt.secret_cert );
+      case PKT_SECRET_SUBKEY:
+      case PKT_SECRET_KEY:
+       rc = do_secret_key( out, ctb, pkt->pkt.secret_key );
        break;
       case PKT_SYMKEY_ENC:
        rc = do_symkey_enc( out, ctb, pkt->pkt.symkey_enc );
@@ -109,7 +121,7 @@ build_packet( IOBUF out, PACKET *pkt )
        break;
       case PKT_RING_TRUST:
       default:
-       log_bug("invalid packet type in build_packet()");
+       log_bug("invalid packet type in build_packet()\n");
        break;
     }
 
@@ -123,16 +135,18 @@ 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:
-      case PKT_PUBLIC_CERT:
-      case PKT_SECRET_CERT:
+      case PKT_PUBLIC_KEY:
+      case PKT_SECRET_KEY:
       case PKT_SYMKEY_ENC:
       case PKT_PUBKEY_ENC:
       case PKT_ENCRYPTED:
@@ -144,10 +158,24 @@ 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 );
+    }
+}
+
 
 static int
 do_comment( IOBUF out, int ctb, PKT_comment *rem )
@@ -170,44 +198,36 @@ do_user_id( IOBUF out, int ctb, PKT_user_id *uid )
 }
 
 static int
-do_public_cert( IOBUF out, int ctb, PKT_public_cert *pkc )
+do_public_key( IOBUF out, int ctb, PKT_public_key *pk )
 {
     int rc = 0;
+    int n, i;
     IOBUF a = iobuf_temp();
 
-    if( !pkc->version )
+    if( !pk->version )
        iobuf_put( a, 3 );
     else
-       iobuf_put( a, pkc->version );
-    write_32(a, pkc->timestamp );
-    if( pkc->version < 4 )
-       write_16(a, pkc->valid_days );
-    iobuf_put(a, pkc->pubkey_algo );
-    if( pkc->pubkey_algo == PUBKEY_ALGO_ELGAMAL ) {
-       mpi_write(a, pkc->d.elg.p );
-       mpi_write(a, pkc->d.elg.g );
-       mpi_write(a, pkc->d.elg.y );
-    }
-    else if( pkc->pubkey_algo == PUBKEY_ALGO_DSA ) {
-       mpi_write(a, pkc->d.dsa.p );
-       mpi_write(a, pkc->d.dsa.q );
-       mpi_write(a, pkc->d.dsa.g );
-       mpi_write(a, pkc->d.dsa.y );
-    }
-    else if( pkc->pubkey_algo == PUBKEY_ALGO_RSA ) {
-       mpi_write(a, pkc->d.rsa.n );
-       mpi_write(a, pkc->d.rsa.e );
-    }
-    else {
-       rc = G10ERR_PUBKEY_ALGO;
-       goto leave;
+       iobuf_put( a, pk->version );
+    write_32(a, pk->timestamp );
+    if( pk->version < 4 ) {
+       u16 ndays;
+       if( pk->expiredate )
+           ndays = (u16)((pk->expiredate - pk->timestamp) / 86400L);
+       else
+           ndays = 0;
+       write_16(a, ndays );
     }
-
-    write_header2(out, ctb, iobuf_get_temp_length(a), pkc->hdrbytes, 1 );
+    iobuf_put(a, pk->pubkey_algo );
+    n = pubkey_get_npkey( pk->pubkey_algo );
+    if( !n )
+       write_fake_data( a, pk->pkey[0] );
+    for(i=0; i < n; i++ )
+       mpi_write(a, pk->pkey[i] );
+
+    write_header2(out, ctb, iobuf_get_temp_length(a), pk->hdrbytes, 1 );
     if( iobuf_write_temp( out, a ) )
        rc = G10ERR_WRITE_FILE;
 
-  leave:
     iobuf_close(a);
     return rc;
 }
@@ -217,25 +237,25 @@ do_public_cert( IOBUF out, int ctb, PKT_public_cert *pkc )
  * Make a hash value from the public key certificate
  */
 void
-hash_public_cert( MD_HANDLE md, PKT_public_cert *pkc )
+hash_public_key( MD_HANDLE md, PKT_public_key *pk )
 {
     PACKET pkt;
     int rc = 0;
     int c;
     IOBUF a = iobuf_temp();
   #if 0
-    FILE *fp = fopen("dump.pkc", "a");
+    FILE *fp = fopen("dump.pk", "a");
     int i=0;
 
-    fprintf(fp, "\nHashing PKC:\n");
+    fprintf(fp, "\nHashing PK (v%d):\n", pk->version);
   #endif
 
     /* build the packet */
     init_packet(&pkt);
-    pkt.pkttype = PKT_PUBLIC_CERT;
-    pkt.pkt.public_cert = pkc;
+    pkt.pkttype = PKT_PUBLIC_KEY;
+    pkt.pkt.public_key = pk;
     if( (rc = build_packet( a, &pkt )) )
-       log_fatal("build public_cert for hashing failed: %s\n", g10_errstr(rc));
+       log_fatal("build public_key for hashing failed: %s\n", g10_errstr(rc));
     while( (c=iobuf_get(a)) != -1 ) {
       #if 0
        fprintf( fp," %02x", c );
@@ -255,66 +275,74 @@ hash_public_cert( MD_HANDLE md, PKT_public_cert *pkc )
 
 
 static int
-do_secret_cert( IOBUF out, int ctb, PKT_secret_cert *skc )
+do_secret_key( IOBUF out, int ctb, PKT_secret_key *sk )
 {
     int rc = 0;
+    int i, nskey, npkey;
     IOBUF a = iobuf_temp();
 
-    if( !skc->version )
+    if( !sk->version )
        iobuf_put( a, 3 );
     else
-       iobuf_put( a, skc->version );
-    write_32(a, skc->timestamp );
-    if( skc->version < 4 )
-       write_16(a, skc->valid_days );
-    iobuf_put(a, skc->pubkey_algo );
-    if( skc->pubkey_algo == PUBKEY_ALGO_ELGAMAL ) {
-       mpi_write(a, skc->d.elg.p );
-       mpi_write(a, skc->d.elg.g );
-       mpi_write(a, skc->d.elg.y );
-       if( skc->is_protected ) {
-           iobuf_put(a, 0xff );
-           iobuf_put(a, skc->protect.algo );
-           iobuf_put(a, skc->protect.s2k.mode );
-           iobuf_put(a, skc->protect.s2k.hash_algo );
-           if( skc->protect.s2k.mode == 1
-               || skc->protect.s2k.mode == 4 )
-               iobuf_write(a, skc->protect.s2k.salt, 8 );
-           if( skc->protect.s2k.mode == 4 )
-               write_32(a, skc->protect.s2k.count );
-           iobuf_write(a, skc->protect.iv, 8 );
-       }
+       iobuf_put( a, sk->version );
+    write_32(a, sk->timestamp );
+    if( sk->version < 4 ) {
+       u16 ndays;
+       if( sk->expiredate )
+           ndays = (u16)((sk->expiredate - sk->timestamp) / 86400L);
        else
-           iobuf_put(a, 0 );
-
-       mpi_write(a, skc->d.elg.x );
-       write_16(a, skc->csum );
+           ndays = 0;
+       write_16(a, 0 );
     }
-    else if( skc->pubkey_algo == PUBKEY_ALGO_RSA ) {
-       mpi_write(a, skc->d.rsa.n );
-       mpi_write(a, skc->d.rsa.e );
-       if( skc->is_protected ) {
-           iobuf_put(a, skc->protect.algo );
-           iobuf_write(a, skc->protect.iv, 8 );
+    iobuf_put(a, sk->pubkey_algo );
+    nskey = pubkey_get_nskey( sk->pubkey_algo );
+    npkey = pubkey_get_npkey( sk->pubkey_algo );
+    if( !npkey ) {
+       write_fake_data( a, sk->skey[0] );
+       goto leave;
+    }
+    assert( npkey < nskey );
+
+    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
+                                   && !sk->protect.s2k.mode ) {
+           iobuf_put(a, sk->protect.algo );
+           iobuf_write(a, sk->protect.iv, 8 );
        }
-       else
-           iobuf_put(a, 0 );
-       mpi_write(a, skc->d.rsa.d );
-       mpi_write(a, skc->d.rsa.p );
-       mpi_write(a, skc->d.rsa.q );
-       mpi_write(a, skc->d.rsa.u );
-       write_16(a, skc->csum );
+       else {
+           iobuf_put(a, 0xff );
+           iobuf_put(a, sk->protect.algo );
+           iobuf_put(a, sk->protect.s2k.mode );
+           iobuf_put(a, sk->protect.s2k.hash_algo );
+           if( sk->protect.s2k.mode == 1
+               || sk->protect.s2k.mode == 3 )
+               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 );
+       }
+    }
+    else
+       iobuf_put(a, 0 );
+    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 {
-       rc = G10ERR_PUBKEY_ALGO;
-       goto leave;
+       for(   ; i < nskey; i++ )
+           mpi_write(a, sk->skey[i] );
+       write_16(a, sk->csum );
     }
 
-    write_header2(out, ctb, iobuf_get_temp_length(a), skc->hdrbytes, 1 );
+  leave:
+    write_header2(out, ctb, iobuf_get_temp_length(a), sk->hdrbytes, 1 );
     if( iobuf_write_temp( out, a ) )
        rc = G10ERR_WRITE_FILE;
 
-  leave:
     iobuf_close(a);
     return rc;
 }
@@ -327,17 +355,17 @@ do_symkey_enc( IOBUF out, int ctb, PKT_symkey_enc *enc )
 
     assert( enc->version == 4 );
     switch( enc->s2k.mode ) {
-      case 0: case 1: case 4: break;
+      case 0: case 1: case 3: break;
       default: log_bug("do_symkey_enc: s2k=%d\n", enc->s2k.mode );
     }
     iobuf_put( a, enc->version );
     iobuf_put( a, enc->cipher_algo );
     iobuf_put( a, enc->s2k.mode );
     iobuf_put( a, enc->s2k.hash_algo );
-    if( enc->s2k.mode == 1 || enc->s2k.mode == 4 ) {
+    if( enc->s2k.mode == 1 || enc->s2k.mode == 3 ) {
        iobuf_write(a, enc->s2k.salt, 8 );
-       if( enc->s2k.mode == 4 )
-           write_32(a, enc->s2k.count);
+       if( enc->s2k.mode == 3 )
+           iobuf_put(a, enc->s2k.count);
     }
     if( enc->seskeylen )
        iobuf_write(a, enc->seskey, enc->seskeylen );
@@ -350,33 +378,36 @@ do_symkey_enc( IOBUF out, int ctb, PKT_symkey_enc *enc )
     return rc;
 }
 
+
+
+
 static int
 do_pubkey_enc( IOBUF out, int ctb, PKT_pubkey_enc *enc )
 {
     int rc = 0;
+    int n, i;
     IOBUF a = iobuf_temp();
 
     write_version( a, ctb );
-    write_32(a, enc->keyid[0] );
-    write_32(a, enc->keyid[1] );
-    iobuf_put(a,enc->pubkey_algo );
-    if( enc->pubkey_algo == PUBKEY_ALGO_ELGAMAL ) {
-       mpi_write(a, enc->d.elg.a );
-       mpi_write(a, enc->d.elg.b );
-    }
-    else if( enc->pubkey_algo == PUBKEY_ALGO_RSA ) {
-       mpi_write(a, enc->d.rsa.rsa_integer );
+    if( enc->throw_keyid ) {
+       write_32(a, 0 );  /* don't tell Eve who can decrypt the message */
+       write_32(a, 0 );
     }
     else {
-       rc = G10ERR_PUBKEY_ALGO;
-       goto leave;
+       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 )
+       write_fake_data( a, enc->data[0] );
+    for(i=0; i < n; i++ )
+       mpi_write(a, enc->data[i] );
 
     write_header(out, ctb, iobuf_get_temp_length(a) );
     if( iobuf_write_temp( out, a ) )
        rc = G10ERR_WRITE_FILE;
 
-  leave:
     iobuf_close(a);
     return rc;
 }
@@ -393,8 +424,10 @@ calc_plaintext( PKT_plaintext *pt )
 static int
 do_plaintext( IOBUF out, int ctb, PKT_plaintext *pt )
 {
-    int c, i, rc = 0;
+    int i, rc = 0;
     u32 n;
+    byte buf[1000]; /* FIXME: this buffer has the plaintext! */
+    int nbytes;
 
     write_header(out, ctb, calc_plaintext( pt ) );
     iobuf_put(out, pt->mode );
@@ -405,13 +438,14 @@ do_plaintext( IOBUF out, int ctb, PKT_plaintext *pt )
        rc = G10ERR_WRITE_FILE;
 
     n = 0;
-    while( (c=iobuf_get(pt->buf)) != -1 ) {
-       if( iobuf_put(out, c) ) {
+    while( (nbytes=iobuf_read(pt->buf, buf, 1000)) != -1 ) {
+       if( iobuf_write(out, buf, nbytes) == -1 ) {
            rc = G10ERR_WRITE_FILE;
            break;
        }
-       n++;
+       n += nbytes;
     }
+    memset(buf,0,1000); /* at least burn the buffer */
     if( !pt->len )
        iobuf_set_block_mode(out, 0 ); /* write end marker */
     else if( n != pt->len )
@@ -452,10 +486,180 @@ do_compressed( IOBUF out, int ctb, PKT_compressed *cd )
 }
 
 
+
+/****************
+ * Find a subpacket of type REQTYPE in BUFFER and a return a pointer
+ * to the first byte of that subpacket data.
+ * And return the length of the packet in RET_N and the number of
+ * header bytes in RET_HLEN (length header and type byte).
+ */
+byte *
+find_subpkt( byte *buffer, sigsubpkttype_t reqtype,
+            size_t *ret_hlen, size_t *ret_n )
+{
+    int buflen;
+    sigsubpkttype_t type;
+    byte *bufstart;
+    size_t n;
+
+    if( !buffer )
+       return NULL;
+    buflen = (*buffer << 8) | buffer[1];
+    buffer += 2;
+    for(;;) {
+       if( !buflen )
+           return NULL; /* end of packets; not found */
+       bufstart = buffer;
+       n = *buffer++; buflen--;
+       if( n == 255 ) {
+           if( buflen < 4 )
+               break;
+           n = (buffer[0] << 24) | (buffer[1] << 16)
+                                 | (buffer[2] << 8) | buffer[3];
+           buffer += 4;
+           buflen -= 4;
+       }
+       else if( n >= 192 ) {
+           if( buflen < 2 )
+               break;
+           n = (( n - 192 ) << 8) + *buffer + 192;
+           buflen--;
+       }
+       if( buflen < n )
+           break;
+       type = *buffer & 0x7f;
+       if( type == reqtype ) {
+           buffer++;
+           n--;
+           if( n > buflen )
+               break;
+           if( ret_hlen )
+               *ret_hlen = buffer - bufstart;
+           if( ret_n )
+               *ret_n = n;
+           return buffer;
+       }
+       buffer += n; buflen -=n;
+    }
+
+    log_error("find_subpkt: buffer shorter than subpacket\n");
+    return NULL;
+}
+
+
+/****************
+ * Create or update a signature subpacket for SIG of TYPE.
+ * This functions know, 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
+ * in buffer with a length of buflen.
+ */
+void
+build_sig_subpkt( PKT_signature *sig, sigsubpkttype_t type,
+                 const byte *buffer, size_t buflen )
+{
+
+    byte *data;
+    size_t hlen, dlen;
+    int found, hashed, realloced;
+    size_t n, n0;
+
+    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");
+
+    switch( type ) {
+      case SIGSUBPKT_SIG_CREATED:
+      case SIGSUBPKT_PRIV_ADD_SIG:
+      case SIGSUBPKT_PREF_SYM:
+      case SIGSUBPKT_PREF_HASH:
+      case SIGSUBPKT_PREF_COMPR:
+      case SIGSUBPKT_KS_FLAGS:
+      case SIGSUBPKT_KEY_EXPIRE:
+              hashed = 1; break;
+      default: hashed = 0; break;
+    }
+
+    if( hashed ) {
+       n0 = sig->hashed_data ? ((*sig->hashed_data << 8)
+                                   | sig->hashed_data[1]) : 0;
+       n = n0 + 1 + 1 + buflen; /* length, type, buffer */
+       realloced = !!sig->hashed_data;
+       data = sig->hashed_data ? m_realloc( sig->hashed_data, n+2 )
+                               : m_alloc( n+2 );
+    }
+    else {
+       n0 = sig->unhashed_data ? ((*sig->unhashed_data << 8)
+                                     | sig->unhashed_data[1]) : 0;
+       n = n0 + 1 + 1 + buflen; /* length, type, buffer */
+       realloced = !!sig->unhashed_data;
+       data = sig->unhashed_data ? m_realloc( sig->unhashed_data, n+2 )
+                                 : m_alloc( n+2 );
+    }
+
+    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( hashed ) {
+       if( !realloced )
+           m_free(sig->hashed_data);
+       sig->hashed_data = data;
+    }
+    else {
+       if( !realloced )
+           m_free(sig->unhashed_data);
+       sig->unhashed_data = data;
+    }
+}
+
+
+/****************
+ * Put all the required stuff from SIG into subpackets of sig.
+ */
+void
+build_sig_subpkt_from_sig( PKT_signature *sig )
+{
+    u32  u;
+    byte buf[8];
+
+    u = sig->keyid[0];
+    buf[0] = (u >> 24) & 0xff;
+    buf[1] = (u >> 16) & 0xff;
+    buf[2] = (u >>  8) & 0xff;
+    buf[3] = u & 0xff;
+    u = sig->keyid[1];
+    buf[4] = (u >> 24) & 0xff;
+    buf[5] = (u >> 16) & 0xff;
+    buf[6] = (u >>  8) & 0xff;
+    buf[7] = u & 0xff;
+    build_sig_subpkt( sig, SIGSUBPKT_ISSUER, buf, 8 );
+
+    u = sig->timestamp;
+    buf[0] = (u >> 24) & 0xff;
+    buf[1] = (u >> 16) & 0xff;
+    buf[2] = (u >>  8) & 0xff;
+    buf[3] = u & 0xff;
+    build_sig_subpkt( sig, SIGSUBPKT_SIG_CREATED, buf, 4 );
+}
+
+
 static int
 do_signature( IOBUF out, int ctb, PKT_signature *sig )
 {
     int rc = 0;
+    int n, i;
     IOBUF a = iobuf_temp();
 
     if( !sig->version )
@@ -473,32 +677,36 @@ 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 ) {
-       /* fixme: write v4 subpackets here */
-       log_error("WARNING: note writing of v4 subpackets is not implemented\n");
+       size_t n;
+       /* 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 );
     }
     iobuf_put(a, sig->digest_start[0] );
     iobuf_put(a, sig->digest_start[1] );
-    if( sig->pubkey_algo == PUBKEY_ALGO_ELGAMAL ) {
-       mpi_write(a, sig->d.elg.a );
-       mpi_write(a, sig->d.elg.b );
-    }
-    else if( sig->pubkey_algo == PUBKEY_ALGO_DSA ) {
-       mpi_write(a, sig->d.dsa.r );
-       mpi_write(a, sig->d.dsa.s );
-    }
-    else if( sig->pubkey_algo == PUBKEY_ALGO_RSA ) {
-       mpi_write(a, sig->d.rsa.rsa_integer );
-    }
-    else {
-       rc = G10ERR_PUBKEY_ALGO;
-       goto leave;
-    }
-
-    write_header(out, ctb, iobuf_get_temp_length(a) );
+    n = pubkey_get_nsig( sig->pubkey_algo );
+    if( !n )
+       write_fake_data( a, sig->data[0] );
+    for(i=0; i < n; i++ )
+       mpi_write(a, sig->data[i] );
+
+    if( is_RSA(sig->pubkey_algo) && sig->version < 4 )
+       write_sign_packet_header(out, ctb, iobuf_get_temp_length(a) );
+    else
+       write_header(out, ctb, iobuf_get_temp_length(a) );
     if( iobuf_write_temp( out, a ) )
        rc = G10ERR_WRITE_FILE;
 
-  leave:
     iobuf_close(a);
     return rc;
 }
@@ -552,16 +760,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;
 }
 
 /****************
@@ -573,6 +790,18 @@ write_header( IOBUF out, int ctb, u32 len )
     return write_header2( out, ctb, len, 0, 1 );
 }
 
+
+static int
+write_sign_packet_header( IOBUF out, int ctb, u32 len )
+{
+    /* work around a bug in the pgp read function for signature packets,
+     * which are not correctly coded and silently assume at some
+     * point 2 byte length headers.*/
+    iobuf_put(out, 0x89 );
+    iobuf_put(out, len >> 8 );
+    return iobuf_put(out, len ) == -1 ? -1:0;
+}
+
 /****************
  * if HDRLEN is > 0, try to build a header of this length.
  * we need this, so that we can hash packets without reading them again.
@@ -632,7 +861,7 @@ write_new_header( IOBUF out, int ctb, u32 len, int hdrlen )
     if( iobuf_put(out, ctb ) )
        return -1;
     if( !len ) {
-       log_bug("can't write partial headers yet\n");
+       iobuf_set_partial_block_mode(out, 512 );
     }
     else {
        if( len < 192 ) {
@@ -646,8 +875,18 @@ write_new_header( IOBUF out, int ctb, u32 len, int hdrlen )
            if( iobuf_put( out, (len % 256) )  )
                return -1;
        }
-       else
-           log_bug("need a partial header to code a length %lu\n", (ulong)len);
+       else {
+           if( iobuf_put( out, 0xff ) )
+               return -1;
+           if( iobuf_put( out, (len >> 24)&0xff ) )
+               return -1;
+           if( iobuf_put( out, (len >> 16)&0xff ) )
+               return -1;
+           if( iobuf_put( out, (len >> 8)&0xff )  )
+               return -1;
+           if( iobuf_put( out, len & 0xff ) )
+               return -1;
+       }
     }
     return 0;
 }