[PATCH setup 4/4] Handle multiple signature packets in .sig file

Jon Turney jon.turney@dronecode.org.uk
Tue Feb 25 23:12:00 GMT 2020


Rather than stopping after the first signature packet, handle multiple
signature packets appearing in a .sig file.  If any of them is a valid
signature from a known key, then the signature is good.
---
 crypto.cc | 288 +++++++++++++++++++++++++++++-------------------------
 1 file changed, 156 insertions(+), 132 deletions(-)

diff --git a/crypto.cc b/crypto.cc
index 428b100..2f21cc1 100644
--- a/crypto.cc
+++ b/crypto.cc
@@ -76,6 +76,20 @@ static const char *dsa_data_hash_templ = "(data (flags raw) (hash %s %b))";
 /*  S-expr template for RSA data block to be signed.  */
 static const char *rsa_data_hash_templ = "(data (flags pkcs1) (hash %s %b))";
 
+/*  Information on a key to try */
+struct key_info
+{
+  key_info(std::string _name, bool _builtin, gcry_sexp_t _key, bool _owned=false) :
+    name(_name), builtin(_builtin), key(_key), owned(_owned)
+  {
+  }
+
+  std::string name;
+  bool builtin;  // if true, we don't need to retain this key with add_key_from_sexpr()
+  gcry_sexp_t key;
+  bool owned;    // if true, we own this key and should use gcry_sexp_release() on it
+};
+
 /*  User context data for sig packet walk.  */
 struct sig_data
 {
@@ -97,8 +111,11 @@ struct sig_data
   /*  Converted algo code.  */
   int algo;
 
+  /* Keys */
+  std::vector<struct key_info> *keys_to_try;
+
   /*  Final status.  */
-  bool complete;
+  bool valid;
 };
 
 /*  User context data for key packet walk.  */
@@ -259,6 +276,114 @@ fold_lfs_and_spaces (char *buf, size_t n)
   return ptr2 - buf;
 }
 
