summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorNIIBE Yutaka <gniibe@fsij.org>2021-07-29 11:50:31 +0900
committerNIIBE Yutaka <gniibe@fsij.org>2021-07-29 11:50:31 +0900
commit4a3e71403225bd60a96d6747a8141a268bedda78 (patch)
treea59c87c25d52a801216722d73e8fe7dc36fb9f50
parent877be1bf9df0b4e9aed9036db1592a64582a4bac (diff)
downloadlibgcrypt-4a3e71403225bd60a96d6747a8141a268bedda78.tar.gz
cipher: Support internal hashing for DSA and ECDSA signing.
* cipher/dsa-common.c (_gcry_dsa_compute_hash): New. * cipher/pubkey-internal.h (_gcry_dsa_compute_hash): New. * cipher/dsa.c (verify): Add FLAGS and HASHALGO. (test_keys): Follow the change of verify API. (sign, verify): Support PUBKEY_FLAG_PREHASH flag to hash internally. (selftest_sign): Test with "prehash" flag. * cipher/ecc-common.h (_gcry_ecc_ecdsa_verify): Add FLAGS and HASHALGO. * cipher/ecc-ecdsa.c (_gcry_ecc_ecdsa_sign): Support PUBKEY_FLAG_PREHASH flag to hash internally. (_gcry_ecc_ecdsa_verify): Likewise. * cipher/ecc.c (test_keys): Follow the change of _gcry_ecc_ecdsa_verify API. (selftest_sign): Test with "prehash" flag. * cipher/pubkey-util.c (_gcry_pk_util_data_to_mpi): Support handling of "hash-algo" and "value" with "prehash" flag. -- GnuPG-bug-id: 5530 Signed-off-by: NIIBE Yutaka <gniibe@fsij.org>
-rw-r--r--cipher/dsa-common.c55
-rw-r--r--cipher/dsa.c45
-rw-r--r--cipher/ecc-common.h3
-rw-r--r--cipher/ecc-ecdsa.c35
-rw-r--r--cipher/ecc.c11
-rw-r--r--cipher/pubkey-internal.h3
-rw-r--r--cipher/pubkey-util.c69
7 files changed, 189 insertions, 32 deletions
diff --git a/cipher/dsa-common.c b/cipher/dsa-common.c
index fe49248d..7000903a 100644
--- a/cipher/dsa-common.c
+++ b/cipher/dsa-common.c
@@ -384,6 +384,61 @@ _gcry_dsa_gen_rfc6979_k (gcry_mpi_t *r_k,
return rc;
}
+
+
+/*
+ * For DSA/ECDSA, as prehash function, compute hash with HASHALGO for
+ * INPUT. Result hash value is returned in R_HASH as an opaque MPI.
+ * Returns error code.
+ */
+gpg_err_code_t
+_gcry_dsa_compute_hash (gcry_mpi_t *r_hash, gcry_mpi_t input, int hashalgo)
+{
+ gpg_err_code_t rc = 0;
+ size_t hlen;
+ void *hashbuf;
+ void *abuf;
+ unsigned int abits;
+ unsigned int n;
+
+ hlen = _gcry_md_get_algo_dlen (hashalgo);
+ hashbuf = xtrymalloc (hlen);
+ if (!hashbuf)
+ {
+ rc = gpg_err_code_from_syserror ();
+ return rc;
+ }
+
+ if (mpi_is_opaque (input))
+ {
+ abuf = mpi_get_opaque (input, &abits);
+ n = (abits+7)/8;
+ _gcry_md_hash_buffer (hashalgo, hashbuf, abuf, n);
+ }
+ else
+ {
+ abits = mpi_get_nbits (input);
+ n = (abits+7)/8;
+ abuf = xtrymalloc (n);
+ if (!abuf)
+ {
+ rc = gpg_err_code_from_syserror ();
+ xfree (hashbuf);
+ return rc;
+ }
+ _gcry_mpi_to_octet_string (NULL, abuf, input, n);
+ _gcry_md_hash_buffer (hashalgo, hashbuf, abuf, n);
+ xfree (abuf);
+ }
+
+ *r_hash = mpi_set_opaque (NULL, hashbuf, hlen*8);
+ if (!*r_hash)
+ rc = GPG_ERR_INV_OBJ;
+
+ return rc;
+}
+
+
/*
* Truncate opaque hash value to qbits for DSA.
* Non-opaque input is not truncated, in hope that user
diff --git a/cipher/dsa.c b/cipher/dsa.c
index d793b9aa..a66db334 100644
--- a/cipher/dsa.c
+++ b/cipher/dsa.c
@@ -136,7 +136,7 @@ static gpg_err_code_t generate (DSA_secret_key *sk,
static gpg_err_code_t sign (gcry_mpi_t r, gcry_mpi_t s, gcry_mpi_t input,
DSA_secret_key *skey, int flags, int hashalgo);
static gpg_err_code_t verify (gcry_mpi_t r, gcry_mpi_t s, gcry_mpi_t input,
- DSA_public_key *pkey);
+ DSA_public_key *pkey, int flags, int hashalgo);
static unsigned int dsa_get_nbits (gcry_sexp_t parms);
@@ -185,12 +185,12 @@ test_keys (DSA_secret_key *sk, unsigned int qbits)
sign (sig_a, sig_b, data, sk, 0, 0);
/* Verify the signature using the public key. */
- if ( verify (sig_a, sig_b, data, &pk) )
+ if ( verify (sig_a, sig_b, data, &pk, 0, 0) )
goto leave; /* Signature does not match. */
/* Modify the data and check that the signing fails. */
mpi_add_ui (data, data, 1);
- if ( !verify (sig_a, sig_b, data, &pk) )
+ if ( !verify (sig_a, sig_b, data, &pk, 0, 0) )
goto leave; /* Signature matches but should not. */
result = 0; /* The test succeeded. */
@@ -604,13 +604,25 @@ sign (gcry_mpi_t r, gcry_mpi_t s, gcry_mpi_t input, DSA_secret_key *skey,
const void *abuf;
unsigned int abits, qbits;
int extraloops = 0;
+ gcry_mpi_t hash_computed_internally = NULL;
qbits = mpi_get_nbits (skey->q);
+ if ((flags & PUBKEY_FLAG_PREHASH))
+ {
+ rc = _gcry_dsa_compute_hash (&hash_computed_internally, input, hashalgo);
+ if (rc)
+ return rc;
+ input = hash_computed_internally;
+ }
+
/* Convert the INPUT into an MPI. */
rc = _gcry_dsa_normalize_hash (input, &hash, qbits);
if (rc)
- return rc;
+ {
+ mpi_free (hash_computed_internally);
+ return rc;
+ }
again:
/* Create the K value. */
@@ -669,6 +681,7 @@ sign (gcry_mpi_t r, gcry_mpi_t s, gcry_mpi_t input, DSA_secret_key *skey,
leave:
if (hash != input)
mpi_free (hash);
+ mpi_free (hash_computed_internally);
return rc;
}
@@ -678,7 +691,8 @@ sign (gcry_mpi_t r, gcry_mpi_t s, gcry_mpi_t input, DSA_secret_key *skey,
Returns true if the signature composed from R and S is valid.
*/
static gpg_err_code_t
-verify (gcry_mpi_t r, gcry_mpi_t s, gcry_mpi_t input, DSA_public_key *pkey )
+verify (gcry_mpi_t r, gcry_mpi_t s, gcry_mpi_t input, DSA_public_key *pkey,
+ int flags, int hashalgo)
{
gpg_err_code_t rc = 0;
gcry_mpi_t w, u1, u2, v;
@@ -686,6 +700,7 @@ verify (gcry_mpi_t r, gcry_mpi_t s, gcry_mpi_t input, DSA_public_key *pkey )
gcry_mpi_t ex[3];
gcry_mpi_t hash;
unsigned int nbits;
+ gcry_mpi_t hash_computed_internally = NULL;
if( !(mpi_cmp_ui( r, 0 ) > 0 && mpi_cmp( r, pkey->q ) < 0) )
return GPG_ERR_BAD_SIGNATURE; /* Assertion 0 < r < n failed. */
@@ -693,9 +708,19 @@ verify (gcry_mpi_t r, gcry_mpi_t s, gcry_mpi_t input, DSA_public_key *pkey )
return GPG_ERR_BAD_SIGNATURE; /* Assertion 0 < s < n failed. */
nbits = mpi_get_nbits (pkey->q);
+ if ((flags & PUBKEY_FLAG_PREHASH))
+ {
+ rc = _gcry_dsa_compute_hash (&hash_computed_internally, input, hashalgo);
+ if (rc)
+ return rc;
+ input = hash_computed_internally;
+ }
rc = _gcry_dsa_normalize_hash (input, &hash, nbits);
if (rc)
- return rc;
+ {
+ mpi_free (hash_computed_internally);
+ return rc;
+ }
w = mpi_alloc( mpi_get_nlimbs(pkey->q) );
u1 = mpi_alloc( mpi_get_nlimbs(pkey->q) );
@@ -737,6 +762,7 @@ verify (gcry_mpi_t r, gcry_mpi_t s, gcry_mpi_t input, DSA_public_key *pkey )
mpi_free(v);
if (hash != input)
mpi_free (hash);
+ mpi_free (hash_computed_internally);
return rc;
}
@@ -1136,7 +1162,7 @@ dsa_verify (gcry_sexp_t s_sig, gcry_sexp_t s_data, gcry_sexp_t s_keyparms)
}
/* Verify the signature. */
- rc = verify (sig_r, sig_s, data, &pk);
+ rc = verify (sig_r, sig_s, data, &pk, ctx.flags, ctx.hash_algo);
leave:
_gcry_mpi_release (pk.p);
@@ -1195,8 +1221,9 @@ selftest_sign (gcry_sexp_t pkey, gcry_sexp_t skey)
{
/* Sample data from RFC 6979 section A.2.2, hash is of message "sample" */
static const char sample_data[] =
- "(data (flags rfc6979)"
- " (hash sha256 #af2bdbe1aa9b6ec1e2ade1d694f41fc71a831d0268e9891562113d8a62add1bf#))";
+ "(data (flags rfc6979 prehash)"
+ " (hash-algo sha256)"
+ " (value 6:sample))";
static const char sample_data_bad[] =
"(data (flags rfc6979)"
" (hash sha256 #bf2bdbe1aa9b6ec1e2ade1d694f41fc71a831d0268e9891562113d8a62add1bf#))";
diff --git a/cipher/ecc-common.h b/cipher/ecc-common.h
index edd14295..c128aeb3 100644
--- a/cipher/ecc-common.h
+++ b/cipher/ecc-common.h
@@ -91,7 +91,8 @@ gpg_err_code_t _gcry_ecc_ecdsa_sign (gcry_mpi_t input, mpi_ec_t ec,
gcry_mpi_t r, gcry_mpi_t s,
int flags, int hashalgo);
gpg_err_code_t _gcry_ecc_ecdsa_verify (gcry_mpi_t input, mpi_ec_t ec,
- gcry_mpi_t r, gcry_mpi_t s);
+ gcry_mpi_t r, gcry_mpi_t s,
+ int flags, int hashalgo);
/*-- ecc-eddsa.c --*/
gpg_err_code_t _gcry_ecc_eddsa_recover_x (gcry_mpi_t x, gcry_mpi_t y, int sign,
diff --git a/cipher/ecc-ecdsa.c b/cipher/ecc-ecdsa.c
index 30103f14..c7f4c8d5 100644
--- a/cipher/ecc-ecdsa.c
+++ b/cipher/ecc-ecdsa.c
@@ -51,16 +51,29 @@ _gcry_ecc_ecdsa_sign (gcry_mpi_t input, mpi_ec_t ec,
unsigned int abits, qbits;
gcry_mpi_t b; /* Random number needed for blinding. */
gcry_mpi_t bi; /* multiplicative inverse of B. */
+ gcry_mpi_t hash_computed_internally = NULL;
if (DBG_CIPHER)
log_mpidump ("ecdsa sign hash ", input );
qbits = mpi_get_nbits (ec->n);
+ if ((flags & PUBKEY_FLAG_PREHASH))
+ {
+ rc = _gcry_dsa_compute_hash (&hash_computed_internally, input, hashalgo);
+ if (rc)
+ return rc;
+ input = hash_computed_internally;
+ }
+
/* Convert the INPUT into an MPI if needed. */
rc = _gcry_dsa_normalize_hash (input, &hash, qbits);
+
if (rc)
- return rc;
+ {
+ mpi_free (hash_computed_internally);
+ return rc;
+ }
b = mpi_snew (qbits);
bi = mpi_snew (qbits);
@@ -155,6 +168,7 @@ _gcry_ecc_ecdsa_sign (gcry_mpi_t input, mpi_ec_t ec,
if (hash != input)
mpi_free (hash);
+ mpi_free (hash_computed_internally);
return rc;
}
@@ -165,12 +179,14 @@ _gcry_ecc_ecdsa_sign (gcry_mpi_t input, mpi_ec_t ec,
*/
gpg_err_code_t
_gcry_ecc_ecdsa_verify (gcry_mpi_t input, mpi_ec_t ec,
- gcry_mpi_t r, gcry_mpi_t s)
+ gcry_mpi_t r, gcry_mpi_t s,
+ int flags, int hashalgo)
{
gpg_err_code_t err = 0;
gcry_mpi_t hash, h, h1, h2, x;
mpi_point_struct Q, Q1, Q2;
unsigned int nbits;
+ gcry_mpi_t hash_computed_internally = NULL;
if (!_gcry_mpi_ec_curve_point (ec->Q, ec))
return GPG_ERR_BROKEN_PUBKEY;
@@ -181,9 +197,21 @@ _gcry_ecc_ecdsa_verify (gcry_mpi_t input, mpi_ec_t ec,
return GPG_ERR_BAD_SIGNATURE; /* Assertion 0 < s < n failed. */
nbits = mpi_get_nbits (ec->n);
+ if ((flags & PUBKEY_FLAG_PREHASH))
+ {
+ err = _gcry_dsa_compute_hash (&hash_computed_internally, input,
+ hashalgo);
+ if (err)
+ return err;
+ input = hash_computed_internally;
+ }
+
err = _gcry_dsa_normalize_hash (input, &hash, nbits);
if (err)
- return err;
+ {
+ mpi_free (hash_computed_internally);
+ return err;
+ }
h = mpi_alloc (0);
h1 = mpi_alloc (0);
@@ -243,6 +271,7 @@ _gcry_ecc_ecdsa_verify (gcry_mpi_t input, mpi_ec_t ec,
mpi_free (h);
if (hash != input)
mpi_free (hash);
+ mpi_free (hash_computed_internally);
return err;
}
diff --git a/cipher/ecc.c b/cipher/ecc.c
index db200738..77f9417f 100644
--- a/cipher/ecc.c
+++ b/cipher/ecc.c
@@ -288,7 +288,7 @@ test_keys (mpi_ec_t ec, unsigned int nbits)
if (_gcry_ecc_ecdsa_sign (test, ec, r, s, 0, 0) )
log_fatal ("ECDSA operation: sign failed\n");
- if (_gcry_ecc_ecdsa_verify (test, ec, r, s))
+ if (_gcry_ecc_ecdsa_verify (test, ec, r, s, 0, 0))
{
log_fatal ("ECDSA operation: sign, verify failed\n");
}
@@ -869,7 +869,8 @@ ecc_verify (gcry_sexp_t s_sig, gcry_sexp_t s_data, gcry_sexp_t s_keyparms)
}
else
{
- rc = _gcry_ecc_ecdsa_verify (data, ec, sig_r, sig_s);
+ rc = _gcry_ecc_ecdsa_verify (data, ec, sig_r, sig_s,
+ ctx.flags, ctx.hash_algo);
}
leave:
@@ -1675,9 +1676,9 @@ selftest_sign (gcry_sexp_t pkey, gcry_sexp_t skey)
{
/* Sample data from RFC 6979 section A.2.5, hash is of message "sample" */
static const char sample_data[] =
- "(data (flags rfc6979)"
- " (hash sha256 #af2bdbe1aa9b6ec1e2ade1d694f41fc71a831d0268e98915"
- /**/ "62113d8a62add1bf#))";
+ "(data (flags rfc6979 prehash)"
+ " (hash-algo sha256)"
+ " (value 6:sample))";
static const char sample_data_bad[] =
"(data (flags rfc6979)"
" (hash sha256 #bf2bdbe1aa9b6ec1e2ade1d694f41fc71a831d0268e98915"
diff --git a/cipher/pubkey-internal.h b/cipher/pubkey-internal.h
index 2806fa8b..0ca77099 100644
--- a/cipher/pubkey-internal.h
+++ b/cipher/pubkey-internal.h
@@ -93,7 +93,8 @@ gpg_err_code_t _gcry_dsa_gen_rfc6979_k (gcry_mpi_t *r_k,
unsigned int h1len,
int halgo,
unsigned int extraloops);
-
+gpg_err_code_t _gcry_dsa_compute_hash (gcry_mpi_t *r_hash, gcry_mpi_t input,
+ int hashalgo);
gpg_err_code_t _gcry_dsa_normalize_hash (gcry_mpi_t input,
gcry_mpi_t *out,
unsigned int qbits);
diff --git a/cipher/pubkey-util.c b/cipher/pubkey-util.c
index e2ea9567..970d9d97 100644
--- a/cipher/pubkey-util.c
+++ b/cipher/pubkey-util.c
@@ -807,27 +807,41 @@ _gcry_pk_util_data_to_mpi (gcry_sexp_t input, gcry_mpi_t *ret_mpi,
/* Note that mpi_set_opaque takes ownership of VALUE. */
*ret_mpi = mpi_set_opaque (NULL, value, valuelen*8);
}
- else if (ctx->encoding == PUBKEY_ENC_RAW && lhash
+ else if (ctx->encoding == PUBKEY_ENC_RAW
+ && (lhash || (lvalue && (parsed_flags & PUBKEY_FLAG_PREHASH)))
&& ((parsed_flags & PUBKEY_FLAG_RAW_FLAG)
|| (parsed_flags & PUBKEY_FLAG_RFC6979)))
{
+ void * value;
+ size_t valuelen;
+ gcry_sexp_t list;
+
/* Raw encoding along with a hash element. This is commonly
used for DSA. For better backward error compatibility we
allow this only if either the rfc6979 flag has been given or
the raw flags was explicitly given. */
- if (sexp_length (lhash) != 3)
- rc = GPG_ERR_INV_OBJ;
- else if ( !(s=sexp_nth_data (lhash, 1, &n)) || !n )
- rc = GPG_ERR_INV_OBJ;
- else
+
+ if (lvalue && (parsed_flags & PUBKEY_FLAG_PREHASH))
{
- void *value;
- size_t valuelen;
+ /* Get HASH-ALGO. */
+ list = sexp_find_token (ldata, "hash-algo", 0);
+ if (list)
+ {
+ s = sexp_nth_data (list, 1, &n);
+ if (!s)
+ rc = GPG_ERR_NO_OBJ;
+ else
+ {
+ ctx->hash_algo = get_hash_algo (s, n);
+ if (!ctx->hash_algo)
+ rc = GPG_ERR_DIGEST_ALGO;
+ }
+ sexp_release (list);
+ if (rc)
+ goto leave;
+ }
- ctx->hash_algo = get_hash_algo (s, n);
- if (!ctx->hash_algo)
- rc = GPG_ERR_DIGEST_ALGO;
- else if (!(value=sexp_nth_buffer (lhash, 2, &valuelen)))
+ if ( !(value=sexp_nth_buffer (lvalue, 1, &valuelen)) || !valuelen )
rc = GPG_ERR_INV_OBJ;
else if ((valuelen * 8) < valuelen)
{
@@ -837,10 +851,39 @@ _gcry_pk_util_data_to_mpi (gcry_sexp_t input, gcry_mpi_t *ret_mpi,
else
*ret_mpi = mpi_set_opaque (NULL, value, valuelen*8);
}
+ else if (lhash)
+ {
+ if (sexp_length (lhash) != 3)
+ rc = GPG_ERR_INV_OBJ;
+ else if ( !(s=sexp_nth_data (lhash, 1, &n)) || !n )
+ rc = GPG_ERR_INV_OBJ;
+ else
+ {
+ ctx->hash_algo = get_hash_algo (s, n);
+
+ if (!ctx->hash_algo)
+ rc = GPG_ERR_DIGEST_ALGO;
+ else if ( !(value=sexp_nth_buffer (lhash, 2, &valuelen))
+ || !valuelen )
+ rc = GPG_ERR_INV_OBJ;
+ else if ((valuelen * 8) < valuelen)
+ {
+ xfree (value);
+ rc = GPG_ERR_TOO_LARGE;
+ }
+ else
+ *ret_mpi = mpi_set_opaque (NULL, value, valuelen*8);
+ }
+ }
+ else
+ rc = GPG_ERR_CONFLICT;
+
+ if (rc)
+ goto leave;
}
else if (ctx->encoding == PUBKEY_ENC_RAW && lvalue)
{
- /* RFC6969 may only be used with the a hash value and not the
+ /* RFC6979 may only be used with the a hash value and not the
MPI based value. */
if (parsed_flags & PUBKEY_FLAG_RFC6979)
{