]> git.ipfire.org Git - thirdparty/openssl.git/commitdiff
Add base code to load a SLH_DSA public key.
authorslontis <shane.lontis@oracle.com>
Wed, 6 Nov 2024 03:22:45 +0000 (14:22 +1100)
committerTomas Mraz <tomas@openssl.org>
Tue, 18 Feb 2025 09:13:53 +0000 (10:13 +0100)
This loads a SLH_DSA public key from data.
A simple SLH_DSA keymanager imports this key.

Initially this only has a parameter set for
SLH-DSA-SHA2-128s

Reviewed-by: Paul Dale <ppzgs1@gmail.com>
Reviewed-by: Viktor Dukhovni <viktor@openssl.org>
Reviewed-by: Tim Hudson <tjh@openssl.org>
(Merged from https://github.com/openssl/openssl/pull/25882)

18 files changed:
crypto/build.info
crypto/slh_dsa/build.info [new file with mode: 0644]
crypto/slh_dsa/slh_dsa_key.c [new file with mode: 0644]
crypto/slh_dsa/slh_dsa_key.h [new file with mode: 0644]
crypto/slh_dsa/slh_dsa_local.h [new file with mode: 0644]
crypto/slh_dsa/slh_params.c [new file with mode: 0644]
crypto/slh_dsa/slh_params.h [new file with mode: 0644]
include/crypto/slh_dsa.h [new file with mode: 0644]
include/crypto/types.h
providers/defltprov.c
providers/implementations/include/prov/implementations.h
providers/implementations/include/prov/names.h
providers/implementations/keymgmt/build.info
providers/implementations/keymgmt/slh_dsa_kmgmt.c [new file with mode: 0644]
test/build.info
test/recipes/30-test_slh_dsa.t [new file with mode: 0644]
test/slh_dsa.inc [new file with mode: 0644]
test/slh_dsa_test.c [new file with mode: 0644]

index ee293d4af949221d4d0f81a5ec6b65b858a6a0d1..3d2375778e752a5d78c4b4de54aa1563105c57f9 100644 (file)
@@ -6,7 +6,7 @@ SUBDIRS=objects buffer bio stack lhash hashtable rand evp asn1 pem x509 conf \
         siphash sm3 des aes rc2 rc4 rc5 idea aria bf cast camellia \
         seed sm4 chacha modes bn ec rsa dsa dh sm2 dso engine \
         err comp http ocsp cms ts srp cmac ct async ess crmf cmp encode_decode \
-        ffc hpke thread ml_dsa
+        ffc hpke thread ml_dsa slh_dsa
 
 LIBS=../libcrypto
 
diff --git a/crypto/slh_dsa/build.info b/crypto/slh_dsa/build.info
new file mode 100644 (file)
index 0000000..d6e37a9
--- /dev/null
@@ -0,0 +1,7 @@
+LIBS=../../libcrypto
+
+$COMMON=slh_dsa_key.c slh_params.c
+
+IF[{- !$disabled{'slh_dsa'} -}]
+  SOURCE[../../libcrypto]=$COMMON
+ENDIF
diff --git a/crypto/slh_dsa/slh_dsa_key.c b/crypto/slh_dsa/slh_dsa_key.c
new file mode 100644 (file)
index 0000000..5ccb646
--- /dev/null
@@ -0,0 +1,142 @@
+/*
+ * Copyright 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
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include <assert.h>
+#include <string.h>
+#include <openssl/core_dispatch.h>
+#include <openssl/core_names.h>
+#include <openssl/params.h>
+#include <openssl/rand.h>
+#include "slh_dsa_local.h"
+#include "slh_dsa_key.h"
+
+/**
+ * @brief Create a new SLH_DSA_KEY object
+ *
+ * @param libctx A OSSL_LIB_CTX object used for fetching algorithms.
+ * @param alg The algrithm name associated with the key type
+ * @returns The new SLH_DSA_KEY object on success, or NULL on malloc failure
+ */
+SLH_DSA_KEY *ossl_slh_dsa_key_new(OSSL_LIB_CTX *libctx, const char *alg)
+{
+    SLH_DSA_KEY *ret;
+    const SLH_DSA_PARAMS *params = ossl_slh_dsa_params_get(alg);
+
+    if (params == NULL)
+        return NULL;
+
+    ret = OPENSSL_zalloc(sizeof(*ret));
+    if (ret != NULL) {
+        if (!CRYPTO_NEW_REF(&ret->references, 1)) {
+            OPENSSL_free(ret);
+            return NULL;
+        }
+        ret->libctx = libctx;
+        ret->params = params;
+    }
+    return ret;
+}
+
+/**
+ * @brief Destroy a SLH_DSA_KEY object
+ */
+void ossl_slh_dsa_key_free(SLH_DSA_KEY *key)
+{
+    int i;
+
+    if (key == NULL)
+        return;
+
+    CRYPTO_DOWN_REF(&key->references, &i);
+    REF_PRINT_COUNT("SLH_DSA_KEY", key);
+    if (i > 0)
+        return;
+    REF_ASSERT_ISNT(i < 0);
+
+    OPENSSL_free(key->propq);
+    CRYPTO_FREE_REF(&key->references);
+    OPENSSL_free(key);
+}
+
+/*
+ * @brief Increase the reference count for a SLH_DSA_KEY object.
+ * @returns 1 on success or 0 otherwise.
+ */
+int ossl_slh_dsa_key_up_ref(SLH_DSA_KEY *key)
+{
+    int i;
+
+    if (CRYPTO_UP_REF(&key->references, &i) <= 0)
+        return 0;
+
+    REF_PRINT_COUNT("SLH_DSA_KEY", key);
+    REF_ASSERT_ISNT(i < 2);
+    return ((i > 1) ? 1 : 0);
+}
+
+/**
+ * @brief Are 2 keys equal?
+ *
+ * To be equal the keys must have the same key data.
+ *
+ * @param key1 A SLH_DSA_KEY object
+ * @param key2 A SLH_DSA_KEY object
+ * @param selection to select public and/or private component comparison.
+ * @returns 1 if the keys are equal otherwise it returns 0.
+ */
+int ossl_slh_dsa_key_equal(const SLH_DSA_KEY *key1, const SLH_DSA_KEY *key2,
+                           int selection)
+{
+    int ok = 1;
+
+    if ((selection & OSSL_KEYMGMT_SELECT_KEYPAIR) != 0) {
+        if (key1->key_len != key2->key_len)
+            return 0;
+        ok = (memcmp(key1->pub, key2->pub, key1->key_len) == 0);
+    }
+    return ok;
+}
+
+int ossl_slh_dsa_key_has(const SLH_DSA_KEY *key, int selection)
+{
+    if ((selection & OSSL_KEYMGMT_SELECT_KEYPAIR) != 0) {
+        if (key->key_len == 0)
+            return 0;
+        return 1;
+    }
+    return 0;
+}
+
+int ossl_slh_dsa_key_fromdata(SLH_DSA_KEY *key, const OSSL_PARAM params[])
+{
+    size_t n, key_len, len = 0;
+    const OSSL_PARAM *param_pub;
+    void *p;
+
+    if (key == NULL)
+        return 0;
+    n = key->params->n;
+    assert(n != 0);
+    /* Both the public and private key are composed of 2 elements of size n */
+    key_len = 2 * n;
+
+    param_pub = OSSL_PARAM_locate_const(params, OSSL_PKEY_PARAM_PUB_KEY);
+    if (param_pub == NULL)
+        goto err;
+    p = key->pub;
+    if (!OSSL_PARAM_get_octet_string(param_pub, &p, key_len, &len))
+        goto err;
+    if (len != key_len)
+        goto err;
+    key->key_len = key_len; /* This indicates the public key is present */
+    return 1;
+ err:
+    key->key_len = 0;
+    return 0;
+}
diff --git a/crypto/slh_dsa/slh_dsa_key.h b/crypto/slh_dsa/slh_dsa_key.h
new file mode 100644 (file)
index 0000000..4247ebd
--- /dev/null
@@ -0,0 +1,22 @@
+/*
+ * Copyright 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
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include <openssl/e_os2.h>
+#include "internal/refcount.h"
+
+#define SLH_DSA_MAX_KEYLEN 32 * 2 /* 2 * n */
+
+struct slh_dsa_key_st {
+    uint8_t pub[SLH_DSA_MAX_KEYLEN];
+    size_t key_len; /* This value is set to 2 * n if there is a public key */
+    CRYPTO_REF_COUNT references;
+    OSSL_LIB_CTX *libctx;
+    char *propq;
+    const SLH_DSA_PARAMS *params;
+};
diff --git a/crypto/slh_dsa/slh_dsa_local.h b/crypto/slh_dsa/slh_dsa_local.h
new file mode 100644 (file)
index 0000000..066318f
--- /dev/null
@@ -0,0 +1,11 @@
+/*
+ * Copyright 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
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include "crypto/slh_dsa.h"
+#include "slh_params.h"
diff --git a/crypto/slh_dsa/slh_params.c b/crypto/slh_dsa/slh_params.c
new file mode 100644 (file)
index 0000000..5034f85
--- /dev/null
@@ -0,0 +1,36 @@
+/*
+ * Copyright 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
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+#include <stddef.h>
+#include <string.h>
+#include "slh_params.h"
+
+/*
+ * See FIPS 205 Section 11 Table 2
+ *                         n  h    d  hm  a   k  m  sc  pk   sig
+ */
+#define OSSL_SLH_DSA_128S 16, 63,  7, 9, 12, 14, 30, 1, 32,  7856
+
+static const SLH_DSA_PARAMS slh_dsa_params[] = {
+    {"SLH-DSA-SHA2-128s",  0, OSSL_SLH_DSA_128S},
+    {NULL},
+};
+
+/**
+ * @brief A getter to convert an algorithm name into a SLH_DSA_PARAMS object
+ */
+const SLH_DSA_PARAMS *ossl_slh_dsa_params_get(const char *alg)
+{
+    const SLH_DSA_PARAMS *p;
+
+    for (p = slh_dsa_params; p->alg != NULL; ++p) {
+        if (strcmp(p->alg, alg) == 0)
+            return p;
+    }
+    return NULL;
+}
diff --git a/crypto/slh_dsa/slh_params.h b/crypto/slh_dsa/slh_params.h
new file mode 100644 (file)
index 0000000..80a0ad5
--- /dev/null
@@ -0,0 +1,31 @@
+/*
+ * Copyright 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
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include <openssl/e_os2.h>
+
+/*
+ * Refer to FIPS 205 Section 11 parameter sets.
+ * lgw has been omitted since it is 4 for all algorithms i.e log(16)
+ */
+typedef struct slh_dsa_params_st {
+    const char *alg;
+    int is_shake;
+    uint32_t n;  /* Security parameter (Hash output size in bytes) (16, 24, 32) */
+    uint32_t h;  /* The total height of the tree (63, 64, 66, 68). #keypairs = 2^h */
+    uint32_t d;  /* The number of tree layers (7, 8, 17, 22) */
+    uint32_t hm; /* The height (h') of each merkle tree. (h = hm * d ) */
+    uint32_t a;  /* Height of a FORS tree */
+    uint32_t k;  /* The number of FORS trees */
+    uint32_t m;  /* The size of H_MSG() output */
+    uint32_t security_category;
+    uint32_t pk_len;
+    uint32_t sig_len;
+} SLH_DSA_PARAMS;
+
+const SLH_DSA_PARAMS *ossl_slh_dsa_params_get(const char *alg);
diff --git a/include/crypto/slh_dsa.h b/include/crypto/slh_dsa.h
new file mode 100644 (file)
index 0000000..5065aa4
--- /dev/null
@@ -0,0 +1,28 @@
+/*
+ * Copyright 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
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+/* Internal SLH_DSA functions for other submodules, not for application use */
+
+#ifndef OSSL_CRYPTO_SLH_DSA_H
+# define OSSL_CRYPTO_SLH_DSA_H
+
+# pragma once
+# include <openssl/e_os2.h>
+# include <openssl/types.h>
+# include "crypto/types.h"
+
+SLH_DSA_KEY *ossl_slh_dsa_key_new(OSSL_LIB_CTX *libctx, const char *alg);
+void ossl_slh_dsa_key_free(SLH_DSA_KEY *key);
+int ossl_slh_dsa_key_up_ref(SLH_DSA_KEY *key);
+int ossl_slh_dsa_key_equal(const SLH_DSA_KEY *key1, const SLH_DSA_KEY *key2,
+                           int selection);
+int ossl_slh_dsa_key_has(const SLH_DSA_KEY *key, int selection);
+int ossl_slh_dsa_key_fromdata(SLH_DSA_KEY *key, const OSSL_PARAM *params);
+
+#endif /* OSSL_CRYPTO_SLH_DSA_H */
index 43f04fd5eab32bb9f57c07963170af66fb0a93b6..5dbcd900508b38e14d989b6828586868d20576fa 100644 (file)
@@ -28,6 +28,9 @@ typedef struct dsa_st DSA;
 # ifndef OPENSSL_NO_EC
 typedef struct ecx_key_st ECX_KEY;
 # endif
+# ifndef OPENSSL_NO_SLH_DSA
+typedef struct slh_dsa_key_st SLH_DSA_KEY;
+# endif
 
 typedef struct prov_skey_st PROV_SKEY;
 
index 450a53a610ca1709110dec53f3eeb6846c4648f0..6aea57219ea5260fcd176282b9b6d359775836ca 100644 (file)
@@ -577,6 +577,10 @@ static const OSSL_ALGORITHM deflt_keymgmt[] = {
       PROV_DESCS_SecP384r1MLKEM1024 },
 # endif
 #endif
+#ifndef OPENSSL_NO_SLH_DSA
+    { PROV_NAMES_SLH_DSA_SHA2_128S, "provider=default", ossl_slh_dsa_sha2_128s_keymgmt_functions,
+      PROV_DESCS_SLH_DSA_SHA2_128S },
+#endif /* OPENSSL_NO_SLH_DSA */
     { NULL, NULL, NULL }
 };
 
