]> git.ipfire.org Git - thirdparty/openssl.git/commitdiff
FIPS: Add EDDSA public key validation.
authorslontis <shane.lontis@oracle.com>
Thu, 22 Aug 2024 01:50:17 +0000 (11:50 +1000)
committerTomas Mraz <tomas@openssl.org>
Fri, 23 Aug 2024 19:23:53 +0000 (21:23 +0200)
EVP_PKEY_public_check() can be used by ED25519 and ED448 in order to
determine if the public key is a valid point on the curve.

The FIPS ACVP tests require public key validation tests.
See https://github.com/usnistgov/ACVP-Server/blob/master/gen-val/json-files/EDDSA-KeyVer-1.0/internalProjection.json

Note that this is NOT required to be called before EDDSA signature verification
since it is done internally.

Reviewed-by: Paul Dale <ppzgs1@gmail.com>
Reviewed-by: Tomas Mraz <tomas@openssl.org>
(Merged from https://github.com/openssl/openssl/pull/25265)

crypto/ec/curve25519.c
crypto/ec/curve448/eddsa.c
include/crypto/ecx.h
providers/implementations/keymgmt/ecx_kmgmt.c
test/acvp_test.c
test/acvp_test.inc

index cae2ac101dbaff4537dbfa279e0a3475c08f3538..a097a225c3e7398e614fc38bcd025df39d309ced 100644 (file)
@@ -5538,6 +5538,21 @@ err:
     return res;
 }
 
+/*
+ * This function should not be necessary since ossl_ed25519_verify() already
+ * does this check internally.
+ * For some reason the FIPS ACVP requires a EDDSA KeyVer test.
+ */
+int
+ossl_ed25519_pubkey_verify(const uint8_t *pub, size_t pub_len)
+{
+    ge_p3 A;
+
+    if (pub_len != ED25519_KEYLEN)
+        return 0;
+    return (ge_frombytes_vartime(&A, pub) == 0);
+}
+
 static const char allzeroes[15];
 
 int
index ff7f11dd343ac153bce490aba496ffab29c455bf..9b5140cb1759ee7226cd32a6a6714f2b86d9079a 100644 (file)
@@ -272,6 +272,17 @@ ossl_c448_ed448_sign_prehash(
                                 context, context_len, propq);
 }
 
+static c448_error_t
+c448_ed448_pubkey_verify(const uint8_t *pub, size_t pub_len)
+{
+    curve448_point_t pk_point;
+
+    if (pub_len != EDDSA_448_PUBLIC_BYTES)
+        return C448_FAILURE;
+
+    return ossl_curve448_point_decode_like_eddsa_and_mul_by_ratio(pk_point, pub);
+}
+
 c448_error_t
 ossl_c448_ed448_verify(
                     OSSL_LIB_CTX *ctx,
@@ -380,6 +391,17 @@ ossl_ed448_sign(OSSL_LIB_CTX *ctx, uint8_t *out_sig,
                                 propq) == C448_SUCCESS;
 }
 
