aboutsummaryrefslogtreecommitdiffstats
path: root/g10/build-packet.c
diff options
context:
space:
mode:
authorRepo Admin <[email protected]>2003-06-05 07:14:21 +0000
committerRepo Admin <[email protected]>2003-06-05 07:14:21 +0000
commit7250331472efe70fac928fa06e51c7c80f2b715c (patch)
treed6ec62958ba3971115da3b81c8e1ad0f6d822fe0 /g10/build-packet.c
parentMake use of libgpg-error (diff)
downloadgnupg-7250331472efe70fac928fa06e51c7c80f2b715c.tar.gz
gnupg-7250331472efe70fac928fa06e51c7c80f2b715c.zip
This commit was manufactured by cvs2svn to create branch
'GNUPG-1-9-BRANCH'.
Diffstat (limited to 'g10/build-packet.c')
-rw-r--r--g10/build-packet.c1196
1 files changed, 1196 insertions, 0 deletions
diff --git a/g10/build-packet.c b/g10/build-packet.c
new file mode 100644
index 000000000..86aa07dc2
--- /dev/null
+++ b/g10/build-packet.c
@@ -0,0 +1,1196 @@
+/* build-packet.c - assemble packets and write them
+ * Copyright (C) 1998, 1999, 2000, 2001, 2002,
+ * 2003 Free Software Foundation, Inc.
+ *
+ * This file is part of GnuPG.
+ *
+ * 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,
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
+ */
+
+#include <config.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+
+#include "packet.h"
+#include "errors.h"
+#include "iobuf.h"
+#include "mpi.h"
+#include "util.h"
+#include "cipher.h"
+#include "memory.h"
+#include "options.h"
+
+
+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_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 );
+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, 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 );
+
+/****************
+ * Build a packet and write it to INP
+ * Returns: 0 := okay
+ * >0 := error
+ * Note: Caller must free the packet
+ */
+int
+build_packet( IOBUF out, PACKET *pkt )
+{
+ int new_ctb=0, rc=0, ctb;
+ int pkttype;
+
+ if( DBG_PACKET )
+ log_debug("build_packet() type=%d\n", pkt->pkttype );
+ assert( pkt->pkt.generic );
+
+ switch( (pkttype = pkt->pkttype) ) {
+ case PKT_OLD_COMMENT: pkttype = pkt->pkttype = PKT_COMMENT; break;
+ case PKT_PLAINTEXT: new_ctb = pkt->pkt.plaintext->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;
+ case PKT_USER_ID:
+ if( pkt->pkt.user_id->attrib_data )
+ pkttype = PKT_ATTRIBUTE;
+ break;
+ default: break;
+ }
+
+ if( new_ctb || pkttype > 15 ) /* new format */
+ ctb = 0xc0 | (pkttype & 0x3f);
+ else
+ ctb = 0x80 | ((pkttype & 15)<<2);
+ switch( pkttype ) {
+ case PKT_ATTRIBUTE:
+ case PKT_USER_ID:
+ rc = do_user_id( out, ctb, pkt->pkt.user_id );
+ break;
+ case PKT_COMMENT:
+ rc = do_comment( out, ctb, pkt->pkt.comment );
+ break;
+ case PKT_PUBLIC_SUBKEY:
+ case PKT_PUBLIC_KEY:
+ rc = do_public_key( out, ctb, pkt->pkt.public_key );
+ break;
+ 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 );
+ break;
+ case PKT_PUBKEY_ENC:
+ rc = do_pubkey_enc( out, ctb, pkt->pkt.pubkey_enc );
+ break;
+ case PKT_PLAINTEXT:
+ rc = do_plaintext( out, ctb, pkt->pkt.plaintext );
+ break;
+ 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;
+ case PKT_SIGNATURE:
+ rc = do_signature( out, ctb, pkt->pkt.signature );
+ break;
+ case PKT_ONEPASS_SIG:
+ rc = do_onepass_sig( out, ctb, pkt->pkt.onepass_sig );
+ break;
+ case PKT_RING_TRUST:
+ break; /* ignore it (keyring.c does write it directly)*/
+ case PKT_MDC: /* we write it directly, so we should never see it here. */
+ default:
+ log_bug("invalid packet type in build_packet()\n");
+ break;
+ }
+
+ return rc;
+}
+
+/****************
+ * calculate the length of a packet described by PKT
+ */
+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_ATTRIBUTE:
+ case PKT_USER_ID:
+ case PKT_COMMENT:
+ case PKT_PUBLIC_KEY:
+ case PKT_SECRET_KEY:
+ case PKT_SYMKEY_ENC:
+ case PKT_PUBKEY_ENC:
+ case PKT_ENCRYPTED:
+ case PKT_SIGNATURE:
+ case PKT_ONEPASS_SIG:
+ case PKT_RING_TRUST:
+ case PKT_COMPRESSED:
+ default:
+ log_bug("invalid packet type in calc_packet_length()");
+ break;
+ }
+
+ n += calc_header_length(n, new_ctb);
+ return n;
+}
+
+static void
+write_fake_data( IOBUF out, MPI a )
+{
+ if( a ) {
+ int i;
+ void *p;
+
+ p = mpi_get_opaque( a, &i );
+ iobuf_write( out, p, i );
+ }
+}
+
+
+static int
+do_comment( IOBUF out, int ctb, PKT_comment *rem )
+{
+ if( opt.sk_comments ) {
+ write_header(out, ctb, rem->len);
+ if( iobuf_write( out, rem->data, rem->len ) )
+ return G10ERR_WRITE_FILE;
+ }
+ return 0;
+}
+
+static int
+do_user_id( IOBUF out, int ctb, PKT_user_id *uid )
+{
+ if( uid->attrib_data ) {
+ write_header(out, ctb, uid->attrib_len);
+ if( iobuf_write( out, uid->attrib_data, uid->attrib_len ) )
+ return G10ERR_WRITE_FILE;
+ }
+ else {
+ write_header(out, ctb, uid->len);
+ if( iobuf_write( out, uid->name, uid->len ) )
+ return G10ERR_WRITE_FILE;
+ }
+ return 0;
+}
+
+static int
+do_public_key( IOBUF out, int ctb, PKT_public_key *pk )
+{
+ int rc = 0;
+ int n, i;
+ IOBUF a = iobuf_temp();
+
+ if( !pk->version )
+ iobuf_put( a, 3 );
+ else
+ 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 );
+ }
+ 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;
+
+ iobuf_close(a);
+ return rc;
+}
+
+
+/****************
+ * Make a hash value from the public key certificate
+ */
+void
+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
+ FILE *fp = fopen("dump.pk", "a");
+ int i=0;
+
+ fprintf(fp, "\nHashing PK (v%d):\n", pk->version);
+#endif
+
+ /* build the packet */
+ init_packet(&pkt);
+ pkt.pkttype = PKT_PUBLIC_KEY;
+ 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 );
+ if( (++i == 24) ) {
+ putc('\n', fp);
+ i=0;
+ }
+#endif
+ md_putc( md, c );
+ }
+#if 0
+ putc('\n', fp);
+ fclose(fp);
+#endif
+ iobuf_cancel(a);
+}
+
+
+static int
+do_secret_key( IOBUF out, int ctb, PKT_secret_key *sk )
+{
+ int rc = 0;
+ int i, nskey, npkey;
+ IOBUF a = iobuf_temp(); /* build in a self-enlarging buffer */
+
+ /* Write the version number - if none is specified, use 3 */
+ if( !sk->version )
+ iobuf_put( a, 3 );
+ else
+ iobuf_put( a, sk->version );
+ write_32(a, sk->timestamp );
+
+ /* v3 needs the expiration time */
+ if( sk->version < 4 ) {
+ u16 ndays;
+ if( sk->expiredate )
+ ndays = (u16)((sk->expiredate - sk->timestamp) / 86400L);
+ else
+ ndays = 0;
+ write_16(a, ndays);
+ }
+
+ iobuf_put(a, sk->pubkey_algo );
+
+ /* get number of secret and public parameters. They are held in
+ one array first the public ones, then the secret ones */
+ nskey = pubkey_get_nskey( sk->pubkey_algo );
+ npkey = pubkey_get_npkey( sk->pubkey_algo );
+
+ /* If we don't have any public parameters - which is the case if
+ we don't know the algorithm used - the parameters are stored as
+ one blob in a faked (opaque) MPI */
+ if( !npkey ) {
+ write_fake_data( a, sk->skey[0] );
+ goto leave;
+ }
+ assert( npkey < nskey );
+
+ /* Writing the public parameters is easy */
+ for(i=0; i < npkey; i++ )
+ mpi_write(a, sk->skey[i] );
+
+ /* build the header for protected (encrypted) secret parameters */
+ if( sk->is_protected ) {
+ if( is_RSA(sk->pubkey_algo) && sk->version < 4
+ && !sk->protect.s2k.mode ) {
+ /* the simple rfc1991 (v3) way */
+ iobuf_put(a, sk->protect.algo );
+ iobuf_write(a, sk->protect.iv, sk->protect.ivlen );
+ }
+ else {
+ /* OpenPGP protection according to rfc2440 */
+ iobuf_put(a, sk->protect.sha1chk? 0xfe : 0xff );
+ iobuf_put(a, sk->protect.algo );
+ if( sk->protect.s2k.mode >= 1000 ) {
+ /* These modes are not possible in OpenPGP, we use them
+ to implement our extensions, 101 can be seen as a
+ private/experimental extension (this is not
+ specified in rfc2440 but the same scheme is used
+ for all other algorithm identifiers) */
+ iobuf_put(a, 101 );
+ iobuf_put(a, sk->protect.s2k.hash_algo );
+ iobuf_write(a, "GNU", 3 );
+ iobuf_put(a, sk->protect.s2k.mode - 1000 );
+ }
+ else {
+ 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 );
+
+ /* For out special mode 1001 we do not need an IV */
+ if( sk->protect.s2k.mode != 1001 )
+ iobuf_write(a, sk->protect.iv, sk->protect.ivlen );
+ }
+ }
+ else
+ iobuf_put(a, 0 );
+
+ if( sk->protect.s2k.mode == 1001 )
+ ; /* GnuPG extension - don't write a secret key at all */
+ else if( sk->is_protected && sk->version >= 4 ) {
+ /* The secret key is protected - write it out as it is */
+ byte *p;
+ assert( mpi_is_opaque( sk->skey[npkey] ) );
+ p = mpi_get_opaque( sk->skey[npkey], &i );
+ iobuf_write(a, p, i );
+ }
+ else if( sk->is_protected ) {
+ /* The secret key is protected te old v4 way. */
+ for( ; i < nskey; i++ ) {
+ byte *p;
+ int ndata;
+
+ assert (mpi_is_opaque (sk->skey[i]));
+ p = mpi_get_opaque (sk->skey[i], &ndata);
+ iobuf_write (a, p, ndata);
+ }
+ write_16(a, sk->csum );
+ }
+ else {
+ /* non-protected key */
+ for( ; i < nskey; i++ )
+ mpi_write(a, sk->skey[i] );
+ write_16(a, sk->csum );
+ }
+
+ leave:
+ /* Build the header of the packet - which we must do after writing all
+ the other stuff, so that we know the length of the packet */
+ write_header2(out, ctb, iobuf_get_temp_length(a), sk->hdrbytes, 1 );
+ /* And finally write it out the real stream */
+ if( iobuf_write_temp( out, a ) )
+ rc = G10ERR_WRITE_FILE;
+
+ iobuf_close(a); /* close the remporary buffer */
+ return rc;
+}
+
+static int
+do_symkey_enc( IOBUF out, int ctb, PKT_symkey_enc *enc )
+{
+ int rc = 0;
+ IOBUF a = iobuf_temp();
+
+ assert( enc->version == 4 );
+ switch( enc->s2k.mode ) {
+ 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 == 3 ) {
+ iobuf_write(a, enc->s2k.salt, 8 );
+ if( enc->s2k.mode == 3 )
+ iobuf_put(a, enc->s2k.count);
+ }
+ if( enc->seskeylen )
+ iobuf_write(a, enc->seskey, enc->seskeylen );
+
+ write_header(out, ctb, iobuf_get_temp_length(a) );
+ if( iobuf_write_temp( out, a ) )
+ rc = G10ERR_WRITE_FILE;
+
+ iobuf_close(a);
+ 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 );
+ 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 )
+ 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;
+
+ iobuf_close(a);
+ return rc;
+}
+
+
+
+
+static u32
+calc_plaintext( PKT_plaintext *pt )
+{
+ return pt->len? (1 + 1 + pt->namelen + 4 + pt->len) : 0;
+}
+
+static int
+do_plaintext( IOBUF out, int ctb, PKT_plaintext *pt )
+{
+ int i, rc = 0;
+ u32 n;
+ byte buf[1000]; /* this buffer has the plaintext! */
+ int nbytes;
+
+ /* Truncate namelen to the maximum 255 characters. This does mean
+ that a function that calls build_packet with an illegal literal
+ packet will get it back legalized. */
+ if(pt->namelen>255)
+ pt->namelen=255;
+
+ write_header(out, ctb, calc_plaintext( pt ) );
+ iobuf_put(out, pt->mode );
+ iobuf_put(out, pt->namelen );
+ for(i=0; i < pt->namelen; i++ )
+ iobuf_put(out, pt->name[i] );
+ if( write_32(out, pt->timestamp ) )
+ rc = G10ERR_WRITE_FILE;
+
+ n = 0;
+ while( (nbytes=iobuf_read(pt->buf, buf, 1000)) != -1 ) {
+ if( iobuf_write(out, buf, nbytes) == -1 ) {
+ rc = G10ERR_WRITE_FILE;
+ break;
+ }
+ n += nbytes;
+ }
+ wipememory(buf,1000); /* burn the buffer */
+ if( !pt->len )
+ iobuf_set_block_mode(out, 0 ); /* write end marker */
+ else if( n != pt->len )
+ log_error("do_plaintext(): wrote %lu bytes but expected %lu bytes\n",
+ (ulong)n, (ulong)pt->len );
+
+ return rc;
+}
+
+
+
+static int
+do_encrypted( IOBUF out, int ctb, PKT_encrypted *ed )
+{
+ int rc = 0;
+ u32 n;
+
+ n = ed->len ? (ed->len + ed->extralen) : 0;
+ write_header(out, ctb, n );
+
+ /* This is all. The caller has to write the real data */
+
+ return rc;
+}
+
+static int
+do_encrypted_mdc( IOBUF out, int ctb, PKT_encrypted *ed )
+{
+ int rc = 0;
+ u32 n;
+
+ assert( ed->mdc_method );
+
+ /* Take version number and the following MDC packet in account. */
+ n = ed->len ? (ed->len + ed->extralen + 1 + 22) : 0;
+ write_header(out, ctb, n );
+ iobuf_put(out, 1 ); /* version */
+
+ /* 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;
+
+ /* We must use the old convention and don't use blockmode for tyhe
+ sake of PGP 2 compatibility. However if the new_ctb flag was
+ set, CTB is already formatted as new style and write_header2
+ does create a partial length encoding using new the new
+ style. */
+ write_header2(out, ctb, 0, 0, 0 );
+ iobuf_put(out, cd->algorithm );
+
+ /* This is all. The caller has to write the real data */
+
+ return rc;
+}
+
+
+/****************
+ * Delete all subpackets of type REQTYPE and return a bool whether a packet
+ * was deleted.
+ */
+int
+delete_sig_subpkt (subpktarea_t *area, sigsubpkttype_t reqtype )
+{
+ int buflen;
+ sigsubpkttype_t type;
+ byte *buffer, *bufstart;
+ size_t n;
+ size_t unused = 0;
+ int okay = 0;
+
+ if( !area )
+ return 0;
+ buflen = area->len;
+ buffer = area->data;
+ for(;;) {
+ if( !buflen ) {
+ okay = 1;
+ break;
+ }
+ 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;
+ buffer++;
+ buflen--;
+ }
+ if( buflen < n )
+ break;
+
+ type = *buffer & 0x7f;
+ if( type == reqtype ) {
+ buffer++;
+ buflen--;
+ n--;
+ if( n > buflen )
+ break;
+ buffer += n; /* point to next subpkt */
+ buflen -= n;
+ memmove (bufstart, buffer, buflen); /* shift */
+ unused += buffer - bufstart;
+ buffer = bufstart;
+ }
+ else {
+ buffer += n; buflen -=n;
+ }
+ }
+
+ if (!okay)
+ log_error ("delete_subpkt: buffer shorter than subpacket\n");
+ assert (unused <= area->len);
+ area->len -= unused;
+ return !!unused;
+}
+
+
+/****************
+ * Create or update a signature subpacket for SIG of TYPE. This
+ * functions knows where to put the data (hashed or unhashed). The
+ * function may move data from the unhashed part to the hashed one.
+ * Note: All pointers into sig->[un]hashed (e.g. returned by
+ * parse_sig_subpkt) are not valid after a call to this function. The
+ * data to put into the subpaket should be in a buffer with a length
+ * of buflen.
+ */
+void
+build_sig_subpkt (PKT_signature *sig, sigsubpkttype_t type,
+ const byte *buffer, size_t buflen )
+{
+ byte *p;
+ int critical, hashed;
+ subpktarea_t *oldarea, *newarea;
+ size_t nlen, n, n0;
+
+ critical = (type & SIGSUBPKT_FLAG_CRITICAL);
+ type &= ~SIGSUBPKT_FLAG_CRITICAL;
+
+ /* Sanity check buffer sizes */
+ if(parse_one_sig_subpkt(buffer,buflen,type)<0)
+ BUG();
+
+ switch(type)
+ {
+ case SIGSUBPKT_NOTATION:
+ case SIGSUBPKT_POLICY:
+ case SIGSUBPKT_REV_KEY:
+ /* we do allow multiple subpackets */
+ break;
+
+ default:
+ /* we don't allow multiple subpackets */
+ delete_sig_subpkt(sig->hashed,type);
+ delete_sig_subpkt(sig->unhashed,type);
+ break;
+ }
+
+ /* Any special magic that needs to be done for this type so the
+ packet doesn't need to be reparsed? */
+ switch(type)
+ {
+ case SIGSUBPKT_NOTATION:
+ sig->flags.notation=1;
+ break;
+
+ case SIGSUBPKT_POLICY:
+ sig->flags.policy_url=1;
+ break;
+
+ case SIGSUBPKT_EXPORTABLE:
+ if(buffer[0])
+ sig->flags.exportable=1;
+ else
+ sig->flags.exportable=0;
+ break;
+
+ case SIGSUBPKT_REVOCABLE:
+ if(buffer[0])
+ sig->flags.revocable=1;
+ else
+ sig->flags.revocable=0;
+ break;
+
+ case SIGSUBPKT_TRUST:
+ sig->trust_depth=buffer[0];
+ sig->trust_value=buffer[1];
+ break;
+
+ case SIGSUBPKT_REGEXP:
+ sig->trust_regexp=buffer;
+ break;
+
+ /* This should never happen since we don't currently allow
+ creating such a subpacket, but just in case... */
+ case SIGSUBPKT_SIG_EXPIRE:
+ if(buffer_to_u32(buffer)+sig->timestamp<=make_timestamp())
+ sig->flags.expired=1;
+ else
+ sig->flags.expired=0;
+ break;
+
+ default:
+ break;
+ }
+
+ if( (buflen+1) >= 8384 )
+ nlen = 5; /* write 5 byte length header */
+ else if( (buflen+1) >= 192 )
+ nlen = 2; /* write 2 byte length header */
+ else
+ nlen = 1; /* just a 1 byte length header */
+
+ switch( type ) {
+ /* The issuer being unhashed is a historical oddity. It
+ should work equally as well hashed. Of course, if even an
+ unhashed issuer is tampered with, it makes it awfully hard
+ to verify the sig... */
+ case SIGSUBPKT_ISSUER:
+ hashed = 0;
+ break;
+ default:
+ hashed = 1;
+ break;
+ }
+
+ if( critical )
+ type |= SIGSUBPKT_FLAG_CRITICAL;
+
+ oldarea = hashed? sig->hashed : sig->unhashed;
+
+ /* Calculate new size of the area and allocate */
+ n0 = oldarea? oldarea->len : 0;
+ n = n0 + nlen + 1 + buflen; /* length, type, buffer */
+ if (oldarea && n <= oldarea->size) { /* fits into the unused space */
+ newarea = oldarea;
+ /*log_debug ("updating area for type %d\n", type );*/
+ }
+ else if (oldarea) {
+ newarea = m_realloc (oldarea, sizeof (*newarea) + n - 1);
+ newarea->size = n;
+ /*log_debug ("reallocating area for type %d\n", type );*/
+ }
+ else {
+ newarea = m_alloc (sizeof (*newarea) + n - 1);
+ newarea->size = n;
+ /*log_debug ("allocating area for type %d\n", type );*/
+ }
+ newarea->len = n;
+
+ p = newarea->data + n0;
+ if (nlen == 5) {
+ *p++ = 255;
+ *p++ = (buflen+1) >> 24;
+ *p++ = (buflen+1) >> 16;
+ *p++ = (buflen+1) >> 8;
+ *p++ = (buflen+1);
+ *p++ = type;
+ memcpy (p, buffer, buflen);
+ }
+ else if (nlen == 2) {
+ *p++ = (buflen+1-192) / 256 + 192;
+ *p++ = (buflen+1-192) % 256;
+ *p++ = type;
+ memcpy (p, buffer, buflen);
+ }
+ else {
+ *p++ = buflen+1;
+ *p++ = type;
+ memcpy (p, buffer, buflen);
+ }
+
+ if (hashed)
+ sig->hashed = newarea;
+ else
+ sig->unhashed = newarea;
+}
+
+/****************
+ * Put all the required stuff from SIG into subpackets of sig.
+ * Hmmm, should we delete those subpackets which are in a wrong area?
+ */
+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 );
+
+ if(sig->expiredate)
+ {
+ if(sig->expiredate>sig->timestamp)
+ u=sig->expiredate-sig->timestamp;
+ else
+ u=0;
+
+ buf[0] = (u >> 24) & 0xff;
+ buf[1] = (u >> 16) & 0xff;
+ buf[2] = (u >> 8) & 0xff;
+ buf[3] = u & 0xff;
+
+ /* Mark this CRITICAL, so if any implementation doesn't
+ understand sigs that can expire, it'll just disregard this
+ sig altogether. */
+
+ build_sig_subpkt( sig, SIGSUBPKT_SIG_EXPIRE | SIGSUBPKT_FLAG_CRITICAL,
+ buf, 4 );
+ }
+}
+
+void
+build_attribute_subpkt(PKT_user_id *uid,byte type,
+ const void *buf,u32 buflen,
+ const void *header,u32 headerlen)
+{
+ byte *attrib;
+ int idx;
+
+ if(1+headerlen+buflen>8383)
+ idx=5;
+ else if(1+headerlen+buflen>191)
+ idx=2;
+ else
+ idx=1;
+
+ /* realloc uid->attrib_data to the right size */
+
+ uid->attrib_data=m_realloc(uid->attrib_data,
+ uid->attrib_len+idx+1+headerlen+buflen);
+
+ attrib=&uid->attrib_data[uid->attrib_len];
+
+ if(idx==5)
+ {
+ attrib[0]=255;
+ attrib[1]=(1+headerlen+buflen) >> 24;
+ attrib[2]=(1+headerlen+buflen) >> 16;
+ attrib[3]=(1+headerlen+buflen) >> 8;
+ attrib[4]=1+headerlen+buflen;
+ }
+ else if(idx==2)
+ {
+ attrib[0]=(1+headerlen+buflen-192) / 256 + 192;
+ attrib[1]=(1+headerlen+buflen-192) % 256;
+ }
+ else
+ attrib[0]=1+headerlen+buflen; /* Good luck finding a JPEG this small! */
+
+ attrib[idx++]=type;
+
+ /* Tack on our data at the end */
+
+ if(headerlen>0)
+ memcpy(&attrib[idx],header,headerlen);
+ memcpy(&attrib[idx+headerlen],buf,buflen);
+ uid->attrib_len+=idx+headerlen+buflen;
+}
+
+static int
+do_signature( IOBUF out, int ctb, PKT_signature *sig )
+{
+ int rc = 0;
+ int n, i;
+ IOBUF a = iobuf_temp();
+
+ if( !sig->version )
+ iobuf_put( a, 3 );
+ else
+ iobuf_put( a, sig->version );
+ if( sig->version < 4 )
+ iobuf_put(a, 5 ); /* constant */
+ iobuf_put(a, sig->sig_class );
+ if( sig->version < 4 ) {
+ write_32(a, sig->timestamp );
+ write_32(a, sig->keyid[0] );
+ write_32(a, sig->keyid[1] );
+ }
+ iobuf_put(a, sig->pubkey_algo );
+ iobuf_put(a, sig->digest_algo );
+ if( sig->version >= 4 ) {
+ 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 */
+ nn = sig->hashed? sig->hashed->len : 0;
+ write_16(a, nn);
+ if( nn )
+ iobuf_write( a, sig->hashed->data, nn );
+ nn = sig->unhashed? sig->unhashed->len : 0;
+ write_16(a, nn);
+ if( nn )
+ iobuf_write( a, sig->unhashed->data, nn );
+ }
+ iobuf_put(a, sig->digest_start[0] );
+ iobuf_put(a, sig->digest_start[1] );
+ 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;
+
+ iobuf_close(a);
+ return rc;
+}
+
+
+static int
+do_onepass_sig( IOBUF out, int ctb, PKT_onepass_sig *ops )
+{
+ int rc = 0;
+ IOBUF a = iobuf_temp();
+
+ write_version( a, ctb );
+ iobuf_put(a, ops->sig_class );
+ iobuf_put(a, ops->digest_algo );
+ iobuf_put(a, ops->pubkey_algo );
+ write_32(a, ops->keyid[0] );
+ write_32(a, ops->keyid[1] );
+ iobuf_put(a, ops->last );
+
+ write_header(out, ctb, iobuf_get_temp_length(a) );
+ if( iobuf_write_temp( out, a ) )
+ rc = G10ERR_WRITE_FILE;
+
+ iobuf_close(a);
+ return rc;
+}
+
+
+static int
+write_16(IOBUF out, u16 a)
+{
+ iobuf_put(out, a>>8);
+ if( iobuf_put(out,a) )
+ return -1;
+ return 0;
+}
+
+static int
+write_32(IOBUF out, u32 a)
+{
+ iobuf_put(out, a>> 24);
+ iobuf_put(out, a>> 16);
+ iobuf_put(out, a>> 8);
+ if( iobuf_put(out, a) )
+ return -1;
+ return 0;
+}
+
+
+/****************
+ * calculate the length of a header
+ */
+static int
+calc_header_length( u32 len, int new_ctb )
+{
+ if( !len )
+ return 1; /* only the ctb */
+
+ if( new_ctb ) {
+ if( len < 192 )
+ return 2;
+ if( len < 8384 )
+ return 3;
+ else
+ return 6;
+ }
+ if( len < 256 )
+ return 2;
+ if( len < 65536 )
+ return 3;
+
+ return 5;
+}
+
+/****************
+ * Write the CTB and the packet length
+ */
+static int
+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.
+ */
+static int
+write_header2( IOBUF out, int ctb, u32 len, int hdrlen, int blkmode )
+{
+ if( ctb & 0x40 )
+ return write_new_header( out, ctb, len, hdrlen );
+
+ if( hdrlen ) {
+ if( !len )
+ ctb |= 3;
+ else if( hdrlen == 2 && len < 256 )
+ ;
+ else if( hdrlen == 3 && len < 65536 )
+ ctb |= 1;
+ else
+ ctb |= 2;
+ }
+ else {
+ if( !len )
+ ctb |= 3;
+ else if( len < 256 )
+ ;
+ else if( len < 65536 )
+ ctb |= 1;
+ else
+ ctb |= 2;
+ }
+ if( iobuf_put(out, ctb ) )
+ return -1;
+ if( !len ) {
+ if( blkmode )
+ iobuf_set_block_mode(out, 8196 );
+ }
+ else {
+ if( ctb & 2 ) {
+ iobuf_put(out, len >> 24 );
+ iobuf_put(out, len >> 16 );
+ }
+ if( ctb & 3 )
+ iobuf_put(out, len >> 8 );
+ if( iobuf_put(out, len ) )
+ return -1;
+ }
+ return 0;
+}
+
+
+static int
+write_new_header( IOBUF out, int ctb, u32 len, int hdrlen )
+{
+ if( hdrlen )
+ log_bug("can't cope with hdrlen yet\n");
+
+ if( iobuf_put(out, ctb ) )
+ return -1;
+ if( !len ) {
+ iobuf_set_partial_block_mode(out, 512 );
+ }
+ else {
+ if( len < 192 ) {
+ if( iobuf_put(out, len ) )
+ return -1;
+ }
+ else if( len < 8384 ) {
+ len -= 192;
+ if( iobuf_put( out, (len / 256) + 192) )
+ return -1;
+ if( iobuf_put( out, (len % 256) ) )
+ return -1;
+ }
+ 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;
+}
+
+static int
+write_version( IOBUF out, int ctb )
+{
+ if( iobuf_put( out, 3 ) )
+ return -1;
+ return 0;
+}