Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement PCT for EDDSA #23408

Closed
wants to merge 8 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
2 changes: 2 additions & 0 deletions doc/man7/OSSL_PROVIDER-FIPS.pod
Original file line number Diff line number Diff line change
Expand Up @@ -337,6 +337,8 @@ The FIPS module passes the following descriptions(s) to OSSL_SELF_TEST_onbegin()

=item "ECDSA" (B<OSSL_SELF_TEST_DESC_PCT_ECDSA>)

=item "EDDSA" (B<OSSL_SELF_TEST_DESC_PCT_EDDSA>)

=item "DSA" (B<OSSL_SELF_TEST_DESC_PCT_DSA>)

Key generation tests used with the "Pairwise_Consistency_Test" type.
Expand Down
1 change: 1 addition & 0 deletions include/openssl/self_test.h
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down
142 changes: 134 additions & 8 deletions providers/implementations/keymgmt/ecx_kmgmt.c
Original file line number Diff line number Diff line change
@@ -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
Expand All @@ -16,6 +16,7 @@
#include <openssl/proverr.h>
#include <openssl/evp.h>
#include <openssl/rand.h>
#include <openssl/self_test.h>
#include "internal/param_build_set.h"
#include <openssl/param_build.h>
#include "crypto/ecx.h"
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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())
Expand All @@ -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))
0140454 marked this conversation as resolved.
Show resolved Hide resolved
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())
Expand All @@ -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))
0140454 marked this conversation as resolved.
Show resolved Hide resolved
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)
Expand Down Expand Up @@ -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:
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is the validate required to use the same test? (Not sure they need to be the same,
especially since at some point the X25519/X448 might be allowed, so the PCT in the keygen could be either in that case sign or encrypt.) @t8m do you have any opinion on this?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since X25519/X448 currently is not approved but allowed algorithm, I think it may be not required to use the same test.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

IMO the validate() call should do the same as required for FIPS at least in the FIPS_MODULE case. So it covers the - PCT validation of the keypair on import case. I do not see any problem with doing the existing thing for the X* keys and doing this PCT for ED* keys in validate().

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))
Expand All @@ -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)
{
Expand All @@ -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;
Expand Down
11 changes: 11 additions & 0 deletions test/pairwise_fail_test.c
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down
16 changes: 15 additions & 1 deletion test/recipes/30-test_pairwise_fail.t
Original file line number Diff line number Diff line change
Expand Up @@ -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"]),
Expand Down Expand Up @@ -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");
}