+/*
+ * This function should not be necessary since ossl_ed448_verify() already
+ * does this check internally.
+ * For some reason the FIPS ACVP requires a EDDSA KeyVer test.
+ */
+int
+ossl_ed448_pubkey_verify(const uint8_t *pub, size_t pub_len)
+{
+    return c448_ed448_pubkey_verify(pub, pub_len);
+}
+
 int
 ossl_ed448_verify(OSSL_LIB_CTX *ctx,
                   const uint8_t *message, size_t message_len,
index f35b875fb64f42e3b40bbab159c37c93ec0a1879..802679573928e5705b000b42a7d87f628b22e654 100644 (file)
@@ -108,6 +108,8 @@ ossl_ed25519_verify(const uint8_t *tbs, size_t tbs_len,
                     const uint8_t *context, size_t context_len,
                     OSSL_LIB_CTX *libctx, const char *propq);
 int
+ossl_ed25519_pubkey_verify(const uint8_t *pub, size_t pub_len);
+int
 ossl_ed448_public_from_private(OSSL_LIB_CTX *ctx, uint8_t out_public_key[57],
                                const uint8_t private_key[57], const char *propq);
 int
@@ -124,6 +126,9 @@ ossl_ed448_verify(OSSL_LIB_CTX *ctx,
                   const uint8_t *context, size_t context_len,
                   const uint8_t phflag, const char *propq);
 
+int
+ossl_ed448_pubkey_verify(const uint8_t *pub, size_t pub_len);
+
 int
 ossl_x448(uint8_t out_shared_key[56], const uint8_t private_key[56],
           const uint8_t peer_public_value[56]);
index ae11fd4bc072f146845b42161efc5d1ad700944e..cd9aac2276341b989ae755fc57dfeed62b8488b6 100644 (file)
@@ -865,6 +865,25 @@ static int ecx_key_pairwise_check(const ECX_KEY *ecx, int type)
     return CRYPTO_memcmp(ecx->pubkey, pub, ecx->keylen) == 0;
 }
 
+#ifdef FIPS_MODULE
+/*
+ * FIPS ACVP testing requires the ability to check if the public key is valid
+ * This is not required normally since the ED signature verify does the test
+ * internally.
+ */
+static int ecd_key_pub_check(const ECX_KEY *ecx, int type)
+{
+    switch (type) {
+    case ECX_KEY_TYPE_ED25519:
+        return ossl_ed25519_pubkey_verify(ecx->pubkey, ecx->keylen);
+    case ECX_KEY_TYPE_ED448:
+        return ossl_ed448_pubkey_verify(ecx->pubkey, ecx->keylen);
+    default:
+        return 1;
+    }
+}
+#endif
+
 #ifdef FIPS_MODULE
 static int ecd_key_pairwise_check(const ECX_KEY *ecx, int type)
 {
@@ -893,7 +912,8 @@ static int ecd_key_pairwise_check(const ECX_KEY *ecx, int type)
 }
 #endif
 
-static int ecx_validate(const void *keydata, int selection, int type, size_t keylen)
+static int ecx_validate(const void *keydata, int selection, int type,
+                        size_t keylen)
 {
     const ECX_KEY *ecx = keydata;
     int ok = keylen == ecx->keylen;
@@ -909,8 +929,12 @@ static int ecx_validate(const void *keydata, int selection, int type, size_t key
         return 0;
     }
 
-    if ((selection & OSSL_KEYMGMT_SELECT_PUBLIC_KEY) != 0)
+    if ((selection & OSSL_KEYMGMT_SELECT_PUBLIC_KEY) != 0) {
         ok = ok && ecx->haspubkey;
+#ifdef FIPS_MODULE
+        ok = ok && ecd_key_pub_check(ecx, type);
+#endif
+    }
 
     if ((selection & OSSL_KEYMGMT_SELECT_PRIVATE_KEY) != 0)
         ok = ok && ecx->privkey != NULL;
index c3c81f4ad608549dbcd762b19d55ce31db0b2d42..1625cedc114daf6ad74acf94d60e13841614ddfc 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright 2020-2021 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
@@ -345,7 +345,7 @@ err:
 }
 #endif /* OPENSSL_NO_EC */
 
-#ifndef OPENSSL_NO_DSA
+#if !defined(OPENSSL_NO_DSA) || !defined(OPENSSL_NO_ECX)
 static int pkey_get_octet_bytes(EVP_PKEY *pkey, const char *name,
                                 unsigned char **out, size_t *out_len)
 {
@@ -367,6 +367,91 @@ err:
     OPENSSL_free(buf);
     return 0;
 }
