libgcrypt/libgcrypt-1.10.0-fips-selftest.patch
Jakub Jelen cbe7d48792 Fix FIPS RSA PCT
Resolves: rhbz#2128455
2022-09-26 13:49:58 +02:00

1127 lines
34 KiB
Diff
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

From e62829a907a7179ec6b0d9f47258185860f0a6c0 Mon Sep 17 00:00:00 2001
From: Jakub Jelen <jjelen@redhat.com>
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 <jjelen@redhat.com>
---
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 <jjelen@redhat.com>
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 <jjelen@redhat.com>
---
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 <jjelen@redhat.com>
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 <jjelen@redhat.com>
---
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 <jjelen@redhat.com>
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 <jjelen@redhat.com>
---
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 <jjelen@redhat.com>
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 <jjelen@redhat.com>
---
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 <cllang at redhat.com>
---
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 <https://www.gnu.org/licenses/>.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <gcrypt.h>
+
+#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