gpg: Implemented latest rfc4880bis version 5 packet hashing.
authorWerner Koch <wk@gnupg.org>
Tue, 12 Mar 2019 10:09:52 +0000 (11:09 +0100)
committerWerner Koch <wk@gnupg.org>
Thu, 14 Mar 2019 10:26:54 +0000 (11:26 +0100)
* 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 <wk@gnupg.org>
configure.ac
g10/sig-check.c
g10/sign.c

index 89ef939..41dedf7 100644 (file)
@@ -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 <stdio.h>
index d02c68e..e8782f9 100644 (file)
@@ -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 );
 
index b2d1c18..67556d8 100644 (file)
 #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);
     }