+#endif /* !defined(OPENSSL_NO_DSA) || !defined(OPENSSL_NO_ECX) */
+
+#ifndef OPENSSL_NO_ECX
+static int eddsa_create_pkey(EVP_PKEY **pkey, const char *algname,
+                             const unsigned char *pub, size_t pub_len,
+                             int expected)
+{
+    int ret = 0;
+    EVP_PKEY_CTX *ctx = NULL;
+    OSSL_PARAM_BLD *bld = NULL;
+    OSSL_PARAM *params = NULL;
+
+    if (!TEST_ptr(bld = OSSL_PARAM_BLD_new())
+        || !TEST_true(OSSL_PARAM_BLD_push_octet_string(bld,
+                                                       OSSL_PKEY_PARAM_PUB_KEY,
+                                                       pub, pub_len) > 0)
+        || !TEST_ptr(params = OSSL_PARAM_BLD_to_param(bld))
+        || !TEST_ptr(ctx = EVP_PKEY_CTX_new_from_name(libctx, algname, NULL))
+        || !TEST_int_eq(EVP_PKEY_fromdata_init(ctx), 1)
+        || !TEST_int_eq(EVP_PKEY_fromdata(ctx, pkey, EVP_PKEY_PUBLIC_KEY,
+                                          params), expected))
+        goto err;
+
+    ret = 1;
+err:
+    OSSL_PARAM_free(params);
+    OSSL_PARAM_BLD_free(bld);
+    EVP_PKEY_CTX_free(ctx);
+    return ret;
+}
+
+static int eddsa_pub_verify_test(int id)
+{
+    const struct ecdsa_pub_verify_st *tst = &eddsa_pv_data[id];
+    int ret = 0;
+    EVP_PKEY_CTX *key_ctx = NULL;
+    EVP_PKEY *pkey = NULL;
+
+    if (!TEST_true(eddsa_create_pkey(&pkey, tst->curve_name,
+                                     tst->pub, tst->pub_len, 1)))
+        goto err;
+
+    if (!TEST_ptr(key_ctx = EVP_PKEY_CTX_new_from_pkey(libctx, pkey, ""))
+            || !TEST_int_eq(EVP_PKEY_public_check(key_ctx), tst->pass))
+        goto err;
+    ret = 1;
+err:
+    EVP_PKEY_free(pkey);
+    EVP_PKEY_CTX_free(key_ctx);
+    return ret;
+}
+
+static int eddsa_keygen_test(int id)
+{
+    int ret = 0;
+    EVP_PKEY *pkey = NULL;
+    unsigned char *priv = NULL, *pub = NULL;
+    size_t priv_len = 0, pub_len = 0;
+    const struct ecdsa_pub_verify_st *tst = &eddsa_pv_data[id];
+
+    self_test_args.called = 0;
+    self_test_args.enable = 1;
+    if (!TEST_ptr(pkey = EVP_PKEY_Q_keygen(libctx, NULL, tst->curve_name))
+        || !TEST_int_ge(self_test_args.called, 3)
+        || !TEST_true(pkey_get_octet_bytes(pkey, OSSL_PKEY_PARAM_PRIV_KEY,
+                                           &priv, &priv_len))
+        || !TEST_true(pkey_get_octet_bytes(pkey, OSSL_PKEY_PARAM_PUB_KEY, &pub,
+                                           &pub_len)))
+        goto err;
+
+    test_output_memory("q", pub, pub_len);
+    test_output_memory("d", priv, priv_len);
+    ret = 1;
+err:
+    self_test_args.enable = 0;
+    self_test_args.called = 0;
+    OPENSSL_clear_free(priv, priv_len);
+    OPENSSL_free(pub);
+    EVP_PKEY_free(pkey);
+    return ret;
+}
+
+#endif /* OPENSSL_NO_ECX */
+
+#ifndef OPENSSL_NO_DSA
 
 static EVP_PKEY *dsa_paramgen(int L, int N)
 {
@@ -1605,6 +1690,12 @@ int setup_tests(void)
     ADD_ALL_TESTS(ecdsa_sigver_test, OSSL_NELEM(ecdsa_sigver_data));
 #endif /* OPENSSL_NO_EC */
 
+#ifndef OPENSSL_NO_ECX
+    if (fips_provider_version_ge(libctx, 3, 4, 0)) {
+        ADD_ALL_TESTS(eddsa_keygen_test, OSSL_NELEM(eddsa_pv_data));
+        ADD_ALL_TESTS(eddsa_pub_verify_test, OSSL_NELEM(eddsa_pv_data));
+    }
+#endif
     ADD_ALL_TESTS(drbg_test, OSSL_NELEM(drbg_data));
     return 1;
 }
