From fbce6ebf706cdd273f2569edfea7ade106426e0b Mon Sep 17 00:00:00 2001 From: pohsingwu Date: Sun, 28 Jan 2024 18:18:02 +0800 Subject: [PATCH] Implement PCT for EDDSA According to FIPS 140-3 IG 10.3.A Additonal Comment 1, a PCT shall be performed consistent with the intended use of the keys. This commit implements PCT for EDDSA via performing sign and verify operations after key generated. Also use the same pairwise test logic in EVP_PKEY_keygen and EVP_PKEY_pairwise_check for EDDSA in FIPS_MODULE. Add OSSL_SELF_TEST_DESC_PCT_EDDSA to OSSL_PROVIDER-FIPS page. Reviewed-by: Paul Dale Reviewed-by: Tomas Mraz (Merged from https://github.com/openssl/openssl/pull/23408) --- doc/man7/OSSL_PROVIDER-FIPS.pod | 2 + include/openssl/self_test.h | 1 + providers/implementations/keymgmt/ecx_kmgmt.c | 142 +++++++++++++++++- test/pairwise_fail_test.c | 11 ++ test/recipes/30-test_pairwise_fail.t | 16 +- 5 files changed, 163 insertions(+), 9 deletions(-) diff --git a/doc/man7/OSSL_PROVIDER-FIPS.pod b/doc/man7/OSSL_PROVIDER-FIPS.pod index a8ea27cd88..15bc06a710 100644 --- a/doc/man7/OSSL_PROVIDER-FIPS.pod +++ b/doc/man7/OSSL_PROVIDER-FIPS.pod @@ -337,6 +337,8 @@ The FIPS module passes the following descriptions(s) to OSSL_SELF_TEST_onbegin() =item "ECDSA" (B) +=item "EDDSA" (B) + =item "DSA" (B) Key generation tests used with the "Pairwise_Consistency_Test" type. diff --git a/include/openssl/self_test.h b/include/openssl/self_test.h index 337a3190ce..d7de90262c 100644 --- a/include/openssl/self_test.h +++ b/include/openssl/self_test.h @@ -46,6 +46,7 @@ extern "C" { # define OSSL_SELF_TEST_DESC_INTEGRITY_HMAC "HMAC" # define OSSL_SELF_TEST_DESC_PCT_RSA_PKCS1 "RSA" # define OSSL_SELF_TEST_DESC_PCT_ECDSA "ECDSA" +# define OSSL_SELF_TEST_DESC_PCT_EDDSA "EDDSA" # define OSSL_SELF_TEST_DESC_PCT_DSA "DSA" # define OSSL_SELF_TEST_DESC_CIPHER_AES_GCM "AES_GCM" # define OSSL_SELF_TEST_DESC_CIPHER_AES_ECB "AES_ECB_Decrypt" diff --git a/providers/implementations/keymgmt/ecx_kmgmt.c b/providers/implementations/keymgmt/ecx_kmgmt.c index 8a9fe1b21b..ae11fd4bc0 100644 --- a/providers/implementations/keymgmt/ecx_kmgmt.c +++ b/providers/implementations/keymgmt/ecx_kmgmt.c @@ -1,5 +1,5 @@ /* - * Copyright 2020-2023 The OpenSSL Project Authors. All Rights Reserved. + * Copyright 2020-2024 The OpenSSL Project Authors. All Rights Reserved. * * Licensed under the Apache License 2.0 (the "License"). You may not use * this file except in compliance with the License. You can obtain a copy @@ -16,6 +16,7 @@ #include #include #include +#include #include "internal/param_build_set.h" #include #include "crypto/ecx.h" @@ -588,6 +589,74 @@ static const OSSL_PARAM *ecx_gen_settable_params(ossl_unused void *genctx, return settable; } +#ifdef FIPS_MODULE +/* + * Refer: FIPS 140-3 IG 10.3.A Additional Comment 1 + * Perform a pairwise test for EDDSA by signing and verifying signature. + * + * The parameter `self_test` is used to indicate whether to create OSSL_SELF_TEST + * instance. + */ +static int ecd_fips140_pairwise_test(const ECX_KEY *ecx, int type, int self_test) +{ + int ret = 0; + OSSL_SELF_TEST *st = NULL; + OSSL_CALLBACK *cb = NULL; + void *cbarg = NULL; + + unsigned char msg[16] = {0}; + size_t msg_len = sizeof(msg); + unsigned char sig[ED448_SIGSIZE] = {0}; + + int is_ed25519 = (type == ECX_KEY_TYPE_ED25519) ? 1 : 0; + int operation_result = 0; + + /* + * The functions `OSSL_SELF_TEST_*` will return directly if parameter `st` + * is NULL. + */ + if (self_test) { + OSSL_SELF_TEST_get_callback(ecx->libctx, &cb, &cbarg); + + st = OSSL_SELF_TEST_new(cb, cbarg); + if (st == NULL) + return 0; + } + + OSSL_SELF_TEST_onbegin(st, OSSL_SELF_TEST_TYPE_PCT, + OSSL_SELF_TEST_DESC_PCT_EDDSA); + + if (is_ed25519) + operation_result = ossl_ed25519_sign(sig, msg, msg_len, ecx->pubkey, + ecx->privkey, 0, 0, 0, NULL, 0, + ecx->libctx, ecx->propq); + else + operation_result = ossl_ed448_sign(ecx->libctx, sig, msg, msg_len, + ecx->pubkey, ecx->privkey, NULL, 0, + 0, ecx->propq); + if (operation_result != 1) + goto err; + + OSSL_SELF_TEST_oncorrupt_byte(st, sig); + + if (is_ed25519) + operation_result = ossl_ed25519_verify(msg, msg_len, sig, ecx->pubkey, + 0, 0, 0, NULL, 0, ecx->libctx, + ecx->propq); + else + operation_result = ossl_ed448_verify(ecx->libctx, msg, msg_len, sig, + ecx->pubkey, NULL, 0, 0, ecx->propq); + if (operation_result != 1) + goto err; + + ret = 1; +err: + OSSL_SELF_TEST_onend(st, ret); + OSSL_SELF_TEST_free(st); + return ret; +} +#endif + static void *ecx_gen(struct ecx_gen_ctx *gctx) { ECX_KEY *key; @@ -684,6 +753,7 @@ static void *x448_gen(void *genctx, OSSL_CALLBACK *osslcb, void *cbarg) static void *ed25519_gen(void *genctx, OSSL_CALLBACK *osslcb, void *cbarg) { + ECX_KEY *key = NULL; struct ecx_gen_ctx *gctx = genctx; if (!ossl_prov_is_running()) @@ -693,14 +763,31 @@ static void *ed25519_gen(void *genctx, OSSL_CALLBACK *osslcb, void *cbarg) if (OPENSSL_s390xcap_P.pcc[1] & S390X_CAPBIT(S390X_SCALAR_MULTIPLY_ED25519) && OPENSSL_s390xcap_P.kdsa[0] & S390X_CAPBIT(S390X_EDDSA_SIGN_ED25519) && OPENSSL_s390xcap_P.kdsa[0] - & S390X_CAPBIT(S390X_EDDSA_VERIFY_ED25519)) - return s390x_ecd_keygen25519(gctx); + & S390X_CAPBIT(S390X_EDDSA_VERIFY_ED25519)) { + key = s390x_ecd_keygen25519(gctx); + } else #endif - return ecx_gen(gctx); + { + key = ecx_gen(gctx); + } + +#ifdef FIPS_MODULE + /* Exit if keygen failed OR we are doing parameter generation (blank key) */ + if (!key || ((gctx->selection & OSSL_KEYMGMT_SELECT_KEYPAIR) == 0)) + return key; + if (ecd_fips140_pairwise_test(key, ECX_KEY_TYPE_ED25519, 1) != 1) { + ossl_set_error_state(OSSL_SELF_TEST_TYPE_PCT); + ossl_ecx_key_free(key); + return NULL; + } +#endif + + return key; } static void *ed448_gen(void *genctx, OSSL_CALLBACK *osslcb, void *cbarg) { + ECX_KEY *key = NULL; struct ecx_gen_ctx *gctx = genctx; if (!ossl_prov_is_running()) @@ -709,10 +796,26 @@ static void *ed448_gen(void *genctx, OSSL_CALLBACK *osslcb, void *cbarg) #ifdef S390X_EC_ASM if (OPENSSL_s390xcap_P.pcc[1] & S390X_CAPBIT(S390X_SCALAR_MULTIPLY_ED448) && OPENSSL_s390xcap_P.kdsa[0] & S390X_CAPBIT(S390X_EDDSA_SIGN_ED448) - && OPENSSL_s390xcap_P.kdsa[0] & S390X_CAPBIT(S390X_EDDSA_VERIFY_ED448)) - return s390x_ecd_keygen448(gctx); + && OPENSSL_s390xcap_P.kdsa[0] & S390X_CAPBIT(S390X_EDDSA_VERIFY_ED448)) { + key = s390x_ecd_keygen448(gctx); + } else #endif - return ecx_gen(gctx); + { + key = ecx_gen(gctx); + } + +#ifdef FIPS_MODULE + /* Exit if keygen failed OR we are doing parameter generation (blank key) */ + if (!key || ((gctx->selection & OSSL_KEYMGMT_SELECT_KEYPAIR) == 0)) + return key; + if (ecd_fips140_pairwise_test(key, ECX_KEY_TYPE_ED448, 1) != 1) { + ossl_set_error_state(OSSL_SELF_TEST_TYPE_PCT); + ossl_ecx_key_free(key); + return NULL; + } +#endif + + return key; } static void ecx_gen_cleanup(void *genctx) @@ -756,6 +859,23 @@ static int ecx_key_pairwise_check(const ECX_KEY *ecx, int type) case ECX_KEY_TYPE_X448: ossl_x448_public_from_private(pub, ecx->privkey); break; + default: + return 0; + } + return CRYPTO_memcmp(ecx->pubkey, pub, ecx->keylen) == 0; +} + +#ifdef FIPS_MODULE +static int ecd_key_pairwise_check(const ECX_KEY *ecx, int type) +{ + return ecd_fips140_pairwise_test(ecx, type, 0); +} +#else +static int ecd_key_pairwise_check(const ECX_KEY *ecx, int type) +{ + uint8_t pub[64]; + + switch (type) { case ECX_KEY_TYPE_ED25519: if (!ossl_ed25519_public_from_private(ecx->libctx, pub, ecx->privkey, ecx->propq)) @@ -771,6 +891,7 @@ static int ecx_key_pairwise_check(const ECX_KEY *ecx, int type) } return CRYPTO_memcmp(ecx->pubkey, pub, ecx->keylen) == 0; } +#endif static int ecx_validate(const void *keydata, int selection, int type, size_t keylen) { @@ -794,7 +915,12 @@ static int ecx_validate(const void *keydata, int selection, int type, size_t key if ((selection & OSSL_KEYMGMT_SELECT_PRIVATE_KEY) != 0) ok = ok && ecx->privkey != NULL; - if ((selection & OSSL_KEYMGMT_SELECT_KEYPAIR) == OSSL_KEYMGMT_SELECT_KEYPAIR) + if ((selection & OSSL_KEYMGMT_SELECT_KEYPAIR) != OSSL_KEYMGMT_SELECT_KEYPAIR) + return ok; + + if (type == ECX_KEY_TYPE_ED25519 || type == ECX_KEY_TYPE_ED448) + ok = ok && ecd_key_pairwise_check(ecx, type); + else ok = ok && ecx_key_pairwise_check(ecx, type); return ok; diff --git a/test/pairwise_fail_test.c b/test/pairwise_fail_test.c index d37898338e..9ce11a15fc 100644 --- a/test/pairwise_fail_test.c +++ b/test/pairwise_fail_test.c @@ -122,6 +122,17 @@ static int test_keygen_pairwise_failure(void) goto err; if (!TEST_ptr_null(pkey)) goto err; + } else if (strncmp(pairwise_name, "eddsa", 5) == 0) { + if (!TEST_true(setup_selftest_pairwise_failure(type))) + goto err; + if (!TEST_ptr(ctx = EVP_PKEY_CTX_new_from_name(libctx, "ED25519", NULL))) + goto err; + if (!TEST_int_eq(EVP_PKEY_keygen_init(ctx), 1)) + goto err; + if (!TEST_int_le(EVP_PKEY_keygen(ctx, &pkey), 0)) + goto err; + if (!TEST_ptr_null(pkey)) + goto err; } ret = 1; err: diff --git a/test/recipes/30-test_pairwise_fail.t b/test/recipes/30-test_pairwise_fail.t index c837d48fb4..6bdf04d37c 100644 --- a/test/recipes/30-test_pairwise_fail.t +++ b/test/recipes/30-test_pairwise_fail.t @@ -22,7 +22,7 @@ use lib bldtop_dir('.'); plan skip_all => "These tests are unsupported in a non fips build" if disabled("fips"); -plan tests => 5; +plan tests => 6; my $provconf = srctop_file("test", "fips-and-base.cnf"); run(test(["fips_version_test", "-config", $provconf, ">=3.1.0"]), @@ -63,3 +63,17 @@ SKIP: { "-pairwise", "dsakat", "-dsaparam", data_file("dsaparam.pem")])), "fips provider dsa keygen kat failure test"); } + +SKIP: { + skip "Skip EDDSA test because of no ecx in this build", 1 + if disabled("ecx"); + + run(test(["fips_version_test", "-config", $provconf, ">=3.3.0"]), + capture => 1, statusvar => \my $exit); + skip "FIPS provider version is too old", 1 + if !$exit; + + ok(run(test(["pairwise_fail_test", "-config", $provconf, + "-pairwise", "eddsa"])), + "fips provider eddsa keygen pairwise failure test"); +} -- 2.39.5