From 6e4ea760261c38d0eb1d7259334c8a192e99fa67 Mon Sep 17 00:00:00 2001 From: Packit Date: Sep 14 2020 11:50:30 +0000 Subject: Apply patch libgcrypt-1.8.0-tests.patch patch_name: libgcrypt-1.8.0-tests.patch present_in_specfile: true --- diff --git a/cipher/dsa.c b/cipher/dsa.c index 22d8d78..55c966c 100644 --- a/cipher/dsa.c +++ b/cipher/dsa.c @@ -457,11 +457,22 @@ generate_fips186 (DSA_secret_key *sk, unsigned int nbits, unsigned int qbits, &prime_q, &prime_p, r_counter, r_seed, r_seedlen); - else - ec = _gcry_generate_fips186_3_prime (nbits, qbits, NULL, 0, + else if (!domain->p || !domain->q) + ec = _gcry_generate_fips186_3_prime (nbits, qbits, + initial_seed.seed, + initial_seed.seedlen, &prime_q, &prime_p, r_counter, r_seed, r_seedlen, NULL); + else + { + /* Domain parameters p and q are given; use them. */ + prime_p = mpi_copy (domain->p); + prime_q = mpi_copy (domain->q); + gcry_assert (mpi_get_nbits (prime_p) == nbits); + gcry_assert (mpi_get_nbits (prime_q) == qbits); + ec = 0; + } sexp_release (initial_seed.sexp); if (ec) goto leave; @@ -855,13 +866,12 @@ dsa_generate (const gcry_sexp_t genparms, gcry_sexp_t *r_skey) sexp_release (l1); sexp_release (domainsexp); - /* Check that all domain parameters are available. */ - if (!domain.p || !domain.q || !domain.g) + /* Check that p and q domain parameters are available. */ + if (!domain.p || !domain.q || (!domain.g && !(flags & PUBKEY_FLAG_USE_FIPS186))) { _gcry_mpi_release (domain.p); _gcry_mpi_release (domain.q); _gcry_mpi_release (domain.g); - sexp_release (deriveparms); return GPG_ERR_MISSING_VALUE; } diff --git a/cipher/dsa.c.tests b/cipher/dsa.c.tests new file mode 100644 index 0000000..22d8d78 --- /dev/null +++ b/cipher/dsa.c.tests @@ -0,0 +1,1390 @@ +/* dsa.c - DSA signature algorithm + * Copyright (C) 1998, 2000, 2001, 2002, 2003, + * 2006, 2008 Free Software Foundation, Inc. + * Copyright (C) 2013 g10 Code GmbH. + * + * This file is part of Libgcrypt. + * + * Libgcrypt is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * Libgcrypt is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, see . + */ + +#include +#include +#include +#include + +#include "g10lib.h" +#include "mpi.h" +#include "cipher.h" +#include "pubkey-internal.h" + + +typedef struct +{ + gcry_mpi_t p; /* prime */ + gcry_mpi_t q; /* group order */ + gcry_mpi_t g; /* group generator */ + gcry_mpi_t y; /* g^x mod p */ +} DSA_public_key; + + +typedef struct +{ + gcry_mpi_t p; /* prime */ + gcry_mpi_t q; /* group order */ + gcry_mpi_t g; /* group generator */ + gcry_mpi_t y; /* g^x mod p */ + gcry_mpi_t x; /* secret exponent */ +} DSA_secret_key; + + +/* A structure used to hold domain parameters. */ +typedef struct +{ + gcry_mpi_t p; /* prime */ + gcry_mpi_t q; /* group order */ + gcry_mpi_t g; /* group generator */ +} dsa_domain_t; + + +static const char *dsa_names[] = + { + "dsa", + "openpgp-dsa", + NULL, + }; + + +/* A sample 1024 bit DSA key used for the selftests. Not anymore + * used, kept only for reference. */ +#if 0 +static const char sample_secret_key_1024[] = +"(private-key" +" (dsa" +" (p #00AD7C0025BA1A15F775F3F2D673718391D00456978D347B33D7B49E7F32EDAB" +" 96273899DD8B2BB46CD6ECA263FAF04A28903503D59062A8865D2AE8ADFB5191" +" CF36FFB562D0E2F5809801A1F675DAE59698A9E01EFE8D7DCFCA084F4C6F5A44" +" 44D499A06FFAEA5E8EF5E01F2FD20A7B7EF3F6968AFBA1FB8D91F1559D52D8777B#)" +" (q #00EB7B5751D25EBBB7BD59D920315FD840E19AEBF9#)" +" (g #1574363387FDFD1DDF38F4FBE135BB20C7EE4772FB94C337AF86EA8E49666503" +" AE04B6BE81A2F8DD095311E0217ACA698A11E6C5D33CCDAE71498ED35D13991E" +" B02F09AB40BD8F4C5ED8C75DA779D0AE104BC34C960B002377068AB4B5A1F984" +" 3FBA91F537F1B7CAC4D8DD6D89B0D863AF7025D549F9C765D2FC07EE208F8D15#)" +" (y #64B11EF8871BE4AB572AA810D5D3CA11A6CDBC637A8014602C72960DB135BF46" +" A1816A724C34F87330FC9E187C5D66897A04535CC2AC9164A7150ABFA8179827" +" 6E45831AB811EEE848EBB24D9F5F2883B6E5DDC4C659DEF944DCFD80BF4D0A20" +" 42CAA7DC289F0C5A9D155F02D3D551DB741A81695B74D4C8F477F9C7838EB0FB#)" +" (x #11D54E4ADBD3034160F2CED4B7CD292A4EBF3EC0#)))"; +/* A sample 1024 bit DSA key used for the selftests (public only). */ +static const char sample_public_key_1024[] = +"(public-key" +" (dsa" +" (p #00AD7C0025BA1A15F775F3F2D673718391D00456978D347B33D7B49E7F32EDAB" +" 96273899DD8B2BB46CD6ECA263FAF04A28903503D59062A8865D2AE8ADFB5191" +" CF36FFB562D0E2F5809801A1F675DAE59698A9E01EFE8D7DCFCA084F4C6F5A44" +" 44D499A06FFAEA5E8EF5E01F2FD20A7B7EF3F6968AFBA1FB8D91F1559D52D8777B#)" +" (q #00EB7B5751D25EBBB7BD59D920315FD840E19AEBF9#)" +" (g #1574363387FDFD1DDF38F4FBE135BB20C7EE4772FB94C337AF86EA8E49666503" +" AE04B6BE81A2F8DD095311E0217ACA698A11E6C5D33CCDAE71498ED35D13991E" +" B02F09AB40BD8F4C5ED8C75DA779D0AE104BC34C960B002377068AB4B5A1F984" +" 3FBA91F537F1B7CAC4D8DD6D89B0D863AF7025D549F9C765D2FC07EE208F8D15#)" +" (y #64B11EF8871BE4AB572AA810D5D3CA11A6CDBC637A8014602C72960DB135BF46" +" A1816A724C34F87330FC9E187C5D66897A04535CC2AC9164A7150ABFA8179827" +" 6E45831AB811EEE848EBB24D9F5F2883B6E5DDC4C659DEF944DCFD80BF4D0A20" +" 42CAA7DC289F0C5A9D155F02D3D551DB741A81695B74D4C8F477F9C7838EB0FB#)))"; +#endif /*0*/ + +/* 2048 DSA key from RFC 6979 A.2.2 */ +static const char sample_public_key_2048[] = +"(public-key" +" (dsa" +" (p #9DB6FB5951B66BB6FE1E140F1D2CE5502374161FD6538DF1648218642F0B5C48C8F7A41AADFA187324B87674FA1822B00F1ECF8136943D7C55757264E5A1A44FFE012E9936E00C1D3E9310B01C7D179805D3058B2A9F4BB6F9716BFE6117C6B5B3CC4D9BE341104AD4A80AD6C94E005F4B993E14F091EB51743BF33050C38DE235567E1B34C3D6A5C0CEAA1A0F368213C3D19843D0B4B09DCB9FC72D39C8DE41F1BF14D4BB4563CA28371621CAD3324B6A2D392145BEBFAC748805236F5CA2FE92B871CD8F9C36D3292B5509CA8CAA77A2ADFC7BFD77DDA6F71125A7456FEA153E433256A2261C6A06ED3693797E7995FAD5AABBCFBE3EDA2741E375404AE25B#)" +" (q #F2C3119374CE76C9356990B465374A17F23F9ED35089BD969F61C6DDE9998C1F#)" +" (g #5C7FF6B06F8F143FE8288433493E4769C4D988ACE5BE25A0E24809670716C613D7B0CEE6932F8FAA7C44D2CB24523DA53FBE4F6EC3595892D1AA58C4328A06C46A15662E7EAA703A1DECF8BBB2D05DBE2EB956C142A338661D10461C0D135472085057F3494309FFA73C611F78B32ADBB5740C361C9F35BE90997DB2014E2EF5AA61782F52ABEB8BD6432C4DD097BC5423B285DAFB60DC364E8161F4A2A35ACA3A10B1C4D203CC76A470A33AFDCBDD92959859ABD8B56E1725252D78EAC66E71BA9AE3F1DD2487199874393CD4D832186800654760E1E34C09E4D155179F9EC0DC4473F996BDCE6EED1CABED8B6F116F7AD9CF505DF0F998E34AB27514B0FFE7#)" +" (y #667098C654426C78D7F8201EAC6C203EF030D43605032C2F1FA937E5237DBD949F34A0A2564FE126DC8B715C5141802CE0979C8246463C40E6B6BDAA2513FA611728716C2E4FD53BC95B89E69949D96512E873B9C8F8DFD499CC312882561ADECB31F658E934C0C197F2C4D96B05CBAD67381E7B768891E4DA3843D24D94CDFB5126E9B8BF21E8358EE0E0A30EF13FD6A664C0DCE3731F7FB49A4845A4FD8254687972A2D382599C9BAC4E0ED7998193078913032558134976410B89D2C171D123AC35FD977219597AA7D15C1A9A428E59194F75C721EBCBCFAE44696A499AFA74E04299F132026601638CB87AB79190D4A0986315DA8EEC6561C938996BEADF#)))"; + +static const char sample_secret_key_2048[] = +"(private-key" +" (dsa" +" (p #9DB6FB5951B66BB6FE1E140F1D2CE5502374161FD6538DF1648218642F0B5C48C8F7A41AADFA187324B87674FA1822B00F1ECF8136943D7C55757264E5A1A44FFE012E9936E00C1D3E9310B01C7D179805D3058B2A9F4BB6F9716BFE6117C6B5B3CC4D9BE341104AD4A80AD6C94E005F4B993E14F091EB51743BF33050C38DE235567E1B34C3D6A5C0CEAA1A0F368213C3D19843D0B4B09DCB9FC72D39C8DE41F1BF14D4BB4563CA28371621CAD3324B6A2D392145BEBFAC748805236F5CA2FE92B871CD8F9C36D3292B5509CA8CAA77A2ADFC7BFD77DDA6F71125A7456FEA153E433256A2261C6A06ED3693797E7995FAD5AABBCFBE3EDA2741E375404AE25B#)" +" (q #F2C3119374CE76C9356990B465374A17F23F9ED35089BD969F61C6DDE9998C1F#)" +" (g #5C7FF6B06F8F143FE8288433493E4769C4D988ACE5BE25A0E24809670716C613D7B0CEE6932F8FAA7C44D2CB24523DA53FBE4F6EC3595892D1AA58C4328A06C46A15662E7EAA703A1DECF8BBB2D05DBE2EB956C142A338661D10461C0D135472085057F3494309FFA73C611F78B32ADBB5740C361C9F35BE90997DB2014E2EF5AA61782F52ABEB8BD6432C4DD097BC5423B285DAFB60DC364E8161F4A2A35ACA3A10B1C4D203CC76A470A33AFDCBDD92959859ABD8B56E1725252D78EAC66E71BA9AE3F1DD2487199874393CD4D832186800654760E1E34C09E4D155179F9EC0DC4473F996BDCE6EED1CABED8B6F116F7AD9CF505DF0F998E34AB27514B0FFE7#)" +" (y #667098C654426C78D7F8201EAC6C203EF030D43605032C2F1FA937E5237DBD949F34A0A2564FE126DC8B715C5141802CE0979C8246463C40E6B6BDAA2513FA611728716C2E4FD53BC95B89E69949D96512E873B9C8F8DFD499CC312882561ADECB31F658E934C0C197F2C4D96B05CBAD67381E7B768891E4DA3843D24D94CDFB5126E9B8BF21E8358EE0E0A30EF13FD6A664C0DCE3731F7FB49A4845A4FD8254687972A2D382599C9BAC4E0ED7998193078913032558134976410B89D2C171D123AC35FD977219597AA7D15C1A9A428E59194F75C721EBCBCFAE44696A499AFA74E04299F132026601638CB87AB79190D4A0986315DA8EEC6561C938996BEADF#)" +" (x #69C7548C21D0DFEA6B9A51C9EAD4E27C33D3B3F180316E5BCAB92C933F0E4DBC#)))"; + + + +static int test_keys (DSA_secret_key *sk, unsigned int qbits); +static int check_secret_key (DSA_secret_key *sk); +static gpg_err_code_t generate (DSA_secret_key *sk, + unsigned int nbits, + unsigned int qbits, + int transient_key, + dsa_domain_t *domain, + gcry_mpi_t **ret_factors); +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); +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; + + +void +_gcry_register_pk_dsa_progress (void (*cb) (void *, const char *, + int, int, int), + void *cb_data) +{ + progress_cb = cb; + progress_cb_data = cb_data; +} + + +static void +progress (int c) +{ + if (progress_cb) + progress_cb (progress_cb_data, "pk_dsa", c, 0, 0); +} + + +/* Check that a freshly generated key actually works. Returns 0 on success. */ +static int +test_keys (DSA_secret_key *sk, unsigned int qbits) +{ + int result = -1; /* Default to failure. */ + DSA_public_key pk; + gcry_mpi_t data = mpi_new (qbits); + gcry_mpi_t sig_a = mpi_new (qbits); + gcry_mpi_t sig_b = mpi_new (qbits); + + /* Put the relevant parameters into a public key structure. */ + pk.p = sk->p; + pk.q = sk->q; + pk.g = sk->g; + pk.y = sk->y; + + /* Create a random plaintext. */ + _gcry_mpi_randomize (data, qbits, GCRY_WEAK_RANDOM); + + /* Sign DATA using the secret key. */ + sign (sig_a, sig_b, data, sk, 0, 0); + + /* Verify the signature using the public key. */ + if ( verify (sig_a, sig_b, data, &pk) ) + 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) ) + goto leave; /* Signature matches but should not. */ + + result = 0; /* The test succeeded. */ + + leave: + _gcry_mpi_release (sig_b); + _gcry_mpi_release (sig_a); + _gcry_mpi_release (data); + return result; +} + + + +/* + Generate a DSA key pair with a key of size NBITS. If transient_key + is true the key is generated using the standard RNG and not the + very secure one. + + Returns: 2 structures filled with all needed values + and an array with the n-1 factors of (p-1) + */ +static gpg_err_code_t +generate (DSA_secret_key *sk, unsigned int nbits, unsigned int qbits, + int transient_key, dsa_domain_t *domain, gcry_mpi_t **ret_factors ) +{ + gpg_err_code_t rc; + gcry_mpi_t p; /* the prime */ + gcry_mpi_t q; /* the 160 bit prime factor */ + gcry_mpi_t g; /* the generator */ + gcry_mpi_t y; /* g^x mod p */ + gcry_mpi_t x; /* the secret exponent */ + gcry_mpi_t h, e; /* helper */ + unsigned char *rndbuf; + gcry_random_level_t random_level; + + if (qbits) + ; /* Caller supplied qbits. Use this value. */ + else if ( nbits >= 512 && nbits <= 1024 ) + qbits = 160; + else if ( nbits == 2048 ) + qbits = 224; + else if ( nbits == 3072 ) + qbits = 256; + else if ( nbits == 7680 ) + qbits = 384; + else if ( nbits == 15360 ) + qbits = 512; + else + return GPG_ERR_INV_VALUE; + + if (qbits < 160 || qbits > 512 || (qbits%8) ) + return GPG_ERR_INV_VALUE; + if (nbits < 2*qbits || nbits > 15360) + return GPG_ERR_INV_VALUE; + + if (fips_mode ()) + { + if (nbits < 1024) + return GPG_ERR_INV_VALUE; + if (transient_key) + return GPG_ERR_INV_VALUE; + } + + if (domain->p && domain->q && domain->g) + { + /* Domain parameters are given; use them. */ + p = mpi_copy (domain->p); + q = mpi_copy (domain->q); + g = mpi_copy (domain->g); + gcry_assert (mpi_get_nbits (p) == nbits); + gcry_assert (mpi_get_nbits (q) == qbits); + h = mpi_alloc (0); + e = NULL; + } + else + { + /* Generate new domain parameters. */ + rc = _gcry_generate_elg_prime (1, nbits, qbits, NULL, &p, ret_factors); + if (rc) + return rc; + + /* Get q out of factors. */ + q = mpi_copy ((*ret_factors)[0]); + gcry_assert (mpi_get_nbits (q) == qbits); + + /* Find a generator g (h and e are helpers). + e = (p-1)/q */ + e = mpi_alloc (mpi_get_nlimbs (p)); + mpi_sub_ui (e, p, 1); + mpi_fdiv_q (e, e, q); + g = mpi_alloc (mpi_get_nlimbs (p)); + h = mpi_alloc_set_ui (1); /* (We start with 2.) */ + do + { + mpi_add_ui (h, h, 1); + /* g = h^e mod p */ + mpi_powm (g, h, e, p); + } + while (!mpi_cmp_ui (g, 1)); /* Continue until g != 1. */ + } + + /* Select a random number X with the property: + * 0 < x < q-1 + * + * FIXME: Why do we use the requirement x < q-1 ? It should be + * sufficient to test for x < q. FIPS-186-3 check x < q-1 but it + * does not check for 0 < x because it makes sure that Q is unsigned + * and finally adds one to the result so that 0 will never be + * returned. We should replace the code below with _gcry_dsa_gen_k. + * + * This must be a very good random number because this is the secret + * part. The random quality depends on the transient_key flag. */ + random_level = transient_key ? GCRY_STRONG_RANDOM : GCRY_VERY_STRONG_RANDOM; + if (DBG_CIPHER) + log_debug("choosing a random x%s\n", transient_key? " (transient-key)":""); + gcry_assert( qbits >= 160 ); + x = mpi_alloc_secure( mpi_get_nlimbs(q) ); + mpi_sub_ui( h, q, 1 ); /* put q-1 into h */ + rndbuf = NULL; + do + { + if( DBG_CIPHER ) + progress('.'); + if( !rndbuf ) + rndbuf = _gcry_random_bytes_secure ((qbits+7)/8, random_level); + else + { /* Change only some of the higher bits (= 2 bytes)*/ + char *r = _gcry_random_bytes_secure (2, random_level); + memcpy(rndbuf, r, 2 ); + xfree(r); + } + + _gcry_mpi_set_buffer( x, rndbuf, (qbits+7)/8, 0 ); + mpi_clear_highbit( x, qbits+1 ); + } + while ( !( mpi_cmp_ui( x, 0 )>0 && mpi_cmp( x, h )<0 ) ); + xfree(rndbuf); + mpi_free( e ); + mpi_free( h ); + + /* y = g^x mod p */ + y = mpi_alloc( mpi_get_nlimbs(p) ); + mpi_powm (y, g, x, p); + + if( DBG_CIPHER ) + { + progress('\n'); + log_mpidump("dsa p", p ); + log_mpidump("dsa q", q ); + log_mpidump("dsa g", g ); + log_mpidump("dsa y", y ); + log_mpidump("dsa x", x ); + } + + /* Copy the stuff to the key structures. */ + sk->p = p; + sk->q = q; + sk->g = g; + sk->y = y; + sk->x = x; + + /* Now we can test our keys (this should never fail!). */ + if ( test_keys (sk, qbits) ) + { + _gcry_mpi_release (sk->p); sk->p = NULL; + _gcry_mpi_release (sk->q); sk->q = NULL; + _gcry_mpi_release (sk->g); sk->g = NULL; + _gcry_mpi_release (sk->y); sk->y = NULL; + _gcry_mpi_release (sk->x); sk->x = NULL; + fips_signal_error ("self-test after key generation failed"); + return GPG_ERR_SELFTEST_FAILED; + } + return 0; +} + + +/* Generate a DSA key pair with a key of size NBITS using the + algorithm given in FIPS-186-3. If USE_FIPS186_2 is true, + FIPS-186-2 is used and thus the length is restricted to 1024/160. + If DERIVEPARMS is not NULL it may contain a seed value. If domain + parameters are specified in DOMAIN, DERIVEPARMS may not be given + and NBITS and QBITS must match the specified domain parameters. */ +static gpg_err_code_t +generate_fips186 (DSA_secret_key *sk, unsigned int nbits, unsigned int qbits, + gcry_sexp_t deriveparms, int use_fips186_2, + dsa_domain_t *domain, + int *r_counter, void **r_seed, size_t *r_seedlen, + gcry_mpi_t *r_h) +{ + gpg_err_code_t ec; + struct { + gcry_sexp_t sexp; + const void *seed; + size_t seedlen; + } initial_seed = { NULL, NULL, 0 }; + gcry_mpi_t prime_q = NULL; + gcry_mpi_t prime_p = NULL; + gcry_mpi_t value_g = NULL; /* The generator. */ + gcry_mpi_t value_y = NULL; /* g^x mod p */ + gcry_mpi_t value_x = NULL; /* The secret exponent. */ + gcry_mpi_t value_h = NULL; /* Helper. */ + gcry_mpi_t value_e = NULL; /* Helper. */ + gcry_mpi_t value_c = NULL; /* helper for x */ + gcry_mpi_t value_qm2 = NULL; /* q - 2 */ + + /* Preset return values. */ + *r_counter = 0; + *r_seed = NULL; + *r_seedlen = 0; + *r_h = NULL; + + /* Derive QBITS from NBITS if requested */ + if (!qbits) + { + if (nbits == 1024) + qbits = 160; + else if (nbits == 2048) + qbits = 224; + else if (nbits == 3072) + qbits = 256; + } + + /* Check that QBITS and NBITS match the standard. Note that FIPS + 186-3 uses N for QBITS and L for NBITS. */ + if (nbits == 1024 && qbits == 160 && use_fips186_2) + ; /* Allowed in FIPS 186-2 mode. */ + else if (nbits == 2048 && qbits == 224) + ; + else if (nbits == 2048 && qbits == 256) + ; + else if (nbits == 3072 && qbits == 256) + ; + else + return GPG_ERR_INV_VALUE; + + if (domain->p && domain->q && domain->g) + { + /* Domain parameters are given; use them. */ + prime_p = mpi_copy (domain->p); + prime_q = mpi_copy (domain->q); + value_g = mpi_copy (domain->g); + gcry_assert (mpi_get_nbits (prime_p) == nbits); + gcry_assert (mpi_get_nbits (prime_q) == qbits); + gcry_assert (!deriveparms); + ec = 0; + } + else + { + /* Generate new domain parameters. */ + + /* Get an initial seed value. */ + if (deriveparms) + { + initial_seed.sexp = sexp_find_token (deriveparms, "seed", 0); + if (initial_seed.sexp) + initial_seed.seed = sexp_nth_data (initial_seed.sexp, 1, + &initial_seed.seedlen); + } + + if (use_fips186_2) + ec = _gcry_generate_fips186_2_prime (nbits, qbits, + initial_seed.seed, + initial_seed.seedlen, + &prime_q, &prime_p, + r_counter, + r_seed, r_seedlen); + else + ec = _gcry_generate_fips186_3_prime (nbits, qbits, NULL, 0, + &prime_q, &prime_p, + r_counter, + r_seed, r_seedlen, NULL); + sexp_release (initial_seed.sexp); + if (ec) + goto leave; + + /* Find a generator g (h and e are helpers). + * e = (p-1)/q + */ + value_e = mpi_alloc_like (prime_p); + mpi_sub_ui (value_e, prime_p, 1); + mpi_fdiv_q (value_e, value_e, prime_q ); + value_g = mpi_alloc_like (prime_p); + value_h = mpi_alloc_set_ui (1); + do + { + mpi_add_ui (value_h, value_h, 1); + /* g = h^e mod p */ + mpi_powm (value_g, value_h, value_e, prime_p); + } + while (!mpi_cmp_ui (value_g, 1)); /* Continue until g != 1. */ + } + + value_c = mpi_snew (qbits); + value_x = mpi_snew (qbits); + value_qm2 = mpi_snew (qbits); + mpi_sub_ui (value_qm2, prime_q, 2); + + /* FIPS 186-4 B.1.2 steps 4-6 */ + do + { + if( DBG_CIPHER ) + progress('.'); + _gcry_mpi_randomize (value_c, qbits, GCRY_VERY_STRONG_RANDOM); + mpi_clear_highbit (value_c, qbits+1); + } + while (!(mpi_cmp_ui (value_c, 0) > 0 && mpi_cmp (value_c, value_qm2) < 0)); + /* while (mpi_cmp (value_c, value_qm2) > 0); */ + + /* x = c + 1 */ + mpi_add_ui(value_x, value_c, 1); + + /* y = g^x mod p */ + value_y = mpi_alloc_like (prime_p); + mpi_powm (value_y, value_g, value_x, prime_p); + + if (DBG_CIPHER) + { + progress('\n'); + log_mpidump("dsa p", prime_p ); + log_mpidump("dsa q", prime_q ); + log_mpidump("dsa g", value_g ); + log_mpidump("dsa y", value_y ); + log_mpidump("dsa x", value_x ); + log_mpidump("dsa h", value_h ); + } + + /* Copy the stuff to the key structures. */ + sk->p = prime_p; prime_p = NULL; + sk->q = prime_q; prime_q = NULL; + sk->g = value_g; value_g = NULL; + sk->y = value_y; value_y = NULL; + sk->x = value_x; value_x = NULL; + *r_h = value_h; value_h = NULL; + + leave: + _gcry_mpi_release (prime_p); + _gcry_mpi_release (prime_q); + _gcry_mpi_release (value_g); + _gcry_mpi_release (value_y); + _gcry_mpi_release (value_x); + _gcry_mpi_release (value_h); + _gcry_mpi_release (value_e); + _gcry_mpi_release (value_c); + _gcry_mpi_release (value_qm2); + + /* As a last step test this keys (this should never fail of course). */ + if (!ec && test_keys (sk, qbits) ) + { + _gcry_mpi_release (sk->p); sk->p = NULL; + _gcry_mpi_release (sk->q); sk->q = NULL; + _gcry_mpi_release (sk->g); sk->g = NULL; + _gcry_mpi_release (sk->y); sk->y = NULL; + _gcry_mpi_release (sk->x); sk->x = NULL; + fips_signal_error ("self-test after key generation failed"); + ec = GPG_ERR_SELFTEST_FAILED; + } + + if (ec) + { + *r_counter = 0; + xfree (*r_seed); *r_seed = NULL; + *r_seedlen = 0; + _gcry_mpi_release (*r_h); *r_h = NULL; + } + + return ec; +} + + + +/* + Test whether the secret key is valid. + Returns: if this is a valid key. + */ +static int +check_secret_key( DSA_secret_key *sk ) +{ + int rc; + gcry_mpi_t y = mpi_alloc( mpi_get_nlimbs(sk->y) ); + + mpi_powm( y, sk->g, sk->x, sk->p ); + rc = !mpi_cmp( y, sk->y ); + mpi_free( y ); + return rc; +} + + + +/* + Make a DSA signature from INPUT and put it into r and s. + + INPUT may either be a plain MPI or an opaque MPI which is then + internally converted to a plain MPI. FLAGS and HASHALGO may both + be 0 for standard operation mode. + + The return value is 0 on success or an error code. Note that for + backward compatibility the function will not return any error if + FLAGS and HASHALGO are both 0 and INPUT is a plain MPI. + */ +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) +{ + gpg_err_code_t rc; + gcry_mpi_t hash; + gcry_mpi_t k; + gcry_mpi_t kinv; + gcry_mpi_t tmp; + const void *abuf; + unsigned int abits, qbits; + int extraloops = 0; + + qbits = mpi_get_nbits (skey->q); + + /* Convert the INPUT into an MPI. */ + rc = _gcry_dsa_normalize_hash (input, &hash, qbits); + if (rc) + return rc; + + again: + /* Create the K value. */ + if ((flags & PUBKEY_FLAG_RFC6979) && hashalgo) + { + /* Use Pornin's method for deterministic DSA. If this flag is + set, it is expected that HASH is an opaque MPI with the to be + signed hash. That hash is also used as h1 from 3.2.a. */ + if (!mpi_is_opaque (input)) + { + rc = GPG_ERR_CONFLICT; + goto leave; + } + + abuf = mpi_get_opaque (input, &abits); + rc = _gcry_dsa_gen_rfc6979_k (&k, skey->q, skey->x, + abuf, (abits+7)/8, hashalgo, extraloops); + if (rc) + goto leave; + } + else + { + /* Select a random k with 0 < k < q */ + k = _gcry_dsa_gen_k (skey->q, GCRY_STRONG_RANDOM); + } + + /* r = (a^k mod p) mod q */ + mpi_powm( r, skey->g, k, skey->p ); + mpi_fdiv_r( r, r, skey->q ); + + /* kinv = k^(-1) mod q */ + kinv = mpi_alloc( mpi_get_nlimbs(k) ); + mpi_invm(kinv, k, skey->q ); + + /* s = (kinv * ( hash + x * r)) mod q */ + tmp = mpi_alloc( mpi_get_nlimbs(skey->p) ); + mpi_mul( tmp, skey->x, r ); + mpi_add( tmp, tmp, hash ); + mpi_mulm( s , kinv, tmp, skey->q ); + + mpi_free(k); + mpi_free(kinv); + mpi_free(tmp); + + if (!mpi_cmp_ui (r, 0)) + { + /* This is a highly unlikely code path. */ + extraloops++; + goto again; + } + + rc = 0; + + leave: + if (hash != input) + mpi_free (hash); + + return rc; +} + + +/* + 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 ) +{ + gpg_err_code_t rc = 0; + gcry_mpi_t w, u1, u2, v; + gcry_mpi_t base[3]; + gcry_mpi_t ex[3]; + gcry_mpi_t hash; + unsigned int nbits; + + if( !(mpi_cmp_ui( r, 0 ) > 0 && mpi_cmp( r, pkey->q ) < 0) ) + return GPG_ERR_BAD_SIGNATURE; /* Assertion 0 < r < n failed. */ + if( !(mpi_cmp_ui( s, 0 ) > 0 && mpi_cmp( s, pkey->q ) < 0) ) + return GPG_ERR_BAD_SIGNATURE; /* Assertion 0 < s < n failed. */ + + nbits = mpi_get_nbits (pkey->q); + rc = _gcry_dsa_normalize_hash (input, &hash, nbits); + if (rc) + return rc; + + w = mpi_alloc( mpi_get_nlimbs(pkey->q) ); + u1 = mpi_alloc( mpi_get_nlimbs(pkey->q) ); + u2 = mpi_alloc( mpi_get_nlimbs(pkey->q) ); + v = mpi_alloc( mpi_get_nlimbs(pkey->p) ); + + /* w = s^(-1) mod q */ + mpi_invm( w, s, pkey->q ); + + /* u1 = (hash * w) mod q */ + mpi_mulm( u1, hash, w, pkey->q ); + + /* u2 = r * w mod q */ + mpi_mulm( u2, r, w, pkey->q ); + + /* v = g^u1 * y^u2 mod p mod q */ + base[0] = pkey->g; ex[0] = u1; + base[1] = pkey->y; ex[1] = u2; + base[2] = NULL; ex[2] = NULL; + mpi_mulpowm( v, base, ex, pkey->p ); + mpi_fdiv_r( v, v, pkey->q ); + + if (mpi_cmp( v, r )) + { + if (DBG_CIPHER) + { + log_mpidump (" i", input); + log_mpidump (" h", hash); + log_mpidump (" v", v); + log_mpidump (" r", r); + log_mpidump (" s", s); + } + rc = GPG_ERR_BAD_SIGNATURE; + } + + mpi_free(w); + mpi_free(u1); + mpi_free(u2); + mpi_free(v); + if (hash != input) + mpi_free (hash); + + return rc; +} + + +/********************************************* + ************** interface ****************** + *********************************************/ + +static gcry_err_code_t +dsa_generate (const gcry_sexp_t genparms, gcry_sexp_t *r_skey) +{ + gpg_err_code_t rc; + unsigned int nbits; + gcry_sexp_t domainsexp; + DSA_secret_key sk; + gcry_sexp_t l1; + unsigned int qbits = 0; + gcry_sexp_t deriveparms = NULL; + gcry_sexp_t seedinfo = NULL; + gcry_sexp_t misc_info = NULL; + int flags = 0; + dsa_domain_t domain; + gcry_mpi_t *factors = NULL; + + memset (&sk, 0, sizeof sk); + memset (&domain, 0, sizeof domain); + + rc = _gcry_pk_util_get_nbits (genparms, &nbits); + if (rc) + return rc; + + /* Parse the optional flags list. */ + l1 = sexp_find_token (genparms, "flags", 0); + if (l1) + { + rc = _gcry_pk_util_parse_flaglist (l1, &flags, NULL); + sexp_release (l1); + if (rc) + return rc;\ + } + + /* Parse the optional qbits element. */ + l1 = sexp_find_token (genparms, "qbits", 0); + if (l1) + { + char buf[50]; + const char *s; + size_t n; + + s = sexp_nth_data (l1, 1, &n); + if (!s || n >= DIM (buf) - 1 ) + { + sexp_release (l1); + return GPG_ERR_INV_OBJ; /* No value or value too large. */ + } + memcpy (buf, s, n); + buf[n] = 0; + qbits = (unsigned int)strtoul (buf, NULL, 0); + sexp_release (l1); + } + + /* Parse the optional transient-key flag. */ + if (!(flags & PUBKEY_FLAG_TRANSIENT_KEY)) + { + l1 = sexp_find_token (genparms, "transient-key", 0); + if (l1) + { + flags |= PUBKEY_FLAG_TRANSIENT_KEY; + sexp_release (l1); + } + } + + /* Get the optional derive parameters. */ + deriveparms = sexp_find_token (genparms, "derive-parms", 0); + + /* Parse the optional "use-fips186" flags. */ + if (!(flags & PUBKEY_FLAG_USE_FIPS186)) + { + l1 = sexp_find_token (genparms, "use-fips186", 0); + if (l1) + { + flags |= PUBKEY_FLAG_USE_FIPS186; + sexp_release (l1); + } + } + if (!(flags & PUBKEY_FLAG_USE_FIPS186_2)) + { + l1 = sexp_find_token (genparms, "use-fips186-2", 0); + if (l1) + { + flags |= PUBKEY_FLAG_USE_FIPS186_2; + sexp_release (l1); + } + } + + /* Check whether domain parameters are given. */ + domainsexp = sexp_find_token (genparms, "domain", 0); + if (domainsexp) + { + /* DERIVEPARMS can't be used together with domain parameters. + NBITS abnd QBITS may not be specified because there values + are derived from the domain parameters. */ + if (deriveparms || qbits || nbits) + { + sexp_release (domainsexp); + sexp_release (deriveparms); + return GPG_ERR_INV_VALUE; + } + + /* Put all domain parameters into the domain object. */ + l1 = sexp_find_token (domainsexp, "p", 0); + domain.p = sexp_nth_mpi (l1, 1, GCRYMPI_FMT_USG); + sexp_release (l1); + l1 = sexp_find_token (domainsexp, "q", 0); + domain.q = sexp_nth_mpi (l1, 1, GCRYMPI_FMT_USG); + sexp_release (l1); + l1 = sexp_find_token (domainsexp, "g", 0); + domain.g = sexp_nth_mpi (l1, 1, GCRYMPI_FMT_USG); + sexp_release (l1); + sexp_release (domainsexp); + + /* Check that all domain parameters are available. */ + if (!domain.p || !domain.q || !domain.g) + { + _gcry_mpi_release (domain.p); + _gcry_mpi_release (domain.q); + _gcry_mpi_release (domain.g); + sexp_release (deriveparms); + return GPG_ERR_MISSING_VALUE; + } + + /* Get NBITS and QBITS from the domain parameters. */ + nbits = mpi_get_nbits (domain.p); + qbits = mpi_get_nbits (domain.q); + } + + if (deriveparms + || (flags & PUBKEY_FLAG_USE_FIPS186) + || (flags & PUBKEY_FLAG_USE_FIPS186_2) + || fips_mode ()) + { + int counter; + void *seed; + size_t seedlen; + gcry_mpi_t h_value; + + rc = generate_fips186 (&sk, nbits, qbits, deriveparms, + !!(flags & PUBKEY_FLAG_USE_FIPS186_2), + &domain, + &counter, &seed, &seedlen, &h_value); + if (!rc && h_value) + { + /* Format the seed-values unless domain parameters are used + for which a H_VALUE of NULL is an indication. */ + rc = sexp_build (&seedinfo, NULL, + "(seed-values(counter %d)(seed %b)(h %m))", + counter, (int)seedlen, seed, h_value); + xfree (seed); + _gcry_mpi_release (h_value); + } + } + else + { + rc = generate (&sk, nbits, qbits, + !!(flags & PUBKEY_FLAG_TRANSIENT_KEY), + &domain, &factors); + } + + if (!rc) + { + /* Put the factors into MISC_INFO. Note that the factors are + not confidential thus we can store them in standard memory. */ + int nfactors, i, j; + char *p; + char *format = NULL; + void **arg_list = NULL; + + for (nfactors=0; factors && factors[nfactors]; nfactors++) + ; + /* Allocate space for the format string: + "(misc-key-info%S(pm1-factors%m))" + with one "%m" for each factor and construct it. */ + format = xtrymalloc (50 + 2*nfactors); + if (!format) + rc = gpg_err_code_from_syserror (); + else + { + p = stpcpy (format, "(misc-key-info"); + if (seedinfo) + p = stpcpy (p, "%S"); + if (nfactors) + { + p = stpcpy (p, "(pm1-factors"); + for (i=0; i < nfactors; i++) + p = stpcpy (p, "%m"); + p = stpcpy (p, ")"); + } + p = stpcpy (p, ")"); + + /* Allocate space for the list of factors plus one for the + seedinfo s-exp plus an extra NULL entry for safety and + fill it with the factors. */ + arg_list = xtrycalloc (nfactors+1+1, sizeof *arg_list); + if (!arg_list) + rc = gpg_err_code_from_syserror (); + else + { + i = 0; + if (seedinfo) + arg_list[i++] = &seedinfo; + for (j=0; j < nfactors; j++) + arg_list[i++] = factors + j; + arg_list[i] = NULL; + + rc = sexp_build_array (&misc_info, NULL, format, arg_list); + } + } + + xfree (arg_list); + xfree (format); + } + + if (!rc) + rc = sexp_build (r_skey, NULL, + "(key-data" + " (public-key" + " (dsa(p%m)(q%m)(g%m)(y%m)))" + " (private-key" + " (dsa(p%m)(q%m)(g%m)(y%m)(x%m)))" + " %S)", + sk.p, sk.q, sk.g, sk.y, + sk.p, sk.q, sk.g, sk.y, sk.x, + misc_info); + + + _gcry_mpi_release (sk.p); + _gcry_mpi_release (sk.q); + _gcry_mpi_release (sk.g); + _gcry_mpi_release (sk.y); + _gcry_mpi_release (sk.x); + + _gcry_mpi_release (domain.p); + _gcry_mpi_release (domain.q); + _gcry_mpi_release (domain.g); + + sexp_release (seedinfo); + sexp_release (misc_info); + sexp_release (deriveparms); + if (factors) + { + gcry_mpi_t *mp; + for (mp = factors; *mp; mp++) + mpi_free (*mp); + xfree (factors); + } + return rc; +} + + + +static gcry_err_code_t +dsa_check_secret_key (gcry_sexp_t keyparms) +{ + gcry_err_code_t rc; + DSA_secret_key sk = {NULL, NULL, NULL, NULL, NULL}; + + rc = _gcry_sexp_extract_param (keyparms, NULL, "pqgyx", + &sk.p, &sk.q, &sk.g, &sk.y, &sk.x, + NULL); + if (rc) + goto leave; + + if (!check_secret_key (&sk)) + rc = GPG_ERR_BAD_SECKEY; + + leave: + _gcry_mpi_release (sk.p); + _gcry_mpi_release (sk.q); + _gcry_mpi_release (sk.g); + _gcry_mpi_release (sk.y); + _gcry_mpi_release (sk.x); + if (DBG_CIPHER) + log_debug ("dsa_testkey => %s\n", gpg_strerror (rc)); + return rc; +} + + +static gcry_err_code_t +dsa_sign (gcry_sexp_t *r_sig, gcry_sexp_t s_data, gcry_sexp_t keyparms) +{ + gcry_err_code_t rc; + struct pk_encoding_ctx ctx; + gcry_mpi_t data = NULL; + DSA_secret_key sk = {NULL, NULL, NULL, NULL, NULL}; + gcry_mpi_t sig_r = NULL; + gcry_mpi_t sig_s = NULL; + + _gcry_pk_util_init_encoding_ctx (&ctx, PUBKEY_OP_SIGN, + dsa_get_nbits (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_sign data", data); + + /* Extract the key. */ + rc = _gcry_sexp_extract_param (keyparms, NULL, "pqgyx", + &sk.p, &sk.q, &sk.g, &sk.y, &sk.x, NULL); + if (rc) + goto leave; + if (DBG_CIPHER) + { + log_mpidump ("dsa_sign p", sk.p); + log_mpidump ("dsa_sign q", sk.q); + log_mpidump ("dsa_sign g", sk.g); + log_mpidump ("dsa_sign y", sk.y); + if (!fips_mode ()) + log_mpidump ("dsa_sign x", sk.x); + } + + sig_r = mpi_new (0); + sig_s = mpi_new (0); + rc = sign (sig_r, sig_s, data, &sk, ctx.flags, ctx.hash_algo); + if (rc) + goto leave; + if (DBG_CIPHER) + { + log_mpidump ("dsa_sign sig_r", sig_r); + log_mpidump ("dsa_sign sig_s", sig_s); + } + rc = sexp_build (r_sig, NULL, "(sig-val(dsa(r%M)(s%M)))", sig_r, sig_s); + + leave: + _gcry_mpi_release (sig_r); + _gcry_mpi_release (sig_s); + _gcry_mpi_release (sk.p); + _gcry_mpi_release (sk.q); + _gcry_mpi_release (sk.g); + _gcry_mpi_release (sk.y); + _gcry_mpi_release (sk.x); + _gcry_mpi_release (data); + _gcry_pk_util_free_encoding_ctx (&ctx); + if (DBG_CIPHER) + log_debug ("dsa_sign => %s\n", gpg_strerror (rc)); + return rc; +} + + +static gcry_err_code_t +dsa_verify (gcry_sexp_t s_sig, gcry_sexp_t s_data, gcry_sexp_t s_keyparms) +{ + 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); + + /* Extract the signature value. */ + rc = _gcry_pk_util_preparse_sigval (s_sig, dsa_names, &l1, NULL); + if (rc) + goto leave; + rc = _gcry_sexp_extract_param (l1, NULL, "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); + } + + /* Extract the key. */ + rc = _gcry_sexp_extract_param (s_keyparms, NULL, "pqgy", + &pk.p, &pk.q, &pk.g, &pk.y, NULL); + if (rc) + goto leave; + if (DBG_CIPHER) + { + 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); + } + + /* Verify the signature. */ + rc = verify (sig_r, sig_s, data, &pk); + + 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); + 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; +} + + +/* Return the number of bits for the key described by PARMS. On error + * 0 is returned. The format of PARMS starts with the algorithm name; + * for example: + * + * (dsa + * (p ) + * (q ) + * (g ) + * (y )) + * + * More parameters may be given but we only need P here. + */ +static unsigned int +dsa_get_nbits (gcry_sexp_t parms) +{ + gcry_sexp_t l1; + gcry_mpi_t p; + unsigned int nbits; + + l1 = sexp_find_token (parms, "p", 1); + if (!l1) + return 0; /* Parameter P not found. */ + + p = sexp_nth_mpi (l1, 1, GCRYMPI_FMT_USG); + sexp_release (l1); + nbits = p? mpi_get_nbits (p) : 0; + _gcry_mpi_release (p); + return nbits; +} + + + +/* + Self-test section. + */ + +static const char * +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#))"; + static const char sample_data_bad[] = + "(data (flags rfc6979)" + " (hash sha256 #bf2bdbe1aa9b6ec1e2ade1d694f41fc71a831d0268e9891562113d8a62add1bf#))"; + static const char signature_r[] = + "eace8bdbbe353c432a795d9ec556c6d021f7a03f42c36e9bc87e4ac7932cc809"; + static const char signature_s[] = + "7081e175455f9247b812b74583e9e94f9ea79bd640dc962533b0680793a38d53"; + + const char *errtxt = NULL; + gcry_error_t err; + gcry_sexp_t data = NULL; + gcry_sexp_t data_bad = NULL; + gcry_sexp_t sig = NULL; + gcry_sexp_t l1 = NULL; + gcry_sexp_t l2 = NULL; + gcry_mpi_t r = NULL; + gcry_mpi_t s = NULL; + gcry_mpi_t calculated_r = NULL; + gcry_mpi_t calculated_s = NULL; + int cmp; + + err = sexp_sscan (&data, NULL, sample_data, strlen (sample_data)); + if (!err) + err = sexp_sscan (&data_bad, NULL, + sample_data_bad, strlen (sample_data_bad)); + if (!err) + err = _gcry_mpi_scan (&r, GCRYMPI_FMT_HEX, signature_r, 0, NULL); + if (!err) + err = _gcry_mpi_scan (&s, GCRYMPI_FMT_HEX, signature_s, 0, NULL); + + if (err) + { + errtxt = "converting data failed"; + goto leave; + } + + err = _gcry_pk_sign (&sig, data, skey); + if (err) + { + errtxt = "signing failed"; + goto leave; + } + + /* check against known signature */ + errtxt = "signature validity failed"; + l1 = _gcry_sexp_find_token (sig, "sig-val", 0); + if (!l1) + goto leave; + l2 = _gcry_sexp_find_token (l1, "dsa", 0); + if (!l2) + goto leave; + + sexp_release (l1); + l1 = l2; + + l2 = _gcry_sexp_find_token (l1, "r", 0); + if (!l2) + goto leave; + calculated_r = _gcry_sexp_nth_mpi (l2, 1, GCRYMPI_FMT_USG); + if (!calculated_r) + goto leave; + + sexp_release (l2); + l2 = _gcry_sexp_find_token (l1, "s", 0); + if (!l2) + goto leave; + calculated_s = _gcry_sexp_nth_mpi (l2, 1, GCRYMPI_FMT_USG); + if (!calculated_s) + goto leave; + + errtxt = "known sig check failed"; + + cmp = _gcry_mpi_cmp (r, calculated_r); + if (cmp) + goto leave; + cmp = _gcry_mpi_cmp (s, calculated_s); + if (cmp) + goto leave; + + errtxt = NULL; + + + err = _gcry_pk_verify (sig, data, pkey); + if (err) + { + errtxt = "verify failed"; + goto leave; + } + err = _gcry_pk_verify (sig, data_bad, pkey); + if (gcry_err_code (err) != GPG_ERR_BAD_SIGNATURE) + { + errtxt = "bad signature not detected"; + goto leave; + } + + + leave: + _gcry_mpi_release (calculated_s); + _gcry_mpi_release (calculated_r); + _gcry_mpi_release (s); + _gcry_mpi_release (r); + sexp_release (l2); + sexp_release (l1); + sexp_release (sig); + sexp_release (data_bad); + sexp_release (data); + return errtxt; +} + + +static gpg_err_code_t +selftests_dsa_2048 (selftest_report_func_t report) +{ + const char *what; + const char *errtxt; + gcry_error_t err; + gcry_sexp_t skey = NULL; + gcry_sexp_t pkey = NULL; + + /* Convert the S-expressions into the internal representation. */ + what = "convert"; + err = sexp_sscan (&skey, NULL, sample_secret_key_2048, strlen (sample_secret_key_2048)); + if (!err) + err = sexp_sscan (&pkey, NULL, + sample_public_key_2048, strlen (sample_public_key_2048)); + if (err) + { + errtxt = _gcry_strerror (err); + goto failed; + } + + what = "key consistency"; + err = _gcry_pk_testkey (skey); + if (err) + { + errtxt = _gcry_strerror (err); + goto failed; + } + + what = "sign"; + errtxt = selftest_sign (pkey, skey); + if (errtxt) + goto failed; + + sexp_release (pkey); + sexp_release (skey); + return 0; /* Succeeded. */ + + failed: + sexp_release (pkey); + sexp_release (skey); + if (report) + report ("pubkey", GCRY_PK_DSA, what, errtxt); + return GPG_ERR_SELFTEST_FAILED; +} + + +/* Run a full self-test for ALGO and return 0 on success. */ +static gpg_err_code_t +run_selftests (int algo, int extended, selftest_report_func_t report) +{ + gpg_err_code_t ec; + + (void)extended; + + switch (algo) + { + case GCRY_PK_DSA: + ec = selftests_dsa_2048 (report); + break; + default: + ec = GPG_ERR_PUBKEY_ALGO; + break; + + } + return ec; +} + + + +gcry_pk_spec_t _gcry_pubkey_spec_dsa = + { + GCRY_PK_DSA, { 0, 1 }, + GCRY_PK_USAGE_SIGN, + "DSA", dsa_names, + "pqgy", "pqgyx", "", "rs", "pqgy", + dsa_generate, + dsa_check_secret_key, + NULL, + NULL, + dsa_sign, + dsa_verify, + dsa_get_nbits, + run_selftests + }; diff --git a/cipher/rsa.c b/cipher/rsa.c index 575ea94..c226909 100644 --- a/cipher/rsa.c +++ b/cipher/rsa.c @@ -696,7 +696,7 @@ generate_x931 (RSA_secret_key *sk, unsigned int nbits, unsigned long e_value, *swapped = 0; - if (e_value == 1) /* Alias for a secure value. */ + if (e_value == 1 || e_value == 0) /* Alias for a secure value. */ e_value = 65537; /* Point 1 of section 4.1: k = 1024 + 256s with S >= 0 */ diff --git a/cipher/rsa.c.tests b/cipher/rsa.c.tests new file mode 100644 index 0000000..575ea94 --- /dev/null +++ b/cipher/rsa.c.tests @@ -0,0 +1,2035 @@ +/* rsa.c - RSA implementation + * Copyright (C) 1997, 1998, 1999 by Werner Koch (dd9jn) + * Copyright (C) 2000, 2001, 2002, 2003, 2008 Free Software Foundation, Inc. + * + * This file is part of Libgcrypt. + * + * Libgcrypt is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * Libgcrypt is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, see . + */ + +/* This code uses an algorithm protected by U.S. Patent #4,405,829 + which expired on September 20, 2000. The patent holder placed that + patent into the public domain on Sep 6th, 2000. +*/ + +#include +#include +#include +#include +#include + +#include "g10lib.h" +#include "mpi.h" +#include "cipher.h" +#include "pubkey-internal.h" + + +typedef struct +{ + gcry_mpi_t n; /* modulus */ + gcry_mpi_t e; /* exponent */ +} RSA_public_key; + + +typedef struct +{ + gcry_mpi_t n; /* public modulus */ + gcry_mpi_t e; /* public exponent */ + gcry_mpi_t d; /* exponent */ + gcry_mpi_t p; /* prime p. */ + gcry_mpi_t q; /* prime q. */ + gcry_mpi_t u; /* inverse of p mod q. */ +} RSA_secret_key; + + +static const char *rsa_names[] = + { + "rsa", + "openpgp-rsa", + "oid.1.2.840.113549.1.1.1", + NULL, + }; + + +/* A sample 2048 bit RSA key used for the selftests. */ +static const char sample_secret_key[] = +" (private-key" +" (rsa" +" (n #009F56231A3D82E3E7D613D59D53E9AB921BEF9F08A782AED0B6E46ADBC853EC" +" 7C71C422435A3CD8FA0DB9EFD55CD3295BADC4E8E2E2B94E15AE82866AB8ADE8" +" 7E469FAE76DC3577DE87F1F419C4EB41123DFAF8D16922D5EDBAD6E9076D5A1C" +" 958106F0AE5E2E9193C6B49124C64C2A241C4075D4AF16299EB87A6585BAE917" +" DEF27FCDD165764D069BC18D16527B29DAAB549F7BBED4A7C6A842D203ED6613" +" 6E2411744E432CD26D940132F25874483DCAEECDFD95744819CBCF1EA810681C" +" 42907EBCB1C7EAFBE75C87EC32C5413EA10476545D3FC7B2ADB1B66B7F200918" +" 664B0E5261C2895AA28B0DE321E921B3F877172CCCAB81F43EF98002916156F6CB#)" +" (e #010001#)" +" (d #07EF82500C403899934FE993AC5A36F14FF2DF38CF1EF315F205EE4C83EDAA19" +" 8890FC23DE9AA933CAFB37B6A8A8DBA675411958337287310D3FF2F1DDC0CB93" +" 7E70F57F75F833C021852B631D2B9A520E4431A03C5C3FCB5742DCD841D9FB12" +" 771AA1620DCEC3F1583426066ED9DC3F7028C5B59202C88FDF20396E2FA0EC4F" +" 5A22D9008F3043673931BC14A5046D6327398327900867E39CC61B2D1AFE2F48" +" EC8E1E3861C68D257D7425F4E6F99ABD77D61F10CA100EFC14389071831B33DD" +" 69CC8EABEF860D1DC2AAA84ABEAE5DFC91BC124DAF0F4C8EF5BBEA436751DE84" +" 3A8063E827A024466F44C28614F93B0732A100D4A0D86D532FE1E22C7725E401#)" +" (p #00C29D438F115825779631CD665A5739367F3E128ADC29766483A46CA80897E0" +" 79B32881860B8F9A6A04C2614A904F6F2578DAE13EA67CD60AE3D0AA00A1FF9B" +" 441485E44B2DC3D0B60260FBFE073B5AC72FAF67964DE15C8212C389D20DB9CF" +" 54AF6AEF5C4196EAA56495DD30CF709F499D5AB30CA35E086C2A1589D6283F1783#)" +" (q #00D1984135231CB243FE959C0CBEF551EDD986AD7BEDF71EDF447BE3DA27AF46" +" 79C974A6FA69E4D52FE796650623DE70622862713932AA2FD9F2EC856EAEAA77" +" 88B4EA6084DC81C902F014829B18EA8B2666EC41586818E0589E18876065F97E" +" 8D22CE2DA53A05951EC132DCEF41E70A9C35F4ACC268FFAC2ADF54FA1DA110B919#)" +" (u #67CF0FD7635205DD80FA814EE9E9C267C17376BF3209FB5D1BC42890D2822A04" +" 479DAF4D5B6ED69D0F8D1AF94164D07F8CD52ECEFE880641FA0F41DDAB1785E4" +" A37A32F997A516480B4CD4F6482B9466A1765093ED95023CA32D5EDC1E34CEE9" +" AF595BC51FE43C4BF810FA225AF697FB473B83815966188A4312C048B885E3F7#)))"; + +/* A sample 2048 bit RSA key used for the selftests (public only). */ +static const char sample_public_key[] = +" (public-key" +" (rsa" +" (n #009F56231A3D82E3E7D613D59D53E9AB921BEF9F08A782AED0B6E46ADBC853EC" +" 7C71C422435A3CD8FA0DB9EFD55CD3295BADC4E8E2E2B94E15AE82866AB8ADE8" +" 7E469FAE76DC3577DE87F1F419C4EB41123DFAF8D16922D5EDBAD6E9076D5A1C" +" 958106F0AE5E2E9193C6B49124C64C2A241C4075D4AF16299EB87A6585BAE917" +" DEF27FCDD165764D069BC18D16527B29DAAB549F7BBED4A7C6A842D203ED6613" +" 6E2411744E432CD26D940132F25874483DCAEECDFD95744819CBCF1EA810681C" +" 42907EBCB1C7EAFBE75C87EC32C5413EA10476545D3FC7B2ADB1B66B7F200918" +" 664B0E5261C2895AA28B0DE321E921B3F877172CCCAB81F43EF98002916156F6CB#)" +" (e #010001#)))"; + + +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. */ +static int +test_keys (RSA_secret_key *sk, unsigned int nbits) +{ + int result = -1; /* Default to failure. */ + RSA_public_key pk; + gcry_mpi_t plaintext = mpi_new (nbits); + gcry_mpi_t ciphertext = mpi_new (nbits); + gcry_mpi_t decr_plaintext = mpi_new (nbits); + gcry_mpi_t signature = mpi_new (nbits); + + /* Put the relevant parameters into a public key structure. */ + pk.n = sk->n; + pk.e = sk->e; + + /* Create a random plaintext. */ + _gcry_mpi_randomize (plaintext, nbits, GCRY_WEAK_RANDOM); + + /* Encrypt using the public key. */ + public (ciphertext, plaintext, &pk); + + /* Check that the cipher text does not match the plaintext. */ + if (!mpi_cmp (ciphertext, plaintext)) + goto leave; /* Ciphertext is identical to the plaintext. */ + + /* Decrypt using the secret key. */ + secret (decr_plaintext, ciphertext, sk); + + /* Check that the decrypted plaintext matches the original plaintext. */ + if (mpi_cmp (decr_plaintext, plaintext)) + goto leave; /* Plaintext does not match. */ + + /* Create another random plaintext as data for signature checking. */ + _gcry_mpi_randomize (plaintext, nbits, GCRY_WEAK_RANDOM); + + /* Use the RSA secret function to create a signature of the plaintext. */ + secret (signature, plaintext, sk); + + /* Use the RSA public function to verify this signature. */ + public (decr_plaintext, signature, &pk); + if (mpi_cmp (decr_plaintext, plaintext)) + goto leave; /* Signature does not match. */ + + /* Modify the signature and check that the signing fails. */ + mpi_add_ui (signature, signature, 1); + public (decr_plaintext, signature, &pk); + if (!mpi_cmp (decr_plaintext, plaintext)) + goto leave; /* Signature matches but should not. */ + + result = 0; /* All tests succeeded. */ + + leave: + _gcry_mpi_release (signature); + _gcry_mpi_release (decr_plaintext); + _gcry_mpi_release (ciphertext); + _gcry_mpi_release (plaintext); + return result; +} + + +/* Callback used by the prime generation to test whether the exponent + is suitable. Returns 0 if the test has been passed. */ +static int +check_exponent (void *arg, gcry_mpi_t a) +{ + gcry_mpi_t e = arg; + gcry_mpi_t tmp; + int result; + + mpi_sub_ui (a, a, 1); + tmp = _gcry_mpi_alloc_like (a); + result = !mpi_gcd(tmp, e, a); /* GCD is not 1. */ + _gcry_mpi_release (tmp); + mpi_add_ui (a, a, 1); + return result; +} + +/**************** + * Generate a key pair with a key of size NBITS. + * USE_E = 0 let Libcgrypt decide what exponent to use. + * = 1 request the use of a "secure" exponent; this is required by some + * specification to be 65537. + * > 2 Use this public exponent. If the given exponent + * is not odd one is internally added to it. + * TRANSIENT_KEY: If true, generate the primes using the standard RNG. + * Returns: 2 structures filled with all needed values + */ +static gpg_err_code_t +generate_std (RSA_secret_key *sk, unsigned int nbits, unsigned long use_e, + int transient_key) +{ + gcry_mpi_t p, q; /* the two primes */ + gcry_mpi_t d; /* the private key */ + gcry_mpi_t u; + gcry_mpi_t t1, t2; + gcry_mpi_t n; /* the public key */ + gcry_mpi_t e; /* the exponent */ + gcry_mpi_t phi; /* helper: (p-1)(q-1) */ + gcry_mpi_t g; + gcry_mpi_t f; + gcry_random_level_t random_level; + + if (fips_mode ()) + { + if (nbits < 1024) + return GPG_ERR_INV_VALUE; + if (transient_key) + return GPG_ERR_INV_VALUE; + } + + /* The random quality depends on the transient_key flag. */ + random_level = transient_key ? GCRY_STRONG_RANDOM : GCRY_VERY_STRONG_RANDOM; + + /* Make sure that nbits is even so that we generate p, q of equal size. */ + if ( (nbits&1) ) + nbits++; + + if (use_e == 1) /* Alias for a secure value */ + use_e = 65537; /* as demanded by Sphinx. */ + + /* Public exponent: + In general we use 41 as this is quite fast and more secure than the + commonly used 17. Benchmarking the RSA verify function + with a 1024 bit key yields (2001-11-08): + e=17 0.54 ms + e=41 0.75 ms + e=257 0.95 ms + e=65537 1.80 ms + */ + e = mpi_alloc( (32+BITS_PER_MPI_LIMB-1)/BITS_PER_MPI_LIMB ); + if (!use_e) + mpi_set_ui (e, 41); /* This is a reasonable secure and fast value */ + else + { + use_e |= 1; /* make sure this is odd */ + mpi_set_ui (e, use_e); + } + + n = mpi_new (nbits); + + p = q = NULL; + do + { + /* select two (very secret) primes */ + if (p) + _gcry_mpi_release (p); + if (q) + _gcry_mpi_release (q); + if (use_e) + { /* Do an extra test to ensure that the given exponent is + suitable. */ + p = _gcry_generate_secret_prime (nbits/2, random_level, + check_exponent, e); + q = _gcry_generate_secret_prime (nbits/2, random_level, + check_exponent, e); + } + else + { /* We check the exponent later. */ + p = _gcry_generate_secret_prime (nbits/2, random_level, NULL, NULL); + q = _gcry_generate_secret_prime (nbits/2, random_level, NULL, NULL); + } + if (mpi_cmp (p, q) > 0 ) /* p shall be smaller than q (for calc of u)*/ + mpi_swap(p,q); + /* calculate the modulus */ + mpi_mul( n, p, q ); + } + while ( mpi_get_nbits(n) != nbits ); + + /* calculate Euler totient: phi = (p-1)(q-1) */ + t1 = mpi_alloc_secure( mpi_get_nlimbs(p) ); + t2 = mpi_alloc_secure( mpi_get_nlimbs(p) ); + phi = mpi_snew ( nbits ); + g = mpi_snew ( nbits ); + f = mpi_snew ( nbits ); + mpi_sub_ui( t1, p, 1 ); + mpi_sub_ui( t2, q, 1 ); + mpi_mul( phi, t1, t2 ); + mpi_gcd (g, t1, t2); + mpi_fdiv_q(f, phi, g); + + while (!mpi_gcd(t1, e, phi)) /* (while gcd is not 1) */ + { + if (use_e) + BUG (); /* The prime generator already made sure that we + never can get to here. */ + mpi_add_ui (e, e, 2); + } + + /* calculate the secret key d = e^-1 mod phi */ + d = mpi_snew ( nbits ); + mpi_invm (d, e, f ); + /* calculate the inverse of p and q (used for chinese remainder theorem)*/ + u = mpi_snew ( nbits ); + mpi_invm(u, p, q ); + + if( DBG_CIPHER ) + { + log_mpidump(" p= ", p ); + log_mpidump(" q= ", q ); + log_mpidump("phi= ", phi ); + log_mpidump(" g= ", g ); + log_mpidump(" f= ", f ); + log_mpidump(" n= ", n ); + log_mpidump(" e= ", e ); + log_mpidump(" d= ", d ); + log_mpidump(" u= ", u ); + } + + _gcry_mpi_release (t1); + _gcry_mpi_release (t2); + _gcry_mpi_release (phi); + _gcry_mpi_release (f); + _gcry_mpi_release (g); + + sk->n = n; + sk->e = e; + sk->p = p; + sk->q = q; + sk->d = d; + sk->u = u; + + /* Now we can test our keys. */ + if (test_keys (sk, nbits - 64)) + { + _gcry_mpi_release (sk->n); sk->n = NULL; + _gcry_mpi_release (sk->e); sk->e = NULL; + _gcry_mpi_release (sk->p); sk->p = NULL; + _gcry_mpi_release (sk->q); sk->q = NULL; + _gcry_mpi_release (sk->d); sk->d = NULL; + _gcry_mpi_release (sk->u); sk->u = NULL; + fips_signal_error ("self-test after key generation failed"); + return GPG_ERR_SELFTEST_FAILED; + } + + return 0; +} + + +/**************** + * Generate a key pair with a key of size NBITS. + * USE_E = 0 let Libcgrypt decide what exponent to use. + * = 1 request the use of a "secure" exponent; this is required by some + * specification to be 65537. + * > 2 Use this public exponent. If the given exponent + * is not odd one is internally added to it. + * TESTPARMS: If set, do not generate but test whether the p,q is probably prime + * Returns key with zeroes to not break code calling this function. + * TRANSIENT_KEY: If true, generate the primes using the standard RNG. + * Returns: 2 structures filled with all needed values + */ +static gpg_err_code_t +generate_fips (RSA_secret_key *sk, unsigned int nbits, unsigned long use_e, + gcry_sexp_t testparms, int transient_key) +{ + gcry_mpi_t p, q; /* the two primes */ + gcry_mpi_t d; /* the private key */ + gcry_mpi_t u; + gcry_mpi_t p1, q1; + gcry_mpi_t n; /* the public key */ + gcry_mpi_t e; /* the exponent */ + gcry_mpi_t g; + gcry_mpi_t minp; + gcry_mpi_t diff, mindiff; + gcry_random_level_t random_level; + unsigned int pbits = nbits/2; + unsigned int i; + int pqswitch; + gpg_err_code_t ec = GPG_ERR_NO_PRIME; + + if (nbits < 1024 || (nbits & 0x1FF)) + return GPG_ERR_INV_VALUE; + if (_gcry_enforced_fips_mode() && nbits != 2048 && nbits != 3072) + return GPG_ERR_INV_VALUE; + + /* The random quality depends on the transient_key flag. */ + random_level = transient_key ? GCRY_STRONG_RANDOM : GCRY_VERY_STRONG_RANDOM; + + if (testparms) + { + /* Parameters to derive the key are given. */ + /* Note that we explicitly need to setup the values of tbl + because some compilers (e.g. OpenWatcom, IRIX) don't allow to + initialize a structure with automatic variables. */ + struct { const char *name; gcry_mpi_t *value; } tbl[] = { + { "e" }, + { "p" }, + { "q" }, + { NULL } + }; + int idx; + gcry_sexp_t oneparm; + + tbl[0].value = &e; + tbl[1].value = &p; + tbl[2].value = &q; + + for (idx=0; tbl[idx].name; idx++) + { + oneparm = sexp_find_token (testparms, tbl[idx].name, 0); + if (oneparm) + { + *tbl[idx].value = sexp_nth_mpi (oneparm, 1, GCRYMPI_FMT_USG); + sexp_release (oneparm); + } + } + for (idx=0; tbl[idx].name; idx++) + if (!*tbl[idx].value) + break; + if (tbl[idx].name) + { + /* At least one parameter is missing. */ + for (idx=0; tbl[idx].name; idx++) + _gcry_mpi_release (*tbl[idx].value); + return GPG_ERR_MISSING_VALUE; + } + } + else + { + if (use_e < 65537) + use_e = 65537; /* This is the smallest value allowed by FIPS */ + + e = mpi_alloc ((32+BITS_PER_MPI_LIMB-1)/BITS_PER_MPI_LIMB); + + use_e |= 1; /* make sure this is odd */ + mpi_set_ui (e, use_e); + + p = mpi_snew (pbits); + q = mpi_snew (pbits); + } + + n = mpi_new (nbits); + d = mpi_snew (nbits); + u = mpi_snew (nbits); + + /* prepare approximate minimum p and q */ + minp = mpi_new (pbits); + mpi_set_ui (minp, 0xB504F334); + mpi_lshift (minp, minp, pbits - 32); + + /* prepare minimum p and q difference */ + diff = mpi_new (pbits); + mindiff = mpi_new (pbits - 99); + mpi_set_ui (mindiff, 1); + mpi_lshift (mindiff, mindiff, pbits - 100); + + p1 = mpi_snew (pbits); + q1 = mpi_snew (pbits); + g = mpi_snew (pbits); + + retry: + /* generate p and q */ + for (i = 0; i < 5 * pbits; i++) + { + ploop: + if (!testparms) + { + _gcry_mpi_randomize (p, pbits, random_level); + } + if (mpi_cmp (p, minp) < 0) + { + if (testparms) + goto err; + goto ploop; + } + + mpi_sub_ui (p1, p, 1); + if (mpi_gcd (g, p1, e)) + { + if (_gcry_fips186_4_prime_check (p, pbits) != GPG_ERR_NO_ERROR) + { + /* not a prime */ + if (testparms) + goto err; + } + else + break; + } + else if (testparms) + goto err; + } + if (i >= 5 * pbits) + goto err; + + for (i = 0; i < 5 * pbits; i++) + { + qloop: + if (!testparms) + { + _gcry_mpi_randomize (q, pbits, random_level); + } + if (mpi_cmp (q, minp) < 0) + { + if (testparms) + goto err; + goto qloop; + } + if (mpi_cmp (p, q) > 0) + { + pqswitch = 1; + mpi_sub (diff, p, q); + } + else + { + pqswitch = 0; + mpi_sub (diff, q, p); + } + if (mpi_cmp (diff, mindiff) < 0) + { + if (testparms) + goto err; + goto qloop; + } + + mpi_sub_ui (q1, q, 1); + if (mpi_gcd (g, q1, e)) + { + if (_gcry_fips186_4_prime_check (q, pbits) != GPG_ERR_NO_ERROR) + { + /* not a prime */ + if (testparms) + goto err; + } + else + break; + } + else if (testparms) + goto err; + } + if (i >= 5 * pbits) + goto err; + + if (testparms) + { + mpi_clear (p); + mpi_clear (q); + } + else + { + gcry_mpi_t f; + + if (pqswitch) + { + gcry_mpi_t tmp; + + tmp = p; + p = q; + q = tmp; + } + + f = mpi_snew (nbits); + + /* calculate the modulus */ + mpi_mul (n, p, q); + + /* calculate the secret key d = e^1 mod phi */ + mpi_gcd (g, p1, q1); + mpi_fdiv_q (f, p1, g); + mpi_mul (f, f, q1); + + mpi_invm (d, e, f); + + _gcry_mpi_release (f); + + if (mpi_get_nbits (d) < pbits) + goto retry; + + /* calculate the inverse of p and q (used for chinese remainder theorem)*/ + mpi_invm (u, p, q ); + } + + ec = 0; + + if (DBG_CIPHER) + { + log_mpidump(" p= ", p ); + log_mpidump(" q= ", q ); + log_mpidump(" n= ", n ); + log_mpidump(" e= ", e ); + log_mpidump(" d= ", d ); + log_mpidump(" u= ", u ); + } + + err: + + _gcry_mpi_release (p1); + _gcry_mpi_release (q1); + _gcry_mpi_release (g); + _gcry_mpi_release (minp); + _gcry_mpi_release (mindiff); + _gcry_mpi_release (diff); + + sk->n = n; + sk->e = e; + sk->p = p; + sk->q = q; + sk->d = d; + sk->u = u; + + /* Now we can test our keys. */ + if (ec || (!testparms && test_keys (sk, nbits - 64))) + { + _gcry_mpi_release (sk->n); sk->n = NULL; + _gcry_mpi_release (sk->e); sk->e = NULL; + _gcry_mpi_release (sk->p); sk->p = NULL; + _gcry_mpi_release (sk->q); sk->q = NULL; + _gcry_mpi_release (sk->d); sk->d = NULL; + _gcry_mpi_release (sk->u); sk->u = NULL; + if (!ec) + { + fips_signal_error ("self-test after key generation failed"); + return GPG_ERR_SELFTEST_FAILED; + } + } + + return ec; +} + + +/* Helper for generate_x931. */ +static gcry_mpi_t +gen_x931_parm_xp (unsigned int nbits) +{ + gcry_mpi_t xp; + + xp = mpi_snew (nbits); + _gcry_mpi_randomize (xp, nbits, GCRY_VERY_STRONG_RANDOM); + + /* The requirement for Xp is: + + sqrt{2}*2^{nbits-1} <= xp <= 2^{nbits} - 1 + + We set the two high order bits to 1 to satisfy the lower bound. + By using mpi_set_highbit we make sure that the upper bound is + satisfied as well. */ + mpi_set_highbit (xp, nbits-1); + mpi_set_bit (xp, nbits-2); + gcry_assert ( mpi_get_nbits (xp) == nbits ); + + return xp; +} + + +/* Helper for generate_x931. */ +static gcry_mpi_t +gen_x931_parm_xi (void) +{ + gcry_mpi_t xi; + + xi = mpi_snew (101); + _gcry_mpi_randomize (xi, 101, GCRY_VERY_STRONG_RANDOM); + mpi_set_highbit (xi, 100); + gcry_assert ( mpi_get_nbits (xi) == 101 ); + + return xi; +} + + + +/* Variant of the standard key generation code using the algorithm + from X9.31. Using this algorithm has the advantage that the + generation can be made deterministic which is required for CAVS + testing. */ +static gpg_err_code_t +generate_x931 (RSA_secret_key *sk, unsigned int nbits, unsigned long e_value, + gcry_sexp_t deriveparms, int *swapped) +{ + gcry_mpi_t p, q; /* The two primes. */ + gcry_mpi_t e; /* The public exponent. */ + gcry_mpi_t n; /* The public key. */ + gcry_mpi_t d; /* The private key */ + gcry_mpi_t u; /* The inverse of p and q. */ + gcry_mpi_t pm1; /* p - 1 */ + gcry_mpi_t qm1; /* q - 1 */ + gcry_mpi_t phi; /* Euler totient. */ + gcry_mpi_t f, g; /* Helper. */ + + *swapped = 0; + + if (e_value == 1) /* Alias for a secure value. */ + e_value = 65537; + + /* Point 1 of section 4.1: k = 1024 + 256s with S >= 0 */ + if (nbits < 1024 || (nbits % 256)) + return GPG_ERR_INV_VALUE; + + /* Point 2: 2 <= bitlength(e) < 2^{k-2} + Note that we do not need to check the upper bound because we use + an unsigned long for E and thus there is no way for E to reach + that limit. */ + if (e_value < 3) + return GPG_ERR_INV_VALUE; + + /* Our implementation requires E to be odd. */ + if (!(e_value & 1)) + return GPG_ERR_INV_VALUE; + + /* Point 3: e > 0 or e 0 if it is to be randomly generated. + We support only a fixed E and thus there is no need for an extra test. */ + + + /* Compute or extract the derive parameters. */ + { + gcry_mpi_t xp1 = NULL; + gcry_mpi_t xp2 = NULL; + gcry_mpi_t xp = NULL; + gcry_mpi_t xq1 = NULL; + gcry_mpi_t xq2 = NULL; + gcry_mpi_t xq = NULL; + gcry_mpi_t tmpval; + + if (!deriveparms) + { + /* Not given: Generate them. */ + xp = gen_x931_parm_xp (nbits/2); + /* Make sure that |xp - xq| > 2^{nbits - 100} holds. */ + tmpval = mpi_snew (nbits/2); + do + { + _gcry_mpi_release (xq); + xq = gen_x931_parm_xp (nbits/2); + mpi_sub (tmpval, xp, xq); + } + while (mpi_get_nbits (tmpval) <= (nbits/2 - 100)); + _gcry_mpi_release (tmpval); + + xp1 = gen_x931_parm_xi (); + xp2 = gen_x931_parm_xi (); + xq1 = gen_x931_parm_xi (); + xq2 = gen_x931_parm_xi (); + + } + else + { + /* Parameters to derive the key are given. */ + /* Note that we explicitly need to setup the values of tbl + because some compilers (e.g. OpenWatcom, IRIX) don't allow + to initialize a structure with automatic variables. */ + struct { const char *name; gcry_mpi_t *value; } tbl[] = { + { "Xp1" }, + { "Xp2" }, + { "Xp" }, + { "Xq1" }, + { "Xq2" }, + { "Xq" }, + { NULL } + }; + int idx; + gcry_sexp_t oneparm; + + tbl[0].value = &xp1; + tbl[1].value = &xp2; + tbl[2].value = &xp; + tbl[3].value = &xq1; + tbl[4].value = &xq2; + tbl[5].value = &xq; + + for (idx=0; tbl[idx].name; idx++) + { + oneparm = sexp_find_token (deriveparms, tbl[idx].name, 0); + if (oneparm) + { + *tbl[idx].value = sexp_nth_mpi (oneparm, 1, GCRYMPI_FMT_USG); + sexp_release (oneparm); + } + } + for (idx=0; tbl[idx].name; idx++) + if (!*tbl[idx].value) + break; + if (tbl[idx].name) + { + /* At least one parameter is missing. */ + for (idx=0; tbl[idx].name; idx++) + _gcry_mpi_release (*tbl[idx].value); + return GPG_ERR_MISSING_VALUE; + } + } + + e = mpi_alloc_set_ui (e_value); + + /* Find two prime numbers. */ + p = _gcry_derive_x931_prime (xp, xp1, xp2, e, NULL, NULL); + q = _gcry_derive_x931_prime (xq, xq1, xq2, e, NULL, NULL); + _gcry_mpi_release (xp); xp = NULL; + _gcry_mpi_release (xp1); xp1 = NULL; + _gcry_mpi_release (xp2); xp2 = NULL; + _gcry_mpi_release (xq); xq = NULL; + _gcry_mpi_release (xq1); xq1 = NULL; + _gcry_mpi_release (xq2); xq2 = NULL; + if (!p || !q) + { + _gcry_mpi_release (p); + _gcry_mpi_release (q); + _gcry_mpi_release (e); + return GPG_ERR_NO_PRIME; + } + } + + + /* Compute the public modulus. We make sure that p is smaller than + q to allow the use of the CRT. */ + if (mpi_cmp (p, q) > 0 ) + { + mpi_swap (p, q); + *swapped = 1; + } + n = mpi_new (nbits); + mpi_mul (n, p, q); + + /* Compute the Euler totient: phi = (p-1)(q-1) */ + pm1 = mpi_snew (nbits/2); + qm1 = mpi_snew (nbits/2); + phi = mpi_snew (nbits); + mpi_sub_ui (pm1, p, 1); + mpi_sub_ui (qm1, q, 1); + mpi_mul (phi, pm1, qm1); + + g = mpi_snew (nbits); + gcry_assert (mpi_gcd (g, e, phi)); + + /* Compute: f = lcm(p-1,q-1) = phi / gcd(p-1,q-1) */ + mpi_gcd (g, pm1, qm1); + f = pm1; pm1 = NULL; + _gcry_mpi_release (qm1); qm1 = NULL; + mpi_fdiv_q (f, phi, g); + _gcry_mpi_release (phi); phi = NULL; + d = g; g = NULL; + /* Compute the secret key: d = e^{-1} mod lcm(p-1,q-1) */ + mpi_invm (d, e, f); + + /* Compute the inverse of p and q. */ + u = f; f = NULL; + mpi_invm (u, p, q ); + + if( DBG_CIPHER ) + { + if (*swapped) + log_debug ("p and q are swapped\n"); + log_mpidump(" p", p ); + log_mpidump(" q", q ); + log_mpidump(" n", n ); + log_mpidump(" e", e ); + log_mpidump(" d", d ); + log_mpidump(" u", u ); + } + + + sk->n = n; + sk->e = e; + sk->p = p; + sk->q = q; + sk->d = d; + sk->u = u; + + /* Now we can test our keys. */ + if (test_keys (sk, nbits - 64)) + { + _gcry_mpi_release (sk->n); sk->n = NULL; + _gcry_mpi_release (sk->e); sk->e = NULL; + _gcry_mpi_release (sk->p); sk->p = NULL; + _gcry_mpi_release (sk->q); sk->q = NULL; + _gcry_mpi_release (sk->d); sk->d = NULL; + _gcry_mpi_release (sk->u); sk->u = NULL; + fips_signal_error ("self-test after key generation failed"); + return GPG_ERR_SELFTEST_FAILED; + } + + return 0; +} + + +/**************** + * Test whether the secret key is valid. + * Returns: true if this is a valid key. + */ +static int +check_secret_key( RSA_secret_key *sk ) +{ + int rc; + gcry_mpi_t temp = mpi_alloc( mpi_get_nlimbs(sk->p)*2 ); + + mpi_mul(temp, sk->p, sk->q ); + rc = mpi_cmp( temp, sk->n ); + mpi_free(temp); + return !rc; +} + + + +/**************** + * Public key operation. Encrypt INPUT with PKEY and put result into OUTPUT. + * + * c = m^e mod n + * + * Where c is OUTPUT, m is INPUT and e,n are elements of PKEY. + */ +static void +public(gcry_mpi_t output, gcry_mpi_t input, RSA_public_key *pkey ) +{ + if( output == input ) /* powm doesn't like output and input the same */ + { + gcry_mpi_t x = mpi_alloc( mpi_get_nlimbs(input)*2 ); + mpi_powm( x, input, pkey->e, pkey->n ); + mpi_set(output, x); + mpi_free(x); + } + else + mpi_powm( output, input, pkey->e, pkey->n ); +} + +#if 0 +static void +stronger_key_check ( RSA_secret_key *skey ) +{ + gcry_mpi_t t = mpi_alloc_secure ( 0 ); + gcry_mpi_t t1 = mpi_alloc_secure ( 0 ); + gcry_mpi_t t2 = mpi_alloc_secure ( 0 ); + gcry_mpi_t phi = mpi_alloc_secure ( 0 ); + + /* check that n == p * q */ + mpi_mul( t, skey->p, skey->q); + if (mpi_cmp( t, skey->n) ) + log_info ( "RSA Oops: n != p * q\n" ); + + /* check that p is less than q */ + if( mpi_cmp( skey->p, skey->q ) > 0 ) + { + log_info ("RSA Oops: p >= q - fixed\n"); + _gcry_mpi_swap ( skey->p, skey->q); + } + + /* check that e divides neither p-1 nor q-1 */ + mpi_sub_ui(t, skey->p, 1 ); + mpi_fdiv_r(t, t, skey->e ); + if ( !mpi_cmp_ui( t, 0) ) + log_info ( "RSA Oops: e divides p-1\n" ); + mpi_sub_ui(t, skey->q, 1 ); + mpi_fdiv_r(t, t, skey->e ); + if ( !mpi_cmp_ui( t, 0) ) + log_info ( "RSA Oops: e divides q-1\n" ); + + /* check that d is correct */ + mpi_sub_ui( t1, skey->p, 1 ); + mpi_sub_ui( t2, skey->q, 1 ); + mpi_mul( phi, t1, t2 ); + gcry_mpi_gcd(t, t1, t2); + mpi_fdiv_q(t, phi, t); + mpi_invm(t, skey->e, t ); + if ( mpi_cmp(t, skey->d ) ) + { + log_info ( "RSA Oops: d is wrong - fixed\n"); + mpi_set (skey->d, t); + log_printmpi (" fixed d", skey->d); + } + + /* check for correctness of u */ + mpi_invm(t, skey->p, skey->q ); + if ( mpi_cmp(t, skey->u ) ) + { + log_info ( "RSA Oops: u is wrong - fixed\n"); + mpi_set (skey->u, t); + log_printmpi (" fixed u", skey->u); + } + + log_info ( "RSA secret key check finished\n"); + + mpi_free (t); + mpi_free (t1); + mpi_free (t2); + mpi_free (phi); +} +#endif + + + +/* Secret key operation - standard version. + * + * m = c^d mod n + */ +static void +secret_core_std (gcry_mpi_t M, gcry_mpi_t C, + gcry_mpi_t D, gcry_mpi_t N) +{ + mpi_powm (M, C, D, N); +} + + +/* Secret key operation - using the CRT. + * + * m1 = c ^ (d mod (p-1)) mod p + * m2 = c ^ (d mod (q-1)) mod q + * h = u * (m2 - m1) mod q + * m = m1 + h * p + */ +static void +secret_core_crt (gcry_mpi_t M, gcry_mpi_t C, + gcry_mpi_t D, unsigned int Nlimbs, + gcry_mpi_t P, gcry_mpi_t Q, gcry_mpi_t U) +{ + gcry_mpi_t m1 = mpi_alloc_secure ( Nlimbs + 1 ); + gcry_mpi_t m2 = mpi_alloc_secure ( Nlimbs + 1 ); + gcry_mpi_t h = mpi_alloc_secure ( Nlimbs + 1 ); + gcry_mpi_t D_blind = mpi_alloc_secure ( Nlimbs + 1 ); + gcry_mpi_t r; + unsigned int r_nbits; + + r_nbits = mpi_get_nbits (P) / 4; + if (r_nbits < 96) + r_nbits = 96; + r = mpi_secure_new (r_nbits); + + /* d_blind = (d mod (p-1)) + (p-1) * r */ + /* m1 = c ^ d_blind mod p */ + _gcry_mpi_randomize (r, r_nbits, GCRY_WEAK_RANDOM); + mpi_set_highbit (r, r_nbits - 1); + mpi_sub_ui ( h, P, 1 ); + mpi_mul ( D_blind, h, r ); + mpi_fdiv_r ( h, D, h ); + mpi_add ( D_blind, D_blind, h ); + mpi_powm ( m1, C, D_blind, P ); + + /* d_blind = (d mod (q-1)) + (q-1) * r */ + /* m2 = c ^ d_blind mod q */ + _gcry_mpi_randomize (r, r_nbits, GCRY_WEAK_RANDOM); + mpi_set_highbit (r, r_nbits - 1); + mpi_sub_ui ( h, Q, 1 ); + mpi_mul ( D_blind, h, r ); + mpi_fdiv_r ( h, D, h ); + mpi_add ( D_blind, D_blind, h ); + mpi_powm ( m2, C, D_blind, Q ); + + mpi_free ( r ); + mpi_free ( D_blind ); + + /* h = u * ( m2 - m1 ) mod q */ + mpi_sub ( h, m2, m1 ); + if ( mpi_has_sign ( h ) ) + mpi_add ( h, h, Q ); + mpi_mulm ( h, U, h, Q ); + + /* m = m1 + h * p */ + mpi_mul ( h, h, P ); + mpi_add ( M, m1, h ); + + mpi_free ( h ); + mpi_free ( m1 ); + mpi_free ( m2 ); +} + + +/* Secret key operation. + * Encrypt INPUT with SKEY and put result into + * OUTPUT. SKEY has the secret key parameters. + */ +static void +secret (gcry_mpi_t output, gcry_mpi_t input, RSA_secret_key *skey ) +{ + /* Remove superfluous leading zeroes from INPUT. */ + mpi_normalize (input); + + if (!skey->p || !skey->q || !skey->u) + { + secret_core_std (output, input, skey->d, skey->n); + } + else + { + secret_core_crt (output, input, skey->d, mpi_get_nlimbs (skey->n), + skey->p, skey->q, skey->u); + } +} + + +static void +secret_blinded (gcry_mpi_t output, gcry_mpi_t input, + RSA_secret_key *sk, unsigned int nbits) +{ + gcry_mpi_t r; /* Random number needed for blinding. */ + gcry_mpi_t ri; /* Modular multiplicative inverse of r. */ + gcry_mpi_t bldata; /* Blinded data to decrypt. */ + + /* First, we need a random number r between 0 and n - 1, which is + * relatively prime to n (i.e. it is neither p nor q). The random + * number needs to be only unpredictable, thus we employ the + * gcry_create_nonce function by using GCRY_WEAK_RANDOM with + * gcry_mpi_randomize. */ + r = mpi_snew (nbits); + ri = mpi_snew (nbits); + bldata = mpi_snew (nbits); + + do + { + _gcry_mpi_randomize (r, nbits, GCRY_WEAK_RANDOM); + mpi_mod (r, r, sk->n); + } + while (!mpi_invm (ri, r, sk->n)); + + /* Do blinding. We calculate: y = (x * r^e) mod n, where r is the + * random number, e is the public exponent, x is the non-blinded + * input data and n is the RSA modulus. */ + mpi_powm (bldata, r, sk->e, sk->n); + mpi_mulm (bldata, bldata, input, sk->n); + + /* Perform decryption. */ + secret (output, bldata, sk); + _gcry_mpi_release (bldata); + + /* Undo blinding. Here we calculate: y = (x * r^-1) mod n, where x + * is the blinded decrypted data, ri is the modular multiplicative + * inverse of r and n is the RSA modulus. */ + mpi_mulm (output, output, ri, sk->n); + + _gcry_mpi_release (r); + _gcry_mpi_release (ri); +} + + +/********************************************* + ************** interface ****************** + *********************************************/ + +static gcry_err_code_t +rsa_generate (const gcry_sexp_t genparms, gcry_sexp_t *r_skey) +{ + gpg_err_code_t ec; + unsigned int nbits; + unsigned long evalue; + RSA_secret_key sk; + gcry_sexp_t deriveparms; + int flags = 0; + gcry_sexp_t l1; + gcry_sexp_t swap_info = NULL; + + memset (&sk, 0, sizeof sk); + + ec = _gcry_pk_util_get_nbits (genparms, &nbits); + if (ec) + return ec; + + ec = _gcry_pk_util_get_rsa_use_e (genparms, &evalue); + if (ec) + return ec; + + /* Parse the optional flags list. */ + l1 = sexp_find_token (genparms, "flags", 0); + if (l1) + { + ec = _gcry_pk_util_parse_flaglist (l1, &flags, NULL); + sexp_release (l1); + if (ec) + return ec; + } + + deriveparms = (genparms? + sexp_find_token (genparms, "derive-parms", 0) : NULL); + if (!deriveparms) + { + /* Parse the optional "use-x931" flag. */ + l1 = sexp_find_token (genparms, "use-x931", 0); + if (l1) + { + flags |= PUBKEY_FLAG_USE_X931; + sexp_release (l1); + } + } + + if (deriveparms || (flags & PUBKEY_FLAG_USE_X931)) + { + int swapped; + ec = generate_x931 (&sk, nbits, evalue, deriveparms, &swapped); + sexp_release (deriveparms); + if (!ec && swapped) + ec = sexp_new (&swap_info, "(misc-key-info(p-q-swapped))", 0, 1); + } + else + { + /* Parse the optional "transient-key" flag. */ + if (!(flags & PUBKEY_FLAG_TRANSIENT_KEY)) + { + l1 = sexp_find_token (genparms, "transient-key", 0); + if (l1) + { + flags |= PUBKEY_FLAG_TRANSIENT_KEY; + sexp_release (l1); + } + } + deriveparms = (genparms? sexp_find_token (genparms, "test-parms", 0) + /**/ : NULL); + + /* Generate. */ + if (deriveparms || fips_mode()) + { + ec = generate_fips (&sk, nbits, evalue, deriveparms, + !!(flags & PUBKEY_FLAG_TRANSIENT_KEY)); + } + else + { + ec = generate_std (&sk, nbits, evalue, + !!(flags & PUBKEY_FLAG_TRANSIENT_KEY)); + } + sexp_release (deriveparms); + } + + if (!ec) + { + ec = sexp_build (r_skey, NULL, + "(key-data" + " (public-key" + " (rsa(n%m)(e%m)))" + " (private-key" + " (rsa(n%m)(e%m)(d%m)(p%m)(q%m)(u%m)))" + " %S)", + sk.n, sk.e, + sk.n, sk.e, sk.d, sk.p, sk.q, sk.u, + swap_info); + } + + mpi_free (sk.n); + mpi_free (sk.e); + mpi_free (sk.p); + mpi_free (sk.q); + mpi_free (sk.d); + mpi_free (sk.u); + sexp_release (swap_info); + + return ec; +} + + +static gcry_err_code_t +rsa_check_secret_key (gcry_sexp_t keyparms) +{ + gcry_err_code_t rc; + RSA_secret_key sk = {NULL, NULL, NULL, NULL, NULL, NULL}; + + /* To check the key we need the optional parameters. */ + rc = sexp_extract_param (keyparms, NULL, "nedpqu", + &sk.n, &sk.e, &sk.d, &sk.p, &sk.q, &sk.u, + NULL); + if (rc) + goto leave; + + if (!check_secret_key (&sk)) + rc = GPG_ERR_BAD_SECKEY; + + leave: + _gcry_mpi_release (sk.n); + _gcry_mpi_release (sk.e); + _gcry_mpi_release (sk.d); + _gcry_mpi_release (sk.p); + _gcry_mpi_release (sk.q); + _gcry_mpi_release (sk.u); + if (DBG_CIPHER) + log_debug ("rsa_testkey => %s\n", gpg_strerror (rc)); + return rc; +} + + +static gcry_err_code_t +rsa_encrypt (gcry_sexp_t *r_ciph, gcry_sexp_t s_data, gcry_sexp_t keyparms) +{ + gcry_err_code_t rc; + struct pk_encoding_ctx ctx; + gcry_mpi_t data = NULL; + RSA_public_key pk = {NULL, NULL}; + gcry_mpi_t ciph = NULL; + + _gcry_pk_util_init_encoding_ctx (&ctx, PUBKEY_OP_ENCRYPT, + rsa_get_nbits (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_encrypt data", data); + if (!data || mpi_is_opaque (data)) + { + rc = GPG_ERR_INV_DATA; + goto leave; + } + + /* Extract the key. */ + rc = sexp_extract_param (keyparms, NULL, "ne", &pk.n, &pk.e, NULL); + if (rc) + goto leave; + if (DBG_CIPHER) + { + log_mpidump ("rsa_encrypt n", pk.n); + log_mpidump ("rsa_encrypt e", pk.e); + } + + /* Do RSA computation and build result. */ + ciph = mpi_new (0); + public (ciph, data, &pk); + if (DBG_CIPHER) + log_mpidump ("rsa_encrypt res", ciph); + if ((ctx.flags & PUBKEY_FLAG_FIXEDLEN)) + { + /* We need to make sure to return the correct length to avoid + problems with missing leading zeroes. */ + unsigned char *em; + size_t emlen = (mpi_get_nbits (pk.n)+7)/8; + + rc = _gcry_mpi_to_octet_string (&em, NULL, ciph, emlen); + if (!rc) + { + rc = sexp_build (r_ciph, NULL, "(enc-val(rsa(a%b)))", (int)emlen, em); + xfree (em); + } + } + else + rc = sexp_build (r_ciph, NULL, "(enc-val(rsa(a%m)))", ciph); + + leave: + _gcry_mpi_release (ciph); + _gcry_mpi_release (pk.n); + _gcry_mpi_release (pk.e); + _gcry_mpi_release (data); + _gcry_pk_util_free_encoding_ctx (&ctx); + if (DBG_CIPHER) + log_debug ("rsa_encrypt => %s\n", gpg_strerror (rc)); + return rc; +} + + +static gcry_err_code_t +rsa_decrypt (gcry_sexp_t *r_plain, gcry_sexp_t s_data, gcry_sexp_t keyparms) + +{ + gpg_err_code_t rc; + struct pk_encoding_ctx ctx; + gcry_sexp_t l1 = NULL; + gcry_mpi_t data = NULL; + RSA_secret_key sk = {NULL, NULL, NULL, NULL, NULL, NULL}; + gcry_mpi_t plain = NULL; + unsigned char *unpad = NULL; + size_t unpadlen = 0; + + _gcry_pk_util_init_encoding_ctx (&ctx, PUBKEY_OP_DECRYPT, + rsa_get_nbits (keyparms)); + + /* Extract the data. */ + rc = _gcry_pk_util_preparse_encval (s_data, rsa_names, &l1, &ctx); + if (rc) + goto leave; + rc = sexp_extract_param (l1, NULL, "a", &data, NULL); + if (rc) + goto leave; + if (DBG_CIPHER) + log_printmpi ("rsa_decrypt data", data); + if (mpi_is_opaque (data)) + { + rc = GPG_ERR_INV_DATA; + goto leave; + } + + /* Extract the key. */ + rc = sexp_extract_param (keyparms, NULL, "nedp?q?u?", + &sk.n, &sk.e, &sk.d, &sk.p, &sk.q, &sk.u, + NULL); + if (rc) + goto leave; + if (DBG_CIPHER) + { + log_printmpi ("rsa_decrypt n", sk.n); + log_printmpi ("rsa_decrypt e", sk.e); + if (!fips_mode ()) + { + log_printmpi ("rsa_decrypt d", sk.d); + log_printmpi ("rsa_decrypt p", sk.p); + log_printmpi ("rsa_decrypt q", sk.q); + log_printmpi ("rsa_decrypt u", sk.u); + } + } + + /* Better make sure that there are no superfluous leading zeroes in + the input and it has not been "padded" using multiples of N. + This mitigates side-channel attacks (CVE-2013-4576). */ + mpi_normalize (data); + mpi_fdiv_r (data, data, sk.n); + + /* Allocate MPI for the plaintext. */ + plain = mpi_snew (ctx.nbits); + + /* We use blinding by default to mitigate timing attacks which can + be practically mounted over the network as shown by Brumley and + Boney in 2003. */ + if ((ctx.flags & PUBKEY_FLAG_NO_BLINDING)) + secret (plain, data, &sk); + else + secret_blinded (plain, data, &sk, ctx.nbits); + + if (DBG_CIPHER) + log_printmpi ("rsa_decrypt res", plain); + + /* Reverse the encoding and build the s-expression. */ + switch (ctx.encoding) + { + case PUBKEY_ENC_PKCS1: + rc = _gcry_rsa_pkcs1_decode_for_enc (&unpad, &unpadlen, ctx.nbits, plain); + mpi_free (plain); + plain = NULL; + if (!rc) + rc = sexp_build (r_plain, NULL, "(value %b)", (int)unpadlen, unpad); + break; + + case PUBKEY_ENC_OAEP: + rc = _gcry_rsa_oaep_decode (&unpad, &unpadlen, + ctx.nbits, ctx.hash_algo, + plain, ctx.label, ctx.labellen); + mpi_free (plain); + plain = NULL; + if (!rc) + rc = sexp_build (r_plain, NULL, "(value %b)", (int)unpadlen, unpad); + break; + + default: + /* Raw format. For backward compatibility we need to assume a + signed mpi by using the sexp format string "%m". */ + rc = sexp_build (r_plain, NULL, + (ctx.flags & PUBKEY_FLAG_LEGACYRESULT) + ? "%m":"(value %m)", plain); + break; + } + + leave: + xfree (unpad); + _gcry_mpi_release (plain); + _gcry_mpi_release (sk.n); + _gcry_mpi_release (sk.e); + _gcry_mpi_release (sk.d); + _gcry_mpi_release (sk.p); + _gcry_mpi_release (sk.q); + _gcry_mpi_release (sk.u); + _gcry_mpi_release (data); + sexp_release (l1); + _gcry_pk_util_free_encoding_ctx (&ctx); + if (DBG_CIPHER) + log_debug ("rsa_decrypt => %s\n", gpg_strerror (rc)); + return rc; +} + + +static gcry_err_code_t +rsa_sign (gcry_sexp_t *r_sig, gcry_sexp_t s_data, gcry_sexp_t keyparms) +{ + gpg_err_code_t rc; + struct pk_encoding_ctx ctx; + gcry_mpi_t data = NULL; + RSA_secret_key sk = {NULL, NULL, NULL, NULL, NULL, NULL}; + RSA_public_key pk; + gcry_mpi_t sig = NULL; + gcry_mpi_t result = NULL; + + _gcry_pk_util_init_encoding_ctx (&ctx, PUBKEY_OP_SIGN, + rsa_get_nbits (keyparms)); + + /* Extract the data. */ + rc = _gcry_pk_util_data_to_mpi (s_data, &data, &ctx); + if (rc) + goto leave; + if (DBG_CIPHER) + log_printmpi ("rsa_sign data", data); + if (mpi_is_opaque (data)) + { + rc = GPG_ERR_INV_DATA; + goto leave; + } + + /* Extract the key. */ + rc = sexp_extract_param (keyparms, NULL, "nedp?q?u?", + &sk.n, &sk.e, &sk.d, &sk.p, &sk.q, &sk.u, + NULL); + if (rc) + goto leave; + if (DBG_CIPHER) + { + log_printmpi ("rsa_sign n", sk.n); + log_printmpi ("rsa_sign e", sk.e); + if (!fips_mode ()) + { + log_printmpi ("rsa_sign d", sk.d); + log_printmpi ("rsa_sign p", sk.p); + log_printmpi ("rsa_sign q", sk.q); + log_printmpi ("rsa_sign u", sk.u); + } + } + + /* Do RSA computation. */ + sig = mpi_new (0); + if ((ctx.flags & PUBKEY_FLAG_NO_BLINDING)) + secret (sig, data, &sk); + else + secret_blinded (sig, data, &sk, ctx.nbits); + if (DBG_CIPHER) + log_printmpi ("rsa_sign res", sig); + + /* Check that the created signature is good. This detects a failure + of the CRT algorithm (Lenstra's attack on RSA's use of the CRT). */ + result = mpi_new (0); + pk.n = sk.n; + pk.e = sk.e; + public (result, sig, &pk); + if (mpi_cmp (result, data)) + { + rc = GPG_ERR_BAD_SIGNATURE; + goto leave; + } + + /* Convert the result. */ + if ((ctx.flags & PUBKEY_FLAG_FIXEDLEN)) + { + /* We need to make sure to return the correct length to avoid + problems with missing leading zeroes. */ + unsigned char *em; + size_t emlen = (mpi_get_nbits (sk.n)+7)/8; + + rc = _gcry_mpi_to_octet_string (&em, NULL, sig, emlen); + if (!rc) + { + rc = sexp_build (r_sig, NULL, "(sig-val(rsa(s%b)))", (int)emlen, em); + xfree (em); + } + } + else + rc = sexp_build (r_sig, NULL, "(sig-val(rsa(s%M)))", sig); + + + leave: + _gcry_mpi_release (result); + _gcry_mpi_release (sig); + _gcry_mpi_release (sk.n); + _gcry_mpi_release (sk.e); + _gcry_mpi_release (sk.d); + _gcry_mpi_release (sk.p); + _gcry_mpi_release (sk.q); + _gcry_mpi_release (sk.u); + _gcry_mpi_release (data); + _gcry_pk_util_free_encoding_ctx (&ctx); + if (DBG_CIPHER) + log_debug ("rsa_sign => %s\n", gpg_strerror (rc)); + return rc; +} + + +static gcry_err_code_t +rsa_verify (gcry_sexp_t s_sig, gcry_sexp_t s_data, gcry_sexp_t keyparms) +{ + 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 (keyparms)); + + /* Extract the data. */ + rc = _gcry_pk_util_data_to_mpi (s_data, &data, &ctx); + if (rc) + goto leave; + if (DBG_CIPHER) + log_printmpi ("rsa_verify data", data); + if (mpi_is_opaque (data)) + { + rc = GPG_ERR_INV_DATA; + goto leave; + } + + /* Extract the signature value. */ + rc = _gcry_pk_util_preparse_sigval (s_sig, rsa_names, &l1, NULL); + if (rc) + goto leave; + rc = sexp_extract_param (l1, NULL, "s", &sig, NULL); + if (rc) + goto leave; + if (DBG_CIPHER) + log_printmpi ("rsa_verify sig", sig); + + /* Extract the key. */ + rc = sexp_extract_param (keyparms, NULL, "ne", &pk.n, &pk.e, NULL); + if (rc) + goto leave; + if (DBG_CIPHER) + { + log_printmpi ("rsa_verify n", pk.n); + log_printmpi ("rsa_verify e", pk.e); + } + + /* Do RSA computation and compare. */ + result = mpi_new (0); + public (result, sig, &pk); + if (DBG_CIPHER) + log_printmpi ("rsa_verify cmp", result); + if (ctx.verify_cmp) + rc = ctx.verify_cmp (&ctx, result); + else + 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); + 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; +} + + + +/* Return the number of bits for the key described by PARMS. On error + * 0 is returned. The format of PARMS starts with the algorithm name; + * for example: + * + * (rsa + * (n ) + * (e )) + * + * More parameters may be given but we only need N here. + */ +static unsigned int +rsa_get_nbits (gcry_sexp_t parms) +{ + gcry_sexp_t l1; + gcry_mpi_t n; + unsigned int nbits; + + l1 = sexp_find_token (parms, "n", 1); + if (!l1) + return 0; /* Parameter N not found. */ + + n = sexp_nth_mpi (l1, 1, GCRYMPI_FMT_USG); + sexp_release (l1); + nbits = n? mpi_get_nbits (n) : 0; + _gcry_mpi_release (n); + return nbits; +} + + +/* Compute a keygrip. MD is the hash context which we are going to + update. KEYPARAM is an S-expression with the key parameters, this + is usually a public key but may also be a secret key. An example + of such an S-expression is: + + (rsa + (n #00B...#) + (e #010001#)) + + PKCS-15 says that for RSA only the modulus should be hashed - + however, it is not clear whether this is meant to use the raw bytes + (assuming this is an unsigned integer) or whether the DER required + 0 should be prefixed. We hash the raw bytes. */ +static gpg_err_code_t +compute_keygrip (gcry_md_hd_t md, gcry_sexp_t keyparam) +{ + gcry_sexp_t l1; + const char *data; + size_t datalen; + + l1 = sexp_find_token (keyparam, "n", 1); + if (!l1) + return GPG_ERR_NO_OBJ; + + data = sexp_nth_data (l1, 1, &datalen); + if (!data) + { + sexp_release (l1); + return GPG_ERR_NO_OBJ; + } + + _gcry_md_write (md, data, datalen); + sexp_release (l1); + + return 0; +} + + + + +/* + Self-test section. + */ + +static const char * +selftest_sign_2048 (gcry_sexp_t pkey, gcry_sexp_t skey) +{ + static const char sample_data[] = + "(data (flags pkcs1)" + " (hash sha256 #11223344556677889900aabbccddeeff" + /**/ "102030405060708090a0b0c0d0f01121#))"; + static const char sample_data_bad[] = + "(data (flags pkcs1)" + " (hash sha256 #11223344556677889900aabbccddeeff" + /**/ "802030405060708090a0b0c0d0f01121#))"; + + const char *errtxt = NULL; + gcry_error_t err; + gcry_sexp_t data = NULL; + gcry_sexp_t data_bad = NULL; + gcry_sexp_t sig = NULL; + /* raw signature data reference */ + const char ref_data[] = + "6252a19a11e1d5155ed9376036277193d644fa239397fff03e9b92d6f86415d6" + "d30da9273775f290e580d038295ff8ff89522becccfa6ae870bf76b76df402a8" + "54f69347e3db3de8e1e7d4dada281ec556810c7a8ecd0b5f51f9b1c0e7aa7557" + "61aa2b8ba5f811304acc6af0eca41fe49baf33bf34eddaf44e21e036ac7f0b68" + "03cdef1c60021fb7b5b97ebacdd88ab755ce29af568dbc5728cc6e6eff42618d" + "62a0386ca8beed46402bdeeef29b6a3feded906bace411a06a39192bf516ae10" + "67e4320fa8ea113968525f4574d022a3ceeaafdc41079efe1f22cc94bf59d8d3" + "328085da9674857db56de5978a62394aab48aa3b72e23a1b16260cfd9daafe65"; + gcry_mpi_t ref_mpi = NULL; + gcry_mpi_t sig_mpi = NULL; + + err = sexp_sscan (&data, NULL, sample_data, strlen (sample_data)); + if (!err) + err = sexp_sscan (&data_bad, NULL, + sample_data_bad, strlen (sample_data_bad)); + if (err) + { + errtxt = "converting data failed"; + goto leave; + } + + err = _gcry_pk_sign (&sig, data, skey); + if (err) + { + errtxt = "signing failed"; + goto leave; + } + + err = _gcry_mpi_scan(&ref_mpi, GCRYMPI_FMT_HEX, ref_data, 0, NULL); + if (err) + { + errtxt = "converting ref_data to mpi failed"; + goto leave; + } + + err = _gcry_sexp_extract_param(sig, "sig-val!rsa", "s", &sig_mpi, NULL); + if (err) + { + errtxt = "extracting signature data failed"; + goto leave; + } + + if (mpi_cmp (sig_mpi, ref_mpi)) + { + errtxt = "signature does not match reference data"; + goto leave; + } + + err = _gcry_pk_verify (sig, data, pkey); + if (err) + { + errtxt = "verify failed"; + goto leave; + } + err = _gcry_pk_verify (sig, data_bad, pkey); + if (gcry_err_code (err) != GPG_ERR_BAD_SIGNATURE) + { + errtxt = "bad signature not detected"; + goto leave; + } + + + leave: + sexp_release (sig); + sexp_release (data_bad); + sexp_release (data); + _gcry_mpi_release (ref_mpi); + _gcry_mpi_release (sig_mpi); + return errtxt; +} + + + +/* Given an S-expression ENCR_DATA of the form: + + (enc-val + (rsa + (a a-value))) + + as returned by gcry_pk_decrypt, return the the A-VALUE. On error, + return NULL. */ +static gcry_mpi_t +extract_a_from_sexp (gcry_sexp_t encr_data) +{ + gcry_sexp_t l1, l2, l3; + gcry_mpi_t a_value; + + l1 = sexp_find_token (encr_data, "enc-val", 0); + if (!l1) + return NULL; + l2 = sexp_find_token (l1, "rsa", 0); + sexp_release (l1); + if (!l2) + return NULL; + l3 = sexp_find_token (l2, "a", 0); + sexp_release (l2); + if (!l3) + return NULL; + a_value = sexp_nth_mpi (l3, 1, 0); + sexp_release (l3); + + return a_value; +} + + +static const char * +selftest_encr_2048 (gcry_sexp_t pkey, gcry_sexp_t skey) +{ + const char *errtxt = NULL; + gcry_error_t err; + static const char plaintext[] = + "Jim quickly realized that the beautiful gowns are expensive."; + gcry_sexp_t plain = NULL; + gcry_sexp_t encr = NULL; + gcry_mpi_t ciphertext = NULL; + gcry_sexp_t decr = NULL; + char *decr_plaintext = NULL; + gcry_sexp_t tmplist = NULL; + /* expected result of encrypting the plaintext with sample_secret_key */ + static const char ref_data[] = + "18022e2593a402a737caaa93b4c7e750e20ca265452980e1d6b7710fbd3e" + "7dce72be5c2110fb47691cb38f42170ee3b4a37f2498d4a51567d762585e" + "4cb81d04fbc7df4144f8e5eac2d4b8688521b64011f11d7ad53f4c874004" + "819856f2e2a6f83d1c9c4e73ac26089789c14482b0b8d44139133c88c4a5" + "2dba9dd6d6ffc622666b7d129168333d999706af30a2d7d272db7734e5ed" + "fb8c64ea3018af3ad20f4a013a5060cb0f5e72753967bebe294280a6ed0d" + "dbd3c4f11d0a8696e9d32a0dc03deb0b5e49b2cbd1503392642d4e1211f3" + "e8e2ee38abaa3671ccd57fcde8ca76e85fd2cb77c35706a970a213a27352" + "cec92a9604d543ddb5fc478ff50e0622"; + gcry_mpi_t ref_mpi = NULL; + + /* Put the plaintext into an S-expression. */ + err = sexp_build (&plain, NULL, "(data (flags raw) (value %s))", plaintext); + if (err) + { + errtxt = "converting data failed"; + goto leave; + } + + /* Encrypt. */ + err = _gcry_pk_encrypt (&encr, plain, pkey); + if (err) + { + errtxt = "encrypt failed"; + goto leave; + } + + err = _gcry_mpi_scan(&ref_mpi, GCRYMPI_FMT_HEX, ref_data, 0, NULL); + if (err) + { + errtxt = "converting encrydata to mpi failed"; + goto leave; + } + + /* Extraxt the ciphertext from the returned S-expression. */ + /*sexp_dump (encr);*/ + ciphertext = extract_a_from_sexp (encr); + if (!ciphertext) + { + errtxt = "gcry_pk_decrypt returned garbage"; + goto leave; + } + + /* Check that the ciphertext does no match the plaintext. */ + /* _gcry_log_printmpi ("plaintext", plaintext); */ + /* _gcry_log_printmpi ("ciphertxt", ciphertext); */ + if (mpi_cmp (ref_mpi, ciphertext)) + { + errtxt = "ciphertext doesn't match reference data"; + goto leave; + } + + /* Decrypt. */ + err = _gcry_pk_decrypt (&decr, encr, skey); + if (err) + { + errtxt = "decrypt failed"; + goto leave; + } + + /* Extract the decrypted data from the S-expression. Note that the + output of gcry_pk_decrypt depends on whether a flags lists occurs + in its input data. Because we passed the output of + gcry_pk_encrypt directly to gcry_pk_decrypt, such a flag value + won't be there as of today. To be prepared for future changes we + take care of it anyway. */ + tmplist = sexp_find_token (decr, "value", 0); + if (tmplist) + decr_plaintext = sexp_nth_string (tmplist, 1); + else + decr_plaintext = sexp_nth_string (decr, 0); + if (!decr_plaintext) + { + errtxt = "decrypt returned no plaintext"; + goto leave; + } + + /* Check that the decrypted plaintext matches the original plaintext. */ + if (strcmp (plaintext, decr_plaintext)) + { + errtxt = "mismatch"; + goto leave; + } + + leave: + sexp_release (tmplist); + xfree (decr_plaintext); + sexp_release (decr); + _gcry_mpi_release (ciphertext); + _gcry_mpi_release (ref_mpi); + sexp_release (encr); + sexp_release (plain); + return errtxt; +} + + +static gpg_err_code_t +selftests_rsa (selftest_report_func_t report) +{ + const char *what; + const char *errtxt; + gcry_error_t err; + gcry_sexp_t skey = NULL; + gcry_sexp_t pkey = NULL; + + /* Convert the S-expressions into the internal representation. */ + what = "convert"; + err = sexp_sscan (&skey, NULL, sample_secret_key, strlen (sample_secret_key)); + if (!err) + err = sexp_sscan (&pkey, NULL, + sample_public_key, strlen (sample_public_key)); + if (err) + { + errtxt = _gcry_strerror (err); + goto failed; + } + + what = "key consistency"; + err = _gcry_pk_testkey (skey); + if (err) + { + errtxt = _gcry_strerror (err); + goto failed; + } + + what = "sign"; + errtxt = selftest_sign_2048 (pkey, skey); + if (errtxt) + goto failed; + + what = "encrypt"; + errtxt = selftest_encr_2048 (pkey, skey); + if (errtxt) + goto failed; + + sexp_release (pkey); + sexp_release (skey); + return 0; /* Succeeded. */ + + failed: + sexp_release (pkey); + sexp_release (skey); + if (report) + report ("pubkey", GCRY_PK_RSA, what, errtxt); + return GPG_ERR_SELFTEST_FAILED; +} + + +/* Run a full self-test for ALGO and return 0 on success. */ +static gpg_err_code_t +run_selftests (int algo, int extended, selftest_report_func_t report) +{ + gpg_err_code_t ec; + + (void)extended; + + switch (algo) + { + case GCRY_PK_RSA: + ec = selftests_rsa (report); + break; + default: + ec = GPG_ERR_PUBKEY_ALGO; + break; + + } + return ec; +} + + + + +gcry_pk_spec_t _gcry_pubkey_spec_rsa = + { + GCRY_PK_RSA, { 0, 1 }, + (GCRY_PK_USAGE_SIGN | GCRY_PK_USAGE_ENCR), + "RSA", rsa_names, + "ne", "nedpqu", "a", "s", "n", + rsa_generate, + rsa_check_secret_key, + rsa_encrypt, + rsa_decrypt, + rsa_sign, + rsa_verify, + rsa_get_nbits, + run_selftests, + compute_keygrip + }; diff --git a/tests/keygen.c b/tests/keygen.c index 6b6a60a..81cefb1 100644 --- a/tests/keygen.c +++ b/tests/keygen.c @@ -200,11 +200,11 @@ check_rsa_keys (void) if (verbose) - info ("creating 512 bit RSA key with e=257\n"); + info ("creating 1024 bit RSA key with e=257\n"); rc = gcry_sexp_new (&keyparm, "(genkey\n" " (rsa\n" - " (nbits 3:512)\n" + " (nbits 4:1024)\n" " (rsa-use-e 3:257)\n" " ))", 0, 1); if (rc) @@ -225,11 +225,11 @@ check_rsa_keys (void) gcry_sexp_release (key); if (verbose) - info ("creating 512 bit RSA key with default e\n"); + info ("creating 1024 bit RSA key with default e\n"); rc = gcry_sexp_new (&keyparm, "(genkey\n" " (rsa\n" - " (nbits 3:512)\n" + " (nbits 4:1024)\n" " (rsa-use-e 1:0)\n" " ))", 0, 1); if (rc) @@ -309,12 +309,12 @@ check_dsa_keys (void) } if (verbose) - info ("creating 1536 bit DSA key\n"); + info ("creating 2048 bit DSA key\n"); rc = gcry_sexp_new (&keyparm, "(genkey\n" " (dsa\n" - " (nbits 4:1536)\n" - " (qbits 3:224)\n" + " (nbits 4:2048)\n" + " (qbits 3:256)\n" " ))", 0, 1); if (rc) die ("error creating S-expression: %s\n", gpg_strerror (rc)); diff --git a/tests/keygen.c.tests b/tests/keygen.c.tests new file mode 100644 index 0000000..6b6a60a --- /dev/null +++ b/tests/keygen.c.tests @@ -0,0 +1,787 @@ +/* keygen.c - key generation regression tests + * Copyright (C) 2003, 2005, 2012 Free Software Foundation, Inc. + * Copyright (C) 2013, 2015 g10 Code GmbH + * + * This file is part of Libgcrypt. + * + * Libgcrypt is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * Libgcrypt is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, see . + */ + +#ifdef HAVE_CONFIG_H +#include +#endif +#include +#include +#include +#include +#include "../src/gcrypt-int.h" + + +#define PGM "keygen" +#include "t-common.h" + +static int in_fips_mode; + + +/* static void */ +/* show_note (const char *format, ...) */ +/* { */ +/* va_list arg_ptr; */ + +/* if (!verbose && getenv ("srcdir")) */ +/* fputs (" ", stderr); /\* To align above "PASS: ". *\/ */ +/* else */ +/* fprintf (stderr, "%s: ", PGM); */ +/* va_start (arg_ptr, format); */ +/* vfprintf (stderr, format, arg_ptr); */ +/* if (*format && format[strlen(format)-1] != '\n') */ +/* putc ('\n', stderr); */ +/* va_end (arg_ptr); */ +/* } */ + + +static void +show_sexp (const char *prefix, gcry_sexp_t a) +{ + char *buf; + size_t size; + + fprintf (stderr, "%s: ", PGM); + if (prefix) + fputs (prefix, stderr); + size = gcry_sexp_sprint (a, GCRYSEXP_FMT_ADVANCED, NULL, 0); + buf = xmalloc (size); + + gcry_sexp_sprint (a, GCRYSEXP_FMT_ADVANCED, buf, size); + fprintf (stderr, "%.*s", (int)size, buf); + gcry_free (buf); +} + + +static void +show_mpi (const char *prefix, gcry_mpi_t a) +{ + char *buf; + void *bufaddr = &buf; + gcry_error_t rc; + + fprintf (stderr, "%s: ", PGM); + if (prefix) + fputs (prefix, stderr); + rc = gcry_mpi_aprint (GCRYMPI_FMT_HEX, bufaddr, NULL, a); + if (rc) + fprintf (stderr, "[error printing number: %s]\n", gpg_strerror (rc)); + else + { + fprintf (stderr, "%s\n", buf); + gcry_free (buf); + } +} + + +static void +check_generated_rsa_key (gcry_sexp_t key, unsigned long expected_e) +{ + gcry_sexp_t skey, pkey, list; + + pkey = gcry_sexp_find_token (key, "public-key", 0); + if (!pkey) + fail ("public part missing in return value\n"); + else + { + gcry_mpi_t e = NULL; + + list = gcry_sexp_find_token (pkey, "e", 0); + if (!list || !(e=gcry_sexp_nth_mpi (list, 1, 0)) ) + fail ("public exponent not found\n"); + else if (!expected_e) + { + if (verbose) + show_mpi ("public exponent: ", e); + } + else if ( gcry_mpi_cmp_ui (e, expected_e)) + { + show_mpi ("public exponent: ", e); + fail ("public exponent is not %lu\n", expected_e); + } + gcry_sexp_release (list); + gcry_mpi_release (e); + gcry_sexp_release (pkey); + } + + skey = gcry_sexp_find_token (key, "private-key", 0); + if (!skey) + fail ("private part missing in return value\n"); + else + { + int rc = gcry_pk_testkey (skey); + if (rc) + fail ("gcry_pk_testkey failed: %s\n", gpg_strerror (rc)); + gcry_sexp_release (skey); + } +} + + +static void +check_rsa_keys (void) +{ + gcry_sexp_t keyparm, key; + int rc; + + if (verbose) + info ("creating 2048 bit RSA key\n"); + rc = gcry_sexp_new (&keyparm, + "(genkey\n" + " (rsa\n" + " (nbits 4:2048)\n" + " ))", 0, 1); + if (rc) + die ("error creating S-expression: %s\n", gpg_strerror (rc)); + rc = gcry_pk_genkey (&key, keyparm); + gcry_sexp_release (keyparm); + if (rc) + die ("error generating RSA key: %s\n", gpg_strerror (rc)); + + if (verbose) + info ("creating 1024 bit RSA key\n"); + rc = gcry_sexp_new (&keyparm, + "(genkey\n" + " (rsa\n" + " (nbits 4:1024)\n" + " ))", 0, 1); + if (rc) + die ("error creating S-expression: %s\n", gpg_strerror (rc)); + + gcry_sexp_release (key); + rc = gcry_pk_genkey (&key, keyparm); + gcry_sexp_release (keyparm); + if (rc && !in_fips_mode) + fail ("error generating RSA key: %s\n", gpg_strerror (rc)); + else if (!rc && in_fips_mode) + fail ("generating 1024 bit RSA key must not work!"); + + if (!rc) + { + if (verbose > 1) + show_sexp ("1024 bit RSA key:\n", key); + check_generated_rsa_key (key, 65537); + } + gcry_sexp_release (key); + + if (verbose) + info ("creating 2048 bit RSA key with e=65539\n"); + rc = gcry_sexp_new (&keyparm, + "(genkey\n" + " (rsa\n" + " (nbits 4:2048)\n" + " (rsa-use-e 5:65539)\n" + " ))", 0, 1); + if (rc) + die ("error creating S-expression: %s\n", gpg_strerror (rc)); + rc = gcry_pk_genkey (&key, keyparm); + gcry_sexp_release (keyparm); + if (rc) + fail ("error generating RSA key: %s\n", gpg_strerror (rc)); + + if (!rc) + check_generated_rsa_key (key, 65539); + gcry_sexp_release (key); + + + if (verbose) + info ("creating 512 bit RSA key with e=257\n"); + rc = gcry_sexp_new (&keyparm, + "(genkey\n" + " (rsa\n" + " (nbits 3:512)\n" + " (rsa-use-e 3:257)\n" + " ))", 0, 1); + if (rc) + die ("error creating S-expression: %s\n", gpg_strerror (rc)); + rc = gcry_pk_genkey (&key, keyparm); + gcry_sexp_release (keyparm); + if (rc && !in_fips_mode) + fail ("error generating RSA key: %s\n", gpg_strerror (rc)); + else if (!rc && in_fips_mode) + fail ("generating 512 bit RSA key must not work!"); + + if (verbose && rc && in_fips_mode) + info ("... correctly rejected key creation in FIPS mode (%s)\n", + gpg_strerror (rc)); + + if (!rc) + check_generated_rsa_key (key, 257); + gcry_sexp_release (key); + + if (verbose) + info ("creating 512 bit RSA key with default e\n"); + rc = gcry_sexp_new (&keyparm, + "(genkey\n" + " (rsa\n" + " (nbits 3:512)\n" + " (rsa-use-e 1:0)\n" + " ))", 0, 1); + if (rc) + die ("error creating S-expression: %s\n", gpg_strerror (rc)); + rc = gcry_pk_genkey (&key, keyparm); + gcry_sexp_release (keyparm); + if (rc && !in_fips_mode) + fail ("error generating RSA key: %s\n", gpg_strerror (rc)); + else if (!rc && in_fips_mode) + fail ("generating 512 bit RSA key must not work!"); + + if (verbose && rc && in_fips_mode) + info ("... correctly rejected key creation in FIPS mode (%s)\n", + gpg_strerror (rc)); + + + if (!rc) + check_generated_rsa_key (key, 0); /* We don't expect a constant exponent. */ + gcry_sexp_release (key); +} + + +static void +check_elg_keys (void) +{ + gcry_sexp_t keyparm, key; + int rc; + + if (verbose) + info ("creating 1024 bit Elgamal key\n"); + rc = gcry_sexp_new (&keyparm, + "(genkey\n" + " (elg\n" + " (nbits 4:1024)\n" + " ))", 0, 1); + if (rc) + die ("error creating S-expression: %s\n", gpg_strerror (rc)); + rc = gcry_pk_genkey (&key, keyparm); + gcry_sexp_release (keyparm); + if (rc) + die ("error generating Elgamal key: %s\n", gpg_strerror (rc)); + if (verbose > 1) + show_sexp ("1024 bit Elgamal key:\n", key); + gcry_sexp_release (key); +} + + +static void +check_dsa_keys (void) +{ + gcry_sexp_t keyparm, key; + int rc; + int i; + + /* Check that DSA generation works and that it can grok the qbits + argument. */ + if (verbose) + info ("creating 5 1024 bit DSA keys\n"); + for (i=0; i < 5; i++) + { + rc = gcry_sexp_new (&keyparm, + "(genkey\n" + " (dsa\n" + " (nbits 4:1024)\n" + " ))", 0, 1); + if (rc) + die ("error creating S-expression: %s\n", gpg_strerror (rc)); + rc = gcry_pk_genkey (&key, keyparm); + gcry_sexp_release (keyparm); + if (rc && !in_fips_mode) + die ("error generating DSA key: %s\n", gpg_strerror (rc)); + else if (!rc && in_fips_mode) + die ("generating 1024 bit DSA key must not work!"); + if (!i && verbose > 1) + show_sexp ("1024 bit DSA key:\n", key); + gcry_sexp_release (key); + } + + if (verbose) + info ("creating 1536 bit DSA key\n"); + rc = gcry_sexp_new (&keyparm, + "(genkey\n" + " (dsa\n" + " (nbits 4:1536)\n" + " (qbits 3:224)\n" + " ))", 0, 1); + if (rc) + die ("error creating S-expression: %s\n", gpg_strerror (rc)); + rc = gcry_pk_genkey (&key, keyparm); + gcry_sexp_release (keyparm); + if (rc && !in_fips_mode) + die ("error generating DSA key: %s\n", gpg_strerror (rc)); + else if (!rc && in_fips_mode) + die ("generating 1536 bit DSA key must not work!"); + if (verbose > 1) + show_sexp ("1536 bit DSA key:\n", key); + gcry_sexp_release (key); + + if (verbose) + info ("creating 3072 bit DSA key\n"); + rc = gcry_sexp_new (&keyparm, + "(genkey\n" + " (dsa\n" + " (nbits 4:3072)\n" + " (qbits 3:256)\n" + " ))", 0, 1); + if (rc) + die ("error creating S-expression: %s\n", gpg_strerror (rc)); + rc = gcry_pk_genkey (&key, keyparm); + gcry_sexp_release (keyparm); + if (rc) + die ("error generating DSA key: %s\n", gpg_strerror (rc)); + if (verbose > 1) + show_sexp ("3072 bit DSA key:\n", key); + gcry_sexp_release (key); + + if (verbose) + info ("creating 2048/256 bit DSA key\n"); + rc = gcry_sexp_new (&keyparm, + "(genkey\n" + " (dsa\n" + " (nbits 4:2048)\n" + " (qbits 3:256)\n" + " ))", 0, 1); + if (rc) + die ("error creating S-expression: %s\n", gpg_strerror (rc)); + rc = gcry_pk_genkey (&key, keyparm); + gcry_sexp_release (keyparm); + if (rc) + die ("error generating DSA key: %s\n", gpg_strerror (rc)); + if (verbose > 1) + show_sexp ("2048 bit DSA key:\n", key); + gcry_sexp_release (key); + + if (verbose) + info ("creating 2048/224 bit DSA key\n"); + rc = gcry_sexp_new (&keyparm, + "(genkey\n" + " (dsa\n" + " (nbits 4:2048)\n" + " (qbits 3:224)\n" + " ))", 0, 1); + if (rc) + die ("error creating S-expression: %s\n", gpg_strerror (rc)); + rc = gcry_pk_genkey (&key, keyparm); + gcry_sexp_release (keyparm); + if (rc) + die ("error generating DSA key: %s\n", gpg_strerror (rc)); + if (verbose > 1) + show_sexp ("2048 bit DSA key:\n", key); + gcry_sexp_release (key); +} + + +static void +check_generated_ecc_key (gcry_sexp_t key) +{ + gcry_sexp_t skey, pkey; + + pkey = gcry_sexp_find_token (key, "public-key", 0); + if (!pkey) + fail ("public part missing in return value\n"); + else + { + /* Fixme: Check more stuff. */ + gcry_sexp_release (pkey); + } + + skey = gcry_sexp_find_token (key, "private-key", 0); + if (!skey) + fail ("private part missing in return value\n"); + else + { + int rc = gcry_pk_testkey (skey); + if (rc) + fail ("gcry_pk_testkey failed: %s\n", gpg_strerror (rc)); + gcry_sexp_release (skey); + } + + /* Finally check that gcry_pk_testkey also works on the entire + S-expression. */ + { + int rc = gcry_pk_testkey (key); + if (rc) + fail ("gcry_pk_testkey failed on key pair: %s\n", gpg_strerror (rc)); + } +} + + +static void +check_ecc_keys (void) +{ + const char *curves[] = { "NIST P-521", "NIST P-384", "NIST P-256", + "Ed25519", NULL }; + int testno; + gcry_sexp_t keyparm, key; + int rc; + + for (testno=0; curves[testno]; testno++) + { + if (verbose) + info ("creating ECC key using curve %s\n", curves[testno]); + if (!strcmp (curves[testno], "Ed25519")) + { + /* Ed25519 isn't allowed in fips mode */ + if (in_fips_mode) + continue; + rc = gcry_sexp_build (&keyparm, NULL, + "(genkey(ecc(curve %s)(flags param eddsa)))", + curves[testno]); + } + else + rc = gcry_sexp_build (&keyparm, NULL, + "(genkey(ecc(curve %s)(flags param)))", + curves[testno]); + if (rc) + die ("error creating S-expression: %s\n", gpg_strerror (rc)); + rc = gcry_pk_genkey (&key, keyparm); + gcry_sexp_release (keyparm); + if (rc) + die ("error generating ECC key using curve %s: %s\n", + curves[testno], gpg_strerror (rc)); + + if (verbose > 1) + show_sexp ("ECC key:\n", key); + + check_generated_ecc_key (key); + + gcry_sexp_release (key); + } + + if (verbose) + info ("creating ECC key using curve Ed25519 for ECDSA\n"); + rc = gcry_sexp_build (&keyparm, NULL, "(genkey(ecc(curve Ed25519)))"); + if (rc) + die ("error creating S-expression: %s\n", gpg_strerror (rc)); + rc = gcry_pk_genkey (&key, keyparm); + gcry_sexp_release (keyparm); + if (rc && !in_fips_mode) + die ("error generating ECC key using curve Ed25519 for ECDSA: %s\n", + gpg_strerror (rc)); + else if (!rc && in_fips_mode) + fail ("generating Ed25519 key must not work!"); + + if (verbose && rc && in_fips_mode) + info ("... correctly rejected key creation in FIPS mode (%s)\n", + gpg_strerror (rc)); + + if (!rc) + { + if (verbose > 1) + show_sexp ("ECC key:\n", key); + + check_generated_ecc_key (key); + } + gcry_sexp_release (key); + + if (verbose) + info ("creating ECC key using curve Ed25519 for ECDSA (nocomp)\n"); + rc = gcry_sexp_build (&keyparm, NULL, + "(genkey(ecc(curve Ed25519)(flags nocomp)))"); + if (rc) + die ("error creating S-expression: %s\n", gpg_strerror (rc)); + rc = gcry_pk_genkey (&key, keyparm); + gcry_sexp_release (keyparm); + if (rc && !in_fips_mode) + die ("error generating ECC key using curve Ed25519 for ECDSA" + " (nocomp): %s\n", + gpg_strerror (rc)); + else if (!rc && in_fips_mode) + fail ("generating Ed25519 key must not work in FIPS mode!"); + + if (verbose && rc && in_fips_mode) + info ("... correctly rejected key creation in FIPS mode (%s)\n", + gpg_strerror (rc)); + gcry_sexp_release (key); + + if (verbose) + info ("creating ECC key using curve NIST P-384 for ECDSA\n"); + + /* Must be specified as nistp384 (one word), because ecc_generate + * uses _gcry_sexp_nth_string which takes the first word of the name + * and thus libgcrypt can't find it later in its curves table. */ + rc = gcry_sexp_build (&keyparm, NULL, "(genkey(ecc(curve nistp384)))"); + if (rc) + die ("error creating S-expression: %s\n", gpg_strerror (rc)); + rc = gcry_pk_genkey (&key, keyparm); + gcry_sexp_release (keyparm); + if (rc) + die ("error generating ECC key using curve NIST P-384 for ECDSA: %s\n", + gpg_strerror (rc)); + + if (verbose > 1) + show_sexp ("ECC key:\n", key); + + check_generated_ecc_key (key); + gcry_sexp_release (key); + + if (verbose) + info ("creating ECC key using curve NIST P-384 for ECDSA (nocomp)\n"); + rc = gcry_sexp_build (&keyparm, NULL, + "(genkey(ecc(curve nistp384)(flags nocomp)))"); + if (rc) + die ("error creating S-expression: %s\n", gpg_strerror (rc)); + rc = gcry_pk_genkey (&key, keyparm); + gcry_sexp_release (keyparm); + if (rc) + die ("error generating ECC key using curve NIST P-384 for ECDSA" + " (nocomp): %s\n", + gpg_strerror (rc)); + + if (verbose > 1) + show_sexp ("ECC key:\n", key); + + check_generated_ecc_key (key); + gcry_sexp_release (key); + + + if (verbose) + info ("creating ECC key using curve Ed25519 for ECDSA (transient-key)\n"); + rc = gcry_sexp_build (&keyparm, NULL, + "(genkey(ecc(curve Ed25519)(flags transient-key)))"); + if (rc) + die ("error creating S-expression: %s\n", gpg_strerror (rc)); + rc = gcry_pk_genkey (&key, keyparm); + gcry_sexp_release (keyparm); + if (rc && !in_fips_mode) + die ("error generating ECC key using curve Ed25519 for ECDSA" + " (transient-key): %s\n", + gpg_strerror (rc)); + else if (!rc && in_fips_mode) + fail ("generating Ed25519 key must not work in FIPS mode!"); + + if (verbose && rc && in_fips_mode) + info ("... correctly rejected key creation in FIPS mode (%s)\n", + gpg_strerror (rc)); + + if (!rc) + { + if (verbose > 1) + show_sexp ("ECC key:\n", key); + check_generated_ecc_key (key); + } + gcry_sexp_release (key); + + if (verbose) + info ("creating ECC key using curve Ed25519 for ECDSA " + "(transient-key no-keytest)\n"); + rc = gcry_sexp_build (&keyparm, NULL, + "(genkey(ecc(curve Ed25519)" + "(flags transient-key no-keytest)))"); + if (rc) + die ("error creating S-expression: %s\n", gpg_strerror (rc)); + rc = gcry_pk_genkey (&key, keyparm); + gcry_sexp_release (keyparm); + if (rc && !in_fips_mode) + die ("error generating ECC key using curve Ed25519 for ECDSA" + " (transient-key no-keytest): %s\n", + gpg_strerror (rc)); + else if (!rc && in_fips_mode) + fail ("generating Ed25519 key must not work in FIPS mode!"); + + if (verbose && rc && in_fips_mode) + info ("... correctly rejected key creation in FIPS mode (%s)\n", + gpg_strerror (rc)); + + if (!rc) + { + if (verbose > 1) + show_sexp ("ECC key:\n", key); + check_generated_ecc_key (key); + } + gcry_sexp_release (key); +} + + +static void +check_nonce (void) +{ + char a[32], b[32]; + int i,j; + int oops=0; + + if (verbose) + info ("checking gcry_create_nonce\n"); + + gcry_create_nonce (a, sizeof a); + for (i=0; i < 10; i++) + { + gcry_create_nonce (b, sizeof b); + if (!memcmp (a, b, sizeof a)) + die ("identical nonce found\n"); + } + for (i=0; i < 10; i++) + { + gcry_create_nonce (a, sizeof a); + if (!memcmp (a, b, sizeof a)) + die ("identical nonce found\n"); + } + + again: + for (i=1,j=0; i < sizeof a; i++) + if (a[0] == a[i]) + j++; + if (j+1 == sizeof (a)) + { + if (oops) + die ("impossible nonce found\n"); + oops++; + gcry_create_nonce (a, sizeof a); + goto again; + } +} + + +static void +progress_cb (void *cb_data, const char *what, int printchar, + int current, int total) +{ + (void)cb_data; + (void)what; + (void)current; + (void)total; + + if (printchar == '\n') + fputs ( "", stdout); + else + putchar (printchar); + fflush (stdout); +} + + +static void +usage (int mode) +{ + fputs ("usage: " PGM " [options] [{rsa|elg|dsa|ecc|nonce}]\n" + "Options:\n" + " --verbose be verbose\n" + " --debug flyswatter\n" + " --fips run in FIPS mode\n" + " --no-quick To not use the quick RNG hack\n" + " --progress print progress indicators\n", + mode? stderr : stdout); + if (mode) + exit (1); +} + +int +main (int argc, char **argv) +{ + int last_argc = -1; + int opt_fips = 0; + int with_progress = 0; + int no_quick = 0; + + if (argc) + { argc--; argv++; } + + while (argc && last_argc != argc ) + { + last_argc = argc; + if (!strcmp (*argv, "--")) + { + argc--; argv++; + break; + } + else if (!strcmp (*argv, "--help")) + { + usage (0); + exit (0); + } + else if (!strcmp (*argv, "--verbose")) + { + verbose++; + argc--; argv++; + } + else if (!strcmp (*argv, "--debug")) + { + verbose += 2; + debug++; + argc--; argv++; + } + else if (!strcmp (*argv, "--fips")) + { + argc--; argv++; + opt_fips = 1; + } + else if (!strcmp (*argv, "--progress")) + { + argc--; argv++; + with_progress = 1; + } + else if (!strcmp (*argv, "--no-quick")) + { + argc--; argv++; + no_quick = 1; + } + else if (!strncmp (*argv, "--", 2)) + die ("unknown option '%s'", *argv); + else + break; + } + + xgcry_control (GCRYCTL_SET_VERBOSITY, (int)verbose); + if (opt_fips) + xgcry_control (GCRYCTL_FORCE_FIPS_MODE, 0); + + if (!gcry_check_version (GCRYPT_VERSION)) + die ("version mismatch\n"); + + if (!opt_fips) + xgcry_control (GCRYCTL_DISABLE_SECMEM, 0); + + xgcry_control (GCRYCTL_INITIALIZATION_FINISHED, 0); + if (debug) + xgcry_control (GCRYCTL_SET_DEBUG_FLAGS, 1u , 0); + /* No valuable keys are create, so we can speed up our RNG. */ + if (!no_quick) + xgcry_control (GCRYCTL_ENABLE_QUICK_RANDOM, 0); + if (with_progress) + gcry_set_progress_handler (progress_cb, NULL); + + if ( gcry_fips_mode_active () ) + in_fips_mode = 1; + + if (opt_fips && !in_fips_mode) + die ("failed to switch into FIPS mode\n"); + + if (!argc) + { + check_rsa_keys (); + check_elg_keys (); + check_dsa_keys (); + check_ecc_keys (); + check_nonce (); + } + else + { + for (; argc; argc--, argv++) + if (!strcmp (*argv, "rsa")) + check_rsa_keys (); + else if (!strcmp (*argv, "elg")) + check_elg_keys (); + else if (!strcmp (*argv, "dsa")) + check_dsa_keys (); + else if (!strcmp (*argv, "ecc")) + check_ecc_keys (); + else if (!strcmp (*argv, "nonce")) + check_nonce (); + else + usage (1); + } + + return error_count? 1:0; +} diff --git a/tests/pubkey.c b/tests/pubkey.c index fbb7bbb..a49f903 100644 --- a/tests/pubkey.c +++ b/tests/pubkey.c @@ -595,7 +595,7 @@ get_dsa_key_fips186_with_seed_new (gcry_sexp_t *pkey, gcry_sexp_t *skey) " (use-fips186)" " (transient-key)" " (derive-parms" - " (seed #0cb1990c1fd3626055d7a0096f8fa99807399871#))))", + " (seed #8b4c4d671fff82e8ed932260206d0571e3a1c2cee8cd94cb73fe58f9b67488fa#))))", 0, 1); if (rc) die ("error creating S-expression: %s\n", gcry_strerror (rc)); diff --git a/tests/pubkey.c.tests b/tests/pubkey.c.tests new file mode 100644 index 0000000..fbb7bbb --- /dev/null +++ b/tests/pubkey.c.tests @@ -0,0 +1,1202 @@ +/* pubkey.c - Public key encryption/decryption tests + * Copyright (C) 2001, 2002, 2003, 2005 Free Software Foundation, Inc. + * + * This file is part of Libgcrypt. + * + * Libgcrypt is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * Libgcrypt is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, see . + */ + +#ifdef HAVE_CONFIG_H +#include +#endif +#include +#include +#include +#include + + +#define PGM "pubkey" +#include "t-common.h" + + +/* Sample RSA keys, taken from basic.c. */ + +static const char sample_private_key_1[] = +"(private-key\n" +" (openpgp-rsa\n" +" (n #00e0ce96f90b6c9e02f3922beada93fe50a875eac6bcc18bb9a9cf2e84965caa" + "2d1ff95a7f542465c6c0c19d276e4526ce048868a7a914fd343cc3a87dd74291" + "ffc565506d5bbb25cbac6a0e2dd1f8bcaab0d4a29c2f37c950f363484bf269f7" + "891440464baf79827e03a36e70b814938eebdc63e964247be75dc58b014b7ea251#)\n" +" (e #010001#)\n" +" (d #046129F2489D71579BE0A75FE029BD6CDB574EBF57EA8A5B0FDA942CAB943B11" + "7D7BB95E5D28875E0F9FC5FCC06A72F6D502464DABDED78EF6B716177B83D5BD" + "C543DC5D3FED932E59F5897E92E6F58A0F33424106A3B6FA2CBF877510E4AC21" + "C3EE47851E97D12996222AC3566D4CCB0B83D164074ABF7DE655FC2446DA1781#)\n" +" (p #00e861b700e17e8afe6837e7512e35b6ca11d0ae47d8b85161c67baf64377213" + "fe52d772f2035b3ca830af41d8a4120e1c1c70d12cc22f00d28d31dd48a8d424f1#)\n" +" (q #00f7a7ca5367c661f8e62df34f0d05c10c88e5492348dd7bddc942c9a8f369f9" + "35a07785d2db805215ed786e4285df1658eed3ce84f469b81b50d358407b4ad361#)\n" +" (u #304559a9ead56d2309d203811a641bb1a09626bc8eb36fffa23c968ec5bd891e" + "ebbafc73ae666e01ba7c8990bae06cc2bbe10b75e69fcacb353a6473079d8e9b#)\n" +" )\n" +")\n"; + +/* The same key as above but without p, q and u to test the non CRT case. */ +static const char sample_private_key_1_1[] = +"(private-key\n" +" (openpgp-rsa\n" +" (n #00e0ce96f90b6c9e02f3922beada93fe50a875eac6bcc18bb9a9cf2e84965caa" + "2d1ff95a7f542465c6c0c19d276e4526ce048868a7a914fd343cc3a87dd74291" + "ffc565506d5bbb25cbac6a0e2dd1f8bcaab0d4a29c2f37c950f363484bf269f7" + "891440464baf79827e03a36e70b814938eebdc63e964247be75dc58b014b7ea251#)\n" +" (e #010001#)\n" +" (d #046129F2489D71579BE0A75FE029BD6CDB574EBF57EA8A5B0FDA942CAB943B11" + "7D7BB95E5D28875E0F9FC5FCC06A72F6D502464DABDED78EF6B716177B83D5BD" + "C543DC5D3FED932E59F5897E92E6F58A0F33424106A3B6FA2CBF877510E4AC21" + "C3EE47851E97D12996222AC3566D4CCB0B83D164074ABF7DE655FC2446DA1781#)\n" +" )\n" +")\n"; + +/* The same key as above but just without q to test the non CRT case. This + should fail. */ +static const char sample_private_key_1_2[] = +"(private-key\n" +" (openpgp-rsa\n" +" (n #00e0ce96f90b6c9e02f3922beada93fe50a875eac6bcc18bb9a9cf2e84965caa" + "2d1ff95a7f542465c6c0c19d276e4526ce048868a7a914fd343cc3a87dd74291" + "ffc565506d5bbb25cbac6a0e2dd1f8bcaab0d4a29c2f37c950f363484bf269f7" + "891440464baf79827e03a36e70b814938eebdc63e964247be75dc58b014b7ea251#)\n" +" (e #010001#)\n" +" (d #046129F2489D71579BE0A75FE029BD6CDB574EBF57EA8A5B0FDA942CAB943B11" + "7D7BB95E5D28875E0F9FC5FCC06A72F6D502464DABDED78EF6B716177B83D5BD" + "C543DC5D3FED932E59F5897E92E6F58A0F33424106A3B6FA2CBF877510E4AC21" + "C3EE47851E97D12996222AC3566D4CCB0B83D164074ABF7DE655FC2446DA1781#)\n" +" (p #00e861b700e17e8afe6837e7512e35b6ca11d0ae47d8b85161c67baf64377213" + "fe52d772f2035b3ca830af41d8a4120e1c1c70d12cc22f00d28d31dd48a8d424f1#)\n" +" (u #304559a9ead56d2309d203811a641bb1a09626bc8eb36fffa23c968ec5bd891e" + "ebbafc73ae666e01ba7c8990bae06cc2bbe10b75e69fcacb353a6473079d8e9b#)\n" +" )\n" +")\n"; + +static const char sample_public_key_1[] = +"(public-key\n" +" (rsa\n" +" (n #00e0ce96f90b6c9e02f3922beada93fe50a875eac6bcc18bb9a9cf2e84965caa" + "2d1ff95a7f542465c6c0c19d276e4526ce048868a7a914fd343cc3a87dd74291" + "ffc565506d5bbb25cbac6a0e2dd1f8bcaab0d4a29c2f37c950f363484bf269f7" + "891440464baf79827e03a36e70b814938eebdc63e964247be75dc58b014b7ea251#)\n" +" (e #010001#)\n" +" )\n" +")\n"; + + +static void +show_sexp (const char *prefix, gcry_sexp_t a) +{ + char *buf; + size_t size; + + if (prefix) + fputs (prefix, stderr); + size = gcry_sexp_sprint (a, GCRYSEXP_FMT_ADVANCED, NULL, 0); + buf = gcry_xmalloc (size); + + gcry_sexp_sprint (a, GCRYSEXP_FMT_ADVANCED, buf, size); + fprintf (stderr, "%.*s", (int)size, buf); + gcry_free (buf); +} + +/* from ../cipher/pubkey-util.c */ +static gpg_err_code_t +_gcry_pk_util_get_nbits (gcry_sexp_t list, unsigned int *r_nbits) +{ + char buf[50]; + const char *s; + size_t n; + + *r_nbits = 0; + + list = gcry_sexp_find_token (list, "nbits", 0); + if (!list) + return 0; /* No NBITS found. */ + + s = gcry_sexp_nth_data (list, 1, &n); + if (!s || n >= DIM (buf) - 1 ) + { + /* NBITS given without a cdr. */ + gcry_sexp_release (list); + return GPG_ERR_INV_OBJ; + } + memcpy (buf, s, n); + buf[n] = 0; + *r_nbits = (unsigned int)strtoul (buf, NULL, 0); + gcry_sexp_release (list); + return 0; +} + +/* Convert STRING consisting of hex characters into its binary + representation and return it as an allocated buffer. The valid + length of the buffer is returned at R_LENGTH. The string is + delimited by end of string. The function returns NULL on + error. */ +static void * +data_from_hex (const char *string, size_t *r_length) +{ + const char *s; + unsigned char *buffer; + size_t length; + + buffer = gcry_xmalloc (strlen(string)/2+1); + length = 0; + for (s=string; *s; s +=2 ) + { + if (!hexdigitp (s) || !hexdigitp (s+1)) + die ("error parsing hex string `%s'\n", string); + ((unsigned char*)buffer)[length++] = xtoi_2 (s); + } + *r_length = length; + return buffer; +} + + +static void +extract_cmp_data (gcry_sexp_t sexp, const char *name, const char *expected) +{ + gcry_sexp_t l1; + const void *a; + size_t alen; + void *b; + size_t blen; + + l1 = gcry_sexp_find_token (sexp, name, 0); + a = gcry_sexp_nth_data (l1, 1, &alen); + b = data_from_hex (expected, &blen); + if (!a) + fail ("parameter \"%s\" missing in key\n", name); + else if ( alen != blen || memcmp (a, b, alen) ) + { + fail ("parameter \"%s\" does not match expected value\n", name); + if (verbose) + { + info ("expected: %s\n", expected); + show_sexp ("sexp: ", sexp); + } + } + gcry_free (b); + gcry_sexp_release (l1); +} + + +static void +check_keys_crypt (gcry_sexp_t pkey, gcry_sexp_t skey, + gcry_sexp_t plain0, gpg_err_code_t decrypt_fail_code) +{ + gcry_sexp_t plain1, cipher, l; + gcry_mpi_t x0, x1; + int rc; + int have_flags; + + /* Extract data from plaintext. */ + l = gcry_sexp_find_token (plain0, "value", 0); + x0 = gcry_sexp_nth_mpi (l, 1, GCRYMPI_FMT_USG); + gcry_sexp_release (l); + + /* Encrypt data. */ + rc = gcry_pk_encrypt (&cipher, plain0, pkey); + if (rc) + die ("encryption failed: %s\n", gcry_strerror (rc)); + + l = gcry_sexp_find_token (cipher, "flags", 0); + have_flags = !!l; + gcry_sexp_release (l); + + /* Decrypt data. */ + rc = gcry_pk_decrypt (&plain1, cipher, skey); + gcry_sexp_release (cipher); + if (rc) + { + if (decrypt_fail_code && gpg_err_code (rc) == decrypt_fail_code) + { + gcry_mpi_release (x0); + return; /* This is the expected failure code. */ + } + die ("decryption failed: %s\n", gcry_strerror (rc)); + } + + /* Extract decrypted data. Note that for compatibility reasons, the + output of gcry_pk_decrypt depends on whether a flags lists (even + if empty) occurs in its input data. Because we passed the output + of encrypt directly to decrypt, such a flag value won't be there + as of today. We check it anyway. */ + l = gcry_sexp_find_token (plain1, "value", 0); + if (l) + { + if (!have_flags) + die ("compatibility mode of pk_decrypt broken\n"); + gcry_sexp_release (plain1); + x1 = gcry_sexp_nth_mpi (l, 1, GCRYMPI_FMT_USG); + gcry_sexp_release (l); + } + else + { + if (have_flags) + die ("compatibility mode of pk_decrypt broken\n"); + x1 = gcry_sexp_nth_mpi (plain1, 0, GCRYMPI_FMT_USG); + gcry_sexp_release (plain1); + } + + /* Compare. */ + if (gcry_mpi_cmp (x0, x1)) + die ("data corrupted\n"); + gcry_mpi_release (x0); + gcry_mpi_release (x1); +} + +static void +check_keys (gcry_sexp_t pkey, gcry_sexp_t skey, unsigned int nbits_data, + gpg_err_code_t decrypt_fail_code) +{ + gcry_sexp_t plain; + gcry_mpi_t x; + int rc; + + /* Create plain text. */ + x = gcry_mpi_new (nbits_data); + gcry_mpi_randomize (x, nbits_data, GCRY_WEAK_RANDOM); + + rc = gcry_sexp_build (&plain, NULL, "(data (flags raw) (value %m))", x); + if (rc) + die ("converting data for encryption failed: %s\n", + gcry_strerror (rc)); + + check_keys_crypt (pkey, skey, plain, decrypt_fail_code); + gcry_sexp_release (plain); + gcry_mpi_release (x); + + /* Create plain text. */ + x = gcry_mpi_new (nbits_data); + gcry_mpi_randomize (x, nbits_data, GCRY_WEAK_RANDOM); + + rc = gcry_sexp_build (&plain, NULL, + "(data (flags raw no-blinding) (value %m))", x); + gcry_mpi_release (x); + if (rc) + die ("converting data for encryption failed: %s\n", + gcry_strerror (rc)); + + check_keys_crypt (pkey, skey, plain, decrypt_fail_code); + gcry_sexp_release (plain); +} + +static void +get_keys_sample (gcry_sexp_t *pkey, gcry_sexp_t *skey, int secret_variant) +{ + gcry_sexp_t pub_key, sec_key; + int rc; + static const char *secret; + + + switch (secret_variant) + { + case 0: secret = sample_private_key_1; break; + case 1: secret = sample_private_key_1_1; break; + case 2: secret = sample_private_key_1_2; break; + default: die ("BUG\n"); + } + + rc = gcry_sexp_sscan (&pub_key, NULL, sample_public_key_1, + strlen (sample_public_key_1)); + if (!rc) + rc = gcry_sexp_sscan (&sec_key, NULL, secret, strlen (secret)); + if (rc) + die ("converting sample keys failed: %s\n", gcry_strerror (rc)); + + *pkey = pub_key; + *skey = sec_key; +} + +static void +get_keys_new (gcry_sexp_t *pkey, gcry_sexp_t *skey) +{ + gcry_sexp_t key_spec, key, pub_key, sec_key; + int rc; + + rc = gcry_sexp_new (&key_spec, + "(genkey (rsa (nbits 4:2048)))", 0, 1); + if (rc) + die ("error creating S-expression: %s\n", gcry_strerror (rc)); + rc = gcry_pk_genkey (&key, key_spec); + gcry_sexp_release (key_spec); + if (rc) + die ("error generating RSA key: %s\n", gcry_strerror (rc)); + + if (verbose > 1) + show_sexp ("generated RSA key:\n", key); + + pub_key = gcry_sexp_find_token (key, "public-key", 0); + if (! pub_key) + die ("public part missing in key\n"); + + sec_key = gcry_sexp_find_token (key, "private-key", 0); + if (! sec_key) + die ("private part missing in key\n"); + + gcry_sexp_release (key); + *pkey = pub_key; + *skey = sec_key; +} + + +static void +get_keys_x931_new (gcry_sexp_t *pkey, gcry_sexp_t *skey) +{ + gcry_sexp_t key_spec, key, pub_key, sec_key; + int rc; + + rc = gcry_sexp_new (&key_spec, + "(genkey (rsa (nbits 4:2048)(use-x931)))", 0, 1); + if (rc) + die ("error creating S-expression: %s\n", gcry_strerror (rc)); + rc = gcry_pk_genkey (&key, key_spec); + gcry_sexp_release (key_spec); + if (rc) + die ("error generating RSA key: %s\n", gcry_strerror (rc)); + + if (verbose > 1) + show_sexp ("generated RSA (X9.31) key:\n", key); + + pub_key = gcry_sexp_find_token (key, "public-key", 0); + if (!pub_key) + die ("public part missing in key\n"); + + sec_key = gcry_sexp_find_token (key, "private-key", 0); + if (!sec_key) + die ("private part missing in key\n"); + + gcry_sexp_release (key); + *pkey = pub_key; + *skey = sec_key; +} + + +static void +get_elg_key_new (gcry_sexp_t *pkey, gcry_sexp_t *skey, int fixed_x) +{ + gcry_sexp_t key_spec, key, pub_key, sec_key; + int rc; + + rc = gcry_sexp_new + (&key_spec, + (fixed_x + ? "(genkey (elg (nbits 4:1024)(xvalue my.not-so-secret.key)))" + : "(genkey (elg (nbits 3:512)))"), + 0, 1); + + if (rc) + die ("error creating S-expression: %s\n", gcry_strerror (rc)); + rc = gcry_pk_genkey (&key, key_spec); + gcry_sexp_release (key_spec); + if (rc) + die ("error generating Elgamal key: %s\n", gcry_strerror (rc)); + + if (verbose > 1) + show_sexp ("generated ELG key:\n", key); + + pub_key = gcry_sexp_find_token (key, "public-key", 0); + if (!pub_key) + die ("public part missing in key\n"); + + sec_key = gcry_sexp_find_token (key, "private-key", 0); + if (!sec_key) + die ("private part missing in key\n"); + + gcry_sexp_release (key); + *pkey = pub_key; + *skey = sec_key; +} + + +static void +get_dsa_key_new (gcry_sexp_t *pkey, gcry_sexp_t *skey, int transient_key) +{ + gcry_sexp_t key_spec, key, pub_key, sec_key; + int rc; + + rc = gcry_sexp_new (&key_spec, + transient_key + ? "(genkey (dsa (nbits 4:2048)(transient-key)))" + : "(genkey (dsa (nbits 4:2048)))", + 0, 1); + if (rc) + die ("error creating S-expression: %s\n", gcry_strerror (rc)); + rc = gcry_pk_genkey (&key, key_spec); + gcry_sexp_release (key_spec); + if (rc) + die ("error generating DSA key: %s\n", gcry_strerror (rc)); + + if (verbose > 1) + show_sexp ("generated DSA key:\n", key); + + pub_key = gcry_sexp_find_token (key, "public-key", 0); + if (!pub_key) + die ("public part missing in key\n"); + + sec_key = gcry_sexp_find_token (key, "private-key", 0); + if (!sec_key) + die ("private part missing in key\n"); + + gcry_sexp_release (key); + *pkey = pub_key; + *skey = sec_key; +} + + +static void +get_dsa_key_fips186_new (gcry_sexp_t *pkey, gcry_sexp_t *skey) +{ + gcry_sexp_t key_spec, key, pub_key, sec_key; + int rc; + + rc = gcry_sexp_new + (&key_spec, "(genkey (dsa (nbits 4:2048)(use-fips186)))", 0, 1); + if (rc) + die ("error creating S-expression: %s\n", gcry_strerror (rc)); + rc = gcry_pk_genkey (&key, key_spec); + gcry_sexp_release (key_spec); + if (rc) + die ("error generating DSA key: %s\n", gcry_strerror (rc)); + + if (verbose > 1) + show_sexp ("generated DSA key (fips 186):\n", key); + + pub_key = gcry_sexp_find_token (key, "public-key", 0); + if (!pub_key) + die ("public part missing in key\n"); + + sec_key = gcry_sexp_find_token (key, "private-key", 0); + if (!sec_key) + die ("private part missing in key\n"); + + gcry_sexp_release (key); + *pkey = pub_key; + *skey = sec_key; +} + + +static void +get_dsa_key_with_domain_new (gcry_sexp_t *pkey, gcry_sexp_t *skey) +{ + gcry_sexp_t key_spec, key, pub_key, sec_key; + int rc; + + rc = gcry_sexp_new + (&key_spec, + "(genkey (dsa (transient-key)(domain" + "(p #d3aed1876054db831d0c1348fbb1ada72507e5fbf9a62cbd47a63aeb7859d6921" + "4adeb9146a6ec3f43520f0fd8e3125dd8bbc5d87405d1ac5f82073cd762a3f8d7" + "74322657c9da88a7d2f0e1a9ceb84a39cb40876179e6a76e400498de4bb9379b0" + "5f5feb7b91eb8fea97ee17a955a0a8a37587a272c4719d6feb6b54ba4ab69#)" + "(q #9c916d121de9a03f71fb21bc2e1c0d116f065a4f#)" + "(g #8157c5f68ca40b3ded11c353327ab9b8af3e186dd2e8dade98761a0996dda99ab" + "0250d3409063ad99efae48b10c6ab2bba3ea9a67b12b911a372a2bba260176fad" + "b4b93247d9712aad13aa70216c55da9858f7a298deb670a403eb1e7c91b847f1e" + "ccfbd14bd806fd42cf45dbb69cd6d6b43add2a78f7d16928eaa04458dea44#)" + ")))", 0, 1); + if (rc) + die ("error creating S-expression: %s\n", gcry_strerror (rc)); + rc = gcry_pk_genkey (&key, key_spec); + gcry_sexp_release (key_spec); + if (rc) + die ("error generating DSA key: %s\n", gcry_strerror (rc)); + + if (verbose > 1) + show_sexp ("generated DSA key:\n", key); + + pub_key = gcry_sexp_find_token (key, "public-key", 0); + if (!pub_key) + die ("public part missing in key\n"); + + sec_key = gcry_sexp_find_token (key, "private-key", 0); + if (!sec_key) + die ("private part missing in key\n"); + + gcry_sexp_release (key); + *pkey = pub_key; + *skey = sec_key; +} + +#if 0 +static void +get_dsa_key_fips186_with_domain_new (gcry_sexp_t *pkey, gcry_sexp_t *skey) +{ + gcry_sexp_t key_spec, key, pub_key, sec_key; + int rc; + + rc = gcry_sexp_new + (&key_spec, + "(genkey (dsa (transient-key)(use-fips186)(domain" + "(p #d3aed1876054db831d0c1348fbb1ada72507e5fbf9a62cbd47a63aeb7859d6921" + "4adeb9146a6ec3f43520f0fd8e3125dd8bbc5d87405d1ac5f82073cd762a3f8d7" + "74322657c9da88a7d2f0e1a9ceb84a39cb40876179e6a76e400498de4bb9379b0" + "5f5feb7b91eb8fea97ee17a955a0a8a37587a272c4719d6feb6b54ba4ab69#)" + "(q #9c916d121de9a03f71fb21bc2e1c0d116f065a4f#)" + "(g #8157c5f68ca40b3ded11c353327ab9b8af3e186dd2e8dade98761a0996dda99ab" + "0250d3409063ad99efae48b10c6ab2bba3ea9a67b12b911a372a2bba260176fad" + "b4b93247d9712aad13aa70216c55da9858f7a298deb670a403eb1e7c91b847f1e" + "ccfbd14bd806fd42cf45dbb69cd6d6b43add2a78f7d16928eaa04458dea44#)" + ")))", 0, 1); + if (rc) + die ("error creating S-expression: %s\n", gcry_strerror (rc)); + rc = gcry_pk_genkey (&key, key_spec); + gcry_sexp_release (key_spec); + if (rc) + die ("error generating DSA key: %s\n", gcry_strerror (rc)); + + if (verbose > 1) + show_sexp ("generated DSA key:\n", key); + + pub_key = gcry_sexp_find_token (key, "public-key", 0); + if (!pub_key) + die ("public part missing in key\n"); + + sec_key = gcry_sexp_find_token (key, "private-key", 0); + if (!sec_key) + die ("private part missing in key\n"); + + gcry_sexp_release (key); + *pkey = pub_key; + *skey = sec_key; +} +#endif /*0*/ + +static void +get_dsa_key_fips186_with_seed_new (gcry_sexp_t *pkey, gcry_sexp_t *skey) +{ + gcry_sexp_t key_spec, key, pub_key, sec_key; + int rc; + + rc = gcry_sexp_new + (&key_spec, + "(genkey" + " (dsa" + " (nbits 4:2048)" + " (use-fips186)" + " (transient-key)" + " (derive-parms" + " (seed #0cb1990c1fd3626055d7a0096f8fa99807399871#))))", + 0, 1); + if (rc) + die ("error creating S-expression: %s\n", gcry_strerror (rc)); + rc = gcry_pk_genkey (&key, key_spec); + gcry_sexp_release (key_spec); + if (rc) + die ("error generating DSA key: %s\n", gcry_strerror (rc)); + + if (verbose > 1) + show_sexp ("generated DSA key (fips 186 with seed):\n", key); + + pub_key = gcry_sexp_find_token (key, "public-key", 0); + if (!pub_key) + die ("public part missing in key\n"); + + sec_key = gcry_sexp_find_token (key, "private-key", 0); + if (!sec_key) + die ("private part missing in key\n"); + + gcry_sexp_release (key); + *pkey = pub_key; + *skey = sec_key; +} + + +static void +check_run (void) +{ + gpg_error_t err; + gcry_sexp_t pkey, skey; + int variant; + + for (variant=0; variant < 3; variant++) + { + if (verbose) + fprintf (stderr, "Checking sample key (%d).\n", variant); + get_keys_sample (&pkey, &skey, variant); + /* Check gcry_pk_testkey which requires all elements. */ + err = gcry_pk_testkey (skey); + if ((variant == 0 && err) + || (variant > 0 && gpg_err_code (err) != GPG_ERR_NO_OBJ)) + die ("gcry_pk_testkey failed: %s\n", gpg_strerror (err)); + /* Run the usual check but expect an error from variant 2. */ + check_keys (pkey, skey, 800, variant == 2? GPG_ERR_NO_OBJ : 0); + gcry_sexp_release (pkey); + gcry_sexp_release (skey); + } + + if (verbose) + fprintf (stderr, "Checking generated RSA key.\n"); + get_keys_new (&pkey, &skey); + check_keys (pkey, skey, 800, 0); + gcry_sexp_release (pkey); + gcry_sexp_release (skey); + + if (verbose) + fprintf (stderr, "Checking generated RSA key (X9.31).\n"); + get_keys_x931_new (&pkey, &skey); + check_keys (pkey, skey, 800, 0); + gcry_sexp_release (pkey); + gcry_sexp_release (skey); + + if (verbose) + fprintf (stderr, "Checking generated Elgamal key.\n"); + get_elg_key_new (&pkey, &skey, 0); + check_keys (pkey, skey, 400, 0); + gcry_sexp_release (pkey); + gcry_sexp_release (skey); + + if (verbose) + fprintf (stderr, "Checking passphrase generated Elgamal key.\n"); + get_elg_key_new (&pkey, &skey, 1); + check_keys (pkey, skey, 800, 0); + gcry_sexp_release (pkey); + gcry_sexp_release (skey); + + if (verbose) + fprintf (stderr, "Generating DSA key.\n"); + get_dsa_key_new (&pkey, &skey, 0); + /* Fixme: Add a check function for DSA keys. */ + gcry_sexp_release (pkey); + gcry_sexp_release (skey); + + if (!gcry_fips_mode_active ()) + { + if (verbose) + fprintf (stderr, "Generating transient DSA key.\n"); + get_dsa_key_new (&pkey, &skey, 1); + /* Fixme: Add a check function for DSA keys. */ + gcry_sexp_release (pkey); + gcry_sexp_release (skey); + } + + if (verbose) + fprintf (stderr, "Generating DSA key (FIPS 186).\n"); + get_dsa_key_fips186_new (&pkey, &skey); + /* Fixme: Add a check function for DSA keys. */ + gcry_sexp_release (pkey); + gcry_sexp_release (skey); + + if (verbose) + fprintf (stderr, "Generating DSA key with given domain.\n"); + get_dsa_key_with_domain_new (&pkey, &skey); + /* Fixme: Add a check function for DSA keys. */ + gcry_sexp_release (pkey); + gcry_sexp_release (skey); + + /* We need new test vectors for get_dsa_key_fips186_with_domain_new. */ + if (verbose) + fprintf (stderr, "Generating DSA key with given domain (FIPS 186)" + " - skipped.\n"); + /* get_dsa_key_fips186_with_domain_new (&pkey, &skey); */ + /* /\* Fixme: Add a check function for DSA keys. *\/ */ + /* gcry_sexp_release (pkey); */ + /* gcry_sexp_release (skey); */ + + if (verbose) + fprintf (stderr, "Generating DSA key with given seed (FIPS 186).\n"); + get_dsa_key_fips186_with_seed_new (&pkey, &skey); + /* Fixme: Add a check function for DSA keys. */ + gcry_sexp_release (pkey); + gcry_sexp_release (skey); +} + + + +static gcry_mpi_t +key_param_from_sexp (gcry_sexp_t sexp, const char *topname, const char *name) +{ + gcry_sexp_t l1, l2; + gcry_mpi_t result; + + l1 = gcry_sexp_find_token (sexp, topname, 0); + if (!l1) + return NULL; + + l2 = gcry_sexp_find_token (l1, name, 0); + if (!l2) + { + gcry_sexp_release (l1); + return NULL; + } + + result = gcry_sexp_nth_mpi (l2, 1, GCRYMPI_FMT_USG); + gcry_sexp_release (l2); + gcry_sexp_release (l1); + return result; +} + + +static void +check_x931_derived_key (int what) +{ + static struct { + const char *param; + const char *expected_d; + } testtable[] = { + { /* First example from X9.31 (D.1.1). */ + "(genkey\n" + " (rsa\n" + " (nbits 4:1024)\n" + " (rsa-use-e 1:3)\n" + " (derive-parms\n" + " (Xp1 #1A1916DDB29B4EB7EB6732E128#)\n" + " (Xp2 #192E8AAC41C576C822D93EA433#)\n" + " (Xp #D8CD81F035EC57EFE822955149D3BFF70C53520D\n" + " 769D6D76646C7A792E16EBD89FE6FC5B605A6493\n" + " 39DFC925A86A4C6D150B71B9EEA02D68885F5009\n" + " B98BD984#)\n" + " (Xq1 #1A5CF72EE770DE50CB09ACCEA9#)\n" + " (Xq2 #134E4CAA16D2350A21D775C404#)\n" + " (Xq #CC1092495D867E64065DEE3E7955F2EBC7D47A2D\n" + " 7C9953388F97DDDC3E1CA19C35CA659EDC2FC325\n" + " 6D29C2627479C086A699A49C4C9CEE7EF7BD1B34\n" + " 321DE34A#))))\n", + "1CCDA20BCFFB8D517EE9666866621B11822C7950D55F4BB5BEE37989A7D173" + "12E326718BE0D79546EAAE87A56623B919B1715FFBD7F16028FC4007741961" + "C88C5D7B4DAAAC8D36A98C9EFBB26C8A4A0E6BC15B358E528A1AC9D0F042BE" + "B93BCA16B541B33F80C933A3B769285C462ED5677BFE89DF07BED5C127FD13" + "241D3C4B" + }, + + { /* Second example from X9.31 (D.2.1). */ + "(genkey\n" + " (rsa\n" + " (nbits 4:1536)\n" + " (rsa-use-e 1:3)\n" + " (derive-parms\n" + " (Xp1 #18272558B61316348297EACA74#)\n" + " (Xp2 #1E970E8C6C97CEF91F05B0FA80#)\n" + " (Xp #F7E943C7EF2169E930DCF23FE389EF7507EE8265\n" + " 0D42F4A0D3A3CEFABE367999BB30EE680B2FE064\n" + " 60F707F46005F8AA7CBFCDDC4814BBE7F0F8BC09\n" + " 318C8E51A48D134296E40D0BBDD282DCCBDDEE1D\n" + " EC86F0B1C96EAFF5CDA70F9AEB6EE31E#)\n" + " (Xq1 #11FDDA6E8128DC1629F75192BA#)\n" + " (Xq2 #18AB178ECA907D72472F65E480#)\n" + " (Xq #C47560011412D6E13E3E7D007B5C05DBF5FF0D0F\n" + " CFF1FA2070D16C7ABA93EDFB35D8700567E5913D\n" + " B734E3FBD15862EBC59FA0425DFA131E549136E8\n" + " E52397A8ABE4705EC4877D4F82C4AAC651B33DA6\n" + " EA14B9D5F2A263DC65626E4D6CEAC767#))))\n", + "1FB56069985F18C4519694FB71055721A01F14422DC901C35B03A64D4A5BD1" + "259D573305F5B056AC931B82EDB084E39A0FD1D1A86CC5B147A264F7EF4EB2" + "0ED1E7FAAE5CAE4C30D5328B7F74C3CAA72C88B70DED8EDE207B8629DA2383" + "B78C3CE1CA3F9F218D78C938B35763AF2A8714664CC57F5CECE2413841F5E9" + "EDEC43B728E25A41BF3E1EF8D9EEE163286C9F8BF0F219D3B322C3E4B0389C" + "2E8BB28DC04C47DA2BF38823731266D2CF6CC3FC181738157624EF051874D0" + "BBCCB9F65C83" + /* Note that this example in X9.31 gives this value for D: + + "7ED581A6617C6311465A53EDC4155C86807C5108B724070D6C0E9935296F44" + "96755CCC17D6C15AB24C6E0BB6C2138E683F4746A1B316C51E8993DFBD3AC8" + "3B479FEAB972B930C354CA2DFDD30F2A9CB222DC37B63B7881EE18A7688E0E" + "DE30F38728FE7C8635E324E2CD5D8EBCAA1C51993315FD73B38904E107D7A7" + "B7B10EDCA3896906FCF87BE367BB858CA1B27E2FC3C8674ECC8B0F92C0E270" + "BA2ECA3701311F68AFCE208DCC499B4B3DB30FF0605CE055D893BC1461D342" + "EF32E7D9720B" + + This is a bug in X9.31, obviously introduced by using + + d = e^{-1} mod (p-1)(q-1) + + instead of using the universal exponent as required by 4.1.3: + + d = e^{-1} mod lcm(p-1,q-1) + + The examples in X9.31 seem to be pretty buggy, see + cipher/primegen.c for another bug. Not only that I had to + spend 100 USD for the 66 pages of the document, it also took + me several hours to figure out that the bugs are in the + document and not in my code. + */ + }, + + { /* First example from NIST RSAVS (B.1.1). */ + "(genkey\n" + " (rsa\n" + " (nbits 4:1024)\n" + " (rsa-use-e 1:3)\n" + " (derive-parms\n" + " (Xp1 #1ed3d6368e101dab9124c92ac8#)\n" + " (Xp2 #16e5457b8844967ce83cab8c11#)\n" + " (Xp #b79f2c2493b4b76f329903d7555b7f5f06aaa5ea\n" + " ab262da1dcda8194720672a4e02229a0c71f60ae\n" + " c4f0d2ed8d49ef583ca7d5eeea907c10801c302a\n" + " cab44595#)\n" + " (Xq1 #1a5d9e3fa34fb479bedea412f6#)\n" + " (Xq2 #1f9cca85f185341516d92e82fd#)\n" + " (Xq #c8387fd38fa33ddcea6a9de1b2d55410663502db\n" + " c225655a9310cceac9f4cf1bce653ec916d45788\n" + " f8113c46bc0fa42bf5e8d0c41120c1612e2ea8bb\n" + " 2f389eda#))))\n", + "17ef7ad4fd96011b62d76dfb2261b4b3270ca8e07bc501be954f8719ef586b" + "f237e8f693dd16c23e7adecc40279dc6877c62ab541df5849883a5254fccfd" + "4072a657b7f4663953930346febd6bbd82f9a499038402cbf97fd5f068083a" + "c81ad0335c4aab0da19cfebe060a1bac7482738efafea078e21df785e56ea0" + "dc7e8feb" + }, + + { /* Second example from NIST RSAVS (B.1.1). */ + "(genkey\n" + " (rsa\n" + " (nbits 4:1536)\n" + " (rsa-use-e 1:3)\n" + " (derive-parms\n" + " (Xp1 #1e64c1af460dff8842c22b64d0#)\n" + " (Xp2 #1e948edcedba84039c81f2ac0c#)\n" + " (Xp #c8c67df894c882045ede26a9008ab09ea0672077\n" + " d7bc71d412511cd93981ddde8f91b967da404056\n" + " c39f105f7f239abdaff92923859920f6299e82b9\n" + " 5bd5b8c959948f4a034d81613d6235a3953b49ce\n" + " 26974eb7bb1f14843841281b363b9cdb#)\n" + " (Xq1 #1f3df0f017ddd05611a97b6adb#)\n" + " (Xq2 #143edd7b22d828913abf24ca4d#)\n" + " (Xq #f15147d0e7c04a1e3f37adde802cdc610999bf7a\n" + " b0088434aaeda0c0ab3910b14d2ce56cb66bffd9\n" + " 7552195fae8b061077e03920814d8b9cfb5a3958\n" + " b3a82c2a7fc97e55db543948d3396289245336ec\n" + " 9e3cb308cc655aebd766340da8921383#))))\n", + "1f8b19f3f5f2ac9fc599f110cad403dcd9bdf5f7f00fb2790e78e820398184" + "1f3fb3dd230fb223d898f45719d9b2d3525587ff2b8bcc7425e40550a5b536" + "1c8e9c1d26e83fbd9c33c64029c0e878b829d55def12912b73d94fd758c461" + "0f473e230c41b5e4c86e27c5a5029d82c811c88525d0269b95bd2ff272994a" + "dbd80f2c2ecf69065feb8abd8b445b9c6d306b1585d7d3d7576d49842bc7e2" + "8b4a2f88f4a47e71c3edd35fdf83f547ea5c2b532975c551ed5268f748b2c4" + "2ccf8a84835b" + } + }; + gpg_error_t err; + gcry_sexp_t key_spec = NULL, key = NULL, pub_key = NULL, sec_key = NULL; + gcry_mpi_t d_expected = NULL, d_have = NULL; + + if (what < 0 && what >= sizeof testtable) + die ("invalid WHAT value\n"); + + err = gcry_sexp_new (&key_spec, testtable[what].param, 0, 1); + if (err) + die ("error creating S-expression [%d]: %s\n", what, gpg_strerror (err)); + + { + unsigned nbits; + err = _gcry_pk_util_get_nbits(key_spec, &nbits); + if (err) + die ("nbits not found\n"); + if (gcry_fips_mode_active() && nbits < 2048) + { + info("RSA key test with %d bits skipped in fips mode\n", nbits); + goto leave; + } + } + + err = gcry_pk_genkey (&key, key_spec); + gcry_sexp_release (key_spec); + if (err) + { + fail ("error generating RSA key [%d]: %s\n", what, gpg_strerror (err)); + goto leave; + } + + pub_key = gcry_sexp_find_token (key, "public-key", 0); + if (!pub_key) + die ("public part missing in key [%d]\n", what); + + sec_key = gcry_sexp_find_token (key, "private-key", 0); + if (!sec_key) + die ("private part missing in key [%d]\n", what); + + err = gcry_mpi_scan + (&d_expected, GCRYMPI_FMT_HEX, testtable[what].expected_d, 0, NULL); + if (err) + die ("error converting string [%d]\n", what); + + if (verbose > 1) + show_sexp ("generated key:\n", key); + + d_have = key_param_from_sexp (sec_key, "rsa", "d"); + if (!d_have) + die ("parameter d not found in RSA secret key [%d]\n", what); + if (gcry_mpi_cmp (d_expected, d_have)) + { + show_sexp (NULL, sec_key); + die ("parameter d does match expected value [%d]\n", what); + } +leave: + gcry_mpi_release (d_expected); + gcry_mpi_release (d_have); + + gcry_sexp_release (key); + gcry_sexp_release (pub_key); + gcry_sexp_release (sec_key); +} + + + +static void +check_ecc_sample_key (void) +{ + static const char ecc_private_key[] = + "(private-key\n" + " (ecdsa\n" + " (curve \"NIST P-256\")\n" + " (q #04D4F6A6738D9B8D3A7075C1E4EE95015FC0C9B7E4272D2BEB6644D3609FC781" + "B71F9A8072F58CB66AE2F89BB12451873ABF7D91F9E1FBF96BF2F70E73AAC9A283#)\n" + " (d #5A1EF0035118F19F3110FB81813D3547BCE1E5BCE77D1F744715E1D5BBE70378#)" + "))"; + static const char ecc_private_key_wo_q[] = + "(private-key\n" + " (ecdsa\n" + " (curve \"NIST P-256\")\n" + " (d #5A1EF0035118F19F3110FB81813D3547BCE1E5BCE77D1F744715E1D5BBE70378#)" + "))"; + static const char ecc_public_key[] = + "(public-key\n" + " (ecdsa\n" + " (curve \"NIST P-256\")\n" + " (q #04D4F6A6738D9B8D3A7075C1E4EE95015FC0C9B7E4272D2BEB6644D3609FC781" + "B71F9A8072F58CB66AE2F89BB12451873ABF7D91F9E1FBF96BF2F70E73AAC9A283#)" + "))"; + static const char hash_string[] = + "(data (flags raw)\n" + " (value #00112233445566778899AABBCCDDEEFF" + /* */ "000102030405060708090A0B0C0D0E0F#))"; + static const char hash2_string[] = + "(data (flags raw)\n" + " (hash sha1 #00112233445566778899AABBCCDDEEFF" + /* */ "000102030405060708090A0B0C0D0E0F" + /* */ "000102030405060708090A0B0C0D0E0F" + /* */ "00112233445566778899AABBCCDDEEFF#))"; + /* hash2, but longer than curve length, so it will be truncated */ + static const char hash3_string[] = + "(data (flags raw)\n" + " (hash sha1 #00112233445566778899AABBCCDDEEFF" + /* */ "000102030405060708090A0B0C0D0E0F" + /* */ "000102030405060708090A0B0C0D0E0F" + /* */ "00112233445566778899AABBCCDDEEFF" + /* */ "000102030405060708090A0B0C0D0E0F#))"; + + gpg_error_t err; + gcry_sexp_t key, hash, hash2, hash3, sig, sig2; + + if (verbose) + fprintf (stderr, "Checking sample ECC key.\n"); + + if ((err = gcry_sexp_new (&hash, hash_string, 0, 1))) + die ("line %d: %s", __LINE__, gpg_strerror (err)); + + if ((err = gcry_sexp_new (&hash2, hash2_string, 0, 1))) + die ("line %d: %s", __LINE__, gpg_strerror (err)); + + if ((err = gcry_sexp_new (&hash3, hash3_string, 0, 1))) + die ("line %d: %s", __LINE__, gpg_strerror (err)); + + if ((err = gcry_sexp_new (&key, ecc_private_key, 0, 1))) + die ("line %d: %s", __LINE__, gpg_strerror (err)); + + if ((err = gcry_pk_sign (&sig, hash, key))) + die ("gcry_pk_sign failed: %s", gpg_strerror (err)); + + gcry_sexp_release (key); + if ((err = gcry_sexp_new (&key, ecc_public_key, 0, 1))) + die ("line %d: %s", __LINE__, gpg_strerror (err)); + + if ((err = gcry_pk_verify (sig, hash, key))) + die ("gcry_pk_verify failed: %s", gpg_strerror (err)); + + /* Verify hash truncation */ + gcry_sexp_release (key); + if ((err = gcry_sexp_new (&key, ecc_private_key, 0, 1))) + die ("line %d: %s", __LINE__, gpg_strerror (err)); + + if ((err = gcry_pk_sign (&sig2, hash2, key))) + die ("gcry_pk_sign failed: %s", gpg_strerror (err)); + + gcry_sexp_release (sig); + if ((err = gcry_pk_sign (&sig, hash3, key))) + die ("gcry_pk_sign failed: %s", gpg_strerror (err)); + + gcry_sexp_release (key); + if ((err = gcry_sexp_new (&key, ecc_public_key, 0, 1))) + die ("line %d: %s", __LINE__, gpg_strerror (err)); + + if ((err = gcry_pk_verify (sig, hash2, key))) + die ("gcry_pk_verify failed: %s", gpg_strerror (err)); + + if ((err = gcry_pk_verify (sig2, hash3, key))) + die ("gcry_pk_verify failed: %s", gpg_strerror (err)); + + /* Now try signing without the Q parameter. */ + + gcry_sexp_release (key); + if ((err = gcry_sexp_new (&key, ecc_private_key_wo_q, 0, 1))) + die ("line %d: %s", __LINE__, gpg_strerror (err)); + + gcry_sexp_release (sig); + if ((err = gcry_pk_sign (&sig, hash, key))) + die ("gcry_pk_sign without Q failed: %s", gpg_strerror (err)); + + gcry_sexp_release (key); + if ((err = gcry_sexp_new (&key, ecc_public_key, 0, 1))) + die ("line %d: %s", __LINE__, gpg_strerror (err)); + + if ((err = gcry_pk_verify (sig, hash, key))) + die ("gcry_pk_verify signed without Q failed: %s", gpg_strerror (err)); + + gcry_sexp_release (sig); + gcry_sexp_release (sig2); + gcry_sexp_release (key); + gcry_sexp_release (hash); + gcry_sexp_release (hash2); + gcry_sexp_release (hash3); +} + + +static void +check_ed25519ecdsa_sample_key (void) +{ + static const char ecc_private_key[] = + "(private-key\n" + " (ecc\n" + " (curve \"Ed25519\")\n" + " (q #044C056555BE4084BB3D8D8895FDF7C2893DFE0256251923053010977D12658321" + " 156D1ADDC07987713A418783658B476358D48D582DB53233D9DED3C1C2577B04#)" + " (d #09A0C38E0F1699073541447C19DA12E3A07A7BFDB0C186E4AC5BCE6F23D55252#)" + "))"; + static const char ecc_private_key_wo_q[] = + "(private-key\n" + " (ecc\n" + " (curve \"Ed25519\")\n" + " (d #09A0C38E0F1699073541447C19DA12E3A07A7BFDB0C186E4AC5BCE6F23D55252#)" + "))"; + static const char ecc_public_key[] = + "(public-key\n" + " (ecc\n" + " (curve \"Ed25519\")\n" + " (q #044C056555BE4084BB3D8D8895FDF7C2893DFE0256251923053010977D12658321" + " 156D1ADDC07987713A418783658B476358D48D582DB53233D9DED3C1C2577B04#)" + "))"; + static const char ecc_public_key_comp[] = + "(public-key\n" + " (ecc\n" + " (curve \"Ed25519\")\n" + " (q #047b57c2c1d3ded93332b52d588dd45863478b658387413a718779c0dd1a6d95#)" + "))"; + static const char hash_string[] = + "(data (flags rfc6979)\n" + " (hash sha256 #00112233445566778899AABBCCDDEEFF" + /* */ "000102030405060708090A0B0C0D0E0F#))"; + + gpg_error_t err; + gcry_sexp_t key, hash, sig; + + if (verbose) + fprintf (stderr, "Checking sample Ed25519/ECDSA key.\n"); + + /* Sign. */ + if ((err = gcry_sexp_new (&hash, hash_string, 0, 1))) + die ("line %d: %s", __LINE__, gpg_strerror (err)); + if ((err = gcry_sexp_new (&key, ecc_private_key, 0, 1))) + die ("line %d: %s", __LINE__, gpg_strerror (err)); + if ((err = gcry_pk_sign (&sig, hash, key))) + die ("gcry_pk_sign failed: %s", gpg_strerror (err)); + + /* Verify. */ + gcry_sexp_release (key); + if ((err = gcry_sexp_new (&key, ecc_public_key, 0, 1))) + die ("line %d: %s", __LINE__, gpg_strerror (err)); + if ((err = gcry_pk_verify (sig, hash, key))) + die ("gcry_pk_verify failed: %s", gpg_strerror (err)); + + /* Verify again using a compressed public key. */ + gcry_sexp_release (key); + if ((err = gcry_sexp_new (&key, ecc_public_key_comp, 0, 1))) + die ("line %d: %s", __LINE__, gpg_strerror (err)); + if ((err = gcry_pk_verify (sig, hash, key))) + die ("gcry_pk_verify failed (comp): %s", gpg_strerror (err)); + + /* Sign without a Q parameter. */ + gcry_sexp_release (key); + if ((err = gcry_sexp_new (&key, ecc_private_key_wo_q, 0, 1))) + die ("line %d: %s", __LINE__, gpg_strerror (err)); + gcry_sexp_release (sig); + if ((err = gcry_pk_sign (&sig, hash, key))) + die ("gcry_pk_sign w/o Q failed: %s", gpg_strerror (err)); + + /* Verify. */ + gcry_sexp_release (key); + if ((err = gcry_sexp_new (&key, ecc_public_key, 0, 1))) + die ("line %d: %s", __LINE__, gpg_strerror (err)); + if ((err = gcry_pk_verify (sig, hash, key))) + die ("gcry_pk_verify signed w/o Q failed: %s", gpg_strerror (err)); + + /* Verify again using a compressed public key. */ + gcry_sexp_release (key); + if ((err = gcry_sexp_new (&key, ecc_public_key_comp, 0, 1))) + die ("line %d: %s", __LINE__, gpg_strerror (err)); + if ((err = gcry_pk_verify (sig, hash, key))) + die ("gcry_pk_verify signed w/o Q failed (comp): %s", gpg_strerror (err)); + + extract_cmp_data (sig, "r", ("a63123a783ef29b8276e08987daca4" + "655d0179e22199bf63691fd88eb64e15")); + extract_cmp_data (sig, "s", ("0d9b45c696ab90b96b08812b485df185" + "623ddaf5d02fa65ca5056cb6bd0f16f1")); + + gcry_sexp_release (sig); + gcry_sexp_release (key); + gcry_sexp_release (hash); +} + + +int +main (int argc, char **argv) +{ + int i; + + if (argc > 1 && !strcmp (argv[1], "--verbose")) + verbose = 1; + else if (argc > 1 && !strcmp (argv[1], "--debug")) + { + verbose = 2; + debug = 1; + } + + xgcry_control (GCRYCTL_DISABLE_SECMEM, 0); + if (!gcry_check_version (GCRYPT_VERSION)) + die ("version mismatch\n"); + xgcry_control (GCRYCTL_INITIALIZATION_FINISHED, 0); + if (debug) + xgcry_control (GCRYCTL_SET_DEBUG_FLAGS, 1u , 0); + /* No valuable keys are create, so we can speed up our RNG. */ + xgcry_control (GCRYCTL_ENABLE_QUICK_RANDOM, 0); + + for (i=0; i < 2; i++) + check_run (); + + for (i=0; i < 4; i++) + check_x931_derived_key (i); + + check_ecc_sample_key (); + if (!gcry_fips_mode_active ()) + check_ed25519ecdsa_sample_key (); + + return !!error_count; +}