index 1dace9738d874063df7e4e8471a6e4281fb6d7de..9f0cda93115b25a93ae873d117f6ac1673ad4f88 100644 (file)
@@ -339,6 +339,9 @@ extern const OSSL_DISPATCH ossl_mlx_p256_kem_kmgmt_functions[];
 extern const OSSL_DISPATCH ossl_mlx_p384_kem_kmgmt_functions[];
 # endif
 #endif
+#ifndef OPENSSL_NO_SLH_DSA
+extern const OSSL_DISPATCH ossl_slh_dsa_sha2_128s_keymgmt_functions[];
+#endif /* OPENSSL_NO_SLH_DSA */
 
 /* Key Exchange */
 extern const OSSL_DISPATCH ossl_dh_keyexch_functions[];
index e20c7b6b9dbe703cf36a4099744166df3ea2b7e0..6ff25497f8a2897af8ca071b5093daa900049b33 100644 (file)
 #define PROV_DESCS_SecP256r1MLKEM768 "P-256+ML-KEM-768 TLS hybrid implementation"
 #define PROV_NAMES_SecP384r1MLKEM1024 "SecP384r1MLKEM1024"
 #define PROV_DESCS_SecP384r1MLKEM1024 "P-384+ML-KEM-1024 TLS hybrid implementation"
