Implement PCT for EDDSA
authorpohsingwu <pohsingwu@synology.com>
Sun, 28 Jan 2024 10:18:02 +0000 (18:18 +0800)
committerTomas Mraz <tomas@openssl.org>
Fri, 1 Mar 2024 10:06:03 +0000 (11:06 +0100)
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 <ppzgs1@gmail.com>
Reviewed-by: Tomas Mraz <tomas@openssl.org>
(Merged from https://github.com/openssl/openssl/pull/23408)

doc/man7/OSSL_PROVIDER-FIPS.pod
include/openssl/self_test.h
providers/implementations/keymgmt/ecx_kmgmt.c
test/pairwise_fail_test.c
test/recipes/30-test_pairwise_fail.t

index a8ea27cd88332547e79bb11e80f15c0057179a2a..15bc06a710a8af9b53a48b15dede9c0ed1192ac9 100644 (file)
@@ -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.
index 337a3190ceadf298edc5b9d82b1b71b00ecac466..d7de90262c067052ac10469ab5370de39c3078c2 100644 (file)
@@ -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"
index 8a9fe1b21b813fdd8712f5804eb342cb204a6352..ae11fd4bc072f146845b42161efc5d1ad700944e 100644 (file)
@@ -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 <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"
@@ -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;
index d37898338e3342347ee12a3a16e92d5693131ecc..9ce11a15fcb5c38b4e092065d8b5949a49e1114f 100644 (file)
@@ -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:
index c837d48fb476eb4a52809a8d55f7f7452c83c46b..6bdf04d37c96af51e2392732f9cbf1589a0bd4b5 100644 (file)
@@ -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");
+}