diff options
author | Werner Koch <wk@gnupg.org> | 2013-10-09 15:05:26 +0200 |
---|---|---|
committer | Werner Koch <wk@gnupg.org> | 2013-10-09 15:05:26 +0200 |
commit | 94b652ecb006c29fa2ffb1badc9f02b758581737 (patch) | |
tree | 14eb5e6121fafcf13d46eddb2e53d3b176ac6c8e /cipher | |
parent | 4645f3728bb0900591b0aef85831fdee52c59e3c (diff) | |
download | libgcrypt-94b652ecb006c29fa2ffb1badc9f02b758581737.tar.gz |
pubkey: Move sexp parsing for gcry_pk_verify to the modules.
* cipher/rsa.c (rsa_verify): Revamp.
* cipher/dsa.c (dsa_verify): Revamp.
* cipher/elgamal.c (elg_verify): Revamp.
* cipher/ecc.c (ecc_verify): Revamp.
* cipher/pubkey.c (sexp_to_sig): Remove.
(pss_verify_cmp): Move to pubkey-util.c
(sexp_data_to_mpi): Ditto.
(init_encoding_ctx): Ditto.
(gcry_pk_verify): Simplify.
* cipher/pubkey-util.c (_gcry_pk_util_init_encoding_ctx): Add. Take
from pubkey.c
(get_hash_algo): Ditto.
(_gcry_pk_util_data_to_mpi): Ditto.
(pss_verify_cmp): Ditto.
(_gcry_pk_util_extract_mpis): New.
(_gcry_pk_util_preparse_sigval): New.
(_gcry_pk_util_free_encoding_ctx): New.
* cipher/ecc-curves.c (_gcry_ecc_fill_in_curve): Make curve init
optional.
* src/g10lib.h (GCC_ATTR_SENTINEL): New.
* tests/basic.c (check_pubkey_sign): Print the algo name.
(main): Add option --pubkey.
Signed-off-by: Werner Koch <wk@gnupg.org>
Diffstat (limited to 'cipher')
-rw-r--r-- | cipher/dsa.c | 134 | ||||
-rw-r--r-- | cipher/ecc-curves.c | 29 | ||||
-rw-r--r-- | cipher/ecc.c | 192 | ||||
-rw-r--r-- | cipher/elgamal.c | 103 | ||||
-rw-r--r-- | cipher/pubkey-internal.h | 14 | ||||
-rw-r--r-- | cipher/pubkey-util.c | 767 | ||||
-rw-r--r-- | cipher/pubkey.c | 670 | ||||
-rw-r--r-- | cipher/rsa.c | 99 |
8 files changed, 1177 insertions, 831 deletions
diff --git a/cipher/dsa.c b/cipher/dsa.c index d319f732..deeb1132 100644 --- a/cipher/dsa.c +++ b/cipher/dsa.c @@ -58,6 +58,14 @@ typedef struct } dsa_domain_t; +static const char *dsa_names[] = + { + "dsa", + "openpgp-dsa", + NULL, + }; + + /* A sample 1024 bit DSA key used for the selftests. */ static const char sample_secret_key[] = "(private-key" @@ -109,6 +117,8 @@ 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 int verify (gcry_mpi_t r, gcry_mpi_t s, gcry_mpi_t input, DSA_public_key *pkey); +static unsigned int dsa_get_nbits (gcry_sexp_t parms); + static void (*progress_cb) (void *,const char *, int, int, int ); static void *progress_cb_data; @@ -976,56 +986,95 @@ dsa_sign (int algo, gcry_sexp_t *r_result, gcry_mpi_t data, gcry_mpi_t *skey, return rc; } + static gcry_err_code_t -dsa_verify (int algo, gcry_mpi_t hash, gcry_mpi_t *data, gcry_mpi_t *pkey, - int (*cmp) (void *, gcry_mpi_t), void *opaquev, - int flags, int hashalgo) +dsa_verify (gcry_sexp_t s_sig, gcry_sexp_t s_data, gcry_sexp_t s_keyparms) { - gcry_err_code_t err = GPG_ERR_NO_ERROR; - DSA_public_key pk; + gcry_err_code_t rc; + struct pk_encoding_ctx ctx; + gcry_sexp_t l1 = NULL; + gcry_mpi_t sig_r = NULL; + gcry_mpi_t sig_s = NULL; + gcry_mpi_t data = NULL; + DSA_public_key pk = { NULL, NULL, NULL, NULL }; + + _gcry_pk_util_init_encoding_ctx (&ctx, PUBKEY_OP_VERIFY, + dsa_get_nbits (s_keyparms)); + + /* Extract the data. */ + rc = _gcry_pk_util_data_to_mpi (s_data, &data, &ctx); + if (rc) + goto leave; + if (DBG_CIPHER) + log_mpidump ("dsa_verify data", data); - (void)algo; - (void)cmp; - (void)opaquev; - (void)flags; - (void)hashalgo; + /* Extract the signature value. */ + rc = _gcry_pk_util_preparse_sigval (s_sig, dsa_names, &l1, NULL); + if (rc) + goto leave; + rc = _gcry_pk_util_extract_mpis (l1, "rs", &sig_r, &sig_s, NULL); + if (rc) + goto leave; + if (DBG_CIPHER) + { + log_mpidump ("dsa_verify s_r", sig_r); + log_mpidump ("dsa_verify s_s", sig_s); + } - if ((! data[0]) || (! data[1]) || (! hash) - || (! pkey[0]) || (! pkey[1]) || (! pkey[2]) || (! pkey[3])) - err = GPG_ERR_BAD_MPI; - else + /* Extract the key. */ + rc = _gcry_pk_util_extract_mpis (s_keyparms, "pqgy", + &pk.p, &pk.q, &pk.g, &pk.y, NULL); + if (rc) + return rc; + if (DBG_CIPHER) { - pk.p = pkey[0]; - pk.q = pkey[1]; - pk.g = pkey[2]; - pk.y = pkey[3]; - if (mpi_is_opaque (hash)) - { - const void *abuf; - unsigned int abits, qbits; - gcry_mpi_t a; + log_mpidump ("dsa_verify p", pk.p); + log_mpidump ("dsa_verify q", pk.q); + log_mpidump ("dsa_verify g", pk.g); + log_mpidump ("dsa_verify y", pk.y); + } - qbits = mpi_get_nbits (pk.q); + /* Verify the signature. */ + if (mpi_is_opaque (data)) + { + const void *abuf; + unsigned int abits, qbits; + gcry_mpi_t a; - abuf = gcry_mpi_get_opaque (hash, &abits); - err = gcry_mpi_scan (&a, GCRYMPI_FMT_USG, abuf, (abits+7)/8, NULL); - if (!err) - { - if (abits > qbits) - gcry_mpi_rshift (a, a, abits - qbits); + qbits = mpi_get_nbits (pk.q); - if (!verify (data[0], data[1], a, &pk)) - err = GPG_ERR_BAD_SIGNATURE; - gcry_mpi_release (a); - } - } - else + abuf = gcry_mpi_get_opaque (data, &abits); + rc = gpg_err_code (gcry_mpi_scan (&a, GCRYMPI_FMT_USG, + abuf, (abits+7)/8, NULL)); + if (!rc) { - if (!verify (data[0], data[1], hash, &pk)) - err = GPG_ERR_BAD_SIGNATURE; + if (abits > qbits) + gcry_mpi_rshift (a, a, abits - qbits); + + if (!verify (sig_r, sig_s, a, &pk)) + rc = GPG_ERR_BAD_SIGNATURE; + gcry_mpi_release (a); } } - return err; + else + { + if (!verify (sig_r, sig_s, data, &pk)) + rc = GPG_ERR_BAD_SIGNATURE; + } + + leave: + gcry_mpi_release (pk.p); + gcry_mpi_release (pk.q); + gcry_mpi_release (pk.g); + gcry_mpi_release (pk.y); + gcry_mpi_release (data); + gcry_mpi_release (sig_r); + gcry_mpi_release (sig_s); + gcry_sexp_release (l1); + _gcry_pk_util_free_encoding_ctx (&ctx); + if (DBG_CIPHER) + log_debug ("dsa_verify => %s\n", rc?gpg_strerror (rc):"good"); + return rc; } @@ -1192,13 +1241,6 @@ run_selftests (int algo, int extended, selftest_report_func_t report) -static const char *dsa_names[] = - { - "dsa", - "openpgp-dsa", - NULL, - }; - gcry_pk_spec_t _gcry_pubkey_spec_dsa = { GCRY_PK_DSA, { 0, 1 }, diff --git a/cipher/ecc-curves.c b/cipher/ecc-curves.c index 449a168e..971dd322 100644 --- a/cipher/ecc-curves.c +++ b/cipher/ecc-curves.c @@ -308,7 +308,9 @@ scanval (const char *string) parameters of the named curve or those of a suitable curve. If R_NBITS is not NULL, the chosen number of bits is stored there. NULL may be given for R_CURVE, if the value is not required and for - example only a quick test for availability is desired. */ + example only a quick test for availability is desired. Note that + the curve fields should be initialized to zero because fields which + are not NULL are skipped. */ gpg_err_code_t _gcry_ecc_fill_in_curve (unsigned int nbits, const char *name, elliptic_curve_t *curve, unsigned int *r_nbits) @@ -378,14 +380,22 @@ _gcry_ecc_fill_in_curve (unsigned int nbits, const char *name, { curve->model = domain_parms[idx].model; curve->dialect = domain_parms[idx].dialect; - curve->p = scanval (domain_parms[idx].p); - curve->a = scanval (domain_parms[idx].a); - curve->b = scanval (domain_parms[idx].b); - curve->n = scanval (domain_parms[idx].n); - curve->G.x = scanval (domain_parms[idx].g_x); - curve->G.y = scanval (domain_parms[idx].g_y); - curve->G.z = mpi_alloc_set_ui (1); - curve->name = resname; + if (!curve->p) + curve->p = scanval (domain_parms[idx].p); + if (!curve->a) + curve->a = scanval (domain_parms[idx].a); + if (!curve->b) + curve->b = scanval (domain_parms[idx].b); + if (!curve->n) + curve->n = scanval (domain_parms[idx].n); + if (!curve->G.x) + curve->G.x = scanval (domain_parms[idx].g_x); + if (!curve->G.y) + curve->G.y = scanval (domain_parms[idx].g_y); + if (!curve->G.z) + curve->G.z = mpi_alloc_set_ui (1); + if (!curve->name) + curve->name = resname; } return 0; @@ -764,6 +774,7 @@ _gcry_ecc_get_param (const char *name, gcry_mpi_t *pkey) mpi_ec_t ctx; gcry_mpi_t g_x, g_y; + memset (&E, 0, sizeof E); err = _gcry_ecc_fill_in_curve (0, name, &E, &nbits); if (err) return err; diff --git a/cipher/ecc.c b/cipher/ecc.c index e3d397a6..20ba1334 100644 --- a/cipher/ecc.c +++ b/cipher/ecc.c @@ -65,6 +65,16 @@ #include "ecc-common.h" +static const char *ecc_names[] = + { + "ecc", + "ecdsa", + "ecdh", + "eddsa", + NULL, + }; + + /* Registered progress function and its callback value. */ static void (*progress_cb) (void *, const char*, int, int, int); static void *progress_cb_data; @@ -84,6 +94,7 @@ static gpg_err_code_t verify_ecdsa (gcry_mpi_t input, ECC_public_key *pkey, gcry_mpi_t r, gcry_mpi_t s); static gcry_mpi_t gen_y_2 (gcry_mpi_t x, elliptic_curve_t * base); +static unsigned int ecc_get_nbits (gcry_sexp_t parms); @@ -1536,62 +1547,125 @@ ecc_sign (int algo, gcry_sexp_t *r_result, gcry_mpi_t data, gcry_mpi_t *skey, static gcry_err_code_t -ecc_verify (int algo, gcry_mpi_t hash, gcry_mpi_t *data, gcry_mpi_t *pkey, - int (*cmp)(void *, gcry_mpi_t), void *opaquev, - int flags, int hashalgo) +ecc_verify (gcry_sexp_t s_sig, gcry_sexp_t s_data, gcry_sexp_t s_keyparms) { - gpg_err_code_t err; + gcry_err_code_t rc; + struct pk_encoding_ctx ctx; + gcry_sexp_t l1 = NULL; + char *curvename = NULL; + gcry_mpi_t mpi_g = NULL; + gcry_mpi_t mpi_q = NULL; + gcry_mpi_t sig_r = NULL; + gcry_mpi_t sig_s = NULL; + gcry_mpi_t data = NULL; ECC_public_key pk; + int sigflags; - (void)algo; - (void)cmp; - (void)opaquev; + memset (&pk, 0, sizeof pk); + _gcry_pk_util_init_encoding_ctx (&ctx, PUBKEY_OP_VERIFY, + ecc_get_nbits (s_keyparms)); - if (!data[0] || !data[1] || !hash || !pkey[0] || !pkey[1] || !pkey[2] - || !pkey[3] || !pkey[4] || !pkey[5] ) - return GPG_ERR_BAD_MPI; + /* Extract the data. */ + rc = _gcry_pk_util_data_to_mpi (s_data, &data, &ctx); + if (rc) + goto leave; + if (DBG_CIPHER) + log_mpidump ("ecc_verify data", data); - /* FIXME: The setting of model and dialect are crude hacks. We will - fix that by moving the s-expression parsing from pubkey.c to - here. */ - pk.E.model = ((flags & PUBKEY_FLAG_EDDSA) - ? MPI_EC_TWISTEDEDWARDS - : MPI_EC_WEIERSTRASS); - pk.E.dialect = ((flags & PUBKEY_FLAG_EDDSA) - ? ECC_DIALECT_ED25519 - : ECC_DIALECT_STANDARD); - pk.E.p = pkey[0]; - pk.E.a = pkey[1]; - pk.E.b = pkey[2]; - point_init (&pk.E.G); - err = _gcry_ecc_os2ec (&pk.E.G, pkey[3]); - if (err) + /* + * Extract the signature value. + */ + rc = _gcry_pk_util_preparse_sigval (s_sig, ecc_names, &l1, &sigflags); + if (rc) + goto leave; + rc = _gcry_pk_util_extract_mpis (l1, + (sigflags & PUBKEY_FLAG_EDDSA)? "/rs":"rs", + &sig_r, &sig_s, NULL); + if (rc) + goto leave; + if (DBG_CIPHER) { - point_free (&pk.E.G); - return err; + log_mpidump ("ecc_verify s_r", sig_r); + log_mpidump ("ecc_verify s_s", sig_s); + } + if ((ctx.flags & PUBKEY_FLAG_EDDSA) ^ (sigflags & PUBKEY_FLAG_EDDSA)) + { + rc = GPG_ERR_CONFLICT; /* Inconsistent use of flag/algoname. */ + goto leave; } - pk.E.n = pkey[4]; - if ((flags & PUBKEY_FLAG_EDDSA)) + + /* + * Extract the key. + */ + rc = _gcry_pk_util_extract_mpis (s_keyparms, "-p?a?b?g?n?/q?", + &pk.E.p, &pk.E.a, &pk.E.b, &mpi_g, &pk.E.n, + &mpi_q, NULL); + if (rc) + goto leave; + if (mpi_g) + { + point_init (&pk.E.G); + rc = _gcry_ecc_os2ec (&pk.E.G, mpi_g); + if (rc) + goto leave; + } + /* Add missing parameters using the optional curve parameter. */ + gcry_sexp_release (l1); + l1 = gcry_sexp_find_token (s_keyparms, "curve", 5); + if (l1) + { + curvename = gcry_sexp_nth_string (l1, 1); + if (curvename) + { + rc = _gcry_ecc_fill_in_curve (0, curvename, &pk.E, NULL); + if (rc) + return rc; + } + } + /* Guess required fields if a curve parameter has not been given. + FIXME: This is a crude hacks. We need to fix that. */ + if (!curvename) { - pk.Q.x = NULL; - pk.Q.y = NULL; - pk.Q.z = NULL; + pk.E.model = ((sigflags & PUBKEY_FLAG_EDDSA) + ? MPI_EC_TWISTEDEDWARDS + : MPI_EC_WEIERSTRASS); + pk.E.dialect = ((sigflags & PUBKEY_FLAG_EDDSA) + ? ECC_DIALECT_ED25519 + : ECC_DIALECT_STANDARD); + } - err = verify_eddsa (hash, &pk, data[0], data[1], hashalgo, pkey[5]); + if (DBG_CIPHER) + { + log_debug ("ecc_verify info: %s/%s\n", + _gcry_ecc_model2str (pk.E.model), + _gcry_ecc_dialect2str (pk.E.dialect)); + if (pk.E.name) + log_debug ("ecc_verify name: %s\n", pk.E.name); + log_printmpi ("ecc_verify p", pk.E.p); + log_printmpi ("ecc_verify a", pk.E.a); + log_printmpi ("ecc_verify b", pk.E.b); + log_printpnt ("ecc_verify g", &pk.E.G, NULL); + log_printmpi ("ecc_verify n", pk.E.n); + log_printmpi ("ecc_verify q", mpi_q); + } + + + /* + * Verify the signature. + */ + if ((sigflags & PUBKEY_FLAG_EDDSA)) + { + rc = verify_eddsa (data, &pk, sig_r, sig_s, ctx.hash_algo, mpi_q); } else { point_init (&pk.Q); - err = _gcry_ecc_os2ec (&pk.Q, pkey[5]); - if (err) - { - point_free (&pk.E.G); - point_free (&pk.Q); - return err; - } + rc = _gcry_ecc_os2ec (&pk.Q, mpi_q); + if (rc) + goto leave; - if (mpi_is_opaque (hash)) + if (mpi_is_opaque (data)) { const void *abuf; unsigned int abits, qbits; @@ -1599,24 +1673,39 @@ ecc_verify (int algo, gcry_mpi_t hash, gcry_mpi_t *data, gcry_mpi_t *pkey, qbits = mpi_get_nbits (pk.E.n); - abuf = gcry_mpi_get_opaque (hash, &abits); - err = gcry_mpi_scan (&a, GCRYMPI_FMT_USG, abuf, (abits+7)/8, NULL); - if (!err) + abuf = gcry_mpi_get_opaque (data, &abits); + rc = gpg_err_code (gcry_mpi_scan (&a, GCRYMPI_FMT_USG, + abuf, (abits+7)/8, NULL)); + if (!rc) { if (abits > qbits) gcry_mpi_rshift (a, a, abits - qbits); - err = verify_ecdsa (a, &pk, data[0], data[1]); + rc = verify_ecdsa (a, &pk, sig_r, sig_s); gcry_mpi_release (a); } } else - err = verify_ecdsa (hash, &pk, data[0], data[1]); + rc = verify_ecdsa (data, &pk, sig_r, sig_s); } + leave: + gcry_mpi_release (pk.E.p); + gcry_mpi_release (pk.E.a); + gcry_mpi_release (pk.E.b); point_free (&pk.E.G); + gcry_mpi_release (pk.E.n); point_free (&pk.Q); - return err; + gcry_mpi_release (mpi_g); + gcry_mpi_release (data); + gcry_mpi_release (sig_r); + gcry_mpi_release (sig_s); + gcry_free (curvename); + gcry_sexp_release (l1); + _gcry_pk_util_free_encoding_ctx (&ctx); + if (DBG_CIPHER) + log_debug ("ecc_verify => %s\n", rc?gpg_strerror (rc):"good"); + return rc; } @@ -2125,15 +2214,6 @@ run_selftests (int algo, int extended, selftest_report_func_t report) -static const char *ecc_names[] = - { - "ecc", - "ecdsa", - "ecdh", - "eddsa", - NULL, - }; - gcry_pk_spec_t _gcry_pubkey_spec_ecc = { GCRY_PK_ECC, { 0, 0 }, diff --git a/cipher/elgamal.c b/cipher/elgamal.c index 9a74c54e..b82e83f0 100644 --- a/cipher/elgamal.c +++ b/cipher/elgamal.c @@ -50,6 +50,15 @@ typedef struct } ELG_secret_key; +static const char *elg_names[] = + { + "elg", + "openpgp-elg", + "openpgp-elg-sig", + NULL, + }; + + static int test_keys (ELG_secret_key *sk, unsigned int nbits, int nodie); static gcry_mpi_t gen_k (gcry_mpi_t p, int small_k); static void generate (ELG_secret_key *sk, unsigned nbits, gcry_mpi_t **factors); @@ -62,6 +71,7 @@ static void sign (gcry_mpi_t a, gcry_mpi_t b, gcry_mpi_t input, ELG_secret_key *skey); static int verify (gcry_mpi_t a, gcry_mpi_t b, gcry_mpi_t input, ELG_public_key *pkey); +static unsigned int elg_get_nbits (gcry_sexp_t parms); static void (*progress_cb) (void *, const char *, int, int, int); @@ -880,35 +890,72 @@ elg_sign (int algo, gcry_sexp_t *r_result, gcry_mpi_t data, gcry_mpi_t *skey, static gcry_err_code_t -elg_verify (int algo, gcry_mpi_t hash, gcry_mpi_t *data, gcry_mpi_t *pkey, - int (*cmp) (void *, gcry_mpi_t), void *opaquev, - int flags, int hashalgo) +elg_verify (gcry_sexp_t s_sig, gcry_sexp_t s_data, gcry_sexp_t s_keyparms) { - gcry_err_code_t err = GPG_ERR_NO_ERROR; - ELG_public_key pk; - - (void)algo; - (void)cmp; - (void)opaquev; - (void)flags; - (void)hashalgo; + gcry_err_code_t rc; + struct pk_encoding_ctx ctx; + gcry_sexp_t l1 = NULL; + gcry_mpi_t sig_r = NULL; + gcry_mpi_t sig_s = NULL; + gcry_mpi_t data = NULL; + ELG_public_key pk = { NULL, NULL, NULL }; + + _gcry_pk_util_init_encoding_ctx (&ctx, PUBKEY_OP_VERIFY, + elg_get_nbits (s_keyparms)); + + /* Extract the data. */ + rc = _gcry_pk_util_data_to_mpi (s_data, &data, &ctx); + if (rc) + goto leave; + if (DBG_CIPHER) + log_mpidump ("elg_verify data", data); + if (mpi_is_opaque (data)) + { + rc = GPG_ERR_INV_DATA; + goto leave; + } - if (mpi_is_opaque (hash)) - return GPG_ERR_INV_DATA; + /* Extract the signature value. */ + rc = _gcry_pk_util_preparse_sigval (s_sig, elg_names, &l1, NULL); + if (rc) + goto leave; + rc = _gcry_pk_util_extract_mpis (l1, "rs", &sig_r, &sig_s, NULL); + if (rc) + goto leave; + if (DBG_CIPHER) + { + log_mpidump ("elg_verify s_r", sig_r); + log_mpidump ("elg_verify s_s", sig_s); + } - if ((! data[0]) || (! data[1]) || (! hash) - || (! pkey[0]) || (! pkey[1]) || (! pkey[2])) - err = GPG_ERR_BAD_MPI; - else + /* Extract the key. */ + rc = _gcry_pk_util_extract_mpis (s_keyparms, "pgy", + &pk.p, &pk.g, &pk.y, NULL); + if (rc) + return rc; + if (DBG_CIPHER) { - pk.p = pkey[0]; - pk.g = pkey[1]; - pk.y = pkey[2]; - if (! verify (data[0], data[1], hash, &pk)) - err = GPG_ERR_BAD_SIGNATURE; + log_mpidump ("elg_verify p", pk.p); + log_mpidump ("elg_verify g", pk.g); + log_mpidump ("elg_verify y", pk.y); } - return err; + /* Verify the signature. */ + if (!verify (sig_r, sig_s, data, &pk)) + rc = GPG_ERR_BAD_SIGNATURE; + + leave: + gcry_mpi_release (pk.p); + gcry_mpi_release (pk.g); + gcry_mpi_release (pk.y); + gcry_mpi_release (data); + gcry_mpi_release (sig_r); + gcry_mpi_release (sig_s); + gcry_sexp_release (l1); + _gcry_pk_util_free_encoding_ctx (&ctx); + if (DBG_CIPHER) + log_debug ("elg_verify => %s\n", rc?gpg_strerror (rc):"good"); + return rc; } @@ -942,15 +989,7 @@ elg_get_nbits (gcry_sexp_t parms) } -static const char *elg_names[] = - { - "elg", - "openpgp-elg", - "openpgp-elg-sig", - NULL, - }; - - + gcry_pk_spec_t _gcry_pubkey_spec_elg = { GCRY_PK_ELG, { 0, 0 }, diff --git a/cipher/pubkey-internal.h b/cipher/pubkey-internal.h index 0b20c7dc..3bed609a 100644 --- a/cipher/pubkey-internal.h +++ b/cipher/pubkey-internal.h @@ -25,6 +25,20 @@ gpg_err_code_t _gcry_pk_util_get_nbits (gcry_sexp_t list, unsigned int *r_nbits); gpg_err_code_t _gcry_pk_util_get_rsa_use_e (gcry_sexp_t list, unsigned long *r_e); +gpg_err_code_t _gcry_pk_util_extract_mpis (gcry_sexp_t sexp, + const char *list, ...) + GCC_ATTR_SENTINEL(0); +gpg_err_code_t _gcry_pk_util_preparse_sigval (gcry_sexp_t s_sig, + const char **algo_names, + gcry_sexp_t *r_parms, + int *r_eccflags); +void _gcry_pk_util_init_encoding_ctx (struct pk_encoding_ctx *ctx, + enum pk_operation op, + unsigned int nbits); +void _gcry_pk_util_free_encoding_ctx (struct pk_encoding_ctx *ctx); +gcry_err_code_t _gcry_pk_util_data_to_mpi (gcry_sexp_t input, + gcry_mpi_t *ret_mpi, + struct pk_encoding_ctx *ctx); diff --git a/cipher/pubkey-util.c b/cipher/pubkey-util.c index ef0ef441..debe6ca6 100644 --- a/cipher/pubkey-util.c +++ b/cipher/pubkey-util.c @@ -30,6 +30,21 @@ #include "pubkey-internal.h" +/* Callback for the pubkey algorithm code to verify PSS signatures. + OPAQUE is the data provided by the actual caller. The meaning of + TMP depends on the actual algorithm (but there is only RSA); now + for RSA it is the output of running the public key function on the + input. */ +static int +pss_verify_cmp (void *opaque, gcry_mpi_t tmp) +{ + struct pk_encoding_ctx *ctx = opaque; + gcry_mpi_t hash = ctx->verify_arg; + + return _gcry_rsa_pss_verify (hash, tmp, ctx->nbits - 1, + ctx->hash_algo, ctx->saltlen); +} + /* Get the "nbits" parameter from an s-expression of the format: @@ -125,3 +140,755 @@ _gcry_pk_util_get_rsa_use_e (gcry_sexp_t list, unsigned long *r_e) gcry_sexp_release (list); return 0; } + + +/* Extract MPIs from an s-expression using a list of one letter + * parameters. The names of these parameters are given by the string + * LIST. Some special characters may be given to control the + * conversion: + * + * + :: Switch to unsigned integer format (default). + * - :: Switch to standard signed format. + * / :: Switch to opaque format. + * ? :: The previous parameter is optional. + * + * For each parameter name a pointer to an MPI variable is expected + * and finally a NULL is expected. Example: + * + * _gcry_pk_util_extract_mpis (key, "n/x+ed", &mpi_n, &mpi_x, &mpi_e, NULL) + * + * This stores the parameter "N" from KEY as an unsigned MPI into + * MPI_N, the parameter "X" as an opaque MPI into MPI_X, and the + * parameter "E" again as an unsigned MPI into MPI_E. + * + * The function returns NULL on success. On error an error code is + * returned and and the passed MPIs have no defined value. + */ +gpg_err_code_t +_gcry_pk_util_extract_mpis (gcry_sexp_t sexp, const char *list, ...) +{ + va_list arg_ptr; + const char *s; + gcry_mpi_t *array[10]; + int idx; + gcry_sexp_t l1; + enum gcry_mpi_format mpifmt = GCRYMPI_FMT_USG; + + /* First copy all the args into an array. This is required so that + we are able to release already allocated MPIs if later an error + was found. */ + va_start (arg_ptr, list) ; + for (s=list, idx=0; *s && idx < DIM (array); s++) + { + if (*s == '+' || *s == '-' || *s == '/' || *s == '?') + ; + else + { + array[idx] = va_arg (arg_ptr, gcry_mpi_t *); + if (!array[idx]) + { + va_end (arg_ptr); + return GPG_ERR_INTERNAL; /* NULL pointer given. */ + } + idx++; + } + } + if (*s) + { + va_end (arg_ptr); + return GPG_ERR_INTERNAL; /* Too many list elements. */ + } + if (va_arg (arg_ptr, gcry_mpi_t *)) + { + va_end (arg_ptr); + return GPG_ERR_INTERNAL; /* Not enough list elemends. */ + } + va_end (arg_ptr); + + /* Now extract all parameters. */ + for (s=list, idx=0; *s; s++) + { + if (*s == '+') + mpifmt = GCRYMPI_FMT_USG; + else if (*s == '-') + mpifmt = GCRYMPI_FMT_STD; + else if (*s == '/') + mpifmt = GCRYMPI_FMT_HEX; /* Used to indicate opaque. */ + else if (*s == '?') + ; /* Only used via lookahead. */ + else + { + l1 = gcry_sexp_find_token (sexp, s, 1); + if (!l1 && s[1] == '?') + *array[idx] = NULL; /* Optional element not found. */ + else if (!l1) + { + while (idx--) + gcry_mpi_release (*array[idx]); + return GPG_ERR_NO_OBJ; /* List element not found. */ + } + else + { + if (mpifmt == GCRYMPI_FMT_HEX) + *array[idx] = _gcry_sexp_nth_opaque_mpi (l1, 1); + else + *array[idx] = gcry_sexp_nth_mpi (l1, 1, mpifmt); + gcry_sexp_release (l1); + if (!*array[idx]) + { + while (idx--) + gcry_mpi_release (*array[idx]); + return GPG_ERR_INV_OBJ; /* Conversion failed. */ + } + } + idx++; + } + } + + return 0; +} + + +/* Parse a "sig_val s-expression and store the inner parameter list at + R_PARMS. ALGO_NAMES is used to verify that the algorithm in + "sig-val" is valid. Returns 0 on success and stores a new list at + R_PARMS which must be freed by the caller. On error R_PARMS is set + to NULL and an error code returned. If R_ECCFLAGS is not NULL flag + values are set into it; as of now they are only used with ecc + algorithms. */ +gpg_err_code_t +_gcry_pk_util_preparse_sigval (gcry_sexp_t s_sig, const char **algo_names, + gcry_sexp_t *r_parms, int *r_eccflags) +{ + gpg_err_code_t rc; + gcry_sexp_t l1 = NULL; + gcry_sexp_t l2 = NULL; + char *name = NULL; + int i; + + *r_parms = NULL; + if (r_eccflags) + *r_eccflags = 0; + + /* Extract the signature value. */ + l1 = gcry_sexp_find_token (s_sig, "sig-val", 0); + if (!l1) + { + rc = GPG_ERR_INV_OBJ; /* Does not contain a signature value object. */ + goto leave; + } + + l2 = gcry_sexp_nth (l1, 1); + if (!l2) + { + rc = GPG_ERR_NO_OBJ; /* No cadr for the sig object. */ + goto leave; + } + name = _gcry_sexp_nth_string (l2, 0); + if (!name) + { + rc = GPG_ERR_INV_OBJ; /* Invalid structure of object. */ + goto leave; + } + else if (!strcmp (name, "flags")) + { + /* Skip a "flags" parameter and look again for the algorithm + name. This is not used but here just for the sake of + consistent S-expressions we need to handle it. */ + gcry_sexp_release (l2); + l2 = gcry_sexp_nth (l1, 2); + if (!l2) + { + rc = GPG_ERR_INV_OBJ; + goto leave; + } + gcry_free (name); + name = _gcry_sexp_nth_string (l2, 0); + if (!name) + { + rc = GPG_ERR_INV_OBJ; /* Invalid structure of object. */ + goto leave; + } + } + + for (i=0; algo_names[i]; i++) + if (!stricmp (name, algo_names[i])) + break; + if (!algo_names[i]) + { + rc = GPG_ERR_CONFLICT; /* "sig-val" uses an unexpected algo. */ + goto leave; + } + if (r_eccflags) + { + if (!strcmp (name, "eddsa")) + *r_eccflags = PUBKEY_FLAG_EDDSA; + } + + *r_parms = l2; + l2 = NULL; + rc = 0; + + leave: + gcry_free (name); + gcry_sexp_release (l2); + gcry_sexp_release (l1); + return rc; +} + +/* Initialize an encoding context. */ +void +_gcry_pk_util_init_encoding_ctx (struct pk_encoding_ctx *ctx, + enum pk_operation op, + unsigned int nbits) +{ + ctx->op = op; + ctx->nbits = nbits; + ctx->encoding = PUBKEY_ENC_UNKNOWN; + ctx->flags = 0; + ctx->hash_algo = GCRY_MD_SHA1; + ctx->label = NULL; + ctx->labellen = 0; + ctx->saltlen = 20; + ctx->verify_cmp = NULL; + ctx->verify_arg = NULL; +} + +/* Free a context initialzied by _gcry_pk_util_init_encoding_ctx. */ +void +_gcry_pk_util_free_encoding_ctx (struct pk_encoding_ctx *ctx) +{ + gcry_free (ctx->label); +} + + +static inline int +get_hash_algo (const char *s, size_t n) +{ + static const struct { const char *name; int algo; } hashnames[] = { + { "sha1", GCRY_MD_SHA1 }, + { "md5", GCRY_MD_MD5 }, + { "sha256", GCRY_MD_SHA256 }, + { "ripemd160", GCRY_MD_RMD160 }, + { "rmd160", GCRY_MD_RMD160 }, + { "sha384", GCRY_MD_SHA384 }, + { "sha512", GCRY_MD_SHA512 }, + { "sha224", GCRY_MD_SHA224 }, + { "md2", GCRY_MD_MD2 }, + { "md4", GCRY_MD_MD4 }, + { "tiger", GCRY_MD_TIGER }, + { "haval", GCRY_MD_HAVAL }, + { NULL, 0 } + }; + int algo; + int i; + + for (i=0; hashnames[i].name; i++) + { + if ( strlen (hashnames[i].name) == n + && !memcmp (hashnames[i].name, s, n)) + break; + } + if (hashnames[i].name) + algo = hashnames[i].algo; + else + { + /* In case of not listed or dynamically allocated hash + algorithm we fall back to this somewhat slower + method. Further, it also allows to use OIDs as + algorithm names. */ + char *tmpname; + + tmpname = gcry_malloc (n+1); + if (!tmpname) + algo = 0; /* Out of core - silently give up. */ + else + { + memcpy (tmpname, s, n); + tmpname[n] = 0; + algo = gcry_md_map_name (tmpname); + gcry_free (tmpname); + } + } + return algo; +} + + +/* Take the hash value and convert into an MPI, suitable for + passing to the low level functions. We currently support the + old style way of passing just a MPI and the modern interface which + allows to pass flags so that we can choose between raw and pkcs1 + padding - may be more padding options later. + + (<mpi>) + or + (data + [(flags [raw, direct, pkcs1, oaep, pss, no-blinding, rfc6979, eddsa])] + [(hash <algo> <value>)] + [(value <text>)] + [(hash-algo <algo>)] + [(label <label>)] + [(salt-length <length>)] + [(random-override <data>)] + ) + + Either the VALUE or the HASH element must be present for use + with signatures. VALUE is used for encryption. + + HASH-ALGO is specific to OAEP and EDDSA. + + LABEL is specific to OAEP. + + SALT-LENGTH is for PSS. + + RANDOM-OVERRIDE is used to replace random nonces for regression + testing. */ +gcry_err_code_t +_gcry_pk_util_data_to_mpi (gcry_sexp_t input, gcry_mpi_t *ret_mpi, + struct pk_encoding_ctx *ctx) +{ + gcry_err_code_t rc = 0; + gcry_sexp_t ldata, lhash, lvalue; + int i; + size_t n; + const char *s; + int unknown_flag = 0; + int parsed_flags = 0; + int explicit_raw = 0; + + *ret_mpi = NULL; + ldata = gcry_sexp_find_token (input, "data", 0); + if (!ldata) + { /* assume old style */ + *ret_mpi = gcry_sexp_nth_mpi (input, 0, 0); + return *ret_mpi ? GPG_ERR_NO_ERROR : GPG_ERR_INV_OBJ; + } + + /* see whether there is a flags object */ + { + gcry_sexp_t lflags = gcry_sexp_find_token (ldata, "flags", 0); + if (lflags) + { /* parse the flags list. */ + for (i=gcry_sexp_length (lflags)-1; i > 0; i--) + { + s = gcry_sexp_nth_data (lflags, i, &n); + if (!s) + ; /* not a data element*/ + else if (n == 7 && !memcmp (s, "rfc6979", 7)) + parsed_flags |= PUBKEY_FLAG_RFC6979; + else if (n == 5 && !memcmp (s, "eddsa", 5)) + { + ctx->encoding = PUBKEY_ENC_RAW; + parsed_flags |= PUBKEY_FLAG_EDDSA; + } + else if ( n == 3 && !memcmp (s, "raw", 3) + && ctx->encoding == PUBKEY_ENC_UNKNOWN) + { + ctx->encoding = PUBKEY_ENC_RAW; + explicit_raw = 1; + } + else if ( n == 5 && !memcmp (s, "pkcs1", 5) + && ctx->encoding == PUBKEY_ENC_UNKNOWN) + { + ctx->encoding = PUBKEY_ENC_PKCS1; + parsed_flags |= PUBKEY_FLAG_FIXEDLEN; + } + else if ( n == 4 && !memcmp (s, "oaep", 4) + && ctx->encoding == PUBKEY_ENC_UNKNOWN) + { + ctx->encoding = PUBKEY_ENC_OAEP; + parsed_flags |= PUBKEY_FLAG_FIXEDLEN; + } + else if ( n == 3 && !memcmp (s, "pss", 3) + && ctx->encoding == PUBKEY_ENC_UNKNOWN) + { + ctx->encoding = PUBKEY_ENC_PSS; + parsed_flags |= PUBKEY_FLAG_FIXEDLEN; + } + else if (n == 11 && ! memcmp (s, "no-blinding", 11)) + parsed_flags |= PUBKEY_FLAG_NO_BLINDING; + else + unknown_flag = 1; + } + gcry_sexp_release (lflags); + } + } + + if (ctx->encoding == PUBKEY_ENC_UNKNOWN) + ctx->encoding = PUBKEY_ENC_RAW; /* default to raw */ + + /* Get HASH or MPI */ + lhash = gcry_sexp_find_token (ldata, "hash", 0); + lvalue = lhash? NULL : gcry_sexp_find_token (ldata, "value", 0); + + if (!(!lhash ^ !lvalue)) + rc = GPG_ERR_INV_OBJ; /* none or both given */ + else if (unknown_flag) + rc = GPG_ERR_INV_FLAG; + else if (ctx->encoding == PUBKEY_ENC_RAW + && (parsed_flags & PUBKEY_FLAG_EDDSA)) + { + /* Prepare for EdDSA. */ + gcry_sexp_t list; + void *value; + size_t valuelen; + + if (!lvalue) + { + rc = GPG_ERR_INV_OBJ; + goto leave; + } + /* Get HASH-ALGO. */ + list = gcry_sexp_find_token (ldata, "hash-algo", 0); + if (list) + { + s = gcry_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; + } + gcry_sexp_release (list); + } + else + rc = GPG_ERR_INV_OBJ; + if (rc) + goto leave; + + /* Get VALUE. */ + value = gcry_sexp_nth_buffer (lvalue, 1, &valuelen); + if (!value) + { + /* We assume that a zero length message is meant by + "(value)". This is commonly used by test vectors. Note + that S-expression do not allow zero length items. */ + valuelen = 0; + value = gcry_malloc (1); + if (!value) + rc = gpg_err_code_from_syserror (); + } + else if ((valuelen * 8) < valuelen) + { + gcry_free (value); + rc = GPG_ERR_TOO_LARGE; + } + if (rc) + goto leave; + + /* Note that mpi_set_opaque takes ownership of VALUE. */ + *ret_mpi = gcry_mpi_set_opaque (NULL, value, valuelen*8); + } + else if (ctx->encoding == PUBKEY_ENC_RAW && lhash + && (explicit_raw || (parsed_flags & PUBKEY_FLAG_RFC6979))) + { + /* 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 (gcry_sexp_length (lhash) != 3) + rc = GPG_ERR_INV_OBJ; + else if ( !(s=gcry_sexp_nth_data (lhash, 1, &n)) || !n ) + rc = GPG_ERR_INV_OBJ; + else + { + void *value; + size_t valuelen; + + ctx->hash_algo = get_hash_algo (s, n); + if (!ctx->hash_algo) + rc = GPG_ERR_DIGEST_ALGO; + else if (!(value=gcry_sexp_nth_buffer (lhash, 2, &valuelen))) + rc = GPG_ERR_INV_OBJ; + else if ((valuelen * 8) < valuelen) + { + gcry_free (value); + rc = GPG_ERR_TOO_LARGE; + } + else + *ret_mpi = gcry_mpi_set_opaque (NULL, value, valuelen*8); + } + } + else if (ctx->encoding == PUBKEY_ENC_RAW && lvalue) + { + /* RFC6969 may only be used with the a hash value and not the + MPI based value. */ + if (parsed_flags & PUBKEY_FLAG_RFC6979) + { + rc = GPG_ERR_CONFLICT; + goto leave; + } + + /* Get the value */ + *ret_mpi = gcry_sexp_nth_mpi (lvalue, 1, GCRYMPI_FMT_USG); + if (!*ret_mpi) + rc = GPG_ERR_INV_OBJ; + } + else if (ctx->encoding == PUBKEY_ENC_PKCS1 && lvalue + && ctx->op == PUBKEY_OP_ENCRYPT) + { + const void * value; + size_t valuelen; + gcry_sexp_t list; + void *random_override = NULL; + size_t random_override_len = 0; + + if ( !(value=gcry_sexp_nth_data (lvalue, 1, &valuelen)) || !valuelen ) + rc = GPG_ERR_INV_OBJ; + else + { + /* Get optional RANDOM-OVERRIDE. */ + list = gcry_sexp_find_token (ldata, "random-override", 0); + if (list) + { + s = gcry_sexp_nth_data (list, 1, &n); + if (!s) + rc = GPG_ERR_NO_OBJ; + else if (n > 0) + { + random_override = gcry_malloc (n); + if (!random_override) + rc = gpg_err_code_from_syserror (); + else + { + memcpy (random_override, s, n); + random_override_len = n; + } + } + gcry_sexp_release (list); + if (rc) + goto leave; + } + + rc = _gcry_rsa_pkcs1_encode_for_enc (ret_mpi, ctx->nbits, + value, valuelen, + random_override, + random_override_len); + gcry_free (random_override); + } + } + else if (ctx->encoding == PUBKEY_ENC_PKCS1 && lhash + && (ctx->op == PUBKEY_OP_SIGN || ctx->op == PUBKEY_OP_VERIFY)) + { + if (gcry_sexp_length (lhash) != 3) + rc = GPG_ERR_INV_OBJ; + else if ( !(s=gcry_sexp_nth_data (lhash, 1, &n)) || !n ) + rc = GPG_ERR_INV_OBJ; + else + { + const void * value; + size_t valuelen; + + ctx->hash_algo = get_hash_algo (s, n); + + if (!ctx->hash_algo) + rc = GPG_ERR_DIGEST_ALGO; + else if ( !(value=gcry_sexp_nth_data (lhash, 2, &valuelen)) + || !valuelen ) + rc = GPG_ERR_INV_OBJ; + else + rc = _gcry_rsa_pkcs1_encode_for_sig (ret_mpi, ctx->nbits, + value, valuelen, + ctx->hash_algo); + } + } + else if (ctx->encoding == PUBKEY_ENC_OAEP && lvalue + && ctx->op == PUBKEY_OP_ENCRYPT) + { + const void * value; + size_t valuelen; + + if ( !(value=gcry_sexp_nth_data (lvalue, 1, &valuelen)) || !valuelen ) + rc = GPG_ERR_INV_OBJ; + else + { + gcry_sexp_t list; + void *random_override = NULL; + size_t random_override_len = 0; + + /* Get HASH-ALGO. */ + list = gcry_sexp_find_token (ldata, "hash-algo", 0); + if (list) + { + s = gcry_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; + } + gcry_sexp_release (list); + if (rc) + goto leave; + } + + /* Get LABEL. */ + list = gcry_sexp_find_token (ldata, "label", 0); + if (list) + { + s = gcry_sexp_nth_data (list, 1, &n); + if (!s) + rc = GPG_ERR_NO_OBJ; + else if (n > 0) + { + ctx->label = gcry_malloc (n); + if (!ctx->label) + rc = gpg_err_code_from_syserror (); + else + { + memcpy (ctx->label, s, n); + ctx->labellen = n; + } + } + gcry_sexp_release (list); + if (rc) + goto leave; + } + /* Get optional RANDOM-OVERRIDE. */ + list = gcry_sexp_find_token (ldata, "random-override", 0); + if (list) + { + s = gcry_sexp_nth_data (list, 1, &n); + if (!s) + rc = GPG_ERR_NO_OBJ; + else if (n > 0) + { + random_override = gcry_malloc (n); + if (!random_override) + rc = gpg_err_code_from_syserror (); + else + { + memcpy (random_override, s, n); + random_override_len = n; + } + } + gcry_sexp_release (list); + if (rc) + goto leave; + } + + rc = _gcry_rsa_oaep_encode (ret_mpi, ctx->nbits, ctx->hash_algo, + value, valuelen, + ctx->label, ctx->labellen, + random_override, random_override_len); + + gcry_free (random_override); + } + } + else if (ctx->encoding == PUBKEY_ENC_PSS && lhash + && ctx->op == PUBKEY_OP_SIGN) + { + if (gcry_sexp_length (lhash) != 3) + rc = GPG_ERR_INV_OBJ; + else if ( !(s=gcry_sexp_nth_data (lhash, 1, &n)) || !n ) + rc = GPG_ERR_INV_OBJ; + else + { + const void * value; + size_t valuelen; + void *random_override = NULL; + size_t random_override_len = 0; + + ctx->hash_algo = get_hash_algo (s, n); + + if (!ctx->hash_algo) + rc = GPG_ERR_DIGEST_ALGO; + else if ( !(value=gcry_sexp_nth_data (lhash, 2, &valuelen)) + || !valuelen ) + rc = GPG_ERR_INV_OBJ; + else + { + gcry_sexp_t list; + + /* Get SALT-LENGTH. */ + list = gcry_sexp_find_token (ldata, "salt-length", 0); + if (list) + { + s = gcry_sexp_nth_data (list, 1, &n); + if (!s) + { + rc = GPG_ERR_NO_OBJ; + goto leave; + } + ctx->saltlen = (unsigned int)strtoul (s, NULL, 10); + gcry_sexp_release (list); + } + + /* Get optional RANDOM-OVERRIDE. */ + list = gcry_sexp_find_token (ldata, "random-override", 0); + if (list) + { + s = gcry_sexp_nth_data (list, 1, &n); + if (!s) + rc = GPG_ERR_NO_OBJ; + else if (n > 0) + { + random_override = gcry_malloc (n); + if (!random_override) + rc = gpg_err_code_from_syserror (); + else + { + memcpy (random_override, s, n); + random_override_len = n; + } + } + gcry_sexp_release (list); + if (rc) + goto leave; + } + + /* Encode the data. (NBITS-1 is due to 8.1.1, step 1.) */ + rc = _gcry_rsa_pss_encode (ret_mpi, ctx->nbits - 1, + ctx->hash_algo, + value, valuelen, ctx->saltlen, + random_override, random_override_len); + + gcry_free (random_override); + } + } + } + else if (ctx->encoding == PUBKEY_ENC_PSS && lhash + && ctx->op == PUBKEY_OP_VERIFY) + { + if (gcry_sexp_length (lhash) != 3) + rc = GPG_ERR_INV_OBJ; + else if ( !(s=gcry_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 + { + *ret_mpi = gcry_sexp_nth_mpi (lhash, 2, GCRYMPI_FMT_USG); + if (!*ret_mpi) + rc = GPG_ERR_INV_OBJ; + ctx->verify_cmp = pss_verify_cmp; + ctx->verify_arg = *ret_mpi; + } + } + } + else + rc = GPG_ERR_CONFLICT; + + leave: + gcry_sexp_release (ldata); + gcry_sexp_release (lhash); + gcry_sexp_release (lvalue); + + if (!rc) + ctx->flags = parsed_flags; + else + { + gcry_free (ctx->label); + ctx->label = NULL; + } + + return rc; +} diff --git a/cipher/pubkey.c b/cipher/pubkey.c index cb2177ba..034b00fa 100644 --- a/cipher/pubkey.c +++ b/cipher/pubkey.c @@ -130,6 +130,8 @@ spec_from_sexp (gcry_sexp_t sexp, int want_private, gcry_pk_spec_t *spec; *r_spec = NULL; + if (r_parms) + *r_parms = NULL; /* Check that the first element is valid. If we are looking for a public key but a private key was supplied, we allow the use of @@ -637,88 +639,8 @@ sexp_to_key (gcry_sexp_t sexp, int want_private, int use, } -/* Parse SEXP and store the elements into a newly allocated array of - MPIs which will be stored at RETARRAY. If OPAQUE is set, store the - MPI as opaque data. */ -static gcry_err_code_t -sexp_to_sig (gcry_sexp_t sexp, gcry_mpi_t **retarray, - gcry_pk_spec_t **r_spec, int opaque) -{ - gcry_err_code_t err = 0; - gcry_sexp_t list, l2; - char *name; - const char *elems; - gcry_mpi_t *array; - gcry_pk_spec_t *spec; - - /* Check that the first element is valid. */ - list = gcry_sexp_find_token( sexp, "sig-val" , 0 ); - if (!list) - return GPG_ERR_INV_OBJ; /* Does not contain a signature value object. */ - - l2 = gcry_sexp_nth (list, 1); - if (!l2) - { - gcry_sexp_release (list); - return GPG_ERR_NO_OBJ; /* No cadr for the sig object. */ - } - name = _gcry_sexp_nth_string (l2, 0); - if (!name) - { - gcry_sexp_release (list); - gcry_sexp_release (l2); - return GPG_ERR_INV_OBJ; /* Invalid structure of object. */ - } - else if (!strcmp (name, "flags")) - { - /* Skip flags, since they are not used but here just for the - sake of consistent S-expressions. */ - gcry_free (name); - gcry_sexp_release (l2); - l2 = gcry_sexp_nth (list, 2); - if (!l2) - { - gcry_sexp_release (list); - return GPG_ERR_INV_OBJ; - } - name = _gcry_sexp_nth_string (l2, 0); - } - - spec = spec_from_name (name); - gcry_free (name); - name = NULL; - - if (!spec) - { - gcry_sexp_release (l2); - gcry_sexp_release (list); - return GPG_ERR_PUBKEY_ALGO; /* Unknown algorithm. */ - } - - elems = spec->elements_sig; - array = gcry_calloc (strlen (elems) + 1 , sizeof *array ); - if (!array) - err = gpg_err_code_from_syserror (); - - if (!err) - err = sexp_elements_extract (list, elems, array, NULL, opaque); - - gcry_sexp_release (l2); - gcry_sexp_release (list); - - if (err) - { - gcry_free (array); - } - else - { - *retarray = array; - *r_spec = spec; - } - - return err; -} +/* FIXME: This is a duplicate. */ static inline int get_hash_algo (const char *s, size_t n) { @@ -978,518 +900,6 @@ sexp_to_enc (gcry_sexp_t sexp, gcry_mpi_t **retarray, gcry_pk_spec_t **r_spec, } -/* Callback for the pubkey algorithm code to verify PSS signatures. - OPAQUE is the data provided by the actual caller. The meaning of - TMP depends on the actual algorithm (but there is only RSA); now - for RSA it is the output of running the public key function on the - input. */ -static int -pss_verify_cmp (void *opaque, gcry_mpi_t tmp) -{ - struct pk_encoding_ctx *ctx = opaque; - gcry_mpi_t hash = ctx->verify_arg; - - return _gcry_rsa_pss_verify (hash, tmp, ctx->nbits - 1, - ctx->hash_algo, ctx->saltlen); -} - - -/* Take the hash value and convert into an MPI, suitable for - passing to the low level functions. We currently support the - old style way of passing just a MPI and the modern interface which - allows to pass flags so that we can choose between raw and pkcs1 - padding - may be more padding options later. - - (<mpi>) - or - (data - [(flags [raw, direct, pkcs1, oaep, pss, no-blinding, rfc6979, eddsa])] - [(hash <algo> <value>)] - [(value <text>)] - [(hash-algo <algo>)] - [(label <label>)] - [(salt-length <length>)] - [(random-override <data>)] - ) - - Either the VALUE or the HASH element must be present for use - with signatures. VALUE is used for encryption. - - HASH-ALGO is specific to OAEP and EDDSA. - - LABEL is specific to OAEP. - - SALT-LENGTH is for PSS. - - RANDOM-OVERRIDE is used to replace random nonces for regression - testing. */ -static gcry_err_code_t -sexp_data_to_mpi (gcry_sexp_t input, gcry_mpi_t *ret_mpi, - struct pk_encoding_ctx *ctx) -{ - gcry_err_code_t rc = 0; - gcry_sexp_t ldata, lhash, lvalue; - int i; - size_t n; - const char *s; - int unknown_flag = 0; - int parsed_flags = 0; - int explicit_raw = 0; - - *ret_mpi = NULL; - ldata = gcry_sexp_find_token (input, "data", 0); - if (!ldata) - { /* assume old style */ - *ret_mpi = gcry_sexp_nth_mpi (input, 0, 0); - return *ret_mpi ? GPG_ERR_NO_ERROR : GPG_ERR_INV_OBJ; - } - - /* see whether there is a flags object */ - { - gcry_sexp_t lflags = gcry_sexp_find_token (ldata, "flags", 0); - if (lflags) - { /* parse the flags list. */ - for (i=gcry_sexp_length (lflags)-1; i > 0; i--) - { - s = gcry_sexp_nth_data (lflags, i, &n); - if (!s) - ; /* not a data element*/ - else if (n == 7 && !memcmp (s, "rfc6979", 7)) - parsed_flags |= PUBKEY_FLAG_RFC6979; - else if (n == 5 && !memcmp (s, "eddsa", 5)) - { - ctx->encoding = PUBKEY_ENC_RAW; - parsed_flags |= PUBKEY_FLAG_EDDSA; - } - else if ( n == 3 && !memcmp (s, "raw", 3) - && ctx->encoding == PUBKEY_ENC_UNKNOWN) - { - ctx->encoding = PUBKEY_ENC_RAW; - explicit_raw = 1; - } - else if ( n == 5 && !memcmp (s, "pkcs1", 5) - && ctx->encoding == PUBKEY_ENC_UNKNOWN) - { - ctx->encoding = PUBKEY_ENC_PKCS1; - parsed_flags |= PUBKEY_FLAG_FIXEDLEN; - } - else if ( n == 4 && !memcmp (s, "oaep", 4) - && ctx->encoding == PUBKEY_ENC_UNKNOWN) - { - ctx->encoding = PUBKEY_ENC_OAEP; - parsed_flags |= PUBKEY_FLAG_FIXEDLEN; - } - else if ( n == 3 && !memcmp (s, "pss", 3) - && ctx->encoding == PUBKEY_ENC_UNKNOWN) - { - ctx->encoding = PUBKEY_ENC_PSS; - parsed_flags |= PUBKEY_FLAG_FIXEDLEN; - } - else if (n == 11 && ! memcmp (s, "no-blinding", 11)) - parsed_flags |= PUBKEY_FLAG_NO_BLINDING; - else - unknown_flag = 1; - } - gcry_sexp_release (lflags); - } - } - - if (ctx->encoding == PUBKEY_ENC_UNKNOWN) - ctx->encoding = PUBKEY_ENC_RAW; /* default to raw */ - - /* Get HASH or MPI */ - lhash = gcry_sexp_find_token (ldata, "hash", 0); - lvalue = lhash? NULL : gcry_sexp_find_token (ldata, "value", 0); - - if (!(!lhash ^ !lvalue)) - rc = GPG_ERR_INV_OBJ; /* none or both given */ - else if (unknown_flag) - rc = GPG_ERR_INV_FLAG; - else if (ctx->encoding == PUBKEY_ENC_RAW - && (parsed_flags & PUBKEY_FLAG_EDDSA)) - { - /* Prepare for EdDSA. */ - gcry_sexp_t list; - void *value; - size_t valuelen; - - if (!lvalue) - { - rc = GPG_ERR_INV_OBJ; - goto leave; - } - /* Get HASH-ALGO. */ - list = gcry_sexp_find_token (ldata, "hash-algo", 0); - if (list) - { - s = gcry_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; - } - gcry_sexp_release (list); - } - else - rc = GPG_ERR_INV_OBJ; - if (rc) - goto leave; - - /* Get VALUE. */ - value = gcry_sexp_nth_buffer (lvalue, 1, &valuelen); - if (!value) - { - /* We assume that a zero length message is meant by - "(value)". This is commonly used by test vectors. Note - that S-expression do not allow zero length items. */ - valuelen = 0; - value = gcry_malloc (1); - if (!value) - rc = gpg_err_code_from_syserror (); - } - else if ((valuelen * 8) < valuelen) - { - gcry_free (value); - rc = GPG_ERR_TOO_LARGE; - } - if (rc) - goto leave; - - /* Note that mpi_set_opaque takes ownership of VALUE. */ - *ret_mpi = gcry_mpi_set_opaque (NULL, value, valuelen*8); - } - else if (ctx->encoding == PUBKEY_ENC_RAW && lhash - && (explicit_raw || (parsed_flags & PUBKEY_FLAG_RFC6979))) - { - /* 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 (gcry_sexp_length (lhash) != 3) - rc = GPG_ERR_INV_OBJ; - else if ( !(s=gcry_sexp_nth_data (lhash, 1, &n)) || !n ) - rc = GPG_ERR_INV_OBJ; - else - { - void *value; - size_t valuelen; - - ctx->hash_algo = get_hash_algo (s, n); - if (!ctx->hash_algo) - rc = GPG_ERR_DIGEST_ALGO; - else if (!(value=gcry_sexp_nth_buffer (lhash, 2, &valuelen))) - rc = GPG_ERR_INV_OBJ; - else if ((valuelen * 8) < valuelen) - { - gcry_free (value); - rc = GPG_ERR_TOO_LARGE; - } - else - *ret_mpi = gcry_mpi_set_opaque (NULL, value, valuelen*8); - } - } - else if (ctx->encoding == PUBKEY_ENC_RAW && lvalue) - { - /* RFC6969 may only be used with the a hash value and not the - MPI based value. */ - if (parsed_flags & PUBKEY_FLAG_RFC6979) - { - rc = GPG_ERR_CONFLICT; - goto leave; - } - - /* Get the value */ - *ret_mpi = gcry_sexp_nth_mpi (lvalue, 1, GCRYMPI_FMT_USG); - if (!*ret_mpi) - rc = GPG_ERR_INV_OBJ; - } - else if (ctx->encoding == PUBKEY_ENC_PKCS1 && lvalue - && ctx->op == PUBKEY_OP_ENCRYPT) - { - const void * value; - size_t valuelen; - gcry_sexp_t list; - void *random_override = NULL; - size_t random_override_len = 0; - - if ( !(value=gcry_sexp_nth_data (lvalue, 1, &valuelen)) || !valuelen ) - rc = GPG_ERR_INV_OBJ; - else - { - /* Get optional RANDOM-OVERRIDE. */ - list = gcry_sexp_find_token (ldata, "random-override", 0); - if (list) - { - s = gcry_sexp_nth_data (list, 1, &n); - if (!s) - rc = GPG_ERR_NO_OBJ; - else if (n > 0) - { - random_override = gcry_malloc (n); - if (!random_override) - rc = gpg_err_code_from_syserror (); - else - { - memcpy (random_override, s, n); - random_override_len = n; - } - } - gcry_sexp_release (list); - if (rc) - goto leave; - } - - rc = _gcry_rsa_pkcs1_encode_for_enc (ret_mpi, ctx->nbits, - value, valuelen, - random_override, - random_override_len); - gcry_free (random_override); - } - } - else if (ctx->encoding == PUBKEY_ENC_PKCS1 && lhash - && (ctx->op == PUBKEY_OP_SIGN || ctx->op == PUBKEY_OP_VERIFY)) - { - if (gcry_sexp_length (lhash) != 3) - rc = GPG_ERR_INV_OBJ; - else if ( !(s=gcry_sexp_nth_data (lhash, 1, &n)) || !n ) - rc = GPG_ERR_INV_OBJ; - else - { - const void * value; - size_t valuelen; - - ctx->hash_algo = get_hash_algo (s, n); - - if (!ctx->hash_algo) - rc = GPG_ERR_DIGEST_ALGO; - else if ( !(value=gcry_sexp_nth_data (lhash, 2, &valuelen)) - || !valuelen ) - rc = GPG_ERR_INV_OBJ; - else - rc = _gcry_rsa_pkcs1_encode_for_sig (ret_mpi, ctx->nbits, - value, valuelen, - ctx->hash_algo); - } - } - else if (ctx->encoding == PUBKEY_ENC_OAEP && lvalue - && ctx->op == PUBKEY_OP_ENCRYPT) - { - const void * value; - size_t valuelen; - - if ( !(value=gcry_sexp_nth_data (lvalue, 1, &valuelen)) || !valuelen ) - rc = GPG_ERR_INV_OBJ; - else - { - gcry_sexp_t list; - void *random_override = NULL; - size_t random_override_len = 0; - - /* Get HASH-ALGO. */ - list = gcry_sexp_find_token (ldata, "hash-algo", 0); - if (list) - { - s = gcry_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; - } - gcry_sexp_release (list); - if (rc) - goto leave; - } - - /* Get LABEL. */ - list = gcry_sexp_find_token (ldata, "label", 0); - if (list) - { - s = gcry_sexp_nth_data (list, 1, &n); - if (!s) - rc = GPG_ERR_NO_OBJ; - else if (n > 0) - { - ctx->label = gcry_malloc (n); - if (!ctx->label) - rc = gpg_err_code_from_syserror (); - else - { - memcpy (ctx->label, s, n); - ctx->labellen = n; - } - } - gcry_sexp_release (list); - if (rc) - goto leave; - } - /* Get optional RANDOM-OVERRIDE. */ - list = gcry_sexp_find_token (ldata, "random-override", 0); - if (list) - { - s = gcry_sexp_nth_data (list, 1, &n); - if (!s) - rc = GPG_ERR_NO_OBJ; - else if (n > 0) - { - random_override = gcry_malloc (n); - if (!random_override) - rc = gpg_err_code_from_syserror (); - else - { - memcpy (random_override, s, n); - random_override_len = n; - } - } - gcry_sexp_release (list); - if (rc) - goto leave; - } - - rc = _gcry_rsa_oaep_encode (ret_mpi, ctx->nbits, ctx->hash_algo, - value, valuelen, - ctx->label, ctx->labellen, - random_override, random_override_len); - - gcry_free (random_override); - } - } - else if (ctx->encoding == PUBKEY_ENC_PSS && lhash - && ctx->op == PUBKEY_OP_SIGN) - { - if (gcry_sexp_length (lhash) != 3) - rc = GPG_ERR_INV_OBJ; - else if ( !(s=gcry_sexp_nth_data (lhash, 1, &n)) || !n ) - rc = GPG_ERR_INV_OBJ; - else - { - const void * value; - size_t valuelen; - void *random_override = NULL; - size_t random_override_len = 0; - - ctx->hash_algo = get_hash_algo (s, n); - - if (!ctx->hash_algo) - rc = GPG_ERR_DIGEST_ALGO; - else if ( !(value=gcry_sexp_nth_data (lhash, 2, &valuelen)) - || !valuelen ) - rc = GPG_ERR_INV_OBJ; - else - { - gcry_sexp_t list; - - /* Get SALT-LENGTH. */ - list = gcry_sexp_find_token (ldata, "salt-length", 0); - if (list) - { - s = gcry_sexp_nth_data (list, 1, &n); - if (!s) - { - rc = GPG_ERR_NO_OBJ; - goto leave; - } - ctx->saltlen = (unsigned int)strtoul (s, NULL, 10); - gcry_sexp_release (list); - } - - /* Get optional RANDOM-OVERRIDE. */ - list = gcry_sexp_find_token (ldata, "random-override", 0); - if (list) - { - s = gcry_sexp_nth_data (list, 1, &n); - if (!s) - rc = GPG_ERR_NO_OBJ; - else if (n > 0) - { - random_override = gcry_malloc (n); - if (!random_override) - rc = gpg_err_code_from_syserror (); - else - { - memcpy (random_override, s, n); - random_override_len = n; - } - } - gcry_sexp_release (list); - if (rc) - goto leave; - } - - /* Encode the data. (NBITS-1 is due to 8.1.1, step 1.) */ - rc = _gcry_rsa_pss_encode (ret_mpi, ctx->nbits - 1, - ctx->hash_algo, - value, valuelen, ctx->saltlen, - random_override, random_override_len); - - gcry_free (random_override); - } - } - } - else if (ctx->encoding == PUBKEY_ENC_PSS && lhash - && ctx->op == PUBKEY_OP_VERIFY) - { - if (gcry_sexp_length (lhash) != 3) - rc = GPG_ERR_INV_OBJ; - else if ( !(s=gcry_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 - { - *ret_mpi = gcry_sexp_nth_mpi (lhash, 2, GCRYMPI_FMT_USG); - if (!*ret_mpi) - rc = GPG_ERR_INV_OBJ; - ctx->verify_cmp = pss_verify_cmp; - ctx->verify_arg = *ret_mpi; - } - } - } - else - rc = GPG_ERR_CONFLICT; - - leave: - gcry_sexp_release (ldata); - gcry_sexp_release (lhash); - gcry_sexp_release (lvalue); - - if (!rc) - ctx->flags = parsed_flags; - else - { - gcry_free (ctx->label); - ctx->label = NULL; - } - - return rc; -} - -static void -init_encoding_ctx (struct pk_encoding_ctx *ctx, enum pk_operation op, - unsigned int nbits) -{ - ctx->op = op; - ctx->nbits = nbits; - ctx->encoding = PUBKEY_ENC_UNKNOWN; - ctx->flags = 0; - ctx->hash_algo = GCRY_MD_SHA1; - ctx->label = NULL; - ctx->labellen = 0; - ctx->saltlen = 20; - ctx->verify_cmp = NULL; - ctx->verify_arg = NULL; -} - - /* Do a PK encrypt operation @@ -1502,7 +912,7 @@ init_encoding_ctx (struct pk_encoding_ctx *ctx, enum pk_operation op, Returns: 0 or an errorcode. - s_data = See comment for sexp_data_to_mpi + s_data = See comment for _gcry_pk_util_data_to_mpi s_pkey = <key-as-defined-in-sexp_to_key> r_ciph = (enc-val (<algo> @@ -1532,8 +942,8 @@ gcry_pk_encrypt (gcry_sexp_t *r_ciph, gcry_sexp_t s_data, gcry_sexp_t s_pkey) gcry_assert (spec); /* Get the stuff we want to encrypt. */ - init_encoding_ctx (&ctx, PUBKEY_OP_ENCRYPT, gcry_pk_get_nbits (s_pkey)); - rc = sexp_data_to_mpi (s_data, &data, &ctx); + _gcry_pk_util_init_encoding_ctx (&ctx, PUBKEY_OP_ENCRYPT, gcry_pk_get_nbits (s_pkey)); + rc = _gcry_pk_util_data_to_mpi (s_data, &data, &ctx); if (rc) goto leave; @@ -1622,7 +1032,7 @@ gcry_pk_decrypt (gcry_sexp_t *r_plain, gcry_sexp_t s_data, gcry_sexp_t s_skey) if (rc) goto leave; - init_encoding_ctx (&ctx, PUBKEY_OP_DECRYPT, gcry_pk_get_nbits (s_skey)); + _gcry_pk_util_init_encoding_ctx (&ctx, PUBKEY_OP_DECRYPT, gcry_pk_get_nbits (s_skey)); rc = sexp_to_enc (s_data, &data, &spec_enc, &flags, &ctx); if (rc) goto leave; @@ -1691,7 +1101,7 @@ gcry_pk_decrypt (gcry_sexp_t *r_plain, gcry_sexp_t s_data, gcry_sexp_t s_skey) other arguments but is always suitable to be passed to gcry_pk_verify - s_hash = See comment for sexp_data_to_mpi + s_hash = See comment for _gcry-pk_util_data_to_mpi s_skey = <key-as-defined-in-sexp_to_key> r_sig = (sig-val @@ -1726,9 +1136,9 @@ gcry_pk_sign (gcry_sexp_t *r_sig, gcry_sexp_t s_hash, gcry_sexp_t s_skey) /* Get the stuff we want to sign. Note that pk_get_nbits does also work on a private key. We don't need the number of bits for ECC here, thus set it to 0 so that we don't need to parse it. */ - init_encoding_ctx (&ctx, PUBKEY_OP_SIGN, + _gcry_pk_util_init_encoding_ctx (&ctx, PUBKEY_OP_SIGN, is_ecc? 0 : gcry_pk_get_nbits (s_skey)); - rc = sexp_data_to_mpi (s_hash, &hash, &ctx); + rc = _gcry_pk_util_data_to_mpi (s_hash, &hash, &ctx); if (rc) goto leave; @@ -1786,70 +1196,20 @@ gcry_error_t gcry_pk_verify (gcry_sexp_t s_sig, gcry_sexp_t s_hash, gcry_sexp_t s_pkey) { gcry_err_code_t rc; - gcry_pk_spec_t *spec = NULL; - gcry_pk_spec_t *spec_sig = NULL; - gcry_mpi_t *pkey = NULL; - gcry_mpi_t hash = NULL; - gcry_mpi_t *sig = NULL; - struct pk_encoding_ctx ctx; - int i; - - rc = sexp_to_key (s_pkey, 0, GCRY_PK_USAGE_SIGN, NULL, - &pkey, &spec, NULL); - if (rc) - goto leave; - - /* Get the stuff we want to verify. */ - init_encoding_ctx (&ctx, PUBKEY_OP_VERIFY, gcry_pk_get_nbits (s_pkey)); - rc = sexp_data_to_mpi (s_hash, &hash, &ctx); - if (rc) - goto leave; + gcry_pk_spec_t *spec; + gcry_sexp_t keyparms; - /* Get the signature. */ - rc = sexp_to_sig (s_sig, &sig, &spec_sig, - !!(ctx.flags & PUBKEY_FLAG_EDDSA)); + rc = spec_from_sexp (s_pkey, 0, &spec, &keyparms); if (rc) goto leave; - /* Fixme: Check that the algorithm of S_SIG is compatible to the one - of S_PKEY. */ - - if (spec->algo != spec_sig->algo) - { - rc = GPG_ERR_CONFLICT; - goto leave; - } - - if (DBG_CIPHER && !fips_mode ()) - { - log_debug ("gcry_pk_verify: algo=%d\n", spec->algo); - for (i = 0; i < pubkey_get_npkey (spec->algo); i++) - log_mpidump (" pkey", pkey[i]); - for (i = 0; i < pubkey_get_nsig (spec->algo); i++) - log_mpidump (" sig", sig[i]); - log_mpidump (" hash", hash); - } if (spec->verify) - rc = spec->verify (spec->algo, hash, sig, pkey, - ctx.verify_cmp, &ctx, ctx.flags, ctx.hash_algo); + rc = spec->verify (s_sig, s_hash, keyparms); else rc = GPG_ERR_NOT_IMPLEMENTED; - leave: - if (pkey) - { - release_mpi_array (pkey); - gcry_free (pkey); - } - if (sig) - { - release_mpi_array (sig); - gcry_free (sig); - } - if (hash) - mpi_free (hash); - + gcry_sexp_release (keyparms); return gcry_error (rc); } diff --git a/cipher/rsa.c b/cipher/rsa.c index c400718b..39effcba 100644 --- a/cipher/rsa.c +++ b/cipher/rsa.c @@ -53,6 +53,15 @@ typedef struct } RSA_secret_key; +static const char *rsa_names[] = + { + "rsa", + "openpgp-rsa", + "oid.1.2.840.113549.1.1.1", + NULL, + }; + + /* A sample 1024 bit RSA key used for the selftests. */ static const char sample_secret_key[] = "(private-key" @@ -89,6 +98,7 @@ static int test_keys (RSA_secret_key *sk, unsigned nbits); static int check_secret_key (RSA_secret_key *sk); static void public (gcry_mpi_t output, gcry_mpi_t input, RSA_public_key *skey); static void secret (gcry_mpi_t output, gcry_mpi_t input, RSA_secret_key *skey); +static unsigned int rsa_get_nbits (gcry_sexp_t parms); /* Check that a freshly generated key actually works. Returns 0 on success. */ @@ -1049,40 +1059,71 @@ rsa_sign (int algo, gcry_sexp_t *r_result, gcry_mpi_t data, gcry_mpi_t *skey, static gcry_err_code_t -rsa_verify (int algo, gcry_mpi_t hash, gcry_mpi_t *data, gcry_mpi_t *pkey, - int (*cmp) (void *opaque, gcry_mpi_t tmp), void *opaquev, - int flags, int hashalgo) +rsa_verify (gcry_sexp_t s_sig, gcry_sexp_t s_data, gcry_sexp_t s_keyparms) { - RSA_public_key pk; - gcry_mpi_t result; gcry_err_code_t rc; + struct pk_encoding_ctx ctx; + gcry_sexp_t l1 = NULL; + gcry_mpi_t sig = NULL; + gcry_mpi_t data = NULL; + RSA_public_key pk = { NULL, NULL }; + gcry_mpi_t result = NULL; + + _gcry_pk_util_init_encoding_ctx (&ctx, PUBKEY_OP_VERIFY, + rsa_get_nbits (s_keyparms)); + + /* Extract the data. */ + rc = _gcry_pk_util_data_to_mpi (s_data, &data, &ctx); + if (rc) + goto leave; + if (DBG_CIPHER) + log_mpidump ("rsa_verify data", data); + if (mpi_is_opaque (data)) + { + rc = GPG_ERR_INV_DATA; + goto leave; + } - (void)algo; - (void)cmp; - (void)opaquev; - (void)flags; - (void)hashalgo; - - if (mpi_is_opaque (hash)) - return GPG_ERR_INV_DATA; + /* Extract the signature value. */ + rc = _gcry_pk_util_preparse_sigval (s_sig, rsa_names, &l1, NULL); + if (rc) + goto leave; + rc = _gcry_pk_util_extract_mpis (l1, "s", &sig, NULL); + if (rc) + goto leave; + if (DBG_CIPHER) + log_mpidump ("rsa_verify sig", sig); - pk.n = pkey[0]; - pk.e = pkey[1]; - result = gcry_mpi_new ( 160 ); - public( result, data[0], &pk ); -#ifdef IS_DEVELOPMENT_VERSION + /* Extract the key. */ + rc = _gcry_pk_util_extract_mpis (s_keyparms, "ne", &pk.n, &pk.e, NULL); + if (rc) + return rc; if (DBG_CIPHER) { - log_mpidump ("rsa verify result", result ); - log_mpidump (" hash", hash ); + log_mpidump ("rsa_verify n", pk.n); + log_mpidump ("rsa_verify e", pk.e); } -#endif /*IS_DEVELOPMENT_VERSION*/ - if (cmp) - rc = (*cmp) (opaquev, result); + + /* Do RSA computation and compare. */ + result = gcry_mpi_new (0); + public (result, sig, &pk); + if (DBG_CIPHER) + log_mpidump ("rsa_verify cmp", result); + if (ctx.verify_cmp) + rc = ctx.verify_cmp (&ctx, result); else - rc = mpi_cmp (result, hash) ? GPG_ERR_BAD_SIGNATURE : GPG_ERR_NO_ERROR; - gcry_mpi_release (result); + rc = mpi_cmp (result, data) ? GPG_ERR_BAD_SIGNATURE : 0; + leave: + gcry_mpi_release (result); + gcry_mpi_release (pk.n); + gcry_mpi_release (pk.e); + gcry_mpi_release (data); + gcry_mpi_release (sig); + gcry_sexp_release (l1); + _gcry_pk_util_free_encoding_ctx (&ctx); + if (DBG_CIPHER) + log_debug ("rsa_verify => %s\n", rc?gpg_strerror (rc):"good"); return rc; } @@ -1423,14 +1464,6 @@ run_selftests (int algo, int extended, selftest_report_func_t report) -static const char *rsa_names[] = - { - "rsa", - "openpgp-rsa", - "oid.1.2.840.113549.1.1.1", - NULL, - }; - gcry_pk_spec_t _gcry_pubkey_spec_rsa = { GCRY_PK_RSA, { 0, 1 }, |