+/*  Size and allocate a temp buffer to print a representation
+  of a public key s-expr into, then add that to the extra keys
+  setting so it persists for the next run.  */
+static void
+add_key_from_sexpr (gcry_sexp_t key)
+{
+  size_t n = gcry_sexp_sprint (key, GCRYSEXP_FMT_ADVANCED, 0, ~0);
+  char *sexprbuf = new char[n];
+  n = gcry_sexp_sprint (key, GCRYSEXP_FMT_ADVANCED, sexprbuf, n);
+  // +1 because we want to include the nul-terminator.
+  n = fold_lfs_and_spaces (sexprbuf, n + 1);
+  ExtraKeysSetting::instance().add_key (sexprbuf);
+  MESSAGE ("keep:%d\n'%s'", n, sexprbuf);
+  delete [] sexprbuf;
+}
+
+static bool
+verify_sig(struct sig_data *sigdat, HWND owner)
+{
+  gcry_error_t rv;
+  size_t n;
+  {
+      /*  sig coefficients in s-expr format.  */
+      gcry_sexp_t sig;
+
+      /*  signature hash data in s-expr format.  */
+      gcry_sexp_t hash;
+
+      /* Build everything into s-exprs, and call the libgcrypt verification
+         routine. */
+
+      if (sigdat->pk_alg == RFC4880_PK_DSA)
+        {
+          rv = gcry_sexp_build (&sig, &n, dsa_sig_templ, sigdat->dsa_mpi_r,
+                                sigdat->dsa_mpi_s);
+          if (rv != GPG_ERR_NO_ERROR)
+            {
+              ERRKIND (owner, IDS_CRYPTO_ERROR, rv, "while creating sig s-expr.");
+              return false;
+            }
+
+          rv = gcry_sexp_build (&hash, &n, dsa_data_hash_templ,
+                                gcry_md_algo_name(sigdat->algo),
+                                gcry_md_get_algo_dlen (sigdat->algo),
+                                gcry_md_read (sigdat->md, 0));
+          if (rv != GPG_ERR_NO_ERROR)
+            {
+              ERRKIND (owner, IDS_CRYPTO_ERROR, rv, "while creating hash s-expr.");
+              return false;
+            }
+        }
+      else if (sigdat->pk_alg == RFC4880_PK_RSA)
+        {
+          rv = gcry_sexp_build (&sig, &n, rsa_sig_templ, sigdat->rsa_mpi_s);
+          if (rv != GPG_ERR_NO_ERROR)
+            {
+              ERRKIND (owner, IDS_CRYPTO_ERROR, rv, "while creating sig s-expr.");
+              return false;
+            }
+
+          rv = gcry_sexp_build (&hash, &n, rsa_data_hash_templ,
+                                gcry_md_algo_name(sigdat->algo),
+                                gcry_md_get_algo_dlen (sigdat->algo),
+                                gcry_md_read (sigdat->md, 0));
+          if (rv != GPG_ERR_NO_ERROR)
+            {
+              ERRKIND (owner, IDS_CRYPTO_ERROR, rv, "while creating hash s-expr.");
+              return false;
+            }
+        }
+
+#if CRYPTODEBUGGING
+      n = gcry_sexp_sprint (sig, GCRYSEXP_FMT_ADVANCED, sexprbuf,
+                            GPG_KEY_SEXPR_BUF_SIZE);
+      LogBabblePrintf ("sig:%d\n'%s'", n, sexprbuf);
+      n = gcry_sexp_sprint (hash, GCRYSEXP_FMT_ADVANCED, sexprbuf,
+                            GPG_KEY_SEXPR_BUF_SIZE);
+      LogBabblePrintf ("hash:%d\n'%s'", n, sexprbuf);
+#endif /* CRYPTODEBUGGING */
+
+      // Well, we're actually there!
+      // Try it against each key in turn
+
+      std::vector<key_info>::iterator it;
+      for (it = sigdat->keys_to_try->begin ();
+           it < sigdat->keys_to_try->end ();
+           ++it)
+        {
+          rv = gcry_pk_verify (sig, hash, it->key);
+
+          LogBabblePrintf("signature: tried key %s, returned 0x%08x %s\n",
+                          it->name.c_str(), rv, gcry_strerror(rv));
+
+          if (rv != GPG_ERR_NO_ERROR)
+            continue;
+          // Found it!  This key gets kept!
+          if (!it->builtin)
+            add_key_from_sexpr (it->key);
+          break;
+        }
+
+      gcry_sexp_release (sig);
+      gcry_sexp_release (hash);
+    }
+
+  return (rv == GPG_ERR_NO_ERROR);;
+}
+
 /*  Do-nothing stubs called by the sig file walker to
   walk over the embedded subpackets.  In the event, we don't
   actually need to do this as we aren't inspecting them.  */
