diff options
author | David Shaw <[email protected]> | 2002-06-29 13:46:34 +0000 |
---|---|---|
committer | David Shaw <[email protected]> | 2002-06-29 13:46:34 +0000 |
commit | 3f51f7db3de85574dc5d6efd8b54ef78c1cd3f37 (patch) | |
tree | dac7a3780fb4edf9ca89c93800230e88ab255434 /g10/sign.c | |
parent | Update head to match stable 1.0 (diff) | |
download | gnupg-3f51f7db3de85574dc5d6efd8b54ef78c1cd3f37.tar.gz gnupg-3f51f7db3de85574dc5d6efd8b54ef78c1cd3f37.zip |
Update head to match stable 1.0
Diffstat (limited to 'g10/sign.c')
-rw-r--r-- | g10/sign.c | 1276 |
1 files changed, 840 insertions, 436 deletions
diff --git a/g10/sign.c b/g10/sign.c index bdc5b8afe..6a8ce2991 100644 --- a/g10/sign.c +++ b/g10/sign.c @@ -1,5 +1,5 @@ /* sign.c - sign data - * Copyright (C) 1998, 1999, 2000 Free Software Foundation, Inc. + * Copyright (C) 1998, 1999, 2000, 2001, 2002 Free Software Foundation, Inc. * * This file is part of GnuPG. * @@ -24,13 +24,14 @@ #include <string.h> #include <errno.h> #include <assert.h> +#include <unistd.h> /* need sleep() */ -#include <gcrypt.h> #include "options.h" #include "packet.h" #include "errors.h" #include "iobuf.h" #include "keydb.h" +#include "memory.h" #include "util.h" #include "main.h" #include "filter.h" @@ -40,125 +41,212 @@ #include "i18n.h" -#define ENABLE_BETTER_PGP2_COMPAT 1 - #ifdef HAVE_DOSISH_SYSTEM #define LF "\r\n" + void __stdcall Sleep(ulong); + #define sleep(a) Sleep((a)*1000) #else #define LF "\n" #endif +static int recipient_digest_algo=0; + /**************** - * Emulate our old PK interface here - sometime in the future we might - * change the internal design to directly fit to libgcrypt. + * Create a notation. It is assumed that the stings in STRLIST + * are already checked to contain only printable data and have a valid + * NAME=VALUE format. */ -static int -pk_sign( int algo, MPI *data, MPI hash, MPI *skey ) +static void +mk_notation_and_policy( PKT_signature *sig, + PKT_public_key *pk, PKT_secret_key *sk ) { - GCRY_SEXP s_sig, s_hash, s_skey, list; - int rc; + const char *string; + char *s=NULL; + byte *buf; + unsigned n1, n2; + STRLIST nd=NULL,pu=NULL; + struct expando_args args; - /* make a sexp from skey */ - if( algo == GCRY_PK_DSA ) { - rc = gcry_sexp_build ( &s_skey, NULL, - "(private-key(dsa(p%m)(q%m)(g%m)(y%m)(x%m)))", - skey[0], skey[1], skey[2], skey[3], skey[4] ); - } - else if( algo == GCRY_PK_ELG || algo == GCRY_PK_ELG_E ) { - rc = gcry_sexp_build ( &s_skey, NULL, - "(private-key(elg(p%m)(g%m)(y%m)(x%m)))", - skey[0], skey[1], skey[2], skey[3] ); - } - else - return GPGERR_PUBKEY_ALGO; + memset(&args,0,sizeof(args)); + args.pk=pk; + args.sk=sk; - if ( rc ) - BUG (); + /* notation data */ + if(IS_SIG(sig) && opt.sig_notation_data) + { + if(sig->version<4) + log_info("can't put notation data into v3 signatures\n"); + else + nd=opt.sig_notation_data; + } + else if( IS_CERT(sig) && opt.cert_notation_data ) + { + if(sig->version<4) + log_info("can't put notation data into v3 key signatures\n"); + else + nd=opt.cert_notation_data; + } + + for( ; nd; nd = nd->next ) { + char *expanded; + + string = nd->d; + s = strchr( string, '=' ); + if( !s ) + BUG(); /* we have already parsed this */ + n1 = s - string; + s++; + + expanded=pct_expando(s,&args); + if(!expanded) + { + log_error(_("WARNING: unable to %%-expand notation " + "(too large). Using unexpanded.\n")); + expanded=m_strdup(s); + } + + n2 = strlen(expanded); + buf = m_alloc( 8 + n1 + n2 ); + buf[0] = 0x80; /* human readable */ + buf[1] = buf[2] = buf[3] = 0; + buf[4] = n1 >> 8; + buf[5] = n1; + buf[6] = n2 >> 8; + buf[7] = n2; + memcpy(buf+8, string, n1 ); + memcpy(buf+8+n1, expanded, n2 ); + build_sig_subpkt( sig, SIGSUBPKT_NOTATION + | ((nd->flags & 1)? SIGSUBPKT_FLAG_CRITICAL:0), + buf, 8+n1+n2 ); + m_free(expanded); + m_free(buf); + } - /* put hash into a S-Exp s_hash */ - if ( gcry_sexp_build( &s_hash, NULL, "%m", hash ) ) - BUG (); + if(opt.show_notation) + show_notation(sig,0); - rc = gcry_pk_sign( &s_sig, s_hash, s_skey ); - gcry_sexp_release( s_hash ); - gcry_sexp_release( s_skey ); + /* set policy URL */ + if( IS_SIG(sig) && opt.sig_policy_url ) + { + if(sig->version<4) + log_info("can't put a policy URL into v3 signatures\n"); + else + pu=opt.sig_policy_url; + } + else if( IS_CERT(sig) && opt.cert_policy_url ) + { + if(sig->version<4) + log_info("can't put a policy URL into v3 key signatures\n"); + else + pu=opt.cert_policy_url; + } + + for(;pu;pu=pu->next) + { + string = pu->d; + + s=pct_expando(string,&args); + if(!s) + { + log_error(_("WARNING: unable to %%-expand policy url " + "(too large). Using unexpanded.\n")); + s=m_strdup(string); + } + + build_sig_subpkt(sig,SIGSUBPKT_POLICY| + ((pu->flags & 1)?SIGSUBPKT_FLAG_CRITICAL:0), + s,strlen(s)); + + m_free(s); + } + + if(opt.show_policy_url) + show_policy_url(sig,0); +} - if( rc ) - ; - else { - list = gcry_sexp_find_token( s_sig, "r" , 0 ); - assert( list ); - data[0] = gcry_sexp_nth_mpi( list, 1, 0 ); - assert( data[0] ); - gcry_sexp_release (list); - list = gcry_sexp_find_token( s_sig, "s" , 0 ); - assert( list ); - data[1] = gcry_sexp_nth_mpi( list, 1, 0 ); - assert( data[1] ); - gcry_sexp_release (list); +/* + * Helper to hash a user ID packet. + */ +static void +hash_uid (MD_HANDLE md, int sigversion, const PKT_user_id *uid) +{ + if ( sigversion >= 4 ) { + byte buf[5]; + + if(uid->attrib_data) { + buf[0] = 0xd1; /* indicates an attribute packet */ + buf[1] = uid->attrib_len >> 24; /* always use 4 length bytes */ + buf[2] = uid->attrib_len >> 16; + buf[3] = uid->attrib_len >> 8; + buf[4] = uid->attrib_len; + } + else { + buf[0] = 0xb4; /* indicates a userid packet */ + buf[1] = uid->len >> 24; /* always use 4 length bytes */ + buf[2] = uid->len >> 16; + buf[3] = uid->len >> 8; + buf[4] = uid->len; + } + md_write( md, buf, 5 ); } - - gcry_sexp_release( s_sig ); - return rc; + if(uid->attrib_data) + md_write (md, uid->attrib_data, uid->attrib_len ); + else + md_write (md, uid->name, uid->len ); } -/**************** - * Create a notation. It is assumed that the stings in STRLIST - * are already checked to contain only printable data and have a valid - * NAME=VALUE format. + +/* + * Helper to hash some parts from the signature */ static void -mk_notation_and_policy( PKT_signature *sig ) +hash_sigversion_to_magic (MD_HANDLE md, const PKT_signature *sig) { - const char *string, *s; - byte *buf; - unsigned n1, n2; - - /* notation data */ - if( opt.notation_data && sig->version < 4 ) - log_info("can't put notation data into v3 signatures\n"); - else if( opt.notation_data ) { - STRLIST nd = opt.notation_data; - - for( ; nd; nd = nd->next ) { - string = nd->d; - s = strchr( string, '=' ); - if( !s ) - BUG(); /* we have already parsed this */ - n1 = s - string; - s++; - n2 = strlen(s); - buf = gcry_xmalloc( 8 + n1 + n2 ); - buf[0] = 0x80; /* human readable */ - buf[1] = buf[2] = buf[3] = 0; - buf[4] = n1 >> 8; - buf[5] = n1; - buf[6] = n2 >> 8; - buf[7] = n2; - memcpy(buf+8, string, n1 ); - memcpy(buf+8+n1, s, n2 ); - build_sig_subpkt( sig, SIGSUBPKT_NOTATION - | ((nd->flags & 1)? SIGSUBPKT_FLAG_CRITICAL:0), - buf, 8+n1+n2 ); - } + if (sig->version >= 4) + md_putc (md, sig->version); + md_putc (md, sig->sig_class); + if (sig->version < 4) { + u32 a = sig->timestamp; + md_putc (md, (a >> 24) & 0xff ); + md_putc (md, (a >> 16) & 0xff ); + md_putc (md, (a >> 8) & 0xff ); + md_putc (md, a & 0xff ); } - - /* set policy URL */ - if( (s=opt.set_policy_url) ) { - if( *s == '!' ) - build_sig_subpkt( sig, SIGSUBPKT_POLICY | SIGSUBPKT_FLAG_CRITICAL, - s+1, strlen(s+1) ); - else - build_sig_subpkt( sig, SIGSUBPKT_POLICY, s, strlen(s) ); + else { + byte buf[6]; + size_t n; + + md_putc (md, sig->pubkey_algo); + md_putc (md, sig->digest_algo); + if (sig->hashed) { + n = sig->hashed->len; + md_putc (md, (n >> 8) ); + md_putc (md, n ); + md_write (md, sig->hashed->data, n ); + n += 6; + } + else { + md_putc (md, 0); /* always hash the length of the subpacket*/ + md_putc (md, 0); + n = 6; + } + /* add some magic */ + buf[0] = sig->version; + buf[1] = 0xff; + buf[2] = n >> 24; /* hmmm, n is only 16 bit, so this is always 0 */ + buf[3] = n >> 16; + buf[4] = n >> 8; + buf[5] = n; + md_write (md, buf, 6); } } static int do_sign( PKT_secret_key *sk, PKT_signature *sig, - GCRY_MD_HD md, int digest_algo ) + MD_HANDLE md, int digest_algo ) { MPI frame; byte *dp; @@ -171,32 +259,59 @@ do_sign( PKT_secret_key *sk, PKT_signature *sig, : _("key has been created %lu seconds " "in future (time warp or clock problem)\n"), d ); if( !opt.ignore_time_conflict ) - return GPGERR_TIME_CONFLICT; + return G10ERR_TIME_CONFLICT; } print_pubkey_algo_note(sk->pubkey_algo); if( !digest_algo ) - digest_algo = gcry_md_get_algo(md); + digest_algo = md_get_algo(md); print_digest_algo_note( digest_algo ); - dp = gcry_md_read( md, digest_algo ); + dp = md_read( md, digest_algo ); sig->digest_algo = digest_algo; sig->digest_start[0] = dp[0]; sig->digest_start[1] = dp[1]; frame = encode_md_value( sk->pubkey_algo, md, - digest_algo, gcry_mpi_get_nbits(sk->skey[0]), 0 ); - rc = pk_sign( sk->pubkey_algo, sig->data, frame, sk->skey ); - mpi_release(frame); + digest_algo, mpi_get_nbits(sk->skey[0]), 0 ); + if (!frame) + return G10ERR_GENERAL; + rc = pubkey_sign( sk->pubkey_algo, sig->data, frame, sk->skey ); + mpi_free(frame); + if (!rc && !opt.no_sig_create_check) { + /* check that the signature verification worked and nothing is + * fooling us e.g. by a bug in the signature create + * code or by deliberately introduced faults. */ + PKT_public_key *pk = m_alloc_clear (sizeof *pk); + + if( get_pubkey( pk, sig->keyid ) ) + rc = G10ERR_NO_PUBKEY; + else { + frame = encode_md_value (pk->pubkey_algo, md, + sig->digest_algo, + mpi_get_nbits(pk->pkey[0]), 0); + if (!frame) + rc = G10ERR_GENERAL; + else + rc = pubkey_verify (pk->pubkey_algo, frame, + sig->data, pk->pkey, + NULL, NULL ); + mpi_free (frame); + } + if (rc) + log_error (_("checking created signature failed: %s\n"), + g10_errstr (rc)); + free_public_key (pk); + } if( rc ) - log_error(_("signing failed: %s\n"), gpg_errstr(rc) ); + log_error(_("signing failed: %s\n"), g10_errstr(rc) ); else { if( opt.verbose ) { char *ustr = get_user_id_string( sig->keyid ); log_info(_("%s signature from: %s\n"), - gcry_pk_algo_name(sk->pubkey_algo), ustr ); - gcry_free(ustr); + pubkey_algo_to_string(sk->pubkey_algo), ustr ); + m_free(ustr); } } return rc; @@ -205,16 +320,12 @@ do_sign( PKT_secret_key *sk, PKT_signature *sig, int -complete_sig( PKT_signature *sig, PKT_secret_key *sk, GCRY_MD_HD md ) +complete_sig( PKT_signature *sig, PKT_secret_key *sk, MD_HANDLE md ) { int rc=0; if( !(rc=check_secret_key( sk, 0 )) ) rc = do_sign( sk, sig, md, 0 ); - - /* fixme: should we check whether the signature is okay? - * maybe by using an option */ - return rc; } @@ -223,10 +334,12 @@ hash_for(int pubkey_algo, int packet_version ) { if( opt.def_digest_algo ) return opt.def_digest_algo; - if( pubkey_algo == GCRY_PK_DSA ) - return GCRY_MD_SHA1; - if( pubkey_algo == GCRY_PK_RSA && packet_version < 4 ) - return GCRY_MD_MD5; + if( recipient_digest_algo ) + return recipient_digest_algo; + if( pubkey_algo == PUBKEY_ALGO_DSA ) + return DIGEST_ALGO_SHA1; + if( pubkey_algo == PUBKEY_ALGO_RSA && packet_version < 4 ) + return DIGEST_ALGO_MD5; return DEFAULT_DIGEST_ALGO; } @@ -239,7 +352,7 @@ only_old_style( SK_LIST sk_list ) /* if there are only old style capable key we use the old sytle */ for( sk_rover = sk_list; sk_rover; sk_rover = sk_rover->next ) { PKT_secret_key *sk = sk_rover->sk; - if( sk->pubkey_algo == GCRY_PK_RSA && sk->version < 4 ) + if( sk->pubkey_algo == PUBKEY_ALGO_RSA && sk->version < 4 ) old_style = 1; else return 0; @@ -267,94 +380,210 @@ print_status_sig_created ( PKT_secret_key *sk, PKT_signature *sig, int what ) write_status_text( STATUS_SIG_CREATED, buf ); } + +/* + * Loop over the secret certificates in SK_LIST and build the one pass + * signature packets. OpenPGP says that the data should be bracket by + * the onepass-sig and signature-packet; so we build these onepass + * packet here in reverse order + */ static int -write_one_signature( IOBUF out, PKT_secret_key *sk, int old_style, - const char *outfile, - GCRY_MD_HD datamd, - int sig_class, - int status_char ) +write_onepass_sig_packets (SK_LIST sk_list, IOBUF out, int sigclass ) { - PKT_signature *sig; - GCRY_MD_HD md; - int rc; + int skcount; + SK_LIST sk_rover; - /* build the signature packet */ - /* fixme: this code is partly duplicated in make_keysig_packet */ - sig = gcry_xcalloc( 1, sizeof *sig ); - sig->version = old_style || opt.force_v3_sigs ? 3 : sk->version; - keyid_from_sk( sk, sig->keyid ); - sig->digest_algo = hash_for(sk->pubkey_algo, sk->version); - sig->pubkey_algo = sk->pubkey_algo; - sig->timestamp = make_timestamp(); - sig->sig_class = sig_class; + for (skcount=0, sk_rover=sk_list; sk_rover; sk_rover = sk_rover->next) + skcount++; - md = gcry_md_copy( datamd ); - if( !md ) - BUG(); - if( sig->version >= 4 ) { - build_sig_subpkt_from_sig( sig ); - gcry_md_putc( md, sig->version ); - } - - mk_notation_and_policy( sig ); - - gcry_md_putc( md, sig->sig_class ); - if( sig->version < 4 ) { - u32 a = sig->timestamp; - gcry_md_putc( md, (a >> 24) & 0xff ); - gcry_md_putc( md, (a >> 16) & 0xff ); - gcry_md_putc( md, (a >> 8) & 0xff ); - gcry_md_putc( md, a & 0xff ); - } - else { - byte buf[6]; - size_t n; + for (; skcount; skcount--) { + PKT_secret_key *sk; + PKT_onepass_sig *ops; + PACKET pkt; + int i, rc; - gcry_md_putc( md, sig->pubkey_algo ); - gcry_md_putc( md, sig->digest_algo ); - if( sig->hashed_data ) { - n = (sig->hashed_data[0] << 8) | sig->hashed_data[1]; - gcry_md_write( md, sig->hashed_data, n+2 ); - n += 6; + for (i=0, sk_rover = sk_list; sk_rover; sk_rover = sk_rover->next ) { + if (++i == skcount) + break; } - else { - gcry_md_putc( md, 0 );/* always hash the length of the subpacket*/ - gcry_md_putc( md, 0 ); - n = 6; + + sk = sk_rover->sk; + ops = m_alloc_clear (sizeof *ops); + ops->sig_class = sigclass; + ops->digest_algo = hash_for (sk->pubkey_algo, sk->version); + ops->pubkey_algo = sk->pubkey_algo; + keyid_from_sk (sk, ops->keyid); + ops->last = (skcount == 1); + + init_packet(&pkt); + pkt.pkttype = PKT_ONEPASS_SIG; + pkt.pkt.onepass_sig = ops; + rc = build_packet (out, &pkt); + free_packet (&pkt); + if (rc) { + log_error ("build onepass_sig packet failed: %s\n", + g10_errstr(rc)); + return rc; } - /* add some magic */ - buf[0] = sig->version; - buf[1] = 0xff; - buf[2] = n >> 24; /* hmmm, n is only 16 bit, so this is always 0 */ - buf[3] = n >> 16; - buf[4] = n >> 8; - buf[5] = n; - gcry_md_write( md, buf, 6 ); } - gcry_md_final( md ); - rc = do_sign( sk, sig, md, hash_for(sig->pubkey_algo, sk->version) ); - gcry_md_close( md ); - /* Hmmm: Do we release sig in case of rc != 0? */ - - if( !rc ) { /* and write it */ - PACKET pkt; + return 0; +} - init_packet(&pkt); - pkt.pkttype = PKT_SIGNATURE; - pkt.pkt.signature = sig; - rc = build_packet( out, &pkt ); - if( !rc && is_status_enabled() ) { - print_status_sig_created ( sk, sig, status_char ); +/* + * Helper to write the plaintext (literal data) packet + */ +static int +write_plaintext_packet (IOBUF out, IOBUF inp, const char *fname, int ptmode) +{ + PKT_plaintext *pt = NULL; + u32 filesize; + int rc = 0; + + if (!opt.no_literal) { + if (fname || opt.set_filename) { + char *s = make_basename (opt.set_filename? opt.set_filename + : fname); + pt = m_alloc (sizeof *pt + strlen(s) - 1); + pt->namelen = strlen (s); + memcpy (pt->name, s, pt->namelen); + m_free (s); + } + else { /* no filename */ + pt = m_alloc (sizeof *pt - 1); + pt->namelen = 0; } - free_packet( &pkt ); - if( rc ) - log_error("build signature packet failed: %s\n", gpg_errstr(rc) ); } + /* try to calculate the length of the data */ + if (fname) { + if( !(filesize = iobuf_get_filelength(inp)) ) + log_info (_("WARNING: `%s' is an empty file\n"), fname); + + /* we can't yet encode the length of very large files, + * so we switch to partial length encoding in this case */ + if (filesize >= IOBUF_FILELENGTH_LIMIT) + filesize = 0; + + /* because the text_filter modifies the length of the + * data, it is not possible to know the used length + * without a double read of the file - to avoid that + * we simple use partial length packets. + */ + if ( ptmode == 't' ) + filesize = 0; + } + else { + filesize = opt.set_filesize? opt.set_filesize : 0; /* stdin */ + } + + if (!opt.no_literal) { + PACKET pkt; + + pt->timestamp = make_timestamp (); + pt->mode = ptmode; + pt->len = filesize; + pt->new_ctb = !pt->len && !opt.rfc1991; + pt->buf = inp; + init_packet(&pkt); + pkt.pkttype = PKT_PLAINTEXT; + pkt.pkt.plaintext = pt; + /*cfx.datalen = filesize? calc_packet_length( &pkt ) : 0;*/ + if( (rc = build_packet (out, &pkt)) ) + log_error ("build_packet(PLAINTEXT) failed: %s\n", + g10_errstr(rc) ); + pt->buf = NULL; + } + else { + byte copy_buffer[4096]; + int bytes_copied; + + while ((bytes_copied = iobuf_read(inp, copy_buffer, 4096)) != -1) + if (iobuf_write(out, copy_buffer, bytes_copied) == -1) { + rc = G10ERR_WRITE_FILE; + log_error ("copying input to output failed: %s\n", + g10_errstr(rc)); + break; + } + memset(copy_buffer, 0, 4096); /* burn buffer */ + } + /* fixme: it seems that we never freed pt/pkt */ + return rc; } +/* + * Write the signatures from the SK_LIST to OUT. HASH must be a non-finalized + * hash which will not be changes here. + */ +static int +write_signature_packets (SK_LIST sk_list, IOBUF out, MD_HANDLE hash, + int sigclass, u32 timestamp, u32 duration, + int status_letter) +{ + SK_LIST sk_rover; + + /* loop over the secret certificates */ + for (sk_rover = sk_list; sk_rover; sk_rover = sk_rover->next) { + PKT_secret_key *sk; + PKT_signature *sig; + MD_HANDLE md; + int rc; + + sk = sk_rover->sk; + + /* build the signature packet */ + sig = m_alloc_clear (sizeof *sig); + if(opt.force_v3_sigs || opt.rfc1991) + sig->version=3; + else if(duration || opt.sig_policy_url || opt.sig_notation_data) + sig->version=4; + else + sig->version=sk->version; + keyid_from_sk (sk, sig->keyid); + sig->digest_algo = hash_for (sk->pubkey_algo, sk->version); + sig->pubkey_algo = sk->pubkey_algo; + if(timestamp) + sig->timestamp = timestamp; + else + sig->timestamp = make_timestamp(); + if(duration) + sig->expiredate = sig->timestamp+duration; + sig->sig_class = sigclass; + + md = md_copy (hash); + + if (sig->version >= 4) + build_sig_subpkt_from_sig (sig); + mk_notation_and_policy (sig, NULL, sk); + + hash_sigversion_to_magic (md, sig); + md_final (md); + + rc = do_sign( sk, sig, md, hash_for (sig->pubkey_algo, sk->version) ); + md_close (md); + + if( !rc ) { /* and write it */ + PACKET pkt; + + init_packet(&pkt); + pkt.pkttype = PKT_SIGNATURE; + pkt.pkt.signature = sig; + rc = build_packet (out, &pkt); + if (!rc && is_status_enabled()) { + print_status_sig_created ( sk, sig, status_letter); + } + free_packet (&pkt); + if (rc) + log_error ("build signature packet failed: %s\n", + g10_errstr(rc) ); + } + if( rc ) + return rc;; + } + + return 0; +} /**************** * Sign the files whose names are in FILENAME. @@ -362,7 +591,7 @@ write_one_signature( IOBUF out, PKT_secret_key *sk, int old_style, * make a detached signature. If FILENAMES->d is NULL read from stdin * and ignore the detached mode. Sign the file with all secret keys * which can be taken from LOCUSR, if this is NULL, use the default one - * If ENCRYPT is true, use REMUSER (or ask if it is NULL) to encrypt the + * If ENCRYPTFLAG is true, use REMUSER (or ask if it is NULL) to encrypt the * signed data for these users. * If OUTFILE is not NULL; this file is used for output and the function * does not ask for overwrite permission; output is then always @@ -370,7 +599,7 @@ write_one_signature( IOBUF out, PKT_secret_key *sk, int old_style, */ int sign_file( STRLIST filenames, int detached, STRLIST locusr, - int encrypt, STRLIST remusr, const char *outfile ) + int encryptflag, STRLIST remusr, const char *outfile ) { const char *fname; armor_filter_context_t afx; @@ -380,16 +609,12 @@ sign_file( STRLIST filenames, int detached, STRLIST locusr, encrypt_filter_context_t efx; IOBUF inp = NULL, out = NULL; PACKET pkt; - PKT_plaintext *pt = NULL; - u32 filesize; int rc = 0; PK_LIST pk_list = NULL; SK_LIST sk_list = NULL; SK_LIST sk_rover = NULL; int multifile = 0; - int old_style = opt.rfc1991; - int compr_algo = -1; /* unknown */ - + u32 timestamp=0,duration=0; memset( &afx, 0, sizeof afx); memset( &zfx, 0, sizeof zfx); @@ -405,20 +630,25 @@ sign_file( STRLIST filenames, int detached, STRLIST locusr, else fname = NULL; - if( fname && filenames->next && (!detached || encrypt) ) + if( fname && filenames->next && (!detached || encryptflag) ) log_bug("multiple files can only be detached signed"); - if( (rc=build_sk_list( locusr, &sk_list, 1, GCRY_PK_USAGE_SIGN )) ) + if(opt.ask_sig_expire && !opt.force_v3_sigs && !opt.batch && !opt.rfc1991) + duration=ask_expire_interval(1); + + if( (rc=build_sk_list( locusr, &sk_list, 1, PUBKEY_USAGE_SIG )) ) goto leave; - if( !old_style ) - old_style = only_old_style( sk_list ); - if( encrypt ) { - if( (rc=build_pk_list( remusr, &pk_list, GCRY_PK_USAGE_ENCR )) ) - goto leave; - if( !old_style ) - compr_algo = select_algo_from_prefs( pk_list, PREFTYPE_COMPR ); - } + if(opt.pgp2 && !only_old_style(sk_list)) + { + log_info(_("you can only detach-sign with PGP 2.x style keys " + "while in --pgp2 mode\n")); + log_info(_("this message may not be usable by PGP 2.x\n")); + opt.pgp2=0; + } + + if(encryptflag && (rc=build_pk_list( remusr, &pk_list, PUBKEY_USAGE_ENC ))) + goto leave; /* prepare iobufs */ if( multifile ) /* have list of filenames */ @@ -426,14 +656,14 @@ sign_file( STRLIST filenames, int detached, STRLIST locusr, else if( !(inp = iobuf_open(fname)) ) { log_error("can't open %s: %s\n", fname? fname: "[stdin]", strerror(errno) ); - rc = GPGERR_OPEN_FILE; + rc = G10ERR_OPEN_FILE; goto leave; } if( outfile ) { if( !(out = iobuf_create( outfile )) ) { log_error(_("can't create %s: %s\n"), outfile, strerror(errno) ); - rc = GPGERR_CREATE_FILE; + rc = G10ERR_CREATE_FILE; goto leave; } else if( opt.verbose ) @@ -445,92 +675,108 @@ sign_file( STRLIST filenames, int detached, STRLIST locusr, /* prepare to calculate the MD over the input */ if( opt.textmode && !outfile ) iobuf_push_filter( inp, text_filter, &tfx ); - if( !(mfx.md = gcry_md_open(0, 0))) - BUG(); + mfx.md = md_open(0, 0); + + /* If we're encrypting and signing, it is reasonable to pick the + hash algorithm to use out of the recepient key prefs. */ + if(pk_list) + { + if(opt.def_digest_algo) + { + if(!opt.expert && + select_algo_from_prefs(pk_list,PREFTYPE_HASH, + opt.def_digest_algo, + NULL)!=opt.def_digest_algo) + log_info(_("forcing digest algorithm %s (%d) " + "violates recipient preferences\n"), + digest_algo_to_string(opt.def_digest_algo), + opt.def_digest_algo); + } + else + { + int hashlen=0,algo; + + /* Of course, if the recipient asks for something + unreasonable (like a non-160-bit hash for DSA, for + example), then don't do it. Check all sk's - if any + are DSA, then the hash must be 160-bit. In the future + this can be more complex with different hashes for each + sk, but so long as there is only one signing algorithm + with hash restrictions, this is ok. -dms */ + + for( sk_rover = sk_list; sk_rover; sk_rover = sk_rover->next ) + if(sk_rover->sk->pubkey_algo==PUBKEY_ALGO_DSA) + hashlen=20; + + if((algo= + select_algo_from_prefs(pk_list,PREFTYPE_HASH,-1,&hashlen))>0) + recipient_digest_algo=algo; + } + } for( sk_rover = sk_list; sk_rover; sk_rover = sk_rover->next ) { PKT_secret_key *sk = sk_rover->sk; - gcry_md_enable(mfx.md, hash_for(sk->pubkey_algo, sk->version )); + md_enable(mfx.md, hash_for(sk->pubkey_algo, sk->version )); } if( !multifile ) iobuf_push_filter( inp, md_filter, &mfx ); - if( detached && !encrypt && !opt.rfc1991 ) + if( detached && !encryptflag && !opt.rfc1991 ) afx.what = 2; if( opt.armor && !outfile ) iobuf_push_filter( out, armor_filter, &afx ); - #ifdef ENABLE_COMMENT_PACKETS - else { - write_comment( out, "#created by GNUPG v" VERSION " (" - PRINTABLE_OS_NAME ")"); - if( opt.comment_string ) - write_comment( out, opt.comment_string ); - } - #endif - if( encrypt ) { + + if( encryptflag ) { efx.pk_list = pk_list; /* fixme: set efx.cfx.datalen if known */ iobuf_push_filter( out, encrypt_filter, &efx ); } - /* Select a compress algorithm */ - if( opt.compress && !outfile && ( !detached || opt.compress_sigs) ) { - if( !compr_algo ) - ; /* don't use compression */ - else { - if( old_style - || compr_algo == 1 - || (compr_algo == -1 && !encrypt) ) - zfx.algo = 1; /* use the non optional algorithm */ + if( opt.compress && !outfile && ( !detached || opt.compress_sigs) ) + { + int compr_algo=opt.def_compress_algo; + + /* If not forced by user */ + if(compr_algo==-1) + { + /* If we're not encrypting, then select_algo_from_prefs + will fail and we'll end up with the default. If we are + encrypting, select_algo_from_prefs cannot fail since + there is an assumed preference for uncompressed data. + Still, if it did fail, we'll also end up with the + default. */ + + if((compr_algo= + select_algo_from_prefs(pk_list,PREFTYPE_ZIP,-1,NULL))==-1) + compr_algo=DEFAULT_COMPRESS_ALGO; + } + else if(!opt.expert && + select_algo_from_prefs(pk_list,PREFTYPE_ZIP, + compr_algo,NULL)!=compr_algo) + log_info(_("forcing compression algorithm %s (%d) " + "violates recipient preferences\n"), + compress_algo_to_string(compr_algo),compr_algo); + + /* algo 0 means no compression */ + if( compr_algo ) + { + zfx.algo = compr_algo; iobuf_push_filter( out, compress_filter, &zfx ); - } - } - - /* Build one-pass signature packets when needed */ - if( !detached && !old_style ) { - int skcount=0; - /* loop over the secret certificates and build headers - * The specs now say that the data should be bracket by - * the onepass-sig and signature-packet; so we must build it - * here in reverse order */ - for( sk_rover = sk_list; sk_rover; sk_rover = sk_rover->next ) - skcount++; - for( ; skcount; skcount-- ) { - PKT_secret_key *sk; - PKT_onepass_sig *ops; - int i = 0; - - for( sk_rover = sk_list; sk_rover; sk_rover = sk_rover->next ) - if( ++i == skcount ) - break; - - sk = sk_rover->sk; - ops = gcry_xcalloc( 1, sizeof *ops ); - ops->sig_class = opt.textmode && !outfile ? 0x01 : 0x00; - ops->digest_algo = hash_for(sk->pubkey_algo, sk->version); - ops->pubkey_algo = sk->pubkey_algo; - keyid_from_sk( sk, ops->keyid ); - ops->last = skcount == 1; - - init_packet(&pkt); - pkt.pkttype = PKT_ONEPASS_SIG; - pkt.pkt.onepass_sig = ops; - rc = build_packet( out, &pkt ); - free_packet( &pkt ); - if( rc ) { - log_error("build onepass_sig packet failed: %s\n", - gpg_errstr(rc)); - goto leave; - } - } + } + } + + /* Write the one-pass signature packets if needed */ + if (!detached && !opt.rfc1991) { + rc = write_onepass_sig_packets (sk_list, out, + opt.textmode && !outfile ? 0x01:0x00); + if (rc) + goto leave; } /* setup the inner packet */ if( detached ) { - /* this is pretty much the same for old and new PGP. So no - * need to cope with different packet ordering */ if( multifile ) { STRLIST sl; @@ -542,7 +788,7 @@ sign_file( STRLIST filenames, int detached, STRLIST locusr, if( !(inp = iobuf_open(sl->d)) ) { log_error(_("can't open %s: %s\n"), sl->d, strerror(errno) ); - rc = GPGERR_OPEN_FILE; + rc = G10ERR_OPEN_FILE; goto leave; } if( opt.verbose ) @@ -562,92 +808,35 @@ sign_file( STRLIST filenames, int detached, STRLIST locusr, } } else { - /* get the filename to be stored into the literal datapacket */ - if (!opt.no_literal) { - if( fname || opt.set_filename ) { - char *s = make_basename( opt.set_filename ? - opt.set_filename : fname ); - pt = gcry_xmalloc( sizeof *pt + strlen(s) - 1 ); - pt->namelen = strlen(s); - memcpy(pt->name, s, pt->namelen ); - gcry_free(s); - } - else { /* no filename */ - pt = gcry_xmalloc( sizeof *pt - 1 ); - pt->namelen = 0; - } - } - - if( fname ) { - if( !(filesize = iobuf_get_filelength(inp)) ) - log_info(_("WARNING: `%s' is an empty file\n"), fname ); - /* we can't yet encode the length of very large files, - * so we switch to partial length encoding in this case */ - if ( filesize >= IOBUF_FILELENGTH_LIMIT ) - filesize = 0; - - /* Because the text_filter modifies the length of the - * data, it is not possible to know the used length - * without a double read of the file - to avoid that - * we simple use partial length packets. - */ - if( opt.textmode && !outfile ) - filesize = 0; - } - else - filesize = opt.set_filesize ? opt.set_filesize : 0; /* stdin */ - - if (!opt.no_literal) { - pt->timestamp = make_timestamp(); - pt->mode = opt.textmode && !outfile ? 't':'b'; - pt->len = filesize; - pt->new_ctb = !pt->len && !opt.rfc1991; - pt->buf = inp; - pkt.pkttype = PKT_PLAINTEXT; - pkt.pkt.plaintext = pt; - /*cfx.datalen = filesize? calc_packet_length( &pkt ) : 0;*/ - if( (rc = build_packet( out, &pkt )) ) - log_error("build_packet(PLAINTEXT) failed: %s\n", - gpg_errstr(rc) ); - pt->buf = NULL; - } - else { - byte copy_buffer[4096]; - int bytes_copied; - while ((bytes_copied = iobuf_read(inp, copy_buffer, 4096)) != -1) - if (iobuf_write(out, copy_buffer, bytes_copied) == -1) { - rc = GPGERR_WRITE_FILE; - log_error("copying input to output failed: %s\n", - gpg_errstr(rc)); - break; - } - memset(copy_buffer, 0, 4096); /* burn buffer */ - } + rc = write_plaintext_packet (out, inp, fname, + opt.textmode && !outfile ? 't':'b'); } /* catch errors from above */ if (rc) goto leave; - /* write all the signature packets */ - for( sk_rover = sk_list; sk_rover && !rc ; sk_rover = sk_rover->next ) { - rc = write_one_signature( out, sk_rover->sk, - old_style, outfile, mfx.md, + /* write the signatures */ + rc = write_signature_packets (sk_list, out, mfx.md, opt.textmode && !outfile? 0x01 : 0x00, - detached ? 'D':'S' ); - } + timestamp, duration, detached ? 'D':'S'); + if( rc ) + goto leave; leave: if( rc ) iobuf_cancel(out); - else + else { iobuf_close(out); + if (encryptflag) + write_status( STATUS_END_ENCRYPTION ); + } iobuf_close(inp); - gcry_md_close( mfx.md ); + md_close( mfx.md ); release_sk_list( sk_list ); release_pk_list( pk_list ); - /* FIXME: Did we release the efx.cfx.dek ? */ + recipient_digest_algo=0; return rc; } @@ -660,7 +849,7 @@ int clearsign_file( const char *fname, STRLIST locusr, const char *outfile ) { armor_filter_context_t afx; - GCRY_MD_HD textmd = NULL; + MD_HANDLE textmd = NULL; IOBUF inp = NULL, out = NULL; PACKET pkt; int rc = 0; @@ -668,27 +857,40 @@ clearsign_file( const char *fname, STRLIST locusr, const char *outfile ) SK_LIST sk_rover = NULL; int old_style = opt.rfc1991; int only_md5 = 0; + u32 timestamp=0,duration=0; memset( &afx, 0, sizeof afx); init_packet( &pkt ); - if( (rc=build_sk_list( locusr, &sk_list, 1, GCRY_PK_USAGE_SIGN )) ) + if(opt.ask_sig_expire && !opt.force_v3_sigs && !opt.batch && !opt.rfc1991) + duration=ask_expire_interval(1); + + if( (rc=build_sk_list( locusr, &sk_list, 1, PUBKEY_USAGE_SIG )) ) goto leave; - if( !old_style ) + + if( !old_style && !duration ) old_style = only_old_style( sk_list ); + if(!old_style && opt.pgp2) + { + log_info(_("you can only clearsign with PGP 2.x style keys " + "while in --pgp2 mode\n")); + log_info(_("this message may not be usable by PGP 2.x\n")); + opt.pgp2=0; + } + /* prepare iobufs */ if( !(inp = iobuf_open(fname)) ) { log_error("can't open %s: %s\n", fname? fname: "[stdin]", strerror(errno) ); - rc = GPGERR_OPEN_FILE; + rc = G10ERR_OPEN_FILE; goto leave; } if( outfile ) { if( !(out = iobuf_create( outfile )) ) { log_error(_("can't create %s: %s\n"), outfile, strerror(errno) ); - rc = GPGERR_CREATE_FILE; + rc = G10ERR_CREATE_FILE; goto leave; } else if( opt.verbose ) @@ -701,7 +903,7 @@ clearsign_file( const char *fname, STRLIST locusr, const char *outfile ) for( sk_rover = sk_list; sk_rover; sk_rover = sk_rover->next ) { PKT_secret_key *sk = sk_rover->sk; - if( hash_for(sk->pubkey_algo, sk->version) == GCRY_MD_MD5 ) + if( hash_for(sk->pubkey_algo, sk->version) == DIGEST_ALGO_MD5 ) only_md5 = 1; else { only_md5 = 0; @@ -709,9 +911,8 @@ clearsign_file( const char *fname, STRLIST locusr, const char *outfile ) } } - if( old_style && only_md5 ) - iobuf_writestr(out, "\n" ); - else { + if( !(old_style && only_md5) ) { + const char *s; int any = 0; byte hashs_seen[256]; @@ -722,33 +923,32 @@ clearsign_file( const char *fname, STRLIST locusr, const char *outfile ) int i = hash_for(sk->pubkey_algo, sk->version); if( !hashs_seen[ i & 0xff ] ) { - if( !openpgp_md_test_algo( i ) ) { + s = digest_algo_to_string( i ); + if( s ) { hashs_seen[ i & 0xff ] = 1; if( any ) iobuf_put(out, ',' ); - iobuf_writestr(out, gcry_md_algo_name( i ) ); + iobuf_writestr(out, s ); any = 1; } } } assert(any); - iobuf_writestr(out, "\n" ); - if( opt.not_dash_escaped ) - iobuf_writestr( out, - "NotDashEscaped: You need GnuPG to verify this message\n" ); - iobuf_writestr(out, "\n" ); + iobuf_writestr(out, LF ); } + if( opt.not_dash_escaped ) + iobuf_writestr( out, + "NotDashEscaped: You need GnuPG to verify this message" LF ); + iobuf_writestr(out, LF ); - textmd = gcry_md_open(0, 0); - if( !textmd ) - BUG(); + textmd = md_open(0, 0); for( sk_rover = sk_list; sk_rover; sk_rover = sk_rover->next ) { PKT_secret_key *sk = sk_rover->sk; - gcry_md_enable(textmd, hash_for(sk->pubkey_algo, sk->version)); + md_enable(textmd, hash_for(sk->pubkey_algo, sk->version)); } if ( DBG_HASHING ) - gcry_md_start_debug( textmd, "clearsign" ); + md_start_debug( textmd, "clearsign" ); copy_clearsig_text( out, inp, textmd, !opt.not_dash_escaped, opt.escape_from, old_style ); /* fixme: check for read errors */ @@ -757,13 +957,11 @@ clearsign_file( const char *fname, STRLIST locusr, const char *outfile ) afx.what = 2; iobuf_push_filter( out, armor_filter, &afx ); - /* write all the signature packets */ - for( sk_rover = sk_list; sk_rover && !rc ; sk_rover = sk_rover->next ) { - rc = write_one_signature( out, sk_rover->sk, - old_style, outfile, textmd, - 0x01, - 'C' ); - } + /* write the signatures */ + rc = write_signature_packets (sk_list, out, textmd, 0x01, + timestamp, duration, 'C'); + if( rc ) + goto leave; leave: if( rc ) @@ -771,68 +969,249 @@ clearsign_file( const char *fname, STRLIST locusr, const char *outfile ) else iobuf_close(out); iobuf_close(inp); - gcry_md_close( textmd ); + md_close( textmd ); release_sk_list( sk_list ); return rc; } +/* + * Sign and conventionally encrypt the given file. + * FIXME: Far too much code is duplicated - revamp the whole file. + */ +int +sign_symencrypt_file (const char *fname, STRLIST locusr) +{ + armor_filter_context_t afx; + compress_filter_context_t zfx; + md_filter_context_t mfx; + text_filter_context_t tfx; + cipher_filter_context_t cfx; + IOBUF inp = NULL, out = NULL; + PACKET pkt; + STRING2KEY *s2k = NULL; + int rc = 0; + SK_LIST sk_list = NULL; + SK_LIST sk_rover = NULL; + int algo; + u32 timestamp=0,duration=0; + + memset( &afx, 0, sizeof afx); + memset( &zfx, 0, sizeof zfx); + memset( &mfx, 0, sizeof mfx); + memset( &tfx, 0, sizeof tfx); + memset( &cfx, 0, sizeof cfx); + init_packet( &pkt ); + + if(opt.ask_sig_expire && !opt.force_v3_sigs && !opt.batch && !opt.rfc1991) + duration=ask_expire_interval(1); + + rc = build_sk_list (locusr, &sk_list, 1, PUBKEY_USAGE_SIG); + if (rc) + goto leave; + + /* prepare iobufs */ + inp = iobuf_open(fname); + if( !inp ) { + log_error("can't open %s: %s\n", fname? fname: "[stdin]", + strerror(errno) ); + rc = G10ERR_OPEN_FILE; + goto leave; + } + + /* prepare key */ + s2k = m_alloc_clear( sizeof *s2k ); + s2k->mode = opt.rfc1991? 0:opt.s2k_mode; + s2k->hash_algo = opt.def_digest_algo ? opt.def_digest_algo + : opt.s2k_digest_algo; + + algo = opt.def_cipher_algo ? opt.def_cipher_algo : opt.s2k_cipher_algo; + if (!opt.quiet || !opt.batch) + log_info (_("%s encryption will be used\n"), + cipher_algo_to_string(algo) ); + cfx.dek = passphrase_to_dek( NULL, 0, algo, s2k, 2, NULL ); + + if (!cfx.dek || !cfx.dek->keylen) { + rc = G10ERR_PASSPHRASE; + log_error(_("error creating passphrase: %s\n"), g10_errstr(rc) ); + goto leave; + } + + /* now create the outfile */ + rc = open_outfile (fname, opt.armor? 1:0, &out); + if (rc) + goto leave; + + /* prepare to calculate the MD over the input */ + if (opt.textmode) + iobuf_push_filter (inp, text_filter, &tfx); + mfx.md = md_open(0, 0); + + for (sk_rover = sk_list; sk_rover; sk_rover = sk_rover->next) { + PKT_secret_key *sk = sk_rover->sk; + md_enable (mfx.md, hash_for (sk->pubkey_algo, sk->version )); + } + + iobuf_push_filter (inp, md_filter, &mfx); + + /* Push armor output filter */ + if (opt.armor) + iobuf_push_filter (out, armor_filter, &afx); + + /* Write the symmetric key packet */ + /*(current filters: armor)*/ + if (!opt.rfc1991) { + PKT_symkey_enc *enc = m_alloc_clear( sizeof *enc ); + enc->version = 4; + enc->cipher_algo = cfx.dek->algo; + enc->s2k = *s2k; + pkt.pkttype = PKT_SYMKEY_ENC; + pkt.pkt.symkey_enc = enc; + if( (rc = build_packet( out, &pkt )) ) + log_error("build symkey packet failed: %s\n", g10_errstr(rc) ); + m_free(enc); + } + + /* Push the encryption filter */ + iobuf_push_filter( out, cipher_filter, &cfx ); + + /* Push the Zip filter */ + if (opt.compress) + { + int compr_algo=opt.def_compress_algo; + + /* Default */ + if(compr_algo==-1) + compr_algo=DEFAULT_COMPRESS_ALGO; + + if (compr_algo) + { + zfx.algo = compr_algo; + iobuf_push_filter( out, compress_filter, &zfx ); + } + } + + /* Write the one-pass signature packets */ + /*(current filters: zip - encrypt - armor)*/ + if (!opt.rfc1991) { + rc = write_onepass_sig_packets (sk_list, out, + opt.textmode? 0x01:0x00); + if (rc) + goto leave; + } + + /* Pipe data through all filters; i.e. write the signed stuff */ + /*(current filters: zip - encrypt - armor)*/ + rc = write_plaintext_packet (out, inp, fname, opt.textmode ? 't':'b'); + if (rc) + goto leave; + + /* Write the signatures */ + /*(current filters: zip - encrypt - armor)*/ + rc = write_signature_packets (sk_list, out, mfx.md, + opt.textmode? 0x01 : 0x00, + timestamp, duration, 'S'); + if( rc ) + goto leave; + + + leave: + if( rc ) + iobuf_cancel(out); + else { + iobuf_close(out); + write_status( STATUS_END_ENCRYPTION ); + } + iobuf_close(inp); + release_sk_list( sk_list ); + md_close( mfx.md ); + m_free(cfx.dek); + m_free(s2k); + return rc; +} + /**************** - * Create a signature packet for the given public key certificate - * and the user id and return it in ret_sig. User signature class SIGCLASS - * user-id is not used (and may be NULL if sigclass is 0x20) - * If digest_algo is 0 the function selects an appropriate one. - */ + * Create a signature packet for the given public key certificate and + * the user id and return it in ret_sig. User signature class SIGCLASS + * user-id is not used (and may be NULL if sigclass is 0x20) If + * DIGEST_ALGO is 0 the function selects an appropriate one. + * SIGVERSION gives the minimal required signature packet version; + * this is needed so that special properties like local sign are not + * applied (actually: dropped) when a v3 key is used. TIMESTAMP is + * the timestamp to use for the signature. 0 means "now" */ int make_keysig_packet( PKT_signature **ret_sig, PKT_public_key *pk, PKT_user_id *uid, PKT_public_key *subpk, PKT_secret_key *sk, int sigclass, int digest_algo, + int sigversion, u32 timestamp, u32 duration, int (*mksubpkt)(PKT_signature *, void *), void *opaque ) { PKT_signature *sig; int rc=0; - GCRY_MD_HD md; + MD_HANDLE md; - assert( (sigclass >= 0x10 && sigclass <= 0x13) + assert( (sigclass >= 0x10 && sigclass <= 0x13) || sigclass == 0x1F || sigclass == 0x20 || sigclass == 0x18 || sigclass == 0x30 || sigclass == 0x28 ); - if( !digest_algo ) { - switch( sk->pubkey_algo ) { - case GCRY_PK_DSA: digest_algo = GCRY_MD_SHA1; break; - case GCRY_PK_RSA_S: - case GCRY_PK_RSA: digest_algo = GCRY_MD_MD5; break; - default: digest_algo = GCRY_MD_RMD160; break; - } - } - if( !(md = gcry_md_open( digest_algo, 0 ))) - BUG(); + + if (opt.force_v4_certs) + sigversion = 4; + + if (sigversion < sk->version) + sigversion = sk->version; + + /* If you are making a signature on a v4 key using your v3 key, it + doesn't make sense to generate a v3 sig. After all, no v3-only + PGP implementation could understand the v4 key in the first + place. */ + if (sigversion < pk->version) + sigversion = pk->version; + + if( !digest_algo ) + { + /* Basically, this means use SHA1 always unless it's a v3 RSA + key making a v3 cert (use MD5), or the user specified + something (use whatever they said). They still must use a + 160-bit hash with DSA, or the signature will fail. Note + that this still allows the caller of make_keysig_packet to + override the user setting if it must. */ + + if(opt.cert_digest_algo) + digest_algo=opt.cert_digest_algo; + else if((sk->pubkey_algo==PUBKEY_ALGO_RSA || + sk->pubkey_algo==PUBKEY_ALGO_RSA_S) && + pk->version<4 && sigversion < 4) + digest_algo = DIGEST_ALGO_MD5; + else + digest_algo = DIGEST_ALGO_SHA1; + } + + md = md_open( digest_algo, 0 ); /* hash the public key certificate and the user id */ hash_public_key( md, pk ); if( sigclass == 0x18 || sigclass == 0x28 ) { /* subkey binding/revocation*/ hash_public_key( md, subpk ); } - else if( sigclass != 0x20 ) { - if( sk->version >=4 ) { - byte buf[5]; - buf[0] = 0xb4; /* indicates a userid packet */ - buf[1] = uid->len >> 24; /* always use 4 length bytes */ - buf[2] = uid->len >> 16; - buf[3] = uid->len >> 8; - buf[4] = uid->len; - gcry_md_write( md, buf, 5 ); - } - gcry_md_write( md, uid->name, uid->len ); + else if( sigclass != 0x1F && sigclass != 0x20 ) { + hash_uid (md, sigversion, uid); } /* and make the signature packet */ - sig = gcry_xcalloc( 1, sizeof *sig ); - sig->version = sk->version; + sig = m_alloc_clear( sizeof *sig ); + sig->version = sigversion; + sig->flags.exportable=1; + sig->flags.revocable=1; keyid_from_sk( sk, sig->keyid ); sig->pubkey_algo = sk->pubkey_algo; sig->digest_algo = digest_algo; - sig->timestamp = make_timestamp(); + if(timestamp) + sig->timestamp=timestamp; + else + sig->timestamp=make_timestamp(); + if(duration) + sig->expiredate=sig->timestamp+duration; sig->sig_class = sigclass; if( sig->version >= 4 ) build_sig_subpkt_from_sig( sig ); @@ -841,49 +1220,14 @@ make_keysig_packet( PKT_signature **ret_sig, PKT_public_key *pk, rc = (*mksubpkt)( sig, opaque ); if( !rc ) { - mk_notation_and_policy( sig ); - if( sig->version >= 4 ) - gcry_md_putc( md, sig->version ); - gcry_md_putc( md, sig->sig_class ); - if( sig->version < 4 ) { - u32 a = sig->timestamp; - gcry_md_putc( md, (a >> 24) & 0xff ); - gcry_md_putc( md, (a >> 16) & 0xff ); - gcry_md_putc( md, (a >> 8) & 0xff ); - gcry_md_putc( md, a & 0xff ); - } - else { - byte buf[6]; - size_t n; - - gcry_md_putc( md, sig->pubkey_algo ); - gcry_md_putc( md, sig->digest_algo ); - if( sig->hashed_data ) { - n = (sig->hashed_data[0] << 8) | sig->hashed_data[1]; - gcry_md_write( md, sig->hashed_data, n+2 ); - n += 6; - } - else { - gcry_md_putc( md, 0 ); /* always hash the length of the subpacket*/ - gcry_md_putc( md, 0 ); - n = 6; - } - /* add some magic */ - buf[0] = sig->version; - buf[1] = 0xff; - buf[2] = n >> 24; /* hmmm, n is only 16 bit, so this is always 0 */ - buf[3] = n >> 16; - buf[4] = n >> 8; - buf[5] = n; - gcry_md_write( md, buf, 6 ); - - } - gcry_md_final(md); + mk_notation_and_policy( sig, pk, sk ); + hash_sigversion_to_magic (md, sig); + md_final(md); rc = complete_sig( sig, sk, md ); } - gcry_md_close( md ); + md_close( md ); if( rc ) free_seckey_enc( sig ); else @@ -892,3 +1236,63 @@ make_keysig_packet( PKT_signature **ret_sig, PKT_public_key *pk, } + +/**************** + * Create a new signature packet based on an existing one. + * Only user ID signatures are supported for now. + * TODO: Merge this with make_keysig_packet. + */ +int +update_keysig_packet( PKT_signature **ret_sig, + PKT_signature *orig_sig, + PKT_public_key *pk, + PKT_user_id *uid, + PKT_secret_key *sk, + int (*mksubpkt)(PKT_signature *, void *), + void *opaque + ) +{ + PKT_signature *sig; + int rc=0; + MD_HANDLE md; + + if (!orig_sig || !pk || !uid || !sk) + return G10ERR_GENERAL; + if (orig_sig->sig_class < 0x10 || orig_sig->sig_class > 0x13 ) + return G10ERR_GENERAL; + + md = md_open( orig_sig->digest_algo, 0 ); + + /* hash the public key certificate and the user id */ + hash_public_key( md, pk ); + hash_uid (md, orig_sig->version, uid); + + /* create a new signature packet */ + sig = copy_signature (NULL, orig_sig); + if ( sig->version >= 4 && mksubpkt) + rc = (*mksubpkt)(sig, opaque); + + /* we increase the timestamp by one second so that a future import + of this key will replace the existing one. We also make sure that + we don't create a timestamp in the future */ + sig->timestamp++; + while (sig->timestamp >= make_timestamp()) + sleep (1); + /* put the updated timestamp back into the data */ + if( sig->version >= 4 ) + build_sig_subpkt_from_sig( sig ); + + if (!rc) { + hash_sigversion_to_magic (md, sig); + md_final(md); + + rc = complete_sig( sig, sk, md ); + } + + md_close (md); + if( rc ) + free_seckey_enc (sig); + else + *ret_sig = sig; + return rc; +} |