From a21ca3a1eff4722dea778cca4abe14a873ccebdf Mon Sep 17 00:00:00 2001 From: Werner Koch Date: Tue, 12 Mar 2019 11:09:52 +0100 Subject: [PATCH] gpg: Implemented latest rfc4880bis version 5 packet hashing. * configure.ac (AC_CHECK_SIZEOF): Test size_t. * g10/sig-check.c (check_signature_end_simple): Support v5 signatures as per current rfc4880bis. For correctness also allow for N > 2^32. * g10/sign.c (pt_extra_hash_data_t): New. (hash_sigversion_to_magic): New arg EXTRAHASH. (write_plaintext_packet): New arg R_EXTRAHASH. (write_signature_packets): Pass EXTRAHASH. (sign_file): Ditto. (sign_symencrypt_file): Ditto. -- Take care: The code path for v5 sigs has not yet been tested. Signed-off-by: Werner Koch --- configure.ac | 1 + g10/sig-check.c | 50 ++++++++++++++------- g10/sign.c | 113 ++++++++++++++++++++++++++++++++++++++++-------- 3 files changed, 131 insertions(+), 33 deletions(-) diff --git a/configure.ac b/configure.ac index 89ef9398e..41dedf72b 100644 --- a/configure.ac +++ b/configure.ac @@ -1367,6 +1367,7 @@ AC_CHECK_SIZEOF(unsigned short) AC_CHECK_SIZEOF(unsigned int) AC_CHECK_SIZEOF(unsigned long) AC_CHECK_SIZEOF(unsigned long long) +AC_CHECK_SIZEOF(size_t) AC_HEADER_TIME AC_CHECK_SIZEOF(time_t,,[[ #include diff --git a/g10/sig-check.c b/g10/sig-check.c index d02c68e33..e8782f90d 100644 --- a/g10/sig-check.c +++ b/g10/sig-check.c @@ -513,6 +513,7 @@ check_signature_end_simple (PKT_public_key *pk, PKT_signature *sig, byte buf[10]; int i; size_t n; + gcry_md_putc (digest, sig->pubkey_algo); gcry_md_putc (digest, sig->digest_algo); if (sig->hashed) @@ -531,22 +532,39 @@ check_signature_end_simple (PKT_public_key *pk, PKT_signature *sig, gcry_md_putc (digest, 0); n = 6; } - /* add some magic per Section 5.2.4 of RFC 4880. */ - i = 0; - buf[i++] = sig->version; - buf[i++] = 0xff; - if (sig->version >= 5) - { - buf[i++] = 0; - buf[i++] = 0; - buf[i++] = 0; - buf[i++] = 0; - } - buf[i++] = n >> 24; - buf[i++] = n >> 16; - buf[i++] = n >> 8; - buf[i++] = n; - gcry_md_write (digest, buf, i); + /* Hash data from the literal data packet. */ + if (sig->version >= 5 + && (sig->sig_class == 0x00 || sig->sig_class == 0x01)) + { + /* - One octet content format + * - File name (one octet length followed by the name) + * - Four octet timestamp */ + memset (buf, 0, 6); + gcry_md_write (digest, buf, 6); + } + /* Add some magic per Section 5.2.4 of RFC 4880. */ + i = 0; + buf[i++] = sig->version; + buf[i++] = 0xff; + if (sig->version >= 5) + { +#if SIZEOF_SIZE_T > 4 + buf[i++] = n >> 56; + buf[i++] = n >> 48; + buf[i++] = n >> 40; + buf[i++] = n >> 32; +#else + buf[i++] = 0; + buf[i++] = 0; + buf[i++] = 0; + buf[i++] = 0; +#endif + } + buf[i++] = n >> 24; + buf[i++] = n >> 16; + buf[i++] = n >> 8; + buf[i++] = n; + gcry_md_write (digest, buf, i); } gcry_md_final( digest ); diff --git a/g10/sign.c b/g10/sign.c index b2d1c1826..67556d8ba 100644 --- a/g10/sign.c +++ b/g10/sign.c @@ -49,7 +49,19 @@ #define LF "\n" #endif -static int recipient_digest_algo=0; +/* A type for the extra data we hash into v5 signature packets. */ +struct pt_extra_hash_data_s +{ + unsigned char mode; + u32 timestamp; + unsigned char namelen; + char name[1]; +}; +typedef struct pt_extra_hash_data_s *pt_extra_hash_data_t; + + +/* Hack */ +static int recipient_digest_algo; /* @@ -216,10 +228,13 @@ hash_uid (gcry_md_hd_t md, int sigversion, const PKT_user_id *uid) /* - * Helper to hash some parts from the signature + * Helper to hash some parts from the signature. EXTRAHASH gives the + * extra data to be hashed into v5 signatures; it may by NULL for + * detached signatures. */ static void -hash_sigversion_to_magic (gcry_md_hd_t md, const PKT_signature *sig) +hash_sigversion_to_magic (gcry_md_hd_t md, const PKT_signature *sig, + pt_extra_hash_data_t extrahash) { byte buf[10]; int i; @@ -243,12 +258,39 @@ hash_sigversion_to_magic (gcry_md_hd_t md, const PKT_signature *sig) gcry_md_putc (md, 0); n = 6; } + /* Hash data from the literal data packet. */ + if (sig->version >= 5 && (sig->sig_class == 0x00 || sig->sig_class == 0x01)) + { + /* - One octet content format + * - File name (one octet length followed by the name) + * - Four octet timestamp */ + if (extrahash) + { + buf[0] = extrahash->mode; + buf[1] = extrahash->namelen; + gcry_md_write (md, buf, 2); + if (extrahash->namelen) + gcry_md_write (md, extrahash->name, extrahash->namelen); + buf[0] = extrahash->timestamp >> 24; + buf[1] = extrahash->timestamp >> 16; + buf[2] = extrahash->timestamp >> 8; + buf[3] = extrahash->timestamp; + gcry_md_write (md, buf, 4); + } + else /* Detached signatures */ + { + memset (buf, 0, 6); + gcry_md_write (md, buf, 6); + } + } /* Add some magic. */ i = 0; buf[i++] = sig->version; buf[i++] = 0xff; if (sig->version >= 5) { + /* Note: We don't hashed any data larger than 2^32 and thus we + * can always use 0 here. See also note below. */ buf[i++] = 0; buf[i++] = 0; buf[i++] = 0; @@ -633,10 +675,14 @@ write_onepass_sig_packets (SK_LIST sk_list, IOBUF out, int sigclass ) /* - * Helper to write the plaintext (literal data) packet + * Helper to write the plaintext (literal data) packet. At + * R_EXTRAHASH a malloced object with the with the extra data hashed + * into v5 signatures is stored. */ static int -write_plaintext_packet (IOBUF out, IOBUF inp, const char *fname, int ptmode) +write_plaintext_packet (iobuf_t out, iobuf_t inp, + const char *fname, int ptmode, + pt_extra_hash_data_t *r_extrahash) { PKT_plaintext *pt = NULL; u32 filesize; @@ -691,6 +737,19 @@ write_plaintext_packet (IOBUF out, IOBUF inp, const char *fname, int ptmode) if ((rc = build_packet (out, &pkt))) log_error ("build_packet(PLAINTEXT) failed: %s\n", gpg_strerror (rc) ); + + *r_extrahash = xtrymalloc (sizeof **r_extrahash + pt->namelen); + if (!*r_extrahash) + rc = gpg_error_from_syserror (); + else + { + (*r_extrahash)->mode = pt->mode; + (*r_extrahash)->timestamp = pt->timestamp; + (*r_extrahash)->namelen = pt->namelen; + /* Note that the last byte or NAME won't be initialized + * becuase we don't need it. */ + memcpy ((*r_extrahash)->name, pt->name, pt->namelen); + } pt->buf = NULL; free_packet (&pkt, NULL); } @@ -699,6 +758,18 @@ write_plaintext_packet (IOBUF out, IOBUF inp, const char *fname, int ptmode) byte copy_buffer[4096]; int bytes_copied; + *r_extrahash = xtrymalloc (sizeof **r_extrahash); + if (!*r_extrahash) + { + rc = gpg_error_from_syserror (); + goto leave; + } + /* FIXME: We need to parse INP to get the to be hashed data from + * it. */ + (*r_extrahash)->mode = 0; + (*r_extrahash)->timestamp = 0; + (*r_extrahash)->namelen = 0; + while ((bytes_copied = iobuf_read (inp, copy_buffer, 4096)) != -1) if ((rc = iobuf_write (out, copy_buffer, bytes_copied))) { @@ -708,19 +779,21 @@ write_plaintext_packet (IOBUF out, IOBUF inp, const char *fname, int ptmode) } wipememory (copy_buffer, 4096); /* burn buffer */ } - /* fixme: it seems that we never freed pt/pkt */ + leave: return rc; } /* - * Write the signatures from the SK_LIST to OUT. HASH must be a non-finalized - * hash which will not be changes here. + * Write the signatures from the SK_LIST to OUT. HASH must be a + * non-finalized hash which will not be changes here. EXTRAHASH is + * either NULL or the extra data tro be hashed into v5 signatures. */ static int write_signature_packets (ctrl_t ctrl, SK_LIST sk_list, IOBUF out, gcry_md_hd_t hash, + pt_extra_hash_data_t extrahash, int sigclass, u32 timestamp, u32 duration, int status_letter, const char *cache_nonce) { @@ -744,7 +817,7 @@ write_signature_packets (ctrl_t ctrl, if (pk->version >= 5) sig->version = 5; /* Required for v5 keys. */ else - sig->version = 4; /*Required. */ + sig->version = 4; /* Required. */ keyid_from_pk (pk, sig->keyid); sig->digest_algo = hash_for (pk); @@ -762,7 +835,7 @@ write_signature_packets (ctrl_t ctrl, build_sig_subpkt_from_sig (sig, pk); mk_notation_policy_etc (sig, NULL, pk); - hash_sigversion_to_magic (md, sig); + hash_sigversion_to_magic (md, sig, extrahash); gcry_md_final (md); rc = do_sign (ctrl, pk, sig, md, hash_for (pk), cache_nonce); @@ -826,6 +899,7 @@ sign_file (ctrl_t ctrl, strlist_t filenames, int detached, strlist_t locusr, SK_LIST sk_rover = NULL; int multifile = 0; u32 duration=0; + pt_extra_hash_data_t extrahash = NULL; pfx = new_progress_context (); afx = new_armor_context (); @@ -1125,7 +1199,8 @@ sign_file (ctrl_t ctrl, strlist_t filenames, int detached, strlist_t locusr, { rc = write_plaintext_packet (out, inp, fname, (opt.textmode && !outfile) ? - (opt.mimemode? 'm' : 't') : 'b'); + (opt.mimemode? 'm' : 't') : 'b', + &extrahash); } /* Catch errors from above. */ @@ -1133,7 +1208,7 @@ sign_file (ctrl_t ctrl, strlist_t filenames, int detached, strlist_t locusr, goto leave; /* Write the signatures. */ - rc = write_signature_packets (ctrl, sk_list, out, mfx.md, + rc = write_signature_packets (ctrl, sk_list, out, mfx.md, extrahash, opt.textmode && !outfile? 0x01 : 0x00, 0, duration, detached ? 'D':'S', NULL); if (rc) @@ -1156,6 +1231,7 @@ sign_file (ctrl_t ctrl, strlist_t filenames, int detached, strlist_t locusr, recipient_digest_algo = 0; release_progress_context (pfx); release_armor_context (afx); + xfree (extrahash); return rc; } @@ -1286,7 +1362,7 @@ clearsign_file (ctrl_t ctrl, push_armor_filter (afx, out); /* Write the signatures. */ - rc = write_signature_packets (ctrl, sk_list, out, textmd, 0x01, 0, + rc = write_signature_packets (ctrl, sk_list, out, textmd, NULL, 0x01, 0, duration, 'C', NULL); if (rc) goto leave; @@ -1328,6 +1404,7 @@ sign_symencrypt_file (ctrl_t ctrl, const char *fname, strlist_t locusr) int algo; u32 duration = 0; int canceled; + pt_extra_hash_data_t extrahash = NULL; pfx = new_progress_context (); afx = new_armor_context (); @@ -1452,13 +1529,14 @@ sign_symencrypt_file (ctrl_t ctrl, const char *fname, strlist_t locusr) /* 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 ? (opt.mimemode?'m':'t'):'b'); + opt.textmode ? (opt.mimemode?'m':'t'):'b', + &extrahash); if (rc) goto leave; /* Write the signatures. */ /* (current filters: zip - encrypt - armor) */ - rc = write_signature_packets (ctrl, sk_list, out, mfx.md, + rc = write_signature_packets (ctrl, sk_list, out, mfx.md, extrahash, opt.textmode? 0x01 : 0x00, 0, duration, 'S', NULL); if (rc) @@ -1480,6 +1558,7 @@ sign_symencrypt_file (ctrl_t ctrl, const char *fname, strlist_t locusr) xfree (s2k); release_progress_context (pfx); release_armor_context (afx); + xfree (extrahash); return rc; } @@ -1599,7 +1678,7 @@ make_keysig_packet (ctrl_t ctrl, if (!rc) { - hash_sigversion_to_magic (md, sig); + hash_sigversion_to_magic (md, sig, NULL); gcry_md_final (md); rc = complete_sig (ctrl, sig, pksk, md, cache_nonce); } @@ -1699,7 +1778,7 @@ update_keysig_packet (ctrl_t ctrl, if (!rc) { - hash_sigversion_to_magic (md, sig); + hash_sigversion_to_magic (md, sig, NULL); gcry_md_final (md); rc = complete_sig (ctrl, sig, pksk, md, NULL); }