@@ -287,7 +412,6 @@ pkt_cb_resp sig_file_walker (struct packet_walker *wlk, unsigned char tag,
 						size_t packetsize, size_t hdrpos)
 {
   struct sig_data *sigdat = (struct sig_data *)(wlk->userdata);
-  sigdat->complete = false;
 
   if (tag != RFC4880_PT_SIGNATURE)
     return pktCONTINUE;
@@ -361,6 +485,7 @@ pkt_cb_resp sig_file_walker (struct packet_walker *wlk, unsigned char tag,
     }
 
   // Now we know hash algo, we can create md context.
+  sigdat->md = 0;
   gcry_error_t rv = gcry_md_open (&sigdat->md, sigdat->algo, 0);
   if (rv != GPG_ERR_NO_ERROR)
     {
@@ -407,6 +532,8 @@ pkt_cb_resp sig_file_walker (struct packet_walker *wlk, unsigned char tag,
       for RSA:
       - MPI of RSA value m^d mod n (aka s)
   */
+  sigdat->dsa_mpi_r = sigdat->dsa_mpi_s = 0;
+  sigdat->rsa_mpi_s = 0;
 
   if (sigdat->pk_alg == RFC4880_PK_DSA)
     {
@@ -449,26 +576,30 @@ pkt_cb_resp sig_file_walker (struct packet_walker *wlk, unsigned char tag,
       gcry_md_putc (sigdat->md, nbytes & 0xff);
     }
 
-  // Hooray, succeeded!
-  sigdat->complete = true; 
+  /* So, we have hashed all the data, and found the sig coefficients. */
 
-  return pktHALT;
-}
+  // finalize the hash
+  gcry_md_final (sigdat->md);
+  MESSAGE("digest length is %d\n",gcry_md_get_algo_dlen (sigdat->algo));
 
-/*  Size and allocate a temp buffer to print a representation
-  of a public key s-expr into, then add that to the extra keys
-  setting so it persists for the next run.  */
-void
-add_key_from_sexpr (gcry_sexp_t key)
-{
-  size_t n = gcry_sexp_sprint (key, GCRYSEXP_FMT_ADVANCED, 0, ~0);
-  char *sexprbuf = new char[n];
-  n = gcry_sexp_sprint (key, GCRYSEXP_FMT_ADVANCED, sexprbuf, n);
-  // +1 because we want to include the nul-terminator.
-  n = fold_lfs_and_spaces (sexprbuf, n + 1);
-  ExtraKeysSetting::instance().add_key (sexprbuf);
-  MESSAGE ("keep:%d\n'%s'", n, sexprbuf);
-  delete [] sexprbuf;
+  // check this signature
+  if (verify_sig (sigdat, wlk->owner))
+      sigdat->valid = true;
+
+  // discard hash
+  if (sigdat->md)
+    gcry_md_close (sigdat->md);
+
+  // discard sig coefffcients
+  if (sigdat->dsa_mpi_r)
+    gcry_mpi_release (sigdat->dsa_mpi_r);
+  if (sigdat->dsa_mpi_s)
+    gcry_mpi_release (sigdat->dsa_mpi_s);
+  if (sigdat->rsa_mpi_s)
+    gcry_mpi_release (sigdat->rsa_mpi_s);
+
+  // we can stop immediately if we found a good signature
+  return sigdat->valid ? pktHALT : pktCONTINUE;
 }
 
 #if CRYPTODEBUGGING
@@ -519,18 +650,6 @@ verify_ini_file_sig (io_stream *ini_file, io_stream *ini_sig_file, HWND owner)
   struct sig_data sigdat;
 
   /*  Vector of keys to use.  */
-  struct key_info
-  {
-    key_info(std::string _name, bool _builtin, gcry_sexp_t _key, bool _owned=false) :
-      name(_name), builtin(_builtin), key(_key), owned(_owned)
-    {
-    }
-
-    std::string name;
-    bool builtin;  // if true, we don't need to retain this key with add_key_from_sexpr()
-    gcry_sexp_t key;
-    bool owned;    // if true, we own this key and should use gcry_sexp_release() on it
-  };
   std::vector<struct key_info> keys_to_try;
 
   /*  Overall status of signature.  */
@@ -688,110 +807,15 @@ verify_ini_file_sig (io_stream *ini_file, io_stream *ini_sig_file, HWND owner)
   // We pass in a pointer to the ini file in the user context data,
   // which the packet walker callback uses to create a new hash
   // context preloaded with all the signature-covered data.
-  sigdat.complete = false;
+  sigdat.valid = false;
   sigdat.sign_data = ini_file;
-  sigdat.dsa_mpi_r = sigdat.dsa_mpi_s = 0;
-  sigdat.rsa_mpi_s = 0;
-  sigdat.md = 0;
-  pkt_walk_packets (ini_sig_file, sig_file_walker, owner, 0,
-				ini_sig_file->get_size (), &sigdat);
-  if (sigdat.complete)
-    {
-      /*  sig coefficients in s-expr format.  */
-      gcry_sexp_t sig;
-
-      /*  signature hash data in s-expr format.  */
-      gcry_sexp_t hash;
-
-      /* So, we have hashed all the data, and found the sig coefficients.
-        Next stages are to finalise the hash, build everything into
-        s-exprs, and call the libgcrypt verification routine.  */
-
-      gcry_md_final (sigdat.md);
-      MESSAGE("digest length is %d\n",gcry_md_get_algo_dlen (sigdat.algo));
-
-      if (sigdat.pk_alg == RFC4880_PK_DSA)
-        {
-          rv = gcry_sexp_build (&sig, &n, dsa_sig_templ, sigdat.dsa_mpi_r,
-                                sigdat.dsa_mpi_s);
-          if (rv != GPG_ERR_NO_ERROR)
-            {
-              ERRKIND (owner, IDS_CRYPTO_ERROR, rv, "while creating sig s-expr.");
-              return false;
-            }
-
-          rv = gcry_sexp_build (&hash, &n, dsa_data_hash_templ,
-                                gcry_md_algo_name(sigdat.algo),
-                                gcry_md_get_algo_dlen (sigdat.algo),
-                                gcry_md_read (sigdat.md, 0));
-          if (rv != GPG_ERR_NO_ERROR)
-            {
-              ERRKIND (owner, IDS_CRYPTO_ERROR, rv, "while creating hash s-expr.");
-              return false;
-            }
-        }
-      else if (sigdat.pk_alg == RFC4880_PK_RSA)
-        {
-          rv = gcry_sexp_build (&sig, &n, rsa_sig_templ, sigdat.rsa_mpi_s);
-          if (rv != GPG_ERR_NO_ERROR)
-            {
-              ERRKIND (owner, IDS_CRYPTO_ERROR, rv, "while creating sig s-expr.");
-              return false;
-            }
-
-          rv = gcry_sexp_build (&hash, &n, rsa_data_hash_templ,
-                                gcry_md_algo_name(sigdat.algo),
-                                gcry_md_get_algo_dlen (sigdat.algo),
-                                gcry_md_read (sigdat.md, 0));
-          if (rv != GPG_ERR_NO_ERROR)
-            {
-              ERRKIND (owner, IDS_CRYPTO_ERROR, rv, "while creating hash s-expr.");
-              return false;
-            }
-        }
-
-#if CRYPTODEBUGGING
-      n = gcry_sexp_sprint (sig, GCRYSEXP_FMT_ADVANCED, sexprbuf,
-                            GPG_KEY_SEXPR_BUF_SIZE);
-      LogBabblePrintf ("sig:%d\n'%s'", n, sexprbuf);
-      n = gcry_sexp_sprint (hash, GCRYSEXP_FMT_ADVANCED, sexprbuf,
-                            GPG_KEY_SEXPR_BUF_SIZE);
-      LogBabblePrintf ("hash:%d\n'%s'", n, sexprbuf);
-#endif /* CRYPTODEBUGGING */
-
-      // Well, we're actually there!
-      // Try it against each key in turn
-
-      std::vector<key_info>::iterator it;
-      for (it = keys_to_try.begin (); it < keys_to_try.end (); ++it)
-        {
-          rv = gcry_pk_verify (sig, hash, it->key);
-
-          LogBabblePrintf("signature: tried key %s, returned 0x%08x %s\n",
-                          it->name.c_str(), rv, gcry_strerror(rv));
+  sigdat.keys_to_try = &keys_to_try;
 
-          if (rv != GPG_ERR_NO_ERROR)
-            continue;
-          // Found it!  This key gets kept!
-          if (!it->builtin)
-            add_key_from_sexpr (it->key);
-          break;
-        }
-      sig_ok = (rv == GPG_ERR_NO_ERROR);
+  pkt_walk_packets (ini_sig_file, sig_file_walker, owner, 0,
+                    ini_sig_file->get_size (), &sigdat);
 
-      gcry_sexp_release (sig);
-      gcry_sexp_release (hash);
-    }
+  sig_ok = sigdat.valid;
 
-  // Discard the temp data then.
-  if (sigdat.dsa_mpi_r)
-    gcry_mpi_release (sigdat.dsa_mpi_r);
-  if (sigdat.dsa_mpi_s)
-    gcry_mpi_release (sigdat.dsa_mpi_s);
-  if (sigdat.rsa_mpi_s)
-    gcry_mpi_release (sigdat.rsa_mpi_s);
-  if (sigdat.md)
-    gcry_md_close (sigdat.md);
   while (keys_to_try.size ())
     {
       if (keys_to_try.back ().owned)
-- 
2.21.0



More information about the Cygwin-apps mailing list