+#define PROV_NAMES_SLH_DSA_SHA2_128S "SLH-DSA-SHA2-128s:2.16.840.1.101.3.4.3.20"
+#define PROV_DESCS_SLH_DSA_SHA2_128S "OpenSSL SLH-DSA-SHA2-128s implementation"
index 8b0b157378d2c60ca47a2cf3f9615c2a9d3e7d41..cd6c51a65a4a43c7394fb7e1a2c91b2e95222e39 100644 (file)
@@ -12,6 +12,7 @@ $TEMPLATE_GOAL=../../libtemplate.a
 $ML_DSA_GOAL=../../libdefault.a ../../libfips.a
 $ML_KEM_GOAL=../../libdefault.a ../../libfips.a
 $TLS_ML_KEM_HYBRID_GOAL=../../libdefault.a ../../libfips.a
+$SLH_DSA_GOAL=../../libdefault.a
 
 IF[{- !$disabled{dh} -}]
   SOURCE[$DH_GOAL]=dh_kmgmt.c
@@ -58,3 +59,7 @@ SOURCE[$TEMPLATE_GOAL]=template_kmgmt.c
 IF[{- !$disabled{'ml-dsa'} -}]
   SOURCE[$ML_DSA_GOAL]=ml_dsa_kmgmt.c
 ENDIF
