libgcrypt/libgcrypt-1.10.0-fips-selftest.patch

1200 lines
36 KiB
Diff
Raw Normal View History

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
From 149f6f8654fdeaf7aa1ff8ac3d00d7454c0e6eff Mon Sep 17 00:00:00 2001
From: Jakub Jelen <jjelen@redhat.com>
Date: Wed, 5 Oct 2022 16:50:08 +0200
Subject: [PATCH] fips: Mark gcry_pk_encrypt/decrypt function non-approved
* src/fips.c (_gcry_fips_indicator_function): Fix typo in sign/verify
function names and add gcry_pk_encrypt and gcry_pk_decrypt.
--
Signed-off-by: Jakub Jelen <jjelen@redhat.com>
---
src/fips.c | 6 ++++--
1 file changed, 4 insertions(+), 2 deletions(-)
diff --git a/src/fips.c b/src/fips.c
index 9a524ea4..6599121c 100644
--- a/src/fips.c
+++ b/src/fips.c
@@ -395,8 +395,10 @@ _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)
+ if (strcmp (function, "gcry_pk_sign") == 0 ||
+ strcmp (function, "gcry_pk_verify") == 0 ||
+ strcmp (function, "gcry_pk_encrypt") == 0 ||
+ strcmp (function, "gcry_pk_decrypt") == 0)
return GPG_ERR_NOT_SUPPORTED;
return GPG_ERR_NO_ERROR;
--
2.37.3
From f91a0ab12d242815f74bf26c6076e9cf7a790023 Mon Sep 17 00:00:00 2001
From: Jakub Jelen <jjelen@redhat.com>
Date: Thu, 6 Oct 2022 09:30:24 +0200
Subject: [PATCH] cipher: Do not run RSA encryption selftest by default
* cipher/rsa.c (selftests_rsa): Skip encryption selftest as this
operation is not claimed as part of the certification.
---
Signed-off-by: Jakub Jelen <jjelen@redhat.com>
---
cipher/rsa.c | 11 +++++++----
1 file changed, 7 insertions(+), 4 deletions(-)
diff --git a/cipher/rsa.c b/cipher/rsa.c
index 56dde3d1..df4af94b 100644
--- a/cipher/rsa.c
+++ b/cipher/rsa.c
@@ -2169,10 +2169,13 @@ selftests_rsa (selftest_report_func_t report, int extended)
if (errtxt)
goto failed;
- what = "encrypt";
- errtxt = selftest_encr_2048 (pkey, skey);
- if (errtxt)
- goto failed;
+ if (extended)
+ {
+ what = "encrypt";
+ errtxt = selftest_encr_2048 (pkey, skey);
+ if (errtxt)
+ goto failed;
+ }
sexp_release (pkey);
sexp_release (skey);
--
2.37.3