From e62829a907a7179ec6b0d9f47258185860f0a6c0 Mon Sep 17 00:00:00 2001 From: Jakub Jelen Date: Tue, 2 Aug 2022 20:53:31 +0200 Subject: [PATCH 1/6] Run digest&sign self tests for RSA and ECC in FIPS mode * cipher/ecc.c (selftest_hash_sign): Implement digest & sign KAT (selftests_ecdsa): Run the original basic test only with extended tests (run_selftests): Pass-through the extended argument * cipher/rsa.c (selftest_hash_sign_2048): Implement digest & sign KAT (selftests_rsa): Run the original basic test only with extended tests (run_selftests): Pass-through the extended argument --- Signed-off-by: Jakub Jelen --- cipher/ecc.c | 138 ++++++++++++++++++++++++++++++++++++++++++++++++--- cipher/rsa.c | 108 +++++++++++++++++++++++++++++++++++++--- 2 files changed, 234 insertions(+), 12 deletions(-) diff --git a/cipher/ecc.c b/cipher/ecc.c index 9f0e7b11..63b0d05e 100644 --- a/cipher/ecc.c +++ b/cipher/ecc.c @@ -1678,6 +1678,126 @@ _gcry_pk_ecc_get_sexp (gcry_sexp_t *r_sexp, int mode, mpi_ec_t ec) Self-test section. */ +static const char * +selftest_hash_sign (gcry_sexp_t pkey, gcry_sexp_t skey) +{ + int md_algo = GCRY_MD_SHA256; + gcry_md_hd_t hd = NULL; + const char *data_tmpl = "(data (flags rfc6979) (hash %s %b))"; + /* Sample data from RFC 6979 section A.2.5, hash is of message "sample" */ + static const char sample_data[] = "sample"; + static const char sample_data_bad[] = "sbmple"; + static const char signature_r[] = + "efd48b2aacb6a8fd1140dd9cd45e81d69d2c877b56aaf991c34d0ea84eaf3716"; + static const char signature_s[] = + "f7cb1c942d657c41d436c7a1b6e29f65f3e900dbb9aff4064dc4ab2f843acda8"; + + const char *errtxt = NULL; + gcry_error_t err; + 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 = _gcry_md_open (&hd, md_algo, 0); + if (err) + { + errtxt = "gcry_md_open failed"; + goto leave; + } + + _gcry_md_write (hd, sample_data, strlen(sample_data)); + + 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_md (&sig, data_tmpl, hd, skey, NULL); + 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, "ecdsa", 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; + + /* verify generated signature */ + err = _gcry_pk_verify_md (sig, data_tmpl, hd, pkey, NULL); + if (err) + { + errtxt = "verify failed"; + goto leave; + } + + _gcry_md_reset(hd); + _gcry_md_write (hd, sample_data_bad, strlen(sample_data_bad)); + err = _gcry_pk_verify_md (sig, data_tmpl, hd, pkey, NULL); + if (gcry_err_code (err) != GPG_ERR_BAD_SIGNATURE) + { + errtxt = "bad signature not detected"; + goto leave; + } + + + leave: + _gcry_md_close (hd); + sexp_release (sig); + sexp_release (l1); + sexp_release (l2); + mpi_release (r); + mpi_release (s); + mpi_release (calculated_r); + mpi_release (calculated_s); + return errtxt; +} + + static const char * selftest_sign (gcry_sexp_t pkey, gcry_sexp_t skey) { @@ -1798,7 +1918,7 @@ selftest_sign (gcry_sexp_t pkey, gcry_sexp_t skey) static gpg_err_code_t -selftests_ecdsa (selftest_report_func_t report) +selftests_ecdsa (selftest_report_func_t report, int extended) { const char *what; const char *errtxt; @@ -1826,8 +1946,16 @@ selftests_ecdsa (selftest_report_func_t report) goto failed; } - what = "sign"; - errtxt = selftest_sign (pkey, skey); + if (extended) + { + what = "sign"; + errtxt = selftest_sign (pkey, skey); + if (errtxt) + goto failed; + } + + what = "digest sign"; + errtxt = selftest_hash_sign (pkey, skey); if (errtxt) goto failed; @@ -1848,12 +1976,10 @@ selftests_ecdsa (selftest_report_func_t report) static gpg_err_code_t run_selftests (int algo, int extended, selftest_report_func_t report) { - (void)extended; - if (algo != GCRY_PK_ECC) return GPG_ERR_PUBKEY_ALGO; - return selftests_ecdsa (report); + return selftests_ecdsa (report, extended); } diff --git a/cipher/rsa.c b/cipher/rsa.c index 9f2b36e8..34c8e490 100644 --- a/cipher/rsa.c +++ b/cipher/rsa.c @@ -1760,6 +1760,96 @@ compute_keygrip (gcry_md_hd_t md, gcry_sexp_t keyparam) Self-test section. */ +static const char * +selftest_hash_sign_2048 (gcry_sexp_t pkey, gcry_sexp_t skey) +{ + int md_algo = GCRY_MD_SHA256; + gcry_md_hd_t hd = NULL; + const char *data_tmpl = "(data (flags pkcs1) (hash %s %b))"; + static const char sample_data[] = + "11223344556677889900aabbccddeeff" + "102030405060708090a0b0c0d0f01121"; + static const char sample_data_bad[] = + "11223344556677889900aabbccddeeff" + "802030405060708090a0b0c0d0f01121"; + + const char *errtxt = NULL; + gcry_error_t err; + gcry_sexp_t sig = NULL; + /* raw signature data reference */ + const char ref_data[] = + "518f41dea3ad884e93eefff8d7ca68a6f4c30d923632e35673651d675cebd652" + "a44ed66f6879b18f3d48b2d235b1dd78f6189be1440352cc94231a55c1f93109" + "84616b2841c42fe9a6e37be34cd188207209bd028e2fa93e721fbac40c31a068" + "1253b312d4e07addb9c7f3d508fa89f218ea7c7f7b9f6a9b1e522c19fa1cd839" + "93f9d4ca2f16c3d0b9abafe5e63e848152afc72ce7ee19ea45353116f85209ea" + "b9de42129dbccdac8faa461e8e8cc2ae801101cc6add4ba76ccb752030b0e827" + "7352b11cdecebae9cdc9a626c4701cd9c85cd287618888c5fae8b4d0ba48915d" + "e5cc64e3aee2ba2862d04348ea71f65454f74f9fd1e3108005cc367ca41585a4"; + gcry_mpi_t ref_mpi = NULL; + gcry_mpi_t sig_mpi = NULL; + + err = _gcry_md_open (&hd, md_algo, 0); + if (err) + { + errtxt = "gcry_md_open failed"; + goto leave; + } + + _gcry_md_write (hd, sample_data, sizeof(sample_data)); + + err = _gcry_pk_sign_md (&sig, data_tmpl, hd, skey, NULL); + 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_md (sig, data_tmpl, hd, pkey, NULL); + if (err) + { + errtxt = "verify failed"; + goto leave; + } + + _gcry_md_reset(hd); + _gcry_md_write (hd, sample_data_bad, sizeof(sample_data_bad)); + err = _gcry_pk_verify_md (sig, data_tmpl, hd, pkey, NULL); + if (gcry_err_code (err) != GPG_ERR_BAD_SIGNATURE) + { + errtxt = "bad signature not detected"; + goto leave; + } + + + leave: + sexp_release (sig); + _gcry_md_close (hd); + _gcry_mpi_release (ref_mpi); + _gcry_mpi_release (sig_mpi); + return errtxt; +} + static const char * selftest_sign_2048 (gcry_sexp_t pkey, gcry_sexp_t skey) { @@ -1996,7 +2086,7 @@ selftest_encr_2048 (gcry_sexp_t pkey, gcry_sexp_t skey) static gpg_err_code_t -selftests_rsa (selftest_report_func_t report) +selftests_rsa (selftest_report_func_t report, int extended) { const char *what; const char *errtxt; @@ -2024,8 +2114,16 @@ selftests_rsa (selftest_report_func_t report) goto failed; } - what = "sign"; - errtxt = selftest_sign_2048 (pkey, skey); + if (extended) + { + what = "sign"; + errtxt = selftest_sign_2048 (pkey, skey); + if (errtxt) + goto failed; + } + + what = "digest sign"; + errtxt = selftest_hash_sign_2048 (pkey, skey); if (errtxt) goto failed; @@ -2053,12 +2151,10 @@ 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); + ec = selftests_rsa (report, extended); break; default: ec = GPG_ERR_PUBKEY_ALGO; -- 2.37.1 From b386e9862e7c3c0f6623fb1c43b0cf0481bbebc7 Mon Sep 17 00:00:00 2001 From: Jakub Jelen Date: Mon, 8 Aug 2022 13:50:15 +0200 Subject: [PATCH 2/6] fips: Add function-name based FIPS indicator * doc/gcrypt.texi: Document the new function-based fips indicator GCRYCTL_FIPS_SERVICE_INDICATOR_FUNCTION * src/fips.c (_gcry_fips_indicator_function): New function indicating non-approved functions. * src/gcrypt.h.in (enum gcry_ctl_cmds): New symbol GCRYCTL_FIPS_SERVICE_INDICATOR_FUNCTION * src/global.c (_gcry_vcontrol): Handle new FIPS indicator. --- Signed-off-by: Jakub Jelen --- doc/gcrypt.texi | 7 +++++++ src/fips.c | 12 ++++++++++++ src/g10lib.h | 1 + src/gcrypt.h.in | 3 ++- src/global.c | 7 +++++++ 5 files changed, 29 insertions(+), 1 deletion(-) diff --git a/doc/gcrypt.texi b/doc/gcrypt.texi index f2c1cc94..b608dba2 100644 --- a/doc/gcrypt.texi +++ b/doc/gcrypt.texi @@ -995,6 +995,13 @@ certification. If the KDF is approved, this function returns @code{GPG_ERR_NO_ERROR}. Otherwise @code{GPG_ERR_NOT_SUPPORTED} is returned. +@item GCRYCTL_FIPS_SERVICE_INDICATOR_FUNCTION; Arguments: const char * + +Check if the given function is approved under the current FIPS 140-3 +certification. If the function is approved, this function returns +@code{GPG_ERR_NO_ERROR} (other restrictions might still apply). +Otherwise @code{GPG_ERR_NOT_SUPPORTED} is returned. + @end table @end deftypefun diff --git a/src/fips.c b/src/fips.c index a1958b14..9a524ea4 100644 --- a/src/fips.c +++ b/src/fips.c @@ -390,6 +390,18 @@ _gcry_fips_indicator_kdf (va_list arg_ptr) } } +int +_gcry_fips_indicator_function (va_list arg_ptr) +{ + const char *function = va_arg (arg_ptr, const char *); + + if (strcmp (function, "gcry_sign") == 0 || + strcmp (function, "gcry_verify") == 0) + return GPG_ERR_NOT_SUPPORTED; + + return GPG_ERR_NO_ERROR; +} + /* This is a test on whether the library is in the error or operational state. */ diff --git a/src/g10lib.h b/src/g10lib.h index 8ba0a5c2..eff6295f 100644 --- a/src/g10lib.h +++ b/src/g10lib.h @@ -468,6 +468,7 @@ void _gcry_fips_signal_error (const char *srcfile, int _gcry_fips_indicator_cipher (va_list arg_ptr); int _gcry_fips_indicator_kdf (va_list arg_ptr); +int _gcry_fips_indicator_function (va_list arg_ptr); int _gcry_fips_is_operational (void); diff --git a/src/gcrypt.h.in b/src/gcrypt.h.in index 299261db..d6a1d516 100644 --- a/src/gcrypt.h.in +++ b/src/gcrypt.h.in @@ -329,7 +329,8 @@ enum gcry_ctl_cmds GCRYCTL_SET_DECRYPTION_TAG = 80, GCRYCTL_FIPS_SERVICE_INDICATOR_CIPHER = 81, GCRYCTL_FIPS_SERVICE_INDICATOR_KDF = 82, - GCRYCTL_NO_FIPS_MODE = 83 + GCRYCTL_NO_FIPS_MODE = 83, + GCRYCTL_FIPS_SERVICE_INDICATOR_FUNCTION = 84 }; /* Perform various operations defined by CMD. */ diff --git a/src/global.c b/src/global.c index 258ea4d1..debf6194 100644 --- a/src/global.c +++ b/src/global.c @@ -797,6 +797,13 @@ _gcry_vcontrol (enum gcry_ctl_cmds cmd, va_list arg_ptr) rc = _gcry_fips_indicator_kdf (arg_ptr); break; + case GCRYCTL_FIPS_SERVICE_INDICATOR_FUNCTION: + /* Get FIPS Service Indicator for a given function from the API. + * Returns GPG_ERR_NO_ERROR if the function is allowed or + * GPG_ERR_NOT_SUPPORTED otherwise */ + rc = _gcry_fips_indicator_function (arg_ptr); + break; + case PRIV_CTL_INIT_EXTRNG_TEST: /* Init external random test. */ rc = GPG_ERR_NOT_SUPPORTED; break; -- 2.37.1 From 756cdfaf30c2b12d2b2a931591089b1de22444f4 Mon Sep 17 00:00:00 2001 From: Jakub Jelen Date: Mon, 8 Aug 2022 15:58:16 +0200 Subject: [PATCH 4/6] rsa: Run PCT in FIPS mode also with digest step Signed-off-by: Jakub Jelen --- cipher/rsa.c | 69 +++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 68 insertions(+), 1 deletion(-) diff --git a/cipher/rsa.c b/cipher/rsa.c index 6e7be8e8..78c26f2f 100644 --- a/cipher/rsa.c +++ b/cipher/rsa.c @@ -177,6 +177,73 @@ test_keys (RSA_secret_key *sk, unsigned int nbits) return result; } +static int +test_keys_fips (RSA_secret_key *sk) +{ + int result = -1; /* Default to failure. */ + char plaintext[128]; + gcry_sexp_t sig = NULL; + gcry_sexp_t skey = NULL, pkey = NULL; + const char *data_tmpl = "(data (flags pkcs1) (hash %s %b))"; + gcry_md_hd_t hd = NULL; + int ec; + + /* Put the relevant parameters into a public key structure. */ + ec = sexp_build (&pkey, NULL, + "(key-data" + " (public-key" + " (rsa(n%m)(e%m))))", + sk->n, sk->e); + if (ec) + goto leave; + + /* Put the relevant parameters into a secret key structure. */ + ec = sexp_build (&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))))", + sk->n, sk->e, + sk->n, sk->e, sk->d, sk->p, sk->q, sk->u); + if (ec) + goto leave; + + /* Create a random plaintext. */ + _gcry_randomize (plaintext, sizeof plaintext, GCRY_WEAK_RANDOM); + + /* Open MD context and feed the random data in */ + ec = _gcry_md_open (&hd, GCRY_MD_SHA256, 0); + if (ec) + goto leave; + _gcry_md_write (hd, plaintext, sizeof(plaintext)); + + /* Use the RSA secret function to create a signature of the plaintext. */ + ec = _gcry_pk_sign_md (&sig, data_tmpl, hd, skey, NULL); + if (ec) + goto leave; + + /* Use the RSA public function to verify this signature. */ + ec = _gcry_pk_verify_md (sig, data_tmpl, hd, pkey, NULL); + if (ec) + goto leave; + + /* Modify the data and check that the signing fails. */ + _gcry_md_reset(hd); + plaintext[sizeof plaintext / 2] ^= 1; + _gcry_md_write (hd, plaintext, sizeof(plaintext)); + ec = _gcry_pk_verify_md (sig, data_tmpl, hd, pkey, NULL); + if (ec != GPG_ERR_BAD_SIGNATURE) + goto leave; /* Signature verification worked on modified data */ + + result = 0; /* All tests succeeded. */ + leave: + sexp_release (sig); + _gcry_md_close (hd); + sexp_release (pkey); + sexp_release (skey); + return result; +} /* Callback used by the prime generation to test whether the exponent is suitable. Returns 0 if the test has been passed. */ @@ -648,7 +715,7 @@ generate_fips (RSA_secret_key *sk, unsigned int nbits, unsigned long use_e, sk->u = u; /* Now we can test our keys. */ - if (ec || (!testparms && test_keys (sk, nbits - 64))) + if (ec || (!testparms && test_keys_fips (sk))) { _gcry_mpi_release (sk->n); sk->n = NULL; _gcry_mpi_release (sk->e); sk->e = NULL; -- 2.37.1 From de7ad6375be11a0cef45c6e9aa8119c4ce7a3258 Mon Sep 17 00:00:00 2001 From: Jakub Jelen Date: Mon, 15 Aug 2022 19:55:33 +0200 Subject: [PATCH 5/6] ecc: Run PCT also with the digest step * cipher/ecc.c (test_keys_fips): New function (nist_generate_key): In FIPS mode, execute new PCT test --- Signed-off-by: Jakub Jelen --- cipher/ecc.c | 81 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 81 insertions(+) diff --git a/cipher/ecc.c b/cipher/ecc.c index 63b0d05e..783e249d 100644 --- a/cipher/ecc.c +++ b/cipher/ecc.c @@ -101,6 +101,7 @@ static void *progress_cb_data; /* Local prototypes. */ static void test_keys (mpi_ec_t ec, unsigned int nbits); +static void test_keys_fips (mpi_ec_t ec, gcry_mpi_t x, gcry_mpi_t y); static void test_ecdh_only_keys (mpi_ec_t ec, unsigned int nbits, int flags); static unsigned int ecc_get_nbits (gcry_sexp_t parms); @@ -255,6 +256,8 @@ nist_generate_key (mpi_ec_t ec, int flags, ; /* User requested to skip the test. */ else if (ec->model == MPI_EC_MONTGOMERY) test_ecdh_only_keys (ec, ec->nbits - 63, flags); + else if (fips_mode ()) + test_keys_fips (ec, x, y); else test_keys (ec, ec->nbits - 64); @@ -304,6 +307,84 @@ test_keys (mpi_ec_t ec, unsigned int nbits) mpi_free (test); } +/* We should get here only with the NIST curves as they are the only ones + * having the fips bit set in ecc_domain_parms_t struct so this is slightly + * simpler than the whole ecc_generate function */ +static void +test_keys_fips (mpi_ec_t ec, gcry_mpi_t Qx, gcry_mpi_t Qy) +{ + gcry_md_hd_t hd = NULL; + const char *data_tmpl = "(data (flags rfc6979) (hash %s %b))"; + gcry_sexp_t skey = NULL, pkey = NULL; + gcry_sexp_t curve_info = NULL; + gcry_sexp_t sig = NULL; + gcry_mpi_t public = NULL; + char plaintext[128]; + int rc; + + /* Build keys structures */ + if (ec->name) + { + rc = sexp_build (&curve_info, NULL, "(curve %s)", ec->name); + if (rc) + log_fatal ("ECDSA operation: failed to build curve_info\n"); + } + + public = _gcry_ecc_ec2os (Qx, Qy, ec->p); + rc = sexp_build (&pkey, NULL, + "(key-data" + " (public-key" + " (ecc%S(q%m)))" + " )", + curve_info, + public); + if (rc) + log_fatal ("ECDSA operation: failed to build public key: %s\n", gpg_strerror (rc)); + rc = sexp_build (&skey, NULL, + "(key-data" + " (private-key" + " (ecc%S(q%m)(d%m)))" + " )", + curve_info, + public, ec->d); + if (rc) + log_fatal ("ECDSA operation: failed to build private key: %s\n", gpg_strerror (rc)); + + /* Create a random plaintext. */ + _gcry_randomize (plaintext, sizeof plaintext, GCRY_WEAK_RANDOM); + + /* Open MD context and feed the random data in */ + rc = _gcry_md_open (&hd, GCRY_MD_SHA256, 0); + if (rc) + log_fatal ("ECDSA operation: failed to initialize MD context: %s\n", gpg_strerror (rc)); + _gcry_md_write (hd, plaintext, sizeof(plaintext)); + + /* Sign the data */ + rc = _gcry_pk_sign_md (&sig, data_tmpl, hd, skey, NULL); + if (rc) + log_fatal ("ECDSA operation: signing failed: %s\n", gpg_strerror (rc)); + + /* Verify this signature. */ + rc = _gcry_pk_verify_md (sig, data_tmpl, hd, pkey, NULL); + if (rc) + log_fatal ("ECDSA operation: verification failed: %s\n", gpg_strerror (rc)); + + /* Modify the data and check that the signing fails. */ + _gcry_md_reset(hd); + plaintext[sizeof plaintext / 2] ^= 1; + _gcry_md_write (hd, plaintext, sizeof(plaintext)); + rc = _gcry_pk_verify_md (sig, data_tmpl, hd, pkey, NULL); + if (rc != GPG_ERR_BAD_SIGNATURE) + log_fatal ("ECDSA operation: signature verification worked on modified data\n"); + + mpi_free (public); + sexp_release (curve_info); + _gcry_md_close (hd); + sexp_release (pkey); + sexp_release (skey); + sexp_release (sig); +} + static void test_ecdh_only_keys (mpi_ec_t ec, unsigned int nbits, int flags) -- 2.37.1 From 2cca95e488d58ae79975dd867e7782504b155212 Mon Sep 17 00:00:00 2001 From: Jakub Jelen Date: Tue, 16 Aug 2022 10:27:46 +0200 Subject: [PATCH 6/6] Simplify the PCT for RSA and ECDSA Could be squashed. * cipher/ecc.c (test_keys_fips): Simplify to accept key in SEXP format (nist_generate_key): Skip call to test keys (ecc_generate): Call test keys in FIPS mode later, when we have complete SEXP key structure. * cipher/rsa.c (test_keys_fips): Simplify to accept key in SEXP format (generate_fips): Skip selftest at this stage (rsa_generate): Test the keys later when we already have key in SEXP format --- Signed-off-by: Jakub Jelen --- cipher/ecc.c | 50 ++++++++------------------------------------------ cipher/rsa.c | 47 ++++++++++++----------------------------------- 2 files changed, 20 insertions(+), 77 deletions(-) diff --git a/cipher/ecc.c b/cipher/ecc.c index 783e249d..1e80200e 100644 --- a/cipher/ecc.c +++ b/cipher/ecc.c @@ -101,7 +101,7 @@ static void *progress_cb_data; /* Local prototypes. */ static void test_keys (mpi_ec_t ec, unsigned int nbits); -static void test_keys_fips (mpi_ec_t ec, gcry_mpi_t x, gcry_mpi_t y); +static void test_keys_fips (gcry_sexp_t skey); static void test_ecdh_only_keys (mpi_ec_t ec, unsigned int nbits, int flags); static unsigned int ecc_get_nbits (gcry_sexp_t parms); @@ -256,9 +256,7 @@ nist_generate_key (mpi_ec_t ec, int flags, ; /* User requested to skip the test. */ else if (ec->model == MPI_EC_MONTGOMERY) test_ecdh_only_keys (ec, ec->nbits - 63, flags); - else if (fips_mode ()) - test_keys_fips (ec, x, y); - else + else if (!fips_mode ()) test_keys (ec, ec->nbits - 64); return 0; @@ -311,45 +309,14 @@ test_keys (mpi_ec_t ec, unsigned int nbits) * having the fips bit set in ecc_domain_parms_t struct so this is slightly * simpler than the whole ecc_generate function */ static void -test_keys_fips (mpi_ec_t ec, gcry_mpi_t Qx, gcry_mpi_t Qy) +test_keys_fips (gcry_sexp_t skey) { gcry_md_hd_t hd = NULL; const char *data_tmpl = "(data (flags rfc6979) (hash %s %b))"; - gcry_sexp_t skey = NULL, pkey = NULL; - gcry_sexp_t curve_info = NULL; gcry_sexp_t sig = NULL; - gcry_mpi_t public = NULL; char plaintext[128]; int rc; - /* Build keys structures */ - if (ec->name) - { - rc = sexp_build (&curve_info, NULL, "(curve %s)", ec->name); - if (rc) - log_fatal ("ECDSA operation: failed to build curve_info\n"); - } - - public = _gcry_ecc_ec2os (Qx, Qy, ec->p); - rc = sexp_build (&pkey, NULL, - "(key-data" - " (public-key" - " (ecc%S(q%m)))" - " )", - curve_info, - public); - if (rc) - log_fatal ("ECDSA operation: failed to build public key: %s\n", gpg_strerror (rc)); - rc = sexp_build (&skey, NULL, - "(key-data" - " (private-key" - " (ecc%S(q%m)(d%m)))" - " )", - curve_info, - public, ec->d); - if (rc) - log_fatal ("ECDSA operation: failed to build private key: %s\n", gpg_strerror (rc)); - /* Create a random plaintext. */ _gcry_randomize (plaintext, sizeof plaintext, GCRY_WEAK_RANDOM); @@ -365,7 +332,7 @@ test_keys_fips (mpi_ec_t ec, gcry_mpi_t Qx, gcry_mpi_t Qy) log_fatal ("ECDSA operation: signing failed: %s\n", gpg_strerror (rc)); /* Verify this signature. */ - rc = _gcry_pk_verify_md (sig, data_tmpl, hd, pkey, NULL); + rc = _gcry_pk_verify_md (sig, data_tmpl, hd, skey, NULL); if (rc) log_fatal ("ECDSA operation: verification failed: %s\n", gpg_strerror (rc)); @@ -373,15 +340,11 @@ test_keys_fips (mpi_ec_t ec, gcry_mpi_t Qx, gcry_mpi_t Qy) _gcry_md_reset(hd); plaintext[sizeof plaintext / 2] ^= 1; _gcry_md_write (hd, plaintext, sizeof(plaintext)); - rc = _gcry_pk_verify_md (sig, data_tmpl, hd, pkey, NULL); + rc = _gcry_pk_verify_md (sig, data_tmpl, hd, skey, NULL); if (rc != GPG_ERR_BAD_SIGNATURE) log_fatal ("ECDSA operation: signature verification worked on modified data\n"); - mpi_free (public); - sexp_release (curve_info); _gcry_md_close (hd); - sexp_release (pkey); - sexp_release (skey); sexp_release (sig); } @@ -714,6 +677,9 @@ ecc_generate (const gcry_sexp_t genparms, gcry_sexp_t *r_skey) log_debug ("ecgen result using Ed25519+EdDSA\n"); } + if (!(flags & PUBKEY_FLAG_NO_KEYTEST) && fips_mode ()) + test_keys_fips (*r_skey); + leave: mpi_free (public); mpi_free (base); diff --git a/cipher/rsa.c b/cipher/rsa.c index 78c26f2f..9d14a474 100644 --- a/cipher/rsa.c +++ b/cipher/rsa.c @@ -178,37 +178,15 @@ test_keys (RSA_secret_key *sk, unsigned int nbits) } static int -test_keys_fips (RSA_secret_key *sk) +test_keys_fips (gcry_sexp_t skey) { int result = -1; /* Default to failure. */ char plaintext[128]; gcry_sexp_t sig = NULL; - gcry_sexp_t skey = NULL, pkey = NULL; const char *data_tmpl = "(data (flags pkcs1) (hash %s %b))"; gcry_md_hd_t hd = NULL; int ec; - /* Put the relevant parameters into a public key structure. */ - ec = sexp_build (&pkey, NULL, - "(key-data" - " (public-key" - " (rsa(n%m)(e%m))))", - sk->n, sk->e); - if (ec) - goto leave; - - /* Put the relevant parameters into a secret key structure. */ - ec = sexp_build (&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))))", - sk->n, sk->e, - sk->n, sk->e, sk->d, sk->p, sk->q, sk->u); - if (ec) - goto leave; - /* Create a random plaintext. */ _gcry_randomize (plaintext, sizeof plaintext, GCRY_WEAK_RANDOM); @@ -224,7 +202,7 @@ test_keys_fips (RSA_secret_key *sk) goto leave; /* Use the RSA public function to verify this signature. */ - ec = _gcry_pk_verify_md (sig, data_tmpl, hd, pkey, NULL); + ec = _gcry_pk_verify_md (sig, data_tmpl, hd, skey, NULL); if (ec) goto leave; @@ -232,7 +210,7 @@ test_keys_fips (RSA_secret_key *sk) _gcry_md_reset(hd); plaintext[sizeof plaintext / 2] ^= 1; _gcry_md_write (hd, plaintext, sizeof(plaintext)); - ec = _gcry_pk_verify_md (sig, data_tmpl, hd, pkey, NULL); + ec = _gcry_pk_verify_md (sig, data_tmpl, hd, skey, NULL); if (ec != GPG_ERR_BAD_SIGNATURE) goto leave; /* Signature verification worked on modified data */ @@ -240,8 +218,6 @@ test_keys_fips (RSA_secret_key *sk) leave: sexp_release (sig); _gcry_md_close (hd); - sexp_release (pkey); - sexp_release (skey); return result; } @@ -714,8 +690,7 @@ generate_fips (RSA_secret_key *sk, unsigned int nbits, unsigned long use_e, sk->d = d; sk->u = u; - /* Now we can test our keys. */ - if (ec || (!testparms && test_keys_fips (sk))) + if (ec) { _gcry_mpi_release (sk->n); sk->n = NULL; _gcry_mpi_release (sk->e); sk->e = NULL; @@ -723,11 +698,6 @@ generate_fips (RSA_secret_key *sk, unsigned int nbits, unsigned long use_e, _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; @@ -1306,7 +1276,7 @@ rsa_generate (const gcry_sexp_t genparms, gcry_sexp_t *r_skey) /**/ : NULL); /* Generate. */ - if (deriveparms || fips_mode()) + if (deriveparms || fips_mode ()) { ec = generate_fips (&sk, nbits, evalue, deriveparms, !!(flags & PUBKEY_FLAG_TRANSIENT_KEY)); @@ -1341,6 +1311,13 @@ rsa_generate (const gcry_sexp_t genparms, gcry_sexp_t *r_skey) mpi_free (sk.u); sexp_release (swap_info); + if (!ec && fips_mode () && test_keys_fips (*r_skey)) + { + sexp_release (*r_skey); *r_skey = NULL; + fips_signal_error ("self-test after key generation failed"); + return GPG_ERR_SELFTEST_FAILED; + } + return ec; } -- 2.37.1 -- ACVP testing uses the test-parms option to specify p and q to be checked for primality. When test-parms is specified, generate_fips() always returns keys with p=q=0. These keys then fail the pairwise consistency test, because they cannot be used to successfully sign a message and verify the signature. Skip the PCT when test-parms is specified. Add a regression test to check that this functionality continues to work in the future. Signed-off-by: Clemens Lang --- cipher/rsa.c | 5 +- tests/Makefile.am | 2 +- tests/t-rsa-testparm.c | 130 +++++++++++++++++++++++++++++++++++++++++ 3 files changed, 135 insertions(+), 2 deletions(-) create mode 100644 tests/t-rsa-testparm.c diff --git a/cipher/rsa.c b/cipher/rsa.c index 87f57b55..1a935d80 100644 --- a/cipher/rsa.c +++ b/cipher/rsa.c @@ -1218,6 +1218,7 @@ rsa_generate (const gcry_sexp_t genparms, gcry_sexp_t *r_skey) int flags = 0; gcry_sexp_t l1; gcry_sexp_t swap_info = NULL; + int testparms = 0; memset (&sk, 0, sizeof sk); @@ -1274,6 +1275,8 @@ rsa_generate (const gcry_sexp_t genparms, gcry_sexp_t *r_skey) } deriveparms = (genparms? sexp_find_token (genparms, "test-parms", 0) /**/ : NULL); + if (deriveparms) + testparms = 1; /* Generate. */ if (deriveparms || fips_mode ()) @@ -1311,7 +1314,7 @@ rsa_generate (const gcry_sexp_t genparms, gcry_sexp_t *r_skey) mpi_free (sk.u); sexp_release (swap_info); - if (!ec && fips_mode () && test_keys_fips (*r_skey)) + if (!ec && !testparms && fips_mode () && test_keys_fips (*r_skey)) { sexp_release (*r_skey); *r_skey = NULL; fips_signal_error ("self-test after key generation failed"); diff --git a/tests/Makefile.am b/tests/Makefile.am index f65725bc..302d923b 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -28,7 +28,7 @@ tests_bin = \ t-mpi-bit t-mpi-point curves t-lock \ prime basic keygen pubkey hmac hashtest t-kdf keygrip \ fips186-dsa aeswrap pkcs1v2 random dsa-rfc6979 \ - t-dsa t-ecdsa t-rsa-pss t-rsa-15 \ + t-dsa t-ecdsa t-rsa-pss t-rsa-15 t-rsa-testparm \ t-ed25519 t-cv25519 t-x448 t-ed448 tests_bin_last = benchmark bench-slope diff --git a/tests/t-rsa-testparm.c b/tests/t-rsa-testparm.c new file mode 100644 index 00000000..65617855 --- /dev/null +++ b/tests/t-rsa-testparm.c @@ -0,0 +1,130 @@ +/* t-rsa-testparm.c - Check the RSA Key Generation test-parm parameter + * Copyright (C) 2022 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 "stopwatch.h" + +#define PGM "t-rsa-testparm" +#include "t-common.h" + + +static void +check_rsa_testparm () +{ + gpg_error_t err; + gcry_sexp_t keyspec = NULL; + gcry_sexp_t key = NULL; + const char *sexp = "(genkey (rsa (nbits \"2048\") (test-parms " + "(e \"65537\")" + "(p #00bbccabcee15d343944a47e492d4b1f4de79633e20cbb46f7d2d6813392a807ad048" + "cf77528edd19f77e7453f25173b9dcb70423afa2037aae147b81a33d541fc58f875ef" + "f1e852ab55e2e09a3debfbc151b3b0d17fef6f74d81fca14fbae531418e211ef81859" + "2af70de5cec3b92795cc3578572bf456099cd8727150e523261#)" + "(q #00ca87ecf2883f4ed00a9ec65abdeba81d28edbfcc34ecc563d587f166b52d42bfbe2" + "2bbc095b0b8426a2f8bbc55baaa8859b42cbc376ed3067db3ef7b135b63481322911e" + "bbd7014db83aa051e0ca2dbf302b75cd37f2ae8df90e134226e92f6353a284b28bb30" + "af0bbf925b345b955328379866ebac11d55bc80fe84f105d415#)" + ")))"; + + info ("Checking RSA KeyGen test-parm parameter.\n"); + + err = gcry_sexp_build (&keyspec, NULL, sexp); + if (err) + { + fail ("error building SEXP for test: %s", gpg_strerror (err)); + goto leave; + } + + err = gcry_pk_genkey (&key, keyspec); + if (err) + { + fail ("gcry_pk_genkey failed for test: %s", gpg_strerror (err)); + goto leave; + } + +leave: + if (key) + gcry_sexp_release (key); + if (keyspec) + gcry_sexp_release (keyspec); +} + + +int +main (int argc, char **argv) +{ + int last_argc = -1; + + if (argc) + { argc--; argv++; } + + while (argc && last_argc != argc ) + { + last_argc = argc; + if (!strcmp (*argv, "--")) + { + argc--; argv++; + break; + } + else if (!strcmp (*argv, "--help")) + { + fputs ("usage: " PGM " [options]\n" + "Options:\n" + " --verbose print timings etc.\n" + " --debug flyswatter\n", + stdout); + exit (0); + } + else if (!strcmp (*argv, "--verbose")) + { + verbose++; + argc--; argv++; + } + else if (!strcmp (*argv, "--debug")) + { + verbose += 2; + debug++; + argc--; argv++; + } + else if (!strncmp (*argv, "--", 2)) + die ("unknown option '%s'", *argv); + + } + + xgcry_control ((GCRYCTL_DISABLE_SECMEM, 0)); + if (!gcry_check_version (GCRYPT_VERSION)) + die ("version mismatch\n"); + if (debug) + xgcry_control ((GCRYCTL_SET_DEBUG_FLAGS, 0xffffffff, 0)); + + start_timer (); + check_rsa_testparm (); + stop_timer (); + + info ("All tests completed in %s. Errors: %d\n", + elapsed_time (1), error_count); + return !!error_count; +} -- 2.37.3