[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