index 8670cfa0ab90c7b83963639bc0049d781a3d1139..5264c57ad1d73795624ffc92ec3aa3aa4c30115f 100644 (file)
@@ -233,6 +233,120 @@ static const struct ecdsa_sigver_st ecdsa_sigver_data[] = {
 
 #endif /* OPENSSL_NO_EC */
 
+#ifndef OPENSSL_NO_ECX
+
+/*
+ * Test vectors obtained from
+ * https://github.com/usnistgov/ACVP-Server/blob/master/gen-val/json-files/EDDSA-KeyVer-1.0
+ */
+static const unsigned char ed25519_pv_pub0[] = {
+    0xBE, 0xE1, 0x6F, 0x5B, 0x4A, 0x24, 0xEF, 0xF8,
+    0xA6, 0x54, 0x0C, 0x04, 0x5C, 0xC4, 0x51, 0xCA,
+    0x3A, 0x4E, 0x9B, 0x86, 0xDC, 0x5D, 0xE8, 0x12,
+    0x0C, 0xDD, 0x1C, 0x23, 0x8E, 0x3F, 0x22, 0x7D
+};
+static const unsigned char ed25519_pv_pub1[] = {
+    0x1E, 0xE7, 0x74, 0x94, 0x73, 0xA1, 0xB8, 0x98,
+    0xBF, 0x8C, 0x81, 0x11, 0x8E, 0x76, 0xEE, 0x8B,
+    0xDB, 0xD4, 0x8C, 0x19, 0x29, 0xF7, 0x47, 0x2A,
+    0x18, 0xAD, 0xCE, 0xFE, 0x2F, 0x8A, 0x25, 0x69
+};
+static const unsigned char ed25519_pv_pub2[] = {
+    0x61, 0x4B, 0xC0, 0xBE, 0x80, 0xE6, 0xC6, 0x35,
+    0xDC, 0xF5, 0x65, 0xE6, 0xCE, 0xEE, 0x1C, 0x14,
+    0x3C, 0xF4, 0x46, 0xAC, 0x22, 0x82, 0xA0, 0xCE,
+    0x28, 0xE6, 0x53, 0x62, 0x48, 0x3D, 0x8B, 0x94
+};
+static const unsigned char ed25519_pv_pub3[] = {
+    0x38, 0x95, 0x95, 0x90, 0x4D, 0x7E, 0xDC, 0x9B,
+    0xF3, 0xB6, 0xF9, 0x52, 0x40, 0xC4, 0x50, 0xC4,
+    0x72, 0xC0, 0x5E, 0x83, 0x8E, 0x84, 0xD5, 0x9A,
+    0x10, 0x3D, 0xCC, 0xFA, 0xD6, 0x19, 0x61, 0x07
+};
+static const unsigned char ed448_pv_pub0[] = {
+    0xC3, 0xA9, 0x2B, 0xDD, 0xF1, 0x9C, 0x1F, 0xF1,
+    0x69, 0x0E, 0xB0, 0x42, 0x73, 0x85, 0xCB, 0x8F,
+    0x74, 0xE6, 0x49, 0x63, 0xF0, 0xF3, 0xA6, 0x28,
+    0x11, 0xDB, 0x10, 0x54, 0x70, 0x52, 0x38, 0xB6,
+    0xCE, 0x62, 0xE0, 0x9F, 0x7A, 0xD5, 0xA2, 0xFF,
+    0xA9, 0xB4, 0xA1, 0xCD, 0x5E, 0x67, 0x6E, 0xFB,
+    0x1B, 0x1C, 0xAE, 0x58, 0xF5, 0xE1, 0x74, 0x8C,
+    0x00
+};
+static const unsigned char ed448_pv_pub1[] = {
+    0x94, 0xFE, 0x99, 0x25, 0x2F, 0x5C, 0x05, 0x69,
+    0xBF, 0x8B, 0x5B, 0xDD, 0x32, 0x61, 0x50, 0x08,
+    0x95, 0x05, 0xEE, 0x44, 0x04, 0xCF, 0x76, 0x44,
+    0x17, 0x56, 0x82, 0x03, 0xF1, 0x3A, 0xBB, 0x13,
+    0xBB, 0xC6, 0x3E, 0xCE, 0xE2, 0x1F, 0xEC, 0x06,
+    0x90, 0xA9, 0x53, 0x10, 0xB6, 0x86, 0x4D, 0x71,
+    0x29, 0x1B, 0x12, 0xCE, 0x3A, 0x86, 0xFD, 0xE0,
+    0x80
+};
+static const unsigned char ed448_pv_pub2[] = {
+    0xD0, 0x88, 0xF0, 0xA9, 0x94, 0x86, 0x31, 0x9A,
+    0xC7, 0xD0, 0x8C, 0x7C, 0xE4, 0xEB, 0xA0, 0x6C,
+    0xF3, 0xF7, 0x20, 0x3A, 0xA9, 0x4C, 0x85, 0xEC,
+    0x30, 0x10, 0xD7, 0x1A, 0x4B, 0x21, 0xA2, 0xFF,
+    0x7F, 0x3D, 0xEF, 0xA9, 0x45, 0x28, 0x53, 0x30,
+    0x16, 0x34, 0x3C, 0x4F, 0x19, 0xF5, 0xA3, 0x80,
+    0xF4, 0x42, 0xFB, 0xE6, 0x3B, 0xEE, 0x35, 0x4D,
+    0x80
+};
+static const unsigned char ed448_pv_pub3[] = {
+    0x45, 0x42, 0x38, 0x5F, 0x3D, 0xD3, 0x4A, 0x84,
+    0x87, 0x74, 0x56, 0x27, 0x62, 0x4E, 0xA2, 0xA9,
+    0xE8, 0xB2, 0x45, 0x9E, 0x1A, 0xF4, 0x5D, 0xCB,
+    0x70, 0x51, 0xBD, 0xD2, 0xEE, 0x07, 0xB9, 0x32,
+    0xFD, 0x5E, 0xCA, 0x47, 0x56, 0xB0, 0x06, 0xC0,
+    0xEF, 0xC0, 0x43, 0x5F, 0xE2, 0x1C, 0xAA, 0xE9,
+    0x10, 0x6F, 0xD7, 0x16, 0xFC, 0xBE, 0xF4, 0xB8,
+    0x80
+};
+
+static const struct ecdsa_pub_verify_st eddsa_pv_data[] = {
+    {
+        "ED25519",
+        ITM(ed25519_pv_pub0),
+        FAIL
+    },
+    {
+        "ED25519",
+        ITM(ed25519_pv_pub1),
+        FAIL
+    },
+    {
+        "ED25519",
+        ITM(ed25519_pv_pub2),
+        PASS
+    },
+    {
+        "ED25519",
+        ITM(ed25519_pv_pub3),
+        PASS
+    },
+    {
+        "ED448",
+        ITM(ed448_pv_pub0),
+        FAIL
+    },
+    {
+        "ED448",
+        ITM(ed448_pv_pub1),
+        FAIL
+    },
+    {
+        "ED448",
+        ITM(ed448_pv_pub2),
+        PASS
+    },
+    {
+        "ED448",
+        ITM(ed448_pv_pub3),
+        PASS
+    },
+};
+#endif /* OPENSSL_NO_ECX */
 
 #ifndef OPENSSL_NO_DSA