+
+IF[{- !$disabled{'slh-dsa'} -}]
+  SOURCE[$SLH_DSA_GOAL]=slh_dsa_kmgmt.c
+ENDIF
diff --git a/providers/implementations/keymgmt/slh_dsa_kmgmt.c b/providers/implementations/keymgmt/slh_dsa_kmgmt.c
new file mode 100644 (file)
index 0000000..1d6bad1
--- /dev/null
@@ -0,0 +1,105 @@
+/*
+ * Copyright 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
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include <openssl/core_dispatch.h>
+#include <openssl/core_names.h>
+#include <openssl/param_build.h>
+#include "crypto/slh_dsa.h"
+#include "internal/param_build_set.h"
+#include "prov/implementations.h"
+#include "prov/providercommon.h"
+#include "prov/provider_ctx.h"
+
+static OSSL_FUNC_keymgmt_free_fn slh_dsa_free_key;
+static OSSL_FUNC_keymgmt_has_fn slh_dsa_has;
+static OSSL_FUNC_keymgmt_match_fn slh_dsa_match;
+static OSSL_FUNC_keymgmt_import_fn slh_dsa_import;
+static OSSL_FUNC_keymgmt_import_types_fn slh_dsa_imexport_types;
+
+#define SLH_DSA_POSSIBLE_SELECTIONS (OSSL_KEYMGMT_SELECT_PUBLIC_KEY)
+
+static void *slh_dsa_new_key(void *provctx, const char *alg)
+{
+    if (!ossl_prov_is_running())
+        return 0;
+
+    return ossl_slh_dsa_key_new(PROV_LIBCTX_OF(provctx), alg);
+}
+
+static void slh_dsa_free_key(void *keydata)
+{
+    ossl_slh_dsa_key_free((SLH_DSA_KEY *)keydata);
+}
+
+static int slh_dsa_has(const void *keydata, int selection)
+{
+    const SLH_DSA_KEY *key = keydata;
+
+    if (!ossl_prov_is_running() || key == NULL)
+        return 0;
+    if ((selection & SLH_DSA_POSSIBLE_SELECTIONS) == 0)
+        return 1; /* the selection is not missing */
+
+    return ossl_slh_dsa_key_has(key, selection);
+}
+
+static int slh_dsa_match(const void *keydata1, const void *keydata2, int selection)
+{
+    const SLH_DSA_KEY *key1 = keydata1;
+    const SLH_DSA_KEY *key2 = keydata2;
+
+    if (!ossl_prov_is_running())
+        return 0;
+    if (key1 == NULL || key2 == NULL)
+        return 0;
+    return ossl_slh_dsa_key_equal(key1, key2, selection);
+}
+
+static int slh_dsa_import(void *keydata, int selection, const OSSL_PARAM params[])
+{
+    SLH_DSA_KEY *key = keydata;
+
+    if (!ossl_prov_is_running() || key == NULL)
+        return 0;
+
+    if ((selection & SLH_DSA_POSSIBLE_SELECTIONS) == 0)
+        return 0;
+
+    return ossl_slh_dsa_key_fromdata(key, params);
+}
+
+static const OSSL_PARAM slh_dsa_key_types[] = {
+    OSSL_PARAM_octet_string(OSSL_PKEY_PARAM_PUB_KEY, NULL, 0),
+    OSSL_PARAM_END
+};
+
+static const OSSL_PARAM *slh_dsa_imexport_types(int selection)
+{
+    if ((selection & SLH_DSA_POSSIBLE_SELECTIONS) == 0)
+        return NULL;
+    return slh_dsa_key_types;
+}
+
+#define MAKE_KEYMGMT_FUNCTIONS(alg, fn)                                        \
+static OSSL_FUNC_keymgmt_new_fn slh_dsa_##fn##_new_key;                        \
+static void *slh_dsa_##fn##_new_key(void *provctx)                             \
+{                                                                              \
+    return slh_dsa_new_key(provctx, alg);                                      \
+}                                                                              \
+const OSSL_DISPATCH ossl_slh_dsa_##fn##_keymgmt_functions[] = {                \
+    { OSSL_FUNC_KEYMGMT_NEW, (void (*)(void))slh_dsa_##fn##_new_key },         \
+    { OSSL_FUNC_KEYMGMT_FREE, (void (*)(void))slh_dsa_free_key },              \
+    { OSSL_FUNC_KEYMGMT_HAS, (void (*)(void))slh_dsa_has },                    \
+    { OSSL_FUNC_KEYMGMT_MATCH, (void (*)(void))slh_dsa_match },                \
+    { OSSL_FUNC_KEYMGMT_IMPORT, (void (*)(void))slh_dsa_import },              \
+    { OSSL_FUNC_KEYMGMT_IMPORT_TYPES, (void (*)(void))slh_dsa_imexport_types },\
+    OSSL_DISPATCH_END                                                          \
+}
+
+MAKE_KEYMGMT_FUNCTIONS("SLH-DSA-SHA2-128s", sha2_128s);
index e1fa8e928c4eb484f1f07c36c6ffc3336c1ee8d8..8cab6deb15b8a00f2f6d7547bba2677dace64789 100644 (file)
@@ -273,6 +273,13 @@ IF[{- !$disabled{tests} -}]
   INCLUDE[evp_pkey_dhkem_test]=../include ../apps/include
   DEPEND[evp_pkey_dhkem_test]=../libcrypto.a libtestutil.a
 
