From: slontis Date: Fri, 12 Dec 2025 02:52:25 +0000 (+1100) Subject: Added LMS support for OpenSSL commandline signature verification using pkeyutl. X-Git-Tag: openssl-4.0.0-alpha1~87 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=3d82b990d1f;p=thirdparty%2Fopenssl.git Added LMS support for OpenSSL commandline signature verification using pkeyutl. Added LMS 'SubjectPublicKeyInfo' encoder/decoder support. Modified LMS keymanager and signature code to work with pkey and pkeyutl. Test data for public keys and signatures were generated by modifying BouncyCastle code tests. Reviewed-by: Viktor Dukhovni Reviewed-by: Paul Dale Reviewed-by: Matt Caswell MergeDate: Fri Feb 27 14:40:27 2026 (Merged from https://github.com/openssl/openssl/pull/29381) --- diff --git a/CHANGES.md b/CHANGES.md index 34b959f152d..51e2eb5dda8 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -32,6 +32,12 @@ OpenSSL 4.0 ### Changes between 3.6 and 4.0 [xx XXX xxxx] + * Added LMS support for signature verification to `pkeyutl' command. + To enable this, LMS 'SubjectPublicKeyInfo' encoder and decoders were + added, and the LMS keymanager and signature code were updated. + + *Shane Lontis* + * New `SSL_get0_sigalg()` and `SSL_get0_shared_sigalg()` functions report the TLS signature algorithm name and codepoint for the peer advertised and shared algorithms respectively. These supersede the existing `SSL_get_sigalgs()` and diff --git a/crypto/lms/lms_key.c b/crypto/lms/lms_key.c index a5d87ad8453..752ffea5192 100644 --- a/crypto/lms/lms_key.c +++ b/crypto/lms/lms_key.c @@ -106,3 +106,25 @@ int ossl_lms_key_has(const LMS_KEY *key, int selection) return 0; return 1; } + +/* Returns the public key data or NULL if there is no public key */ +const uint8_t *ossl_lms_key_get_pub(const LMS_KEY *key) +{ + return key->pub.encoded; +} + +/* The encoded public key size */ +size_t ossl_lms_key_get_pub_len(const LMS_KEY *key) +{ + return 24 + key->lms_params->n; +} + +size_t ossl_lms_key_get_collision_strength_bits(const LMS_KEY *key) +{ + return key->lms_params->n * 8; +} + +size_t ossl_lms_key_get_sig_len(const LMS_KEY *key) +{ + return 12 + key->lms_params->n * (1 + key->ots_params->p + key->lms_params->h); +} diff --git a/crypto/lms/lms_params.c b/crypto/lms/lms_params.c index 6ac6dcdb81d..006a5d3768c 100644 --- a/crypto/lms/lms_params.c +++ b/crypto/lms/lms_params.c @@ -11,27 +11,27 @@ /* Refer to SP800-208 Section 4 LMS Parameter Sets */ static const LMS_PARAMS lms_params[] = { - { OSSL_LMS_TYPE_SHA256_N32_H5, "SHA256", 32, 5 }, - { OSSL_LMS_TYPE_SHA256_N32_H10, "SHA256", 32, 10 }, - { OSSL_LMS_TYPE_SHA256_N32_H15, "SHA256", 32, 15 }, - { OSSL_LMS_TYPE_SHA256_N32_H20, "SHA256", 32, 20 }, - { OSSL_LMS_TYPE_SHA256_N32_H25, "SHA256", 32, 25 }, - { OSSL_LMS_TYPE_SHA256_N24_H5, "SHA256-192", 24, 5 }, - { OSSL_LMS_TYPE_SHA256_N24_H10, "SHA256-192", 24, 10 }, - { OSSL_LMS_TYPE_SHA256_N24_H15, "SHA256-192", 24, 15 }, - { OSSL_LMS_TYPE_SHA256_N24_H20, "SHA256-192", 24, 20 }, - { OSSL_LMS_TYPE_SHA256_N24_H25, "SHA256-192", 24, 25 }, - { OSSL_LMS_TYPE_SHAKE_N32_H5, "SHAKE-256", 32, 5 }, - { OSSL_LMS_TYPE_SHAKE_N32_H10, "SHAKE-256", 32, 10 }, - { OSSL_LMS_TYPE_SHAKE_N32_H15, "SHAKE-256", 32, 15 }, - { OSSL_LMS_TYPE_SHAKE_N32_H20, "SHAKE-256", 32, 20 }, - { OSSL_LMS_TYPE_SHAKE_N32_H25, "SHAKE-256", 32, 25 }, + { OSSL_LMS_TYPE_SHA256_N32_H5, "SHA256", 32, 5, 128 }, + { OSSL_LMS_TYPE_SHA256_N32_H10, "SHA256", 32, 10, 128 }, + { OSSL_LMS_TYPE_SHA256_N32_H15, "SHA256", 32, 15, 128 }, + { OSSL_LMS_TYPE_SHA256_N32_H20, "SHA256", 32, 20, 128 }, + { OSSL_LMS_TYPE_SHA256_N32_H25, "SHA256", 32, 25, 128 }, + { OSSL_LMS_TYPE_SHA256_N24_H5, "SHA256-192", 24, 5, 96 }, + { OSSL_LMS_TYPE_SHA256_N24_H10, "SHA256-192", 24, 10, 96 }, + { OSSL_LMS_TYPE_SHA256_N24_H15, "SHA256-192", 24, 15, 96 }, + { OSSL_LMS_TYPE_SHA256_N24_H20, "SHA256-192", 24, 20, 96 }, + { OSSL_LMS_TYPE_SHA256_N24_H25, "SHA256-192", 24, 25, 96 }, + { OSSL_LMS_TYPE_SHAKE_N32_H5, "SHAKE-256", 32, 5, 256 }, + { OSSL_LMS_TYPE_SHAKE_N32_H10, "SHAKE-256", 32, 10, 256 }, + { OSSL_LMS_TYPE_SHAKE_N32_H15, "SHAKE-256", 32, 15, 256 }, + { OSSL_LMS_TYPE_SHAKE_N32_H20, "SHAKE-256", 32, 20, 256 }, + { OSSL_LMS_TYPE_SHAKE_N32_H25, "SHAKE-256", 32, 25, 256 }, /* SHAKE-256/192 */ - { OSSL_LMS_TYPE_SHAKE_N24_H5, "SHAKE-256", 24, 5 }, - { OSSL_LMS_TYPE_SHAKE_N24_H10, "SHAKE-256", 24, 10 }, - { OSSL_LMS_TYPE_SHAKE_N24_H15, "SHAKE-256", 24, 15 }, - { OSSL_LMS_TYPE_SHAKE_N24_H20, "SHAKE-256", 24, 20 }, - { OSSL_LMS_TYPE_SHAKE_N24_H25, "SHAKE-256", 24, 25 }, + { OSSL_LMS_TYPE_SHAKE_N24_H5, "SHAKE-256", 24, 5, 192 }, + { OSSL_LMS_TYPE_SHAKE_N24_H10, "SHAKE-256", 24, 10, 192 }, + { OSSL_LMS_TYPE_SHAKE_N24_H15, "SHAKE-256", 24, 15, 192 }, + { OSSL_LMS_TYPE_SHAKE_N24_H20, "SHAKE-256", 24, 20, 192 }, + { OSSL_LMS_TYPE_SHAKE_N24_H25, "SHAKE-256", 24, 25, 192 }, { 0, NULL, 0, 0 } }; diff --git a/doc/man7/EVP_PKEY-LMS.pod b/doc/man7/EVP_PKEY-LMS.pod index a51e9be436a..4a5323e2aae 100644 --- a/doc/man7/EVP_PKEY-LMS.pod +++ b/doc/man7/EVP_PKEY-LMS.pod @@ -27,6 +27,16 @@ is expected to be in XDR format. =back +The following parameters is gettable using EVP_PKEY_get_utf8_string_param(). + +=over 4 + +=item "mandatory-digest" (B) + +The empty string, signifying that no digest may be specified. + +=back + =head1 CONFORMING TO =over 4 @@ -91,6 +101,8 @@ L =head1 HISTORY This functionality was added in OpenSSL 3.6. +The gettable "mandatory-digest" and support for loading LMS public keys in +SubjectPublicKeyInfo format was added in OpenSSL 4.0. =head1 COPYRIGHT diff --git a/doc/man7/EVP_SIGNATURE-LMS.pod b/doc/man7/EVP_SIGNATURE-LMS.pod index bd936bfbfe8..7bffe8b9120 100644 --- a/doc/man7/EVP_SIGNATURE-LMS.pod +++ b/doc/man7/EVP_SIGNATURE-LMS.pod @@ -24,6 +24,9 @@ that specify the digest name are not necessary. LMS support is disabled by default at compile-time. To enable, specify the B build configuration option. +For backwards compatibility reasons EVP_DigestVerifyInit_ex() and +EVP_DigestVerify() may also be used, but the digest passed in I must be NULL. + LMS should only be used for older deployments. New deployments should use either L or . @@ -56,6 +59,8 @@ L, =head1 HISTORY This functionality was added in OpenSSL 3.6. +Support for EVP_DigestVerifyInit_ex() and EVP_DigestVerify() was added in +OpenSSL 4.0. =head1 COPYRIGHT diff --git a/doc/man7/OSSL_PROVIDER-base.pod b/doc/man7/OSSL_PROVIDER-base.pod index fdc7d0e54ee..ca8416f755e 100644 --- a/doc/man7/OSSL_PROVIDER-base.pod +++ b/doc/man7/OSSL_PROVIDER-base.pod @@ -132,6 +132,10 @@ are also available in the default provider. =item SLH-DSA-SHAKE-256f +=item LMS + +Private keys are not supported for LMS. + =back In addition to this provider, all of these encoding algorithms are also @@ -202,6 +206,10 @@ combination with the FIPS provider. =item SLH-DSA-SHAKE-256f +=item LMS + +Private keys are not supported for LMS. + =back In addition to this provider, all of these decoding algorithms are also @@ -230,7 +238,10 @@ L, L This functionality was added in OpenSSL 3.0. -Support for B and was added in OpenSSL 3.5. +Support for B and B was added in OpenSSL 3.5. + +Support for B Public Key (SubjectPublicKeyInfo) encoders and decoders +was added in OpenSSL 4.0. =head1 COPYRIGHT diff --git a/doc/man7/OSSL_PROVIDER-default.pod b/doc/man7/OSSL_PROVIDER-default.pod index d49502b5fdb..1c98bb787f4 100644 --- a/doc/man7/OSSL_PROVIDER-default.pod +++ b/doc/man7/OSSL_PROVIDER-default.pod @@ -444,6 +444,10 @@ are also available in the base provider. =item SLH-DSA-SHAKE-256f +=item LMS + +Private keys are not supported for LMS. + =back In addition to this provider, all of these encoding algorithms are also @@ -512,6 +516,10 @@ combination with the FIPS provider. =item SLH-DSA-SHAKE-256f +=item LMS + +Private keys are not supported for LMS. + =back In addition to this provider, all of these decoding algorithms are also @@ -538,6 +546,9 @@ L =head1 HISTORY +Support for B Public Key (SubjectPublicKeyInfo) encoders and decoders +was added in OpenSSL 4.0. + The RIPEMD160 digest was added to the default provider in OpenSSL 3.0.7. The HKDF-SHA256, HKDF-SHA384 and HKDF-SHA512 algorithms were added in OpenSSL 3.6. diff --git a/include/crypto/lms.h b/include/crypto/lms.h index 6fd2835c294..ffb856893fc 100644 --- a/include/crypto/lms.h +++ b/include/crypto/lms.h @@ -117,6 +117,7 @@ typedef struct lms_params_st { const char *digestname; /* One of SHA256, SHA256-192, or SHAKE256 */ uint32_t n; /* The Digest size (either 24 or 32), Useful for setting up SHAKE */ uint32_t h; /* The height of a LMS tree which is one of 5, 10, 15, 20, 25) */ + size_t bit_strength; } LMS_PARAMS; typedef struct lms_pub_key_st { @@ -156,5 +157,10 @@ int ossl_lms_pubkey_decode(const unsigned char *pub, size_t publen, LMS_KEY *lmskey); size_t ossl_lms_pubkey_length(const unsigned char *data, size_t datalen); +const uint8_t *ossl_lms_key_get_pub(const LMS_KEY *key); +size_t ossl_lms_key_get_pub_len(const LMS_KEY *key); +size_t ossl_lms_key_get_collision_strength_bits(const LMS_KEY *key); +size_t ossl_lms_key_get_sig_len(const LMS_KEY *key); + #endif /* OPENSSL_NO_LMS */ #endif /* OSSL_CRYPTO_LMS_H */ diff --git a/providers/implementations/encode_decode/build.info b/providers/implementations/encode_decode/build.info index 09c2d8c435d..2347ef865f3 100644 --- a/providers/implementations/encode_decode/build.info +++ b/providers/implementations/encode_decode/build.info @@ -20,7 +20,7 @@ ENDIF DEPEND[encode_key2any.o]=../../common/include/prov/der_rsa.h IF[{- !$disabled{lms} -}] - SOURCE[$DECODER_GOAL]=decode_lmsxdr2key.c + SOURCE[$DECODER_GOAL]=decode_lmsxdr2key.c lms_codecs.c ENDIF IF[{- !$disabled{'ml-dsa'} -}] diff --git a/providers/implementations/encode_decode/decode_der2key.c b/providers/implementations/encode_decode/decode_der2key.c index b75d73b0fdf..3458fd324a9 100644 --- a/providers/implementations/encode_decode/decode_der2key.c +++ b/providers/implementations/encode_decode/decode_der2key.c @@ -44,6 +44,7 @@ #include "internal/nelem.h" #include "prov/ml_dsa_codecs.h" #include "prov/ml_kem_codecs.h" +#include "prov/lms_codecs.h" #include "providers/implementations/encode_decode/decode_der2key.inc" #ifndef OPENSSL_NO_SLH_DSA @@ -1001,6 +1002,25 @@ static ossl_inline void *ml_dsa_d2i_PUBKEY(const uint8_t **der, long der_len, /* ---------------------------------------------------------------------- */ +#ifndef OPENSSL_NO_LMS +#define lms_evp_type EVP_PKEY_HSS_LMS +#define lms_free (free_key_fn *)ossl_lms_key_free +#define lms_check NULL +#define lms_adjust NULL + +static ossl_inline void *lms_d2i_PUBKEY(const uint8_t **der, long der_len, + struct der2key_ctx_st *ctx) +{ + LMS_KEY *key; + + key = ossl_lms_d2i_PUBKEY(*der, der_len, ctx->provctx); + if (key != NULL) + *der += der_len; + return key; +} +#endif +/* ---------------------------------------------------------------------- */ + /* * The DO_ macros help define the selection mask and the method functions * for each kind of object we want to decode. @@ -1303,3 +1323,7 @@ MAKE_DECODER("ML-DSA-65", ml_dsa_65, ml_dsa_65, SubjectPublicKeyInfo); MAKE_DECODER("ML-DSA-87", ml_dsa_87, ml_dsa_87, PrivateKeyInfo); MAKE_DECODER("ML-DSA-87", ml_dsa_87, ml_dsa_87, SubjectPublicKeyInfo); #endif + +#ifndef OPENSSL_NO_LMS +MAKE_DECODER("LMS", lms, lms, SubjectPublicKeyInfo); +#endif diff --git a/providers/implementations/encode_decode/encode_key2any.c b/providers/implementations/encode_decode/encode_key2any.c index b4deb9b59bc..396c0179985 100644 --- a/providers/implementations/encode_decode/encode_key2any.c +++ b/providers/implementations/encode_decode/encode_key2any.c @@ -41,6 +41,7 @@ #include "prov/endecoder_local.h" #include "prov/ml_dsa_codecs.h" #include "prov/ml_kem_codecs.h" +#include "prov/lms_codecs.h" #include "providers/implementations/encode_decode/encode_key2any.inc" #include @@ -1107,6 +1108,19 @@ static int slh_dsa_pki_priv_to_der(const void *vkey, unsigned char **pder, #define slh_dsa_shake_256f_pem_type "SLH-DSA-SHAKE-256f" #endif /* OPENSSL_NO_SLH_DSA */ +#ifndef OPENSSL_NO_LMS +static int lms_spki_pub_to_der(const void *vkey, unsigned char **pder, + ossl_unused void *ctx) +{ + return ossl_lms_i2d_pubkey(vkey, pder); +} + +#define prepare_lms_params NULL +#define lms_check_key_type NULL +#define lms_evp_type EVP_PKEY_HSS_LMS +#define lms_pem_type "LMS" +#endif /* OPENSSL_NO_LMS */ + /* ---------------------------------------------------------------------- */ static OSSL_FUNC_decoder_newctx_fn key2any_newctx; @@ -1768,3 +1782,8 @@ MAKE_ENCODER(ml_dsa_87, ml_dsa, PrivateKeyInfo, pem); MAKE_ENCODER(ml_dsa_87, ml_dsa, SubjectPublicKeyInfo, der); MAKE_ENCODER(ml_dsa_87, ml_dsa, SubjectPublicKeyInfo, pem); #endif /* OPENSSL_NO_ML_DSA */ + +#ifndef OPENSSL_NO_LMS +MAKE_ENCODER(lms, lms, SubjectPublicKeyInfo, der); +MAKE_ENCODER(lms, lms, SubjectPublicKeyInfo, pem); +#endif diff --git a/providers/implementations/encode_decode/encode_key2text.c b/providers/implementations/encode_decode/encode_key2text.c index 20b26e7024f..8f6aabc4522 100644 --- a/providers/implementations/encode_decode/encode_key2text.c +++ b/providers/implementations/encode_decode/encode_key2text.c @@ -33,6 +33,7 @@ #include "prov/endecoder_local.h" #include "prov/ml_dsa_codecs.h" #include "prov/ml_kem_codecs.h" +#include "prov/lms_codecs.h" DEFINE_SPECIAL_STACK_OF_CONST(BIGNUM_const, BIGNUM) @@ -621,6 +622,14 @@ static int ml_dsa_to_text(BIO *out, const void *key, int selection) return ossl_ml_dsa_key_to_text(out, (const ML_DSA_KEY *)key, selection); } #endif /* OPENSSL_NO_ML_DSA */ + +#ifndef OPENSSL_NO_LMS +static int lms_to_text(BIO *out, const void *key, int selection) +{ + return ossl_lms_key_to_text(out, (LMS_KEY *)key, selection); +} +#endif /* OPENSSL_NO_LMS */ + /* ---------------------------------------------------------------------- */ static void *key2text_newctx(void *provctx) @@ -743,3 +752,7 @@ MAKE_TEXT_ENCODER(slh_dsa_shake_192f, slh_dsa); MAKE_TEXT_ENCODER(slh_dsa_shake_256s, slh_dsa); MAKE_TEXT_ENCODER(slh_dsa_shake_256f, slh_dsa); #endif + +#ifndef OPENSSL_NO_LMS +MAKE_TEXT_ENCODER(lms, lms); +#endif diff --git a/providers/implementations/encode_decode/lms_codecs.c b/providers/implementations/encode_decode/lms_codecs.c new file mode 100644 index 00000000000..1d4a446622c --- /dev/null +++ b/providers/implementations/encode_decode/lms_codecs.c @@ -0,0 +1,191 @@ +/* + * Copyright 2025 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 +#include +#include +#include +#include +#include +#include "internal/encoder.h" +#include "internal/nelem.h" +#include "internal/packet.h" +#include "prov/lms_codecs.h" + +/*- + * The DER ASN.1 encoding of LMS public keys prepends 20 bytes + * to the encoded public key: + * + * - 2 byte outer sequence tag and length + * - 2 byte algorithm sequence tag and length + * - 2 byte algorithm OID tag and length + * - 11 byte algorithm OID (from NIST CSOR OID arc) + * - 2 byte bit string tag and length + * - 1 bitstring lead byte + * + * A HSS key with a single tree also represents LMS public key. + * This has 4 extra bytes after the above data with the value 0x00, 0x00, 0x00, 0x01 + * + * The LMS public key consists of + * 4 byte LMS type + * 4 byte OTS type + * 16 byte Id + * n bytes of K where n = 32 or 24. + * i.e. 24 + n bytes + */ + +#define LMS_SPKI_OVERHEAD 20 +#define HSS_HEADER 4 +#define HSS_LMS_SPKI_OVERHEAD (LMS_SPKI_OVERHEAD + HSS_HEADER) +#define HSS_LMS_HEADER(n) { \ + 0x30, 0x2E + n, 0x30, 0x0d, \ + 0x06, 0x0b, \ + 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x09, 0x10, 0x03, 0x11, \ + 0x03, 0x1D + n, \ + 0x00, \ + 0x00, 0x00, 0x00, 0x01 \ +} + +typedef struct { + const uint8_t header[HSS_LMS_SPKI_OVERHEAD]; +} LMS_SPKI_FMT; + +static const LMS_SPKI_FMT hss_lms_32_spkifmt = { + HSS_LMS_HEADER(32) +}; +static const LMS_SPKI_FMT hss_lms_24_spkifmt = { + HSS_LMS_HEADER(24) +}; + +typedef struct { + const LMS_SPKI_FMT *spkifmt; +} LMS_CODEC; + +static const LMS_CODEC codecs[2] = { + { &hss_lms_32_spkifmt }, + { &hss_lms_24_spkifmt } +}; + +static const LMS_SPKI_FMT *find_spkifmt(const uint8_t *pk, int pk_len) +{ + size_t i; + + if (pk_len <= HSS_LMS_SPKI_OVERHEAD) + return NULL; + + for (i = 0; i < OSSL_NELEM(codecs); ++i) { + if (memcmp(pk, codecs[i].spkifmt->header, HSS_LMS_SPKI_OVERHEAD) == 0) + return codecs[i].spkifmt; + } + return NULL; +} + +LMS_KEY * +ossl_lms_d2i_PUBKEY(const uint8_t *pk, int pk_len, PROV_CTX *provctx) +{ + OSSL_LIB_CTX *libctx = PROV_LIBCTX_OF(provctx); + LMS_KEY *ret; + const LMS_SPKI_FMT *spkifmt; + + spkifmt = find_spkifmt(pk, pk_len); + if (spkifmt == NULL) + return NULL; + + if ((ret = ossl_lms_key_new(libctx)) == NULL) + return NULL; + + pk += sizeof(spkifmt->header); + pk_len -= sizeof(spkifmt->header); + + if (!ossl_lms_pubkey_decode(pk, (size_t)pk_len, ret)) { + ERR_raise_data(ERR_LIB_PROV, PROV_R_BAD_ENCODING, + "error parsing LMS public key from input SPKI"); + ossl_lms_key_free(ret); + return NULL; + } + + return ret; +} + +int ossl_lms_i2d_pubkey(const LMS_KEY *key, unsigned char **out) +{ + if (key->pub.encoded == NULL || key->pub.encodedlen == 0) { + ERR_raise_data(ERR_LIB_PROV, PROV_R_NOT_A_PUBLIC_KEY, + "no LMS public key data available"); + return 0; + } + if (out != NULL) { + WPACKET pkt; + size_t sz = HSS_HEADER + key->pub.encodedlen; + uint8_t *buf = OPENSSL_malloc(sz); + int ret; + + if (buf == NULL) + return 0; + ret = WPACKET_init_static_len(&pkt, buf, sz, 0) + /* Output HSS format which has a 4 byte value (L = 1) */ + && WPACKET_memcpy(&pkt, hss_lms_32_spkifmt.header + sizeof(hss_lms_32_spkifmt.header) - HSS_HEADER, HSS_HEADER) + /* Output the LMS encoded public key */ + && WPACKET_memcpy(&pkt, key->pub.encoded, key->pub.encodedlen); + WPACKET_cleanup(&pkt); + if (ret == 0) { + OPENSSL_free(buf); + return 0; + } + *out = buf; + } + return (int)key->pub.encodedlen + HSS_HEADER; +} + +static const char *get_digest(const char *name) +{ + if (strcmp(name, "SHAKE-256") == 0) + return "SHAKE"; + return strcmp(name, "SHA256-192") == 0 ? "SHA256" : name; +} + +int ossl_lms_key_to_text(BIO *out, const LMS_KEY *key, int selection) +{ + const LMS_PARAMS *lms_params = key->lms_params; + const LM_OTS_PARAMS *ots_params = key->ots_params; + + if (out == NULL || key == NULL) { + ERR_raise(ERR_LIB_PROV, ERR_R_PASSED_NULL_PARAMETER); + return 0; + } + if (key->pub.encoded == NULL || key->pub.encodedlen == 0) { + /* Regardless of the |selection|, there must be a public key */ + ERR_raise_data(ERR_LIB_PROV, PROV_R_MISSING_KEY, + "no LMS key material available"); + return 0; + } + if (BIO_printf(out, "lms-type: %s-N%d-H%d (0x%x)\n", + get_digest(lms_params->digestname), + (int)lms_params->n, (int)lms_params->h, (int)lms_params->lms_type) + <= 0) + return 0; + if (BIO_printf(out, "lm-ots-type: %s-N%d-W%d (0x%x)\n", + get_digest(ots_params->digestname), + (int)ots_params->n, (int)ots_params->w, (int)ots_params->lm_ots_type) + <= 0) + return 0; + if (!ossl_bio_print_labeled_buf(out, "Id:", key->Id, 16)) + return 0; + if ((selection & OSSL_KEYMGMT_SELECT_PRIVATE_KEY) != 0) { + /* Private keys are not supported */ + } else if ((selection & OSSL_KEYMGMT_SELECT_PUBLIC_KEY) != 0) { + if (BIO_printf(out, "LMS Public-Key:\n") <= 0) + return 0; + } + if (!ossl_bio_print_labeled_buf(out, "pub:", key->pub.encoded, key->pub.encodedlen)) + return 0; + if (!ossl_bio_print_labeled_buf(out, "K:", key->pub.K, lms_params->n)) + return 0; + return 1; +} diff --git a/providers/implementations/include/prov/implementations.h b/providers/implementations/include/prov/implementations.h index 86f876a0db7..7ac15cf0d0a 100644 --- a/providers/implementations/include/prov/implementations.h +++ b/providers/implementations/include/prov/implementations.h @@ -748,6 +748,10 @@ extern const OSSL_DISPATCH ossl_slh_dsa_shake_192f_to_text_encoder_functions[]; extern const OSSL_DISPATCH ossl_slh_dsa_shake_256s_to_text_encoder_functions[]; extern const OSSL_DISPATCH ossl_slh_dsa_shake_256f_to_text_encoder_functions[]; +extern const OSSL_DISPATCH ossl_lms_to_SubjectPublicKeyInfo_der_encoder_functions[]; +extern const OSSL_DISPATCH ossl_lms_to_SubjectPublicKeyInfo_pem_encoder_functions[]; +extern const OSSL_DISPATCH ossl_lms_to_text_encoder_functions[]; + /* Decoders */ extern const OSSL_DISPATCH ossl_PrivateKeyInfo_der_to_dh_decoder_functions[]; extern const OSSL_DISPATCH ossl_SubjectPublicKeyInfo_der_to_dh_decoder_functions[]; @@ -864,6 +868,7 @@ extern const OSSL_DISPATCH ossl_file_store_functions[]; extern const OSSL_DISPATCH ossl_winstore_store_functions[]; extern const OSSL_DISPATCH ossl_xdr_to_lms_decoder_functions[]; +extern const OSSL_DISPATCH ossl_SubjectPublicKeyInfo_der_to_lms_decoder_functions[]; extern const OSSL_DISPATCH ossl_PrivateKeyInfo_der_to_ml_dsa_44_decoder_functions[]; extern const OSSL_DISPATCH ossl_SubjectPublicKeyInfo_der_to_ml_dsa_44_decoder_functions[]; diff --git a/providers/implementations/include/prov/lms_codecs.h b/providers/implementations/include/prov/lms_codecs.h new file mode 100644 index 00000000000..d8dc96045ee --- /dev/null +++ b/providers/implementations/include/prov/lms_codecs.h @@ -0,0 +1,25 @@ +/* + * Copyright 2025 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 + */ + +#ifndef PROV_LMS_CODECS_H +#define PROV_LMS_CODECS_H +#pragma once + +#ifndef OPENSSL_NO_LMS +#include +#include "crypto/lms.h" +#include "prov/provider_ctx.h" + +__owur LMS_KEY * +ossl_lms_d2i_PUBKEY(const uint8_t *pubenc, int publen, PROV_CTX *provctx); +__owur int ossl_lms_i2d_pubkey(const LMS_KEY *key, unsigned char **out); +__owur int ossl_lms_key_to_text(BIO *out, const LMS_KEY *key, int selection); + +#endif /* OPENSSL_NO_LMS */ +#endif /* PROV_LMS_CODECS_H */ diff --git a/providers/implementations/include/prov/names.h b/providers/implementations/include/prov/names.h index 8bfed63ff5a..a7006be4be8 100644 --- a/providers/implementations/include/prov/names.h +++ b/providers/implementations/include/prov/names.h @@ -416,7 +416,7 @@ #define PROV_DESCS_SM2 "OpenSSL SM2 implementation" #define PROV_NAMES_curveSM2 "curveSM2" #define PROV_DESCS_curveSM2 "OpenSSL curveSM2 implementation" -#define PROV_NAMES_LMS "LMS" +#define PROV_NAMES_LMS "LMS:id-alg-hss-lms-hashsig:1.2.840.113549.1.9.16.3.17" #define PROV_DESCS_LMS "OpenSSL LMS implementation" #define PROV_NAMES_ML_DSA_44 "ML-DSA-44:MLDSA44:2.16.840.1.101.3.4.3.17:id-ml-dsa-44" #define PROV_DESCS_ML_DSA_44 "OpenSSL ML-DSA-44 implementation" diff --git a/providers/implementations/keymgmt/lms_kmgmt.c b/providers/implementations/keymgmt/lms_kmgmt.c index 8d7cfcc6f4f..af1075b414b 100644 --- a/providers/implementations/keymgmt/lms_kmgmt.c +++ b/providers/implementations/keymgmt/lms_kmgmt.c @@ -28,6 +28,8 @@ static OSSL_FUNC_keymgmt_export_fn lms_export; static OSSL_FUNC_keymgmt_import_types_fn lms_imexport_types; static OSSL_FUNC_keymgmt_export_types_fn lms_imexport_types; static OSSL_FUNC_keymgmt_load_fn lms_load; +static OSSL_FUNC_keymgmt_gettable_params_fn lms_gettable_params; +static OSSL_FUNC_keymgmt_get_params_fn lms_get_params; #define LMS_POSSIBLE_SELECTIONS (OSSL_KEYMGMT_SELECT_PUBLIC_KEY) @@ -49,7 +51,7 @@ static int lms_has(const void *keydata, int selection) if (!ossl_prov_is_running() || key == NULL) return 0; - if ((selection & LMS_POSSIBLE_SELECTIONS) == 0) + if ((selection & OSSL_KEYMGMT_SELECT_KEYPAIR) == 0) return 1; /* the selection is not missing */ return ossl_lms_key_has(key, selection); @@ -150,6 +152,50 @@ static void *lms_load(const void *reference, size_t reference_sz) return NULL; } +static const OSSL_PARAM *lms_gettable_params(void *provctx) +{ + return lms_get_params_list; +} + +static int lms_get_params(void *keydata, OSSL_PARAM params[]) +{ + LMS_KEY *key = keydata; + const uint8_t *d; + size_t len; + struct lms_get_params_st p; + + if (key == NULL || !lms_get_params_decoder(params, &p)) + return 0; + + if (p.bits != NULL + && !OSSL_PARAM_set_size_t(p.bits, 8 * ossl_lms_key_get_pub_len(key))) + return 0; + + if (p.secbits != NULL + && !OSSL_PARAM_set_size_t(p.secbits, ossl_lms_key_get_collision_strength_bits(key))) + return 0; + + if (p.maxsize != NULL + && !OSSL_PARAM_set_size_t(p.maxsize, ossl_lms_key_get_sig_len(key))) + return 0; + + if (p.pubkey != NULL) { + d = ossl_lms_key_get_pub(key); + if (d != NULL) { + len = ossl_lms_key_get_pub_len(key); + if (!OSSL_PARAM_set_octet_string(p.pubkey, d, len)) + return 0; + } + } + /* + * This allows apps to use an empty digest, so that the old API + * for digest signing can be used. + */ + if (p.dgstp != NULL && !OSSL_PARAM_set_utf8_string(p.dgstp, "")) + return 0; + return 1; +} + const OSSL_DISPATCH ossl_lms_keymgmt_functions[] = { { OSSL_FUNC_KEYMGMT_NEW, (void (*)(void))lms_new_key }, { OSSL_FUNC_KEYMGMT_FREE, (void (*)(void))lms_free_key }, @@ -161,5 +207,7 @@ const OSSL_DISPATCH ossl_lms_keymgmt_functions[] = { { OSSL_FUNC_KEYMGMT_EXPORT, (void (*)(void))lms_export }, { OSSL_FUNC_KEYMGMT_EXPORT_TYPES, (void (*)(void))lms_imexport_types }, { OSSL_FUNC_KEYMGMT_LOAD, (void (*)(void))lms_load }, + { OSSL_FUNC_KEYMGMT_GET_PARAMS, (void (*)(void))lms_get_params }, + { OSSL_FUNC_KEYMGMT_GETTABLE_PARAMS, (void (*)(void))lms_gettable_params }, OSSL_DISPATCH_END }; diff --git a/providers/implementations/keymgmt/lms_kmgmt.inc.in b/providers/implementations/keymgmt/lms_kmgmt.inc.in index 59e1ed9f53c..1e34a1eabfc 100644 --- a/providers/implementations/keymgmt/lms_kmgmt.inc.in +++ b/providers/implementations/keymgmt/lms_kmgmt.inc.in @@ -14,3 +14,11 @@ use OpenSSL::paramnames qw(produce_param_decoder); {- produce_param_decoder('lms_import', (['OSSL_PKEY_PARAM_PUB_KEY', 'pub', 'octet_string'], )); -} + +{- produce_param_decoder('lms_get_params', + (['OSSL_PKEY_PARAM_BITS', 'bits', 'int'], + ['OSSL_PKEY_PARAM_SECURITY_BITS', 'secbits', 'int'], + ['OSSL_PKEY_PARAM_MAX_SIZE', 'maxsize', 'int'], + ['OSSL_PKEY_PARAM_MANDATORY_DIGEST', 'dgstp', 'utf8_string'], + ['OSSL_PKEY_PARAM_PUB_KEY', 'pubkey', 'octet_string'], + )); -} diff --git a/providers/implementations/signature/lms_signature.c b/providers/implementations/signature/lms_signature.c index 65ebe68c00e..b88b9a3a961 100644 --- a/providers/implementations/signature/lms_signature.c +++ b/providers/implementations/signature/lms_signature.c @@ -24,6 +24,8 @@ static OSSL_FUNC_signature_newctx_fn lms_newctx; static OSSL_FUNC_signature_freectx_fn lms_freectx; static OSSL_FUNC_signature_verify_message_init_fn lms_verify_msg_init; static OSSL_FUNC_signature_verify_fn lms_verify; +static OSSL_FUNC_signature_digest_verify_init_fn lms_digest_verify_init; +static OSSL_FUNC_signature_digest_verify_fn lms_digest_verify; typedef struct { OSSL_LIB_CTX *libctx; @@ -130,11 +132,37 @@ static int lms_verify(void *vctx, const unsigned char *sigbuf, size_t sigbuf_len return ret; } +static int lms_digest_verify_init(void *vctx, const char *mdname, void *vkey, + const OSSL_PARAM params[]) +{ + PROV_LMS_CTX *ctx = (PROV_LMS_CTX *)vctx; + + if (mdname != NULL && mdname[0] != '\0') { + ERR_raise_data(ERR_LIB_PROV, PROV_R_INVALID_DIGEST, + "Explicit digest not supported for LMS operations"); + return 0; + } + if (vkey == NULL && ctx->key != NULL) + return 1; /* lms_set_ctx_params(ctx, params); */ + + return lms_verify_msg_init(vctx, vkey, params); +} + +static int lms_digest_verify(void *vctx, const uint8_t *sig, size_t siglen, + const uint8_t *tbs, size_t tbslen) +{ + return lms_verify(vctx, sig, siglen, tbs, tbslen); +} + const OSSL_DISPATCH ossl_lms_signature_functions[] = { { OSSL_FUNC_SIGNATURE_NEWCTX, (void (*)(void))lms_newctx }, { OSSL_FUNC_SIGNATURE_FREECTX, (void (*)(void))lms_freectx }, { OSSL_FUNC_SIGNATURE_VERIFY_MESSAGE_INIT, (void (*)(void))lms_verify_msg_init }, { OSSL_FUNC_SIGNATURE_VERIFY, (void (*)(void))lms_verify }, + { OSSL_FUNC_SIGNATURE_DIGEST_VERIFY_INIT, + (void (*)(void))lms_digest_verify_init }, + { OSSL_FUNC_SIGNATURE_DIGEST_VERIFY, + (void (*)(void))lms_digest_verify }, OSSL_DISPATCH_END }; diff --git a/test/lms_test.c b/test/lms_test.c index 579da372768..82af0a2f060 100644 --- a/test/lms_test.c +++ b/test/lms_test.c @@ -281,15 +281,20 @@ static int lms_digest_verify_fail_test(void) LMS_ACVP_TEST_DATA *td = &lms_testdata[0]; EVP_PKEY *pub = NULL; EVP_MD_CTX *vctx = NULL; + int expected = 1; if (!TEST_ptr(pub = lms_pubkey_from_data(td->pub, td->publen))) return 0; if (!TEST_ptr(vctx = EVP_MD_CTX_new())) goto err; - /* Only one shot mode is supported, streaming fails to initialise */ + /* Prior to 4.0 EVP_DigestVerifyInit_ex is not supported */ + if (OSSL_PROVIDER_available(libctx, "fips") + && fips_provider_version_match(libctx, "<4.0.0")) + expected = 0; + if (!TEST_int_eq(EVP_DigestVerifyInit_ex(vctx, NULL, NULL, libctx, NULL, pub, NULL), - 0)) + expected)) goto err; ret = 1; err: diff --git a/test/recipes/15-test_lms_codecs.t b/test/recipes/15-test_lms_codecs.t new file mode 100644 index 00000000000..7f2d046dc51 --- /dev/null +++ b/test/recipes/15-test_lms_codecs.t @@ -0,0 +1,62 @@ +#! /usr/bin/env perl +# Copyright 2025 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 File::Spec; +use File::Copy; +use File::Compare qw/compare_text compare/; +use IO::File; +use OpenSSL::Glob; +use OpenSSL::Test qw/:DEFAULT data_file srctop_file bldtop_dir/; +use OpenSSL::Test::Utils; + +setup("test_lms_codecs"); + +# The test vectors were generated using modified Bouncy Castle tests +# from core/src/test/java/org/bouncycastle/pqc/crypto/test/LMSTest.java +my @algs = qw(sha256_n24_w1 shake_n24_w1 shake_n24_w2 shake_n24_w4 shake_n24_w8 shake_n32_w1 shake_n32_w8); + +plan skip_all => "LMS isn't supported in this build" + if disabled("lms"); + +plan tests => @algs * 7; + +foreach my $alg (@algs) { + my $pubpem =data_file(sprintf("%s_pub.pem", $alg)); + my $pubder = data_file(sprintf("%s_pub.der", $alg)); + my $pubtxt = data_file(sprintf("%s_pub.txt", $alg)); + my $msg = data_file(sprintf("%s_msg.bin", $alg)); + my $sig = data_file(sprintf("%s_sig.bin", $alg)); + my $outpubder = sprintf("%s_pubout.der", $alg); + my $outpubpem = sprintf("%s_pubout.pem", $alg); + my $outpubtxt = sprintf("%s_pubout.txt", $alg); + + # Load Public PEM and generate Public DER + ok(run(app([qw(openssl pkey -pubin -outform DER -in), + $pubpem, '-out', $outpubder]))); + ok(!compare($pubder, $outpubder), + sprintf("pubkey DER match: %s", $alg)); + + # Load Public DER and generate Public PEM + ok(run(app([qw(openssl pkey -pubin -inform DER -outform PEM -in), + $pubder, '-out', $outpubpem]))); + ok(!compare($pubpem, $outpubpem), + sprintf("pubkey PEM match: %s", $alg)); + + # Check text encoding + ok(run(app([qw(openssl pkey -pubin -noout -text -in), + $pubpem, '-out', $outpubtxt]))); + ok(!compare_text($pubtxt, $outpubtxt), + sprintf("pubkey TEXT match: %s", $alg)); + + # Perform verify + ok(run(app([qw(openssl pkeyutl -verify -rawin -pubin -inkey), + $pubpem, '-in', $msg, '-sigfile', $sig]))); +} diff --git a/test/recipes/15-test_lms_codecs_data/sha256_n24_w1_msg.bin b/test/recipes/15-test_lms_codecs_data/sha256_n24_w1_msg.bin new file mode 100644 index 00000000000..aa52d7284fb Binary files /dev/null and b/test/recipes/15-test_lms_codecs_data/sha256_n24_w1_msg.bin differ diff --git a/test/recipes/15-test_lms_codecs_data/sha256_n24_w1_pub.der b/test/recipes/15-test_lms_codecs_data/sha256_n24_w1_pub.der new file mode 100644 index 00000000000..aa33e955609 Binary files /dev/null and b/test/recipes/15-test_lms_codecs_data/sha256_n24_w1_pub.der differ diff --git a/test/recipes/15-test_lms_codecs_data/sha256_n24_w1_pub.pem b/test/recipes/15-test_lms_codecs_data/sha256_n24_w1_pub.pem new file mode 100644 index 00000000000..9525959d86f --- /dev/null +++ b/test/recipes/15-test_lms_codecs_data/sha256_n24_w1_pub.pem @@ -0,0 +1,4 @@ +-----BEGIN PUBLIC KEY----- +MEYwDQYLKoZIhvcNAQkQAxEDNQAAAAABAAAACgAAAAW7nBtfzxKGxB99zen7U6U5 +x+Pl7vX6uHi9NsBOq1Te98wljDKoQsT9 +-----END PUBLIC KEY----- diff --git a/test/recipes/15-test_lms_codecs_data/sha256_n24_w1_pub.txt b/test/recipes/15-test_lms_codecs_data/sha256_n24_w1_pub.txt new file mode 100644 index 00000000000..57dc5966902 --- /dev/null +++ b/test/recipes/15-test_lms_codecs_data/sha256_n24_w1_pub.txt @@ -0,0 +1,12 @@ +lms-type: SHA256-N24-H5 (0xa) +lm-ots-type: SHA256-N24-W1 (0x5) +Id: + bb:9c:1b:5f:cf:12:86:c4:1f:7d:cd:e9:fb:53:a5:39 +LMS Public-Key: +pub: + 00:00:00:0a:00:00:00:05:bb:9c:1b:5f:cf:12:86:c4: + 1f:7d:cd:e9:fb:53:a5:39:c7:e3:e5:ee:f5:fa:b8:78: + bd:36:c0:4e:ab:54:de:f7:cc:25:8c:32:a8:42:c4:fd +K: + c7:e3:e5:ee:f5:fa:b8:78:bd:36:c0:4e:ab:54:de:f7: + cc:25:8c:32:a8:42:c4:fd diff --git a/test/recipes/15-test_lms_codecs_data/sha256_n24_w1_sig.bin b/test/recipes/15-test_lms_codecs_data/sha256_n24_w1_sig.bin new file mode 100644 index 00000000000..573353d7f59 Binary files /dev/null and b/test/recipes/15-test_lms_codecs_data/sha256_n24_w1_sig.bin differ diff --git a/test/recipes/15-test_lms_codecs_data/shake_n24_w1_msg.bin b/test/recipes/15-test_lms_codecs_data/shake_n24_w1_msg.bin new file mode 100644 index 00000000000..289557b085e --- /dev/null +++ b/test/recipes/15-test_lms_codecs_data/shake_n24_w1_msg.bin @@ -0,0 +1,2 @@ +7–\æ7Ь_ô +þM¢0Gùx@&?¼Tˆòv "G('ûEÖhƒp»ò_¡©Ô5ãÖ7^‘¢½ ~hQSº´ÓN=±1ŒŸ·âzô—qß_u·ûv9÷놵ýՑƒ$Ș¦íCK9ÌÿÕ˙©&Ã|>ãj=؛çäŸWi \ No newline at end of file diff --git a/test/recipes/15-test_lms_codecs_data/shake_n24_w1_pub.der b/test/recipes/15-test_lms_codecs_data/shake_n24_w1_pub.der new file mode 100644 index 00000000000..db738593cc9 Binary files /dev/null and b/test/recipes/15-test_lms_codecs_data/shake_n24_w1_pub.der differ diff --git a/test/recipes/15-test_lms_codecs_data/shake_n24_w1_pub.pem b/test/recipes/15-test_lms_codecs_data/shake_n24_w1_pub.pem new file mode 100644 index 00000000000..61a6b6c7a0e --- /dev/null +++ b/test/recipes/15-test_lms_codecs_data/shake_n24_w1_pub.pem @@ -0,0 +1,4 @@ +-----BEGIN PUBLIC KEY----- +MEYwDQYLKoZIhvcNAQkQAxEDNQAAAAABAAAAGAAAAA3hiMFvJkCjYSJ8Y1MrEAPo +BgOLGt8IGv2dTCxXArrkJ9yn/UDYE1gw +-----END PUBLIC KEY----- diff --git a/test/recipes/15-test_lms_codecs_data/shake_n24_w1_pub.txt b/test/recipes/15-test_lms_codecs_data/shake_n24_w1_pub.txt new file mode 100644 index 00000000000..74d176b71d5 --- /dev/null +++ b/test/recipes/15-test_lms_codecs_data/shake_n24_w1_pub.txt @@ -0,0 +1,12 @@ +lms-type: SHAKE-N24-H25 (0x18) +lm-ots-type: SHAKE-N24-W1 (0xd) +Id: + e1:88:c1:6f:26:40:a3:61:22:7c:63:53:2b:10:03:e8 +LMS Public-Key: +pub: + 00:00:00:18:00:00:00:0d:e1:88:c1:6f:26:40:a3:61: + 22:7c:63:53:2b:10:03:e8:06:03:8b:1a:df:08:1a:fd: + 9d:4c:2c:57:02:ba:e4:27:dc:a7:fd:40:d8:13:58:30 +K: + 06:03:8b:1a:df:08:1a:fd:9d:4c:2c:57:02:ba:e4:27: + dc:a7:fd:40:d8:13:58:30 diff --git a/test/recipes/15-test_lms_codecs_data/shake_n24_w1_sig.bin b/test/recipes/15-test_lms_codecs_data/shake_n24_w1_sig.bin new file mode 100644 index 00000000000..9b8a45e6db5 Binary files /dev/null and b/test/recipes/15-test_lms_codecs_data/shake_n24_w1_sig.bin differ diff --git a/test/recipes/15-test_lms_codecs_data/shake_n24_w2_msg.bin b/test/recipes/15-test_lms_codecs_data/shake_n24_w2_msg.bin new file mode 100644 index 00000000000..5028abb9dec --- /dev/null +++ b/test/recipes/15-test_lms_codecs_data/shake_n24_w2_msg.bin @@ -0,0 +1,2 @@ +é^/ BšÁ¢(ßáôÑuV*áª"®Ççiø`ž$g;É8 û­:ÿR±¹}ì1¶ Ÿo?Z… +v+º|™Sáÿ_àÌö™Ü<0(¸>$uú²xI›ª;í—ñG;‚Ùr^§°¼¹Gõ/A÷ “Õ•-S]ïð«};R$ \ No newline at end of file diff --git a/test/recipes/15-test_lms_codecs_data/shake_n24_w2_pub.der b/test/recipes/15-test_lms_codecs_data/shake_n24_w2_pub.der new file mode 100644 index 00000000000..041cc4518cc Binary files /dev/null and b/test/recipes/15-test_lms_codecs_data/shake_n24_w2_pub.der differ diff --git a/test/recipes/15-test_lms_codecs_data/shake_n24_w2_pub.pem b/test/recipes/15-test_lms_codecs_data/shake_n24_w2_pub.pem new file mode 100644 index 00000000000..bd75db43ba0 --- /dev/null +++ b/test/recipes/15-test_lms_codecs_data/shake_n24_w2_pub.pem @@ -0,0 +1,4 @@ +-----BEGIN PUBLIC KEY----- +MEYwDQYLKoZIhvcNAQkQAxEDNQAAAAABAAAAFwAAAA5asYCHy4y+LHiEhTrF+45u +WeqnTn3NQY/AtFRb8S/IlLFjxDjHuZPS +-----END PUBLIC KEY----- diff --git a/test/recipes/15-test_lms_codecs_data/shake_n24_w2_pub.txt b/test/recipes/15-test_lms_codecs_data/shake_n24_w2_pub.txt new file mode 100644 index 00000000000..59572f28b01 --- /dev/null +++ b/test/recipes/15-test_lms_codecs_data/shake_n24_w2_pub.txt @@ -0,0 +1,12 @@ +lms-type: SHAKE-N24-H20 (0x17) +lm-ots-type: SHAKE-N24-W2 (0xe) +Id: + 5a:b1:80:87:cb:8c:be:2c:78:84:85:3a:c5:fb:8e:6e +LMS Public-Key: +pub: + 00:00:00:17:00:00:00:0e:5a:b1:80:87:cb:8c:be:2c: + 78:84:85:3a:c5:fb:8e:6e:59:ea:a7:4e:7d:cd:41:8f: + c0:b4:54:5b:f1:2f:c8:94:b1:63:c4:38:c7:b9:93:d2 +K: + 59:ea:a7:4e:7d:cd:41:8f:c0:b4:54:5b:f1:2f:c8:94: + b1:63:c4:38:c7:b9:93:d2 diff --git a/test/recipes/15-test_lms_codecs_data/shake_n24_w2_sig.bin b/test/recipes/15-test_lms_codecs_data/shake_n24_w2_sig.bin new file mode 100644 index 00000000000..a6c8730b739 Binary files /dev/null and b/test/recipes/15-test_lms_codecs_data/shake_n24_w2_sig.bin differ diff --git a/test/recipes/15-test_lms_codecs_data/shake_n24_w4_msg.bin b/test/recipes/15-test_lms_codecs_data/shake_n24_w4_msg.bin new file mode 100644 index 00000000000..76372b99fb1 Binary files /dev/null and b/test/recipes/15-test_lms_codecs_data/shake_n24_w4_msg.bin differ diff --git a/test/recipes/15-test_lms_codecs_data/shake_n24_w4_pub.der b/test/recipes/15-test_lms_codecs_data/shake_n24_w4_pub.der new file mode 100644 index 00000000000..5a00b90eaab Binary files /dev/null and b/test/recipes/15-test_lms_codecs_data/shake_n24_w4_pub.der differ diff --git a/test/recipes/15-test_lms_codecs_data/shake_n24_w4_pub.pem b/test/recipes/15-test_lms_codecs_data/shake_n24_w4_pub.pem new file mode 100644 index 00000000000..2b1aca7f668 --- /dev/null +++ b/test/recipes/15-test_lms_codecs_data/shake_n24_w4_pub.pem @@ -0,0 +1,4 @@ +-----BEGIN PUBLIC KEY----- +MEYwDQYLKoZIhvcNAQkQAxEDNQAAAAABAAAAFQAAAA8FRHkVfAnrfjSnkWv0wDTi +TwIBexRlvoUYM8uY201/i9qtrkT/IHG7 +-----END PUBLIC KEY----- diff --git a/test/recipes/15-test_lms_codecs_data/shake_n24_w4_pub.txt b/test/recipes/15-test_lms_codecs_data/shake_n24_w4_pub.txt new file mode 100644 index 00000000000..d95cd00a15e --- /dev/null +++ b/test/recipes/15-test_lms_codecs_data/shake_n24_w4_pub.txt @@ -0,0 +1,12 @@ +lms-type: SHAKE-N24-H10 (0x15) +lm-ots-type: SHAKE-N24-W4 (0xf) +Id: + 05:44:79:15:7c:09:eb:7e:34:a7:91:6b:f4:c0:34:e2 +LMS Public-Key: +pub: + 00:00:00:15:00:00:00:0f:05:44:79:15:7c:09:eb:7e: + 34:a7:91:6b:f4:c0:34:e2:4f:02:01:7b:14:65:be:85: + 18:33:cb:98:db:4d:7f:8b:da:ad:ae:44:ff:20:71:bb +K: + 4f:02:01:7b:14:65:be:85:18:33:cb:98:db:4d:7f:8b: + da:ad:ae:44:ff:20:71:bb diff --git a/test/recipes/15-test_lms_codecs_data/shake_n24_w4_sig.bin b/test/recipes/15-test_lms_codecs_data/shake_n24_w4_sig.bin new file mode 100644 index 00000000000..f2b7cce9afa Binary files /dev/null and b/test/recipes/15-test_lms_codecs_data/shake_n24_w4_sig.bin differ diff --git a/test/recipes/15-test_lms_codecs_data/shake_n24_w8_msg.bin b/test/recipes/15-test_lms_codecs_data/shake_n24_w8_msg.bin new file mode 100644 index 00000000000..4116f7fe2ac --- /dev/null +++ b/test/recipes/15-test_lms_codecs_data/shake_n24_w8_msg.bin @@ -0,0 +1,2 @@ + *6YŸ&uä˜ßNð¥p'Øl51ã?lzó‚‹âÁÐô†W$NÊ +°,ó,Χ½ìVÉߪ°)¨i0&Ä®õ­³]}-¢LßP“Ì•"]³6œæ†~êÁ9´¢|lV}†ùdÊÁ#òO^”àêBË$և¶ŽŠ½ÀÉt€p2 \ No newline at end of file diff --git a/test/recipes/15-test_lms_codecs_data/shake_n24_w8_pub.der b/test/recipes/15-test_lms_codecs_data/shake_n24_w8_pub.der new file mode 100644 index 00000000000..55d9a499ca3 Binary files /dev/null and b/test/recipes/15-test_lms_codecs_data/shake_n24_w8_pub.der differ diff --git a/test/recipes/15-test_lms_codecs_data/shake_n24_w8_pub.pem b/test/recipes/15-test_lms_codecs_data/shake_n24_w8_pub.pem new file mode 100644 index 00000000000..b9d91ba8402 --- /dev/null +++ b/test/recipes/15-test_lms_codecs_data/shake_n24_w8_pub.pem @@ -0,0 +1,4 @@ +-----BEGIN PUBLIC KEY----- +MEYwDQYLKoZIhvcNAQkQAxEDNQAAAAABAAAAFgAAABBKkKgaFDJ5ZZeXtXKGbK0k +Wz5VJo3faGque7ml53MEoMgiDstCvQCI +-----END PUBLIC KEY----- diff --git a/test/recipes/15-test_lms_codecs_data/shake_n24_w8_pub.txt b/test/recipes/15-test_lms_codecs_data/shake_n24_w8_pub.txt new file mode 100644 index 00000000000..e1d16691350 --- /dev/null +++ b/test/recipes/15-test_lms_codecs_data/shake_n24_w8_pub.txt @@ -0,0 +1,12 @@ +lms-type: SHAKE-N24-H15 (0x16) +lm-ots-type: SHAKE-N24-W8 (0x10) +Id: + 4a:90:a8:1a:14:32:79:65:97:97:b5:72:86:6c:ad:24 +LMS Public-Key: +pub: + 00:00:00:16:00:00:00:10:4a:90:a8:1a:14:32:79:65: + 97:97:b5:72:86:6c:ad:24:5b:3e:55:26:8d:df:68:6a: + ae:7b:b9:a5:e7:73:04:a0:c8:22:0e:cb:42:bd:00:88 +K: + 5b:3e:55:26:8d:df:68:6a:ae:7b:b9:a5:e7:73:04:a0: + c8:22:0e:cb:42:bd:00:88 diff --git a/test/recipes/15-test_lms_codecs_data/shake_n24_w8_sig.bin b/test/recipes/15-test_lms_codecs_data/shake_n24_w8_sig.bin new file mode 100644 index 00000000000..2705ff88c18 Binary files /dev/null and b/test/recipes/15-test_lms_codecs_data/shake_n24_w8_sig.bin differ diff --git a/test/recipes/15-test_lms_codecs_data/shake_n32_w1_msg.bin b/test/recipes/15-test_lms_codecs_data/shake_n32_w1_msg.bin new file mode 100644 index 00000000000..c7066c13582 --- /dev/null +++ b/test/recipes/15-test_lms_codecs_data/shake_n32_w1_msg.bin @@ -0,0 +1 @@ +Ý«å¸ÁHiä¹Ï«èy˜Ö&´~`bP”lÖՁ]µóÄ×÷{ÿlÐ,†Ìš›o3ê÷n_º<Õ'ä%Ó–j†¬jdD(É7wy’ªîÇ; øÞô\ D•%ßGOÝØ5>ÿ‰˜012)ºùøÝºŠ¿€ú<žzg \ No newline at end of file diff --git a/test/recipes/15-test_lms_codecs_data/shake_n32_w1_pub.der b/test/recipes/15-test_lms_codecs_data/shake_n32_w1_pub.der new file mode 100644 index 00000000000..0474e535d40 Binary files /dev/null and b/test/recipes/15-test_lms_codecs_data/shake_n32_w1_pub.der differ diff --git a/test/recipes/15-test_lms_codecs_data/shake_n32_w1_pub.pem b/test/recipes/15-test_lms_codecs_data/shake_n32_w1_pub.pem new file mode 100644 index 00000000000..745d68e8f16 --- /dev/null +++ b/test/recipes/15-test_lms_codecs_data/shake_n32_w1_pub.pem @@ -0,0 +1,4 @@ +-----BEGIN PUBLIC KEY----- +ME4wDQYLKoZIhvcNAQkQAxEDPQAAAAABAAAAEQAAAAl0e74SphV4ef9P0i/rzbVX +ZhA1zoQ7v9BdHYPpf5Y1di1uJ0J48CEp4+bi4BKFnx4= +-----END PUBLIC KEY----- diff --git a/test/recipes/15-test_lms_codecs_data/shake_n32_w1_pub.txt b/test/recipes/15-test_lms_codecs_data/shake_n32_w1_pub.txt new file mode 100644 index 00000000000..685066ea944 --- /dev/null +++ b/test/recipes/15-test_lms_codecs_data/shake_n32_w1_pub.txt @@ -0,0 +1,13 @@ +lms-type: SHAKE-N32-H15 (0x11) +lm-ots-type: SHAKE-N32-W1 (0x9) +Id: + 74:7b:be:12:a6:15:78:79:ff:4f:d2:2f:eb:cd:b5:57 +LMS Public-Key: +pub: + 00:00:00:11:00:00:00:09:74:7b:be:12:a6:15:78:79: + ff:4f:d2:2f:eb:cd:b5:57:66:10:35:ce:84:3b:bf:d0: + 5d:1d:83:e9:7f:96:35:76:2d:6e:27:42:78:f0:21:29: + e3:e6:e2:e0:12:85:9f:1e +K: + 66:10:35:ce:84:3b:bf:d0:5d:1d:83:e9:7f:96:35:76: + 2d:6e:27:42:78:f0:21:29:e3:e6:e2:e0:12:85:9f:1e diff --git a/test/recipes/15-test_lms_codecs_data/shake_n32_w1_sig.bin b/test/recipes/15-test_lms_codecs_data/shake_n32_w1_sig.bin new file mode 100644 index 00000000000..80da90a09d8 Binary files /dev/null and b/test/recipes/15-test_lms_codecs_data/shake_n32_w1_sig.bin differ diff --git a/test/recipes/15-test_lms_codecs_data/shake_n32_w8_msg.bin b/test/recipes/15-test_lms_codecs_data/shake_n32_w8_msg.bin new file mode 100644 index 00000000000..637df1950f6 --- /dev/null +++ b/test/recipes/15-test_lms_codecs_data/shake_n32_w8_msg.bin @@ -0,0 +1 @@ +K*ÙÇDぅ½“ÀWŠÉªoâ³¼?ã^¥i„|ç—Ò@ØF¿­×*xOFó“±ÁQp® ¥ie é†çùÕ`°'r‚Y&¥ñH,PŸŠ½÷‚gJ_OÛÈ=Yƒžô7;¿aj-¥" ´Å\ÅO%u’—É—ìçsz†H \ No newline at end of file diff --git a/test/recipes/15-test_lms_codecs_data/shake_n32_w8_pub.der b/test/recipes/15-test_lms_codecs_data/shake_n32_w8_pub.der new file mode 100644 index 00000000000..95bd5af430a Binary files /dev/null and b/test/recipes/15-test_lms_codecs_data/shake_n32_w8_pub.der differ diff --git a/test/recipes/15-test_lms_codecs_data/shake_n32_w8_pub.pem b/test/recipes/15-test_lms_codecs_data/shake_n32_w8_pub.pem new file mode 100644 index 00000000000..4ffcce60c67 --- /dev/null +++ b/test/recipes/15-test_lms_codecs_data/shake_n32_w8_pub.pem @@ -0,0 +1,4 @@ +-----BEGIN PUBLIC KEY----- +ME4wDQYLKoZIhvcNAQkQAxEDPQAAAAABAAAAEwAAAAyXL+d31A/h79l2A6YnM80c +0tbxPFSghfIwF7vUaWXAhTq8Nj+rztkRmiYzejBqe4g= +-----END PUBLIC KEY----- diff --git a/test/recipes/15-test_lms_codecs_data/shake_n32_w8_pub.txt b/test/recipes/15-test_lms_codecs_data/shake_n32_w8_pub.txt new file mode 100644 index 00000000000..bd82d8cf904 --- /dev/null +++ b/test/recipes/15-test_lms_codecs_data/shake_n32_w8_pub.txt @@ -0,0 +1,13 @@ +lms-type: SHAKE-N32-H25 (0x13) +lm-ots-type: SHAKE-N32-W8 (0xc) +Id: + 97:2f:e7:77:d4:0f:e1:ef:d9:76:03:a6:27:33:cd:1c +LMS Public-Key: +pub: + 00:00:00:13:00:00:00:0c:97:2f:e7:77:d4:0f:e1:ef: + d9:76:03:a6:27:33:cd:1c:d2:d6:f1:3c:54:a0:85:f2: + 30:17:bb:d4:69:65:c0:85:3a:bc:36:3f:ab:ce:d9:11: + 9a:26:33:7a:30:6a:7b:88 +K: + d2:d6:f1:3c:54:a0:85:f2:30:17:bb:d4:69:65:c0:85: + 3a:bc:36:3f:ab:ce:d9:11:9a:26:33:7a:30:6a:7b:88 diff --git a/test/recipes/15-test_lms_codecs_data/shake_n32_w8_sig.bin b/test/recipes/15-test_lms_codecs_data/shake_n32_w8_sig.bin new file mode 100644 index 00000000000..39a2faf9d1c Binary files /dev/null and b/test/recipes/15-test_lms_codecs_data/shake_n32_w8_sig.bin differ