+  IF[{- !$disabled{'slh-dsa'} -}]
+    PROGRAMS{noinst}=slh_dsa_test
+    SOURCE[slh_dsa_test]=slh_dsa_test.c
+    INCLUDE[slh_dsa_test]=../include ../apps/include
+    DEPEND[slh_dsa_test]=../libcrypto.a libtestutil.a
+  ENDIF
+  
   IF[{- !$disabled{'deprecated-3.0'} -}]
     PROGRAMS{noinst}=igetest bftest casttest
 
diff --git a/test/recipes/30-test_slh_dsa.t b/test/recipes/30-test_slh_dsa.t
new file mode 100644 (file)
index 0000000..d8179c6
--- /dev/null
@@ -0,0 +1,25 @@
+#! /usr/bin/env perl
+# Copyright 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
+# in the file LICENSE in the source distribution or at
+# https://www.openssl.org/source/license.html
+
+use strict;
+use warnings;
+
+use OpenSSL::Test qw(:DEFAULT srctop_dir bldtop_dir);
+use OpenSSL::Test::Utils;
+
+BEGIN {
+    setup("test_slh_dsa");
+}
+
+use lib srctop_dir('Configurations');
+use lib bldtop_dir('.');
+
+plan skip_all => 'SLH-DSA is not supported in this build' if disabled('slh-dsa');
+plan tests => 1;
+
+ok(run(test(["slh_dsa_test"])), "running slh_dsa_test");
diff --git a/test/slh_dsa.inc b/test/slh_dsa.inc
new file mode 100644 (file)
index 0000000..fd2c1c3
--- /dev/null
@@ -0,0 +1,57 @@
+/*
+ * Copyright 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
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+typedef struct SLH_DSA_ACVP_TEST_DATA_st {
+    const char *alg;
+    const unsigned char *pub;
+    size_t pub_len;
+    const unsigned char *priv;
+    size_t priv_len;
+    const unsigned char *msg;
+    size_t msg_len;
+    /* sha256 digest of the signature - this reduces the footprint this file */
+    const unsigned char *sig_digest;
+    size_t sig_digest_len;
+} SLH_DSA_ACVP_TEST_DATA;
+
+#define SLH_DSA_ACVP_ITEM(name, alg) {                                         \
+    alg,                                                                       \
+    name##_pub, sizeof(name##_pub),                                            \
+    name##_priv, sizeof(name##_priv),                                          \
+    name##_msg, sizeof(name##_msg),                                            \
+    name##_sig_digest, sizeof(name##_sig_digest) }
+
+/*
+ * Test vectors from
+ * usnistgov/ACVP-Server/refs/heads/master/gen-val/json-files/SLH-DSA-sigGen-FIPS205/internalProjection.json
+ *
+ * Note that the test vectors store the private & public components in one field
+ * (e.g sk)
+ * The following data separates these fields.
+ */
+static const uint8_t slh_dsa_sha2_128s_0_priv[] = {
+    0x62, 0xb1, 0x97, 0x3a, 0x4d, 0xe0, 0x96, 0x3d, 0x74, 0xc1, 0xcb, 0x30, 0xfc, 0x8f, 0x56, 0x75,
+    0xcf, 0xc8, 0x48, 0x80, 0xe4, 0xf0, 0xe1, 0xb4, 0x46, 0xb4, 0xf5, 0xd1, 0x3b, 0x2d, 0x31, 0xcc,
+};
+static const uint8_t slh_dsa_sha2_128s_0_pub[] = {
+    0xcb, 0x23, 0xeb, 0x45, 0x52, 0x9e, 0x00, 0xd5, 0xf5, 0xe9, 0x51, 0x50, 0x7a, 0x9b, 0x90, 0xe9,
+    0x8b, 0x6e, 0x7a, 0x28, 0x4b, 0xa3, 0xf6, 0x3a, 0x69, 0xe6, 0x9a, 0x78, 0x90, 0x83, 0xbe, 0xf6,
+};
+static const uint8_t slh_dsa_sha2_128s_0_msg[] = {
+    0x9D, 0xDF
+};
+static const uint8_t slh_dsa_sha2_128s_0_sig_digest[] = {
+    0xc7, 0xdf, 0xf0, 0xed, 0x25, 0x38, 0x49, 0xef, 0x51, 0x1e, 0x90, 0xbe, 0x0e, 0x2e, 0xb7, 0x71,
+    0x65, 0x98, 0x91, 0x23, 0x17, 0x52, 0x9a, 0x61, 0xda, 0xe4, 0x32, 0x9b, 0xf1, 0x49, 0xef, 0x8b,
+};
+
+/* We can only use the hss tests that have a single level here */
+static SLH_DSA_ACVP_TEST_DATA slh_dsa_testdata[] = {
+    SLH_DSA_ACVP_ITEM(slh_dsa_sha2_128s_0, "SLH-DSA-SHA2-128s"),
+};
diff --git a/test/slh_dsa_test.c b/test/slh_dsa_test.c
new file mode 100644 (file)
index 0000000..a7f5bff
--- /dev/null
@@ -0,0 +1,123 @@
+/*
+ * Copyright 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
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include <openssl/core_names.h>
+#include <openssl/evp.h>
+#include "crypto/slh_dsa.h"
+#include "internal/nelem.h"
+#include "testutil.h"
+#include "slh_dsa.inc"
+
+static OSSL_LIB_CTX *libctx = NULL;
+
+static EVP_PKEY *slh_dsa_pubkey_from_data(const char *alg,
+                                          const unsigned char *data, size_t datalen)
+{
+    int ret;
+    EVP_PKEY_CTX *ctx = NULL;
+    EVP_PKEY *key = NULL;
+    OSSL_PARAM params[2];
+
+    params[0] = OSSL_PARAM_construct_octet_string(OSSL_PKEY_PARAM_PUB_KEY,
+                                                  (unsigned char *)data, datalen);
+    params[1] = OSSL_PARAM_construct_end();
+    ret = TEST_ptr(ctx = EVP_PKEY_CTX_new_from_name(libctx, alg, NULL))
+        && TEST_int_eq(EVP_PKEY_fromdata_init(ctx), 1)
+        && (EVP_PKEY_fromdata(ctx, &key, EVP_PKEY_PUBLIC_KEY, params) == 1);
+    if (ret == 0) {
+        EVP_PKEY_free(key);
+        key = NULL;
+    }
+    EVP_PKEY_CTX_free(ctx);
+    return key;
+}
+
+static int slh_dsa_bad_pub_len_test(void)
+{
+    int ret = 0;
+    SLH_DSA_ACVP_TEST_DATA *td = &slh_dsa_testdata[0];
+    EVP_PKEY *pkey = NULL;
+    size_t pub_len = 0;
+    unsigned char pubdata[64 + 1];
+
+    if (!TEST_size_t_le(td->pub_len, sizeof(pubdata)))
+        goto end;
+
+    OPENSSL_cleanse(pubdata, sizeof(pubdata));
+    memcpy(pubdata, td->pub, td->pub_len);
+
+    if (!TEST_ptr_null(pkey = slh_dsa_pubkey_from_data(td->alg, pubdata,
+                                                       td->pub_len - 1))
+            || !TEST_ptr_null(pkey = slh_dsa_pubkey_from_data(td->alg, pubdata,
+                                                              td->pub_len + 1)))
+        goto end;
+
+    ret = 1;
+end:
+    if (ret == 0)
+        TEST_note("Incorrectly accepted public key of length %u (expected %u)",
+                  (unsigned)pub_len, (unsigned)td->pub_len);
+    EVP_PKEY_free(pkey);
+    return ret == 1;
+}
+
+static int slh_dsa_key_eq_test(void)
+{
+    int ret = 0;
+    EVP_PKEY *key[2] = { NULL, NULL };
+    SLH_DSA_ACVP_TEST_DATA *td1 = &slh_dsa_testdata[0];
+#ifndef OPENSSL_NO_EC
+    EVP_PKEY *eckey = NULL;
+#endif
+
+    if (!TEST_ptr(key[0] = slh_dsa_pubkey_from_data(td1->alg, td1->pub, td1->pub_len))
+            || !TEST_ptr(key[1] = slh_dsa_pubkey_from_data(td1->alg, td1->pub, td1->pub_len)))
+        goto end;
+
+    ret = TEST_int_eq(EVP_PKEY_eq(key[0], key[1]), 1);
+    if (ret == 0)
+        goto end;
+
+#ifndef OPENSSL_NO_EC
+    if (!TEST_ptr(eckey = EVP_PKEY_Q_keygen(libctx, NULL, "EC", "P-256")))
+        goto end;
+    ret = TEST_int_ne(EVP_PKEY_eq(key[0], eckey), 1);
+    EVP_PKEY_free(eckey);
+#endif
+end:
+    EVP_PKEY_free(key[1]);
+    EVP_PKEY_free(key[0]);
+    return ret;
+}
+
+static int slh_dsa_key_validate_test(void)
+{
+    int ret = 0;
+    SLH_DSA_ACVP_TEST_DATA *td = &slh_dsa_testdata[0];
+    EVP_PKEY_CTX *vctx = NULL;
+    EVP_PKEY *key = NULL;
+
+    if (!TEST_ptr(key = slh_dsa_pubkey_from_data(td->alg, td->pub, td->pub_len)))
+        return 0;
+    if (!TEST_ptr(vctx = EVP_PKEY_CTX_new_from_pkey(libctx, key, NULL)))
+        goto end;
+    ret = TEST_int_eq(EVP_PKEY_check(vctx), 1);
+    EVP_PKEY_CTX_free(vctx);
+end:
+    EVP_PKEY_free(key);
+    return ret;
+}
+
+int setup_tests(void)
+{
+    ADD_TEST(slh_dsa_bad_pub_len_test);
+    ADD_TEST(slh_dsa_key_validate_test);
+    ADD_TEST(slh_dsa_key_eq_test);
+    return 1;
+}