From df231a88abab21c31274b6dd0fb8ff90efebbbfe Mon Sep 17 00:00:00 2001 From: slontis Date: Fri, 10 Jan 2025 12:41:12 +1100 Subject: [PATCH] Add ML_DSA encoders Reviewed-by: Viktor Dukhovni Reviewed-by: Paul Dale (Merged from https://github.com/openssl/openssl/pull/26451) --- crypto/err/openssl.txt | 2 + crypto/ml_dsa/ml_dsa_key.c | 4 +- crypto/ml_dsa/ml_dsa_local.h | 2 - include/crypto/ml_dsa.h | 5 +- include/openssl/evp.h | 4 + include/openssl/proverr.h | 4 +- providers/common/include/prov/proverr.h | 2 +- providers/common/provider_err.c | 6 +- providers/decoders.inc | 8 + providers/encoders.inc | 29 +++ .../encode_decode/decode_der2key.c | 194 ++++++++++++++++++ .../encode_decode/encode_key2any.c | 76 +++++++ .../encode_decode/encode_key2text.c | 50 +++++ .../include/prov/implementations.h | 36 ++++ test/endecode_test.c | 30 ++- 15 files changed, 443 insertions(+), 9 deletions(-) diff --git a/crypto/err/openssl.txt b/crypto/err/openssl.txt index decf2d6f2ec..3b8183da4f5 100644 --- a/crypto/err/openssl.txt +++ b/crypto/err/openssl.txt @@ -1172,6 +1172,8 @@ PROV_R_UNABLE_TO_INITIALISE_CIPHERS:208:unable to initialise ciphers PROV_R_UNABLE_TO_LOAD_SHA256:147:unable to load sha256 PROV_R_UNABLE_TO_LOCK_PARENT:201:unable to lock parent PROV_R_UNABLE_TO_RESEED:204:unable to reseed +PROV_R_UNEXPECTED_KEY_OID:245:unexpected key oid +PROV_R_UNEXPECTED_KEY_PARAMETERS:246:unexpected key parameters PROV_R_UNSUPPORTED_CEK_ALG:145:unsupported cek alg PROV_R_UNSUPPORTED_KEY_SIZE:153:unsupported key size PROV_R_UNSUPPORTED_MAC_TYPE:137:unsupported mac type diff --git a/crypto/ml_dsa/ml_dsa_key.c b/crypto/ml_dsa/ml_dsa_key.c index ff9728f964d..00f1dc4a367 100644 --- a/crypto/ml_dsa/ml_dsa_key.c +++ b/crypto/ml_dsa/ml_dsa_key.c @@ -205,7 +205,7 @@ err: return 1; } -static int ml_dsa_key_public_from_private(ML_DSA_KEY *key) +int ossl_ml_dsa_key_public_from_private(ML_DSA_KEY *key) { int ret = 0; VECTOR t0; @@ -263,7 +263,7 @@ int ossl_ml_dsa_key_fromdata(ML_DSA_KEY *key, const OSSL_PARAM params[], || !ossl_ml_dsa_sk_decode(key, priv_data, priv_data_len)) return 0; /* Always generate the public key from the private key */ - if (!ml_dsa_key_public_from_private(key)) + if (!ossl_ml_dsa_key_public_from_private(key)) return 0; /* Error if the supplied public key does not match the generated key */ if (pub != NULL diff --git a/crypto/ml_dsa/ml_dsa_local.h b/crypto/ml_dsa/ml_dsa_local.h index a93b6b64fc5..9bc56f562bd 100644 --- a/crypto/ml_dsa/ml_dsa_local.h +++ b/crypto/ml_dsa/ml_dsa_local.h @@ -93,9 +93,7 @@ uint32_t ossl_ml_dsa_key_compress_use_hint(uint32_t hint, uint32_t r, uint32_t gamma2); int ossl_ml_dsa_pk_encode(ML_DSA_KEY *key); -int ossl_ml_dsa_pk_decode(ML_DSA_KEY *key, const uint8_t *in, size_t in_len); int ossl_ml_dsa_sk_encode(ML_DSA_KEY *key); -int ossl_ml_dsa_sk_decode(ML_DSA_KEY *key, const uint8_t *in, size_t in_len); int ossl_ml_dsa_sig_encode(const ML_DSA_SIG *sig, const ML_DSA_PARAMS *params, uint8_t *out); diff --git a/include/crypto/ml_dsa.h b/include/crypto/ml_dsa.h index 0434a2baf80..2008da79683 100644 --- a/include/crypto/ml_dsa.h +++ b/include/crypto/ml_dsa.h @@ -41,9 +41,12 @@ __owur size_t ossl_ml_dsa_key_get_priv_len(const ML_DSA_KEY *key); __owur size_t ossl_ml_dsa_key_get_sig_len(const ML_DSA_KEY *key); __owur const char *ossl_ml_dsa_key_get_name(const ML_DSA_KEY *key); __owur int ossl_ml_dsa_key_matches(const ML_DSA_KEY *key, const char *alg); -__owur int ossl_ml_dsa_key_to_text(BIO *out, const ML_DSA_KEY *key, int selection); void ossl_ml_dsa_key_set0_libctx(ML_DSA_KEY *key, OSSL_LIB_CTX *lib_ctx); +__owur int ossl_ml_dsa_key_public_from_private(ML_DSA_KEY *key); +__owur int ossl_ml_dsa_pk_decode(ML_DSA_KEY *key, const uint8_t *in, size_t in_len); +__owur int ossl_ml_dsa_sk_decode(ML_DSA_KEY *key, const uint8_t *in, size_t in_len); + __owur int ossl_ml_dsa_sign(const ML_DSA_KEY *priv, const uint8_t *msg, size_t msg_len, const uint8_t *context, size_t context_len, diff --git a/include/openssl/evp.h b/include/openssl/evp.h index 01caaadcde0..00570edc17e 100644 --- a/include/openssl/evp.h +++ b/include/openssl/evp.h @@ -83,6 +83,10 @@ # define EVP_PKEY_ED25519 NID_ED25519 # define EVP_PKEY_X448 NID_X448 # define EVP_PKEY_ED448 NID_ED448 +# define EVP_PKEY_ML_DSA_44 NID_ML_DSA_44 +# define EVP_PKEY_ML_DSA_65 NID_ML_DSA_65 +# define EVP_PKEY_ML_DSA_87 NID_ML_DSA_87 + /* Special indicator that the object is uniquely provider side */ # define EVP_PKEY_KEYMGMT -1 diff --git a/include/openssl/proverr.h b/include/openssl/proverr.h index d10b653152e..091549ddd8c 100644 --- a/include/openssl/proverr.h +++ b/include/openssl/proverr.h @@ -1,6 +1,6 @@ /* * Generated by util/mkerr.pl DO NOT EDIT - * Copyright 1995-2024 The OpenSSL Project Authors. All Rights Reserved. + * Copyright 1995-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 @@ -146,6 +146,8 @@ # define PROV_R_UNABLE_TO_LOAD_SHA256 147 # define PROV_R_UNABLE_TO_LOCK_PARENT 201 # define PROV_R_UNABLE_TO_RESEED 204 +# define PROV_R_UNEXPECTED_KEY_OID 245 +# define PROV_R_UNEXPECTED_KEY_PARAMETERS 246 # define PROV_R_UNSUPPORTED_CEK_ALG 145 # define PROV_R_UNSUPPORTED_KEY_SIZE 153 # define PROV_R_UNSUPPORTED_MAC_TYPE 137 diff --git a/providers/common/include/prov/proverr.h b/providers/common/include/prov/proverr.h index 34247ed2f7e..87a84e9c21f 100644 --- a/providers/common/include/prov/proverr.h +++ b/providers/common/include/prov/proverr.h @@ -1,6 +1,6 @@ /* * Generated by util/mkerr.pl DO NOT EDIT - * Copyright 2020-2024 The OpenSSL Project Authors. All Rights Reserved. + * Copyright 2020-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 diff --git a/providers/common/provider_err.c b/providers/common/provider_err.c index df4bab0966d..03375d6bbd2 100644 --- a/providers/common/provider_err.c +++ b/providers/common/provider_err.c @@ -1,6 +1,6 @@ /* * Generated by util/mkerr.pl DO NOT EDIT - * Copyright 1995-2024 The OpenSSL Project Authors. All Rights Reserved. + * Copyright 1995-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 @@ -215,6 +215,10 @@ static const ERR_STRING_DATA PROV_str_reasons[] = { {ERR_PACK(ERR_LIB_PROV, 0, PROV_R_UNABLE_TO_LOCK_PARENT), "unable to lock parent"}, {ERR_PACK(ERR_LIB_PROV, 0, PROV_R_UNABLE_TO_RESEED), "unable to reseed"}, + {ERR_PACK(ERR_LIB_PROV, 0, PROV_R_UNEXPECTED_KEY_OID), + "unexpected key oid"}, + {ERR_PACK(ERR_LIB_PROV, 0, PROV_R_UNEXPECTED_KEY_PARAMETERS), + "unexpected key parameters"}, {ERR_PACK(ERR_LIB_PROV, 0, PROV_R_UNSUPPORTED_CEK_ALG), "unsupported cek alg"}, {ERR_PACK(ERR_LIB_PROV, 0, PROV_R_UNSUPPORTED_KEY_SIZE), diff --git a/providers/decoders.inc b/providers/decoders.inc index 0191aa771ea..9effdd08359 100644 --- a/providers/decoders.inc +++ b/providers/decoders.inc @@ -82,6 +82,14 @@ DECODER_w_structure("RSA-PSS", der, PrivateKeyInfo, rsapss, yes), DECODER_w_structure("RSA-PSS", der, SubjectPublicKeyInfo, rsapss, yes), DECODER("RSA", msblob, rsa, yes), DECODER("RSA", pvk, rsa, yes), +#ifndef OPENSSL_NO_ML_DSA +DECODER_w_structure("ML-DSA-44", der, PrivateKeyInfo, ml_dsa_44, no), +DECODER_w_structure("ML-DSA-65", der, PrivateKeyInfo, ml_dsa_65, no), +DECODER_w_structure("ML-DSA-87", der, PrivateKeyInfo, ml_dsa_87, no), +DECODER_w_structure("ML-DSA-44", der, SubjectPublicKeyInfo, ml_dsa_44, no), +DECODER_w_structure("ML-DSA-65", der, SubjectPublicKeyInfo, ml_dsa_65, no), +DECODER_w_structure("ML-DSA-87", der, SubjectPublicKeyInfo, ml_dsa_87, no), +#endif /* OPENSSL_NO_ML_DSA */ /* * A decoder that takes a SubjectPublicKeyInfo and figures out the types of key diff --git a/providers/encoders.inc b/providers/encoders.inc index cd0d1137bb6..10a838c0a79 100644 --- a/providers/encoders.inc +++ b/providers/encoders.inc @@ -68,6 +68,12 @@ ENCODER_TEXT("SM2", sm2, no), # endif #endif +# ifndef OPENSSL_NO_ML_DSA +ENCODER_TEXT("ML-DSA-44", ml_dsa_44, no), +ENCODER_TEXT("ML-DSA-65", ml_dsa_65, no), +ENCODER_TEXT("ML-DSA-87", ml_dsa_87, no), +# endif + /* * Entries for key type specific output formats. The structure name on these * is the same as the key type name. This allows us to say something like: @@ -223,6 +229,29 @@ ENCODER_w_structure("SM2", sm2, no, pem, SubjectPublicKeyInfo), # endif #endif +# ifndef OPENSSL_NO_ML_DSA +ENCODER_w_structure("ML-DSA-44", ml_dsa_44, no, der, EncryptedPrivateKeyInfo), +ENCODER_w_structure("ML-DSA-44", ml_dsa_44, no, pem, EncryptedPrivateKeyInfo), +ENCODER_w_structure("ML-DSA-44", ml_dsa_44, no, der, PrivateKeyInfo), +ENCODER_w_structure("ML-DSA-44", ml_dsa_44, no, pem, PrivateKeyInfo), +ENCODER_w_structure("ML-DSA-44", ml_dsa_44, no, der, SubjectPublicKeyInfo), +ENCODER_w_structure("ML-DSA-44", ml_dsa_44, no, pem, SubjectPublicKeyInfo), + +ENCODER_w_structure("ML-DSA-65", ml_dsa_65, no, der, EncryptedPrivateKeyInfo), +ENCODER_w_structure("ML-DSA-65", ml_dsa_65, no, pem, EncryptedPrivateKeyInfo), +ENCODER_w_structure("ML-DSA-65", ml_dsa_65, no, der, PrivateKeyInfo), +ENCODER_w_structure("ML-DSA-65", ml_dsa_65, no, pem, PrivateKeyInfo), +ENCODER_w_structure("ML-DSA-65", ml_dsa_65, no, der, SubjectPublicKeyInfo), +ENCODER_w_structure("ML-DSA-65", ml_dsa_65, no, pem, SubjectPublicKeyInfo), + +ENCODER_w_structure("ML-DSA-87", ml_dsa_87, no, der, EncryptedPrivateKeyInfo), +ENCODER_w_structure("ML-DSA-87", ml_dsa_87, no, pem, EncryptedPrivateKeyInfo), +ENCODER_w_structure("ML-DSA-87", ml_dsa_87, no, der, PrivateKeyInfo), +ENCODER_w_structure("ML-DSA-87", ml_dsa_87, no, pem, PrivateKeyInfo), +ENCODER_w_structure("ML-DSA-87", ml_dsa_87, no, der, SubjectPublicKeyInfo), +ENCODER_w_structure("ML-DSA-87", ml_dsa_87, no, pem, SubjectPublicKeyInfo), +# endif /* OPENSSL_NO_ML_DSA */ + /* * Entries for key type specific output formats. These are exactly the * same as the type specific above, except that they use the key type diff --git a/providers/implementations/encode_decode/decode_der2key.c b/providers/implementations/encode_decode/decode_der2key.c index e33ae49b51f..e52fa5c9304 100644 --- a/providers/implementations/encode_decode/decode_der2key.c +++ b/providers/implementations/encode_decode/decode_der2key.c @@ -23,6 +23,7 @@ #include #include #include +#include #include "internal/cryptlib.h" /* ossl_assert() */ #include "internal/asn1.h" #include "crypto/dh.h" @@ -31,6 +32,7 @@ #include "crypto/evp.h" #include "crypto/ecx.h" #include "crypto/rsa.h" +#include "crypto/ml_dsa.h" #include "crypto/x509.h" #include "openssl/obj_mac.h" #include "prov/bio.h" @@ -38,6 +40,26 @@ #include "endecoder_local.h" #include "internal/nelem.h" +#ifndef OPENSSL_NO_ML_DSA +typedef struct { + ASN1_OBJECT *oid; +} BARE_ALGOR; + +typedef struct { + BARE_ALGOR algor; + ASN1_BIT_STRING *pubkey; +} BARE_PUBKEY; + +ASN1_SEQUENCE(BARE_ALGOR) = { + ASN1_SIMPLE(BARE_ALGOR, oid, ASN1_OBJECT), +} static_ASN1_SEQUENCE_END(BARE_ALGOR) + +ASN1_SEQUENCE(BARE_PUBKEY) = { + ASN1_EMBED(BARE_PUBKEY, algor, BARE_ALGOR), + ASN1_SIMPLE(BARE_PUBKEY, pubkey, ASN1_BIT_STRING) +} static_ASN1_SEQUENCE_END(BARE_PUBKEY) +#endif /* OPENSSL_NO_ML_DSA */ + struct der2key_ctx_st; /* Forward declaration */ typedef int check_key_fn(void *, struct der2key_ctx_st *ctx); typedef void adjust_key_fn(void *, struct der2key_ctx_st *ctx); @@ -614,6 +636,169 @@ static void rsa_adjust(void *key, struct der2key_ctx_st *ctx) /* ---------------------------------------------------------------------- */ +#ifndef OPENSSL_NO_ML_DSA +static void * +ml_dsa_d2i_PKCS8(const uint8_t **der, long der_len, struct der2key_ctx_st *ctx) +{ + ML_DSA_KEY *key = NULL, *ret = NULL; + OSSL_LIB_CTX *libctx = PROV_LIBCTX_OF(ctx->provctx); + PKCS8_PRIV_KEY_INFO *p8inf = NULL; + const unsigned char *p; + const X509_ALGOR *alg = NULL; + int plen, ptype; + + /* + * The private key format in PKCS8 is the 64-bytes (d, z) seed pair. + * Algorithm parameters must be absent. + */ + if ((p8inf = d2i_PKCS8_PRIV_KEY_INFO(NULL, der, der_len)) == NULL + || !PKCS8_pkey_get0(NULL, &p, &plen, &alg, p8inf)) + goto end; + + if ((X509_ALGOR_get0(NULL, &ptype, NULL, alg), ptype != V_ASN1_UNDEF)) { + ERR_raise_data(ERR_LIB_PROV, PROV_R_UNEXPECTED_KEY_PARAMETERS, + "unexpected parameters with a PKCS#8 %s private key", + ctx->desc->keytype_name); + goto end; + } + if (OBJ_obj2nid(alg->algorithm) != ctx->desc->evp_type) { + ERR_raise_data(ERR_LIB_PROV, PROV_R_UNEXPECTED_KEY_OID, + "unexpected algorithm OID for a PKCS#8 %s private key", + ctx->desc->keytype_name); + goto end; + } + if ((key = ossl_ml_dsa_key_new(libctx, ctx->propq, + ctx->desc->keytype_name)) == NULL) + goto end; + + if (!ossl_ml_dsa_sk_decode(key, p, plen) + || !ossl_ml_dsa_key_public_from_private(key)) + goto end; + ret = key; + end: + PKCS8_PRIV_KEY_INFO_free(p8inf); + if (ret == NULL) + ossl_ml_dsa_key_free(key); + return ret; +} + +static ossl_inline void * ml_dsa_d2i_PUBKEY(const uint8_t **der, long der_len, + struct der2key_ctx_st *ctx) +{ + int ok = 0; + OSSL_LIB_CTX *libctx = PROV_LIBCTX_OF(ctx->provctx); + ML_DSA_KEY *ret = NULL; + BARE_PUBKEY *spki = NULL; + const uint8_t *end = *der; + size_t len; + + ret = ossl_ml_dsa_key_new(libctx, ctx->propq, ctx->desc->keytype_name); + if (ret == NULL) + return NULL; + len = ossl_ml_dsa_key_get_pub_len(ret); + + /*- + * The DER ASN.1 encoding of ML-DSA public keys prepends 22 bytes to the + * encoded public key: + * + * - 4 byte outer sequence tag and length + * - 2 byte algorithm sequence tag and length + * - 2 byte algorithm OID tag and length + * - 9 byte algorithm OID + * - 4 byte bit string tag and length + * - 1 bitstring lead byte + * + * Check that we have the right OID, the bit string has no "bits left" and + * that we consume all the input exactly. + */ + if (der_len != 22 + (long)len) { + ERR_raise_data(ERR_LIB_PROV, PROV_R_BAD_ENCODING, + "unexpected %s public key length: %ld != %ld", + ctx->desc->keytype_name, der_len, + 22 + (long)len); + goto err; + } + + if ((spki = OPENSSL_zalloc(sizeof(*spki))) == NULL) + goto err; + + /* The spki storage is freed on error */ + if (ASN1_item_d2i_ex((ASN1_VALUE **)&spki, &end, der_len, + ASN1_ITEM_rptr(BARE_PUBKEY), NULL, NULL) == NULL) { + ERR_raise_data(ERR_LIB_PROV, PROV_R_BAD_ENCODING, + "malformed %s public key ASN.1 encoding", + ossl_ml_dsa_key_get_name(ret)); + goto err; + } + + /* The spki structure now owns some memory */ + if ((spki->pubkey->flags & 0x7) != 0 || end != *der + der_len) { + ERR_raise_data(ERR_LIB_PROV, PROV_R_BAD_ENCODING, + "malformed %s public key ASN.1 encoding", + ossl_ml_dsa_key_get_name(ret)); + goto err; + } + if (OBJ_cmp(OBJ_nid2obj(ctx->desc->evp_type), spki->algor.oid) != 0) { + ERR_raise_data(ERR_LIB_PROV, PROV_R_BAD_ENCODING, + "unexpected algorithm OID for an %s public key", + ossl_ml_dsa_key_get_name(ret)); + goto err; + } + + if (!ossl_ml_dsa_pk_decode(ret, spki->pubkey->data, spki->pubkey->length)) { + ERR_raise_data(ERR_LIB_PROV, PROV_R_BAD_ENCODING, + "failed to parse %s public key from the input data", + ossl_ml_dsa_key_get_name(ret)); + goto err; + } + ok = 1; + err: + if (spki != NULL) { + ASN1_OBJECT_free(spki->algor.oid); + ASN1_BIT_STRING_free(spki->pubkey); + OPENSSL_free(spki); + } + if (!ok) { + ossl_ml_dsa_key_free(ret); + ret = NULL; + } + return ret; +} + +# define ml_dsa_44_evp_type EVP_PKEY_ML_DSA_44 +# define ml_dsa_44_d2i_private_key NULL +# define ml_dsa_44_d2i_public_key NULL +# define ml_dsa_44_d2i_key_params NULL +# define ml_dsa_44_d2i_PUBKEY ml_dsa_d2i_PUBKEY +# define ml_dsa_44_d2i_PKCS8 ml_dsa_d2i_PKCS8 +# define ml_dsa_44_free (free_key_fn *)ossl_ml_dsa_key_free +# define ml_dsa_44_check NULL +# define ml_dsa_44_adjust NULL + +# define ml_dsa_65_evp_type EVP_PKEY_ML_DSA_65 +# define ml_dsa_65_d2i_private_key NULL +# define ml_dsa_65_d2i_public_key NULL +# define ml_dsa_65_d2i_key_params NULL +# define ml_dsa_65_d2i_PUBKEY ml_dsa_d2i_PUBKEY +# define ml_dsa_65_d2i_PKCS8 ml_dsa_d2i_PKCS8 +# define ml_dsa_65_free (free_key_fn *)ossl_ml_dsa_key_free +# define ml_dsa_65_check NULL +# define ml_dsa_65_adjust NULL + +# define ml_dsa_87_evp_type EVP_PKEY_ML_DSA_87 +# define ml_dsa_87_d2i_private_key NULL +# define ml_dsa_87_d2i_public_key NULL +# define ml_dsa_87_d2i_PUBKEY ml_dsa_d2i_PUBKEY +# define ml_dsa_87_d2i_PKCS8 ml_dsa_d2i_PKCS8 +# define ml_dsa_87_d2i_key_params NULL +# define ml_dsa_87_free (free_key_fn *)ossl_ml_dsa_key_free +# define ml_dsa_87_check NULL +# define ml_dsa_87_adjust NULL + +#endif + +/* ---------------------------------------------------------------------- */ + /* * The DO_ macros help define the selection mask and the method functions * for each kind of object we want to decode. @@ -872,3 +1057,12 @@ MAKE_DECODER("RSA", rsa, rsa, type_specific_keypair); MAKE_DECODER("RSA", rsa, rsa, RSA); MAKE_DECODER("RSA-PSS", rsapss, rsapss, PrivateKeyInfo); MAKE_DECODER("RSA-PSS", rsapss, rsapss, SubjectPublicKeyInfo); + +#ifndef OPENSSL_NO_ML_DSA +MAKE_DECODER("ML-DSA-44", ml_dsa_44, ml_dsa_44, PrivateKeyInfo); +MAKE_DECODER("ML-DSA-44", ml_dsa_44, ml_dsa_44, SubjectPublicKeyInfo); +MAKE_DECODER("ML-DSA-65", ml_dsa_65, ml_dsa_65, PrivateKeyInfo); +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 diff --git a/providers/implementations/encode_decode/encode_key2any.c b/providers/implementations/encode_decode/encode_key2any.c index 846f936f811..d2c4ccaa230 100644 --- a/providers/implementations/encode_decode/encode_key2any.c +++ b/providers/implementations/encode_decode/encode_key2any.c @@ -30,6 +30,7 @@ #include "internal/cryptlib.h" #include "crypto/ecx.h" #include "crypto/rsa.h" +#include "crypto/ml_dsa.h" #include "prov/implementations.h" #include "prov/bio.h" #include "prov/provider_ctx.h" @@ -846,6 +847,58 @@ static int ecx_pki_priv_to_der(const void *vecxkey, unsigned char **pder, /* ---------------------------------------------------------------------- */ +#ifndef OPENSSL_NO_ML_DSA +static int ml_dsa_spki_pub_to_der(const void *vkey, unsigned char **pder) +{ + const ML_DSA_KEY *key = vkey; + size_t publen; + + if (ossl_ml_dsa_key_get_pub(key) == NULL) { + ERR_raise(ERR_LIB_PROV, ERR_R_PASSED_NULL_PARAMETER); + return 0; + } + publen = ossl_ml_dsa_key_get_pub_len(key); + + if (pder != NULL + && ((*pder = OPENSSL_memdup(ossl_ml_dsa_key_get_pub(key), + publen)) == NULL)) + return 0; + + return publen; +} + +static int ml_dsa_pki_priv_to_der(const void *vkey, unsigned char **pder) +{ + const ML_DSA_KEY *key = vkey; + size_t len; + + if (ossl_ml_dsa_key_get_priv(key) == NULL) { + ERR_raise(ERR_LIB_PROV, ERR_R_PASSED_NULL_PARAMETER); + return 0; + } + len = ossl_ml_dsa_key_get_priv_len(key); + + if (pder != NULL + && ((*pder = OPENSSL_memdup(ossl_ml_dsa_key_get_priv(key), len)) == NULL)) + return 0; + + return len; +} + +# define ml_dsa_epki_priv_to_der ml_dsa_pki_priv_to_der +# define prepare_ml_dsa_params NULL +# define ml_dsa_check_key_type NULL + +# define ml_dsa_44_evp_type EVP_PKEY_ML_DSA_44 +# define ml_dsa_44_pem_type "ML-DSA-44" +# define ml_dsa_65_evp_type EVP_PKEY_ML_DSA_65 +# define ml_dsa_65_pem_type "ML-DSA-65" +# define ml_dsa_87_evp_type EVP_PKEY_ML_DSA_87 +# define ml_dsa_87_pem_type "ML-DSA-87" +#endif /* OPENSSL_NO_ML_DSA */ + +/* ---------------------------------------------------------------------- */ + /* * Helper functions to prepare RSA-PSS params for encoding. We would * have simply written the whole AlgorithmIdentifier, but existing libcrypto @@ -1491,3 +1544,26 @@ MAKE_ENCODER(dhx, dh, X9_42, pem); /* parameters only */ MAKE_ENCODER(ec, ec, X9_62, der); MAKE_ENCODER(ec, ec, X9_62, pem); #endif + +#ifndef OPENSSL_NO_ML_DSA +MAKE_ENCODER(ml_dsa_44, ml_dsa, EVP_PKEY_ML_DSA_44, EncryptedPrivateKeyInfo, der); +MAKE_ENCODER(ml_dsa_44, ml_dsa, EVP_PKEY_ML_DSA_44, EncryptedPrivateKeyInfo, pem); +MAKE_ENCODER(ml_dsa_44, ml_dsa, EVP_PKEY_ML_DSA_44, PrivateKeyInfo, der); +MAKE_ENCODER(ml_dsa_44, ml_dsa, EVP_PKEY_ML_DSA_44, PrivateKeyInfo, pem); +MAKE_ENCODER(ml_dsa_44, ml_dsa, EVP_PKEY_ML_DSA_44, SubjectPublicKeyInfo, der); +MAKE_ENCODER(ml_dsa_44, ml_dsa, EVP_PKEY_ML_DSA_44, SubjectPublicKeyInfo, pem); + +MAKE_ENCODER(ml_dsa_65, ml_dsa, EVP_PKEY_ML_DSA_65, EncryptedPrivateKeyInfo, der); +MAKE_ENCODER(ml_dsa_65, ml_dsa, EVP_PKEY_ML_DSA_65, EncryptedPrivateKeyInfo, pem); +MAKE_ENCODER(ml_dsa_65, ml_dsa, EVP_PKEY_ML_DSA_65, PrivateKeyInfo, der); +MAKE_ENCODER(ml_dsa_65, ml_dsa, EVP_PKEY_ML_DSA_65, PrivateKeyInfo, pem); +MAKE_ENCODER(ml_dsa_65, ml_dsa, EVP_PKEY_ML_DSA_65, SubjectPublicKeyInfo, der); +MAKE_ENCODER(ml_dsa_65, ml_dsa, EVP_PKEY_ML_DSA_65, SubjectPublicKeyInfo, pem); + +MAKE_ENCODER(ml_dsa_87, ml_dsa, EVP_PKEY_ML_DSA_87, EncryptedPrivateKeyInfo, der); +MAKE_ENCODER(ml_dsa_87, ml_dsa, EVP_PKEY_ML_DSA_87, EncryptedPrivateKeyInfo, pem); +MAKE_ENCODER(ml_dsa_87, ml_dsa, EVP_PKEY_ML_DSA_87, PrivateKeyInfo, der); +MAKE_ENCODER(ml_dsa_87, ml_dsa, EVP_PKEY_ML_DSA_87, PrivateKeyInfo, pem); +MAKE_ENCODER(ml_dsa_87, ml_dsa, EVP_PKEY_ML_DSA_87, SubjectPublicKeyInfo, der); +MAKE_ENCODER(ml_dsa_87, ml_dsa, EVP_PKEY_ML_DSA_87, SubjectPublicKeyInfo, pem); +#endif /* OPENSSL_NO_ML_DSA */ diff --git a/providers/implementations/encode_decode/encode_key2text.c b/providers/implementations/encode_decode/encode_key2text.c index ebad2e1876f..a67c63d6cc6 100644 --- a/providers/implementations/encode_decode/encode_key2text.c +++ b/providers/implementations/encode_decode/encode_key2text.c @@ -24,6 +24,7 @@ #include "crypto/ec.h" /* ossl_ec_key_get_libctx */ #include "crypto/ecx.h" /* ECX_KEY, etc... */ #include "crypto/rsa.h" /* RSA_PSS_PARAMS_30, etc... */ +#include "crypto/ml_dsa.h" #include "prov/bio.h" #include "prov/implementations.h" #include "internal/encoder.h" @@ -587,6 +588,49 @@ static int rsa_to_text(BIO *out, const void *key, int selection) /* ---------------------------------------------------------------------- */ +#ifndef OPENSSL_NO_ML_DSA +static int ml_dsa_to_text(BIO *out, const void *key, int selection) +{ + const char *name; + + if (out == NULL || key == NULL) { + ERR_raise(ERR_LIB_PROV, ERR_R_PASSED_NULL_PARAMETER); + return 0; + } + if (ossl_ml_dsa_key_get_pub(key) == NULL) { + /* Regardless of the |selection|, there must be a public key */ + ERR_raise(ERR_LIB_PROV, PROV_R_NOT_A_PUBLIC_KEY); + return 0; + } + + name = ossl_ml_dsa_key_get_name(key); + if ((selection & OSSL_KEYMGMT_SELECT_PRIVATE_KEY) != 0) { + if (ossl_ml_dsa_key_get_priv(key) == NULL) { + ERR_raise(ERR_LIB_PROV, PROV_R_NOT_A_PRIVATE_KEY); + return 0; + } + if (BIO_printf(out, "%s Private-Key:\n", name) <= 0) + return 0; + if (!ossl_bio_print_labeled_buf(out, "priv:", + ossl_ml_dsa_key_get_priv(key), + ossl_ml_dsa_key_get_priv_len(key))) + return 0; + } else if ((selection & OSSL_KEYMGMT_SELECT_PUBLIC_KEY) != 0) { + if (BIO_printf(out, "%s Public-Key:\n", name) <= 0) + return 0; + } + + if (!ossl_bio_print_labeled_buf(out, "pub:", + ossl_ml_dsa_key_get_pub(key), + ossl_ml_dsa_key_get_pub_len(key))) + return 0; + + return 1; +} +#endif /* OPENSSL_NO_ML_DSA */ + +/* ---------------------------------------------------------------------- */ + static void *key2text_newctx(void *provctx) { return provctx; @@ -681,3 +725,9 @@ MAKE_TEXT_ENCODER(x448, ecx); #endif MAKE_TEXT_ENCODER(rsa, rsa); MAKE_TEXT_ENCODER(rsapss, rsa); + +#ifndef OPENSSL_NO_ML_DSA +MAKE_TEXT_ENCODER(ml_dsa_44, ml_dsa); +MAKE_TEXT_ENCODER(ml_dsa_65, ml_dsa); +MAKE_TEXT_ENCODER(ml_dsa_87, ml_dsa); +#endif diff --git a/providers/implementations/include/prov/implementations.h b/providers/implementations/include/prov/implementations.h index f4b0be387c1..3863d96a407 100644 --- a/providers/implementations/include/prov/implementations.h +++ b/providers/implementations/include/prov/implementations.h @@ -536,6 +536,33 @@ extern const OSSL_DISPATCH ossl_x448_to_SubjectPublicKeyInfo_pem_encoder_functio extern const OSSL_DISPATCH ossl_x448_to_OSSL_current_der_encoder_functions[]; extern const OSSL_DISPATCH ossl_x448_to_text_encoder_functions[]; +extern const OSSL_DISPATCH ossl_ml_dsa_44_to_EncryptedPrivateKeyInfo_der_encoder_functions[]; +extern const OSSL_DISPATCH ossl_ml_dsa_44_to_EncryptedPrivateKeyInfo_pem_encoder_functions[]; +extern const OSSL_DISPATCH ossl_ml_dsa_44_to_PrivateKeyInfo_der_encoder_functions[]; +extern const OSSL_DISPATCH ossl_ml_dsa_44_to_PrivateKeyInfo_pem_encoder_functions[]; +extern const OSSL_DISPATCH ossl_ml_dsa_44_to_SubjectPublicKeyInfo_der_encoder_functions[]; +extern const OSSL_DISPATCH ossl_ml_dsa_44_to_SubjectPublicKeyInfo_pem_encoder_functions[]; +extern const OSSL_DISPATCH ossl_ml_dsa_44_to_OSSL_current_der_encoder_functions[]; +extern const OSSL_DISPATCH ossl_ml_dsa_44_to_text_encoder_functions[]; + +extern const OSSL_DISPATCH ossl_ml_dsa_65_to_EncryptedPrivateKeyInfo_der_encoder_functions[]; +extern const OSSL_DISPATCH ossl_ml_dsa_65_to_EncryptedPrivateKeyInfo_pem_encoder_functions[]; +extern const OSSL_DISPATCH ossl_ml_dsa_65_to_PrivateKeyInfo_der_encoder_functions[]; +extern const OSSL_DISPATCH ossl_ml_dsa_65_to_PrivateKeyInfo_pem_encoder_functions[]; +extern const OSSL_DISPATCH ossl_ml_dsa_65_to_SubjectPublicKeyInfo_der_encoder_functions[]; +extern const OSSL_DISPATCH ossl_ml_dsa_65_to_SubjectPublicKeyInfo_pem_encoder_functions[]; +extern const OSSL_DISPATCH ossl_ml_dsa_65_to_OSSL_current_der_encoder_functions[]; +extern const OSSL_DISPATCH ossl_ml_dsa_65_to_text_encoder_functions[]; + +extern const OSSL_DISPATCH ossl_ml_dsa_87_to_EncryptedPrivateKeyInfo_der_encoder_functions[]; +extern const OSSL_DISPATCH ossl_ml_dsa_87_to_EncryptedPrivateKeyInfo_pem_encoder_functions[]; +extern const OSSL_DISPATCH ossl_ml_dsa_87_to_PrivateKeyInfo_der_encoder_functions[]; +extern const OSSL_DISPATCH ossl_ml_dsa_87_to_PrivateKeyInfo_pem_encoder_functions[]; +extern const OSSL_DISPATCH ossl_ml_dsa_87_to_SubjectPublicKeyInfo_der_encoder_functions[]; +extern const OSSL_DISPATCH ossl_ml_dsa_87_to_SubjectPublicKeyInfo_pem_encoder_functions[]; +extern const OSSL_DISPATCH ossl_ml_dsa_87_to_OSSL_current_der_encoder_functions[]; +extern const OSSL_DISPATCH ossl_ml_dsa_87_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[]; @@ -593,3 +620,12 @@ extern const OSSL_DISPATCH ossl_pem_to_der_decoder_functions[]; extern const OSSL_DISPATCH ossl_file_store_functions[]; extern const OSSL_DISPATCH ossl_winstore_store_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[]; + +extern const OSSL_DISPATCH ossl_PrivateKeyInfo_der_to_ml_dsa_65_decoder_functions[]; +extern const OSSL_DISPATCH ossl_SubjectPublicKeyInfo_der_to_ml_dsa_65_decoder_functions[]; + +extern const OSSL_DISPATCH ossl_PrivateKeyInfo_der_to_ml_dsa_87_decoder_functions[]; +extern const OSSL_DISPATCH ossl_SubjectPublicKeyInfo_der_to_ml_dsa_87_decoder_functions[]; diff --git a/test/endecode_test.c b/test/endecode_test.c index 1bf99556f94..29a94cd554b 100644 --- a/test/endecode_test.c +++ b/test/endecode_test.c @@ -105,7 +105,8 @@ static EVP_PKEY *make_template(const char *type, OSSL_PARAM *genparams) } #endif -#if !defined(OPENSSL_NO_DH) || !defined(OPENSSL_NO_DSA) || !defined(OPENSSL_NO_EC) +#if !defined(OPENSSL_NO_DH) || !defined(OPENSSL_NO_DSA) \ + || !defined(OPENSSL_NO_EC) || !defined(OPENSSL_NO_ML_DSA) static EVP_PKEY *make_key(const char *type, EVP_PKEY *template, OSSL_PARAM *genparams) { @@ -1064,6 +1065,15 @@ IMPLEMENT_TEST_SUITE_UNPROTECTED_PVK(RSA, "RSA") IMPLEMENT_TEST_SUITE_PROTECTED_PVK(RSA, "RSA") #endif +#ifndef OPENSSL_ML_DSA +KEYS(ML_DSA_44); +KEYS(ML_DSA_65); +KEYS(ML_DSA_87); +IMPLEMENT_TEST_SUITE(ML_DSA_44, "ML-DSA-44", 0) +IMPLEMENT_TEST_SUITE(ML_DSA_65, "ML-DSA-65", 0) +IMPLEMENT_TEST_SUITE(ML_DSA_87, "ML-DSA-87", 0) +#endif /* OPENSSL_ML_DSA */ + #ifndef OPENSSL_NO_EC /* Explicit parameters that match a named curve */ static int do_create_ec_explicit_prime_params(OSSL_PARAM_BLD *bld, @@ -1407,6 +1417,12 @@ int setup_tests(void) MAKE_KEYS(X25519, "X25519", NULL); MAKE_KEYS(X448, "X448", NULL); #endif +#ifndef OPENSSL_ML_DSA + MAKE_KEYS(ML_DSA_44, "ML-DSA-44", NULL); + MAKE_KEYS(ML_DSA_65, "ML-DSA-65", NULL); + MAKE_KEYS(ML_DSA_87, "ML-DSA-87", NULL); +#endif /* OPENSSL_ML_DSA */ + TEST_info("Loading RSA key..."); ok = ok && TEST_ptr(key_RSA = load_pkey_pem(rsa_file, keyctx)); TEST_info("Loading RSA_PSS key..."); @@ -1475,6 +1491,12 @@ int setup_tests(void) # ifndef OPENSSL_NO_RC4 ADD_TEST_SUITE_PROTECTED_PVK(RSA); # endif + +#ifndef OPENSSL_ML_DSA + ADD_TEST_SUITE(ML_DSA_44); + ADD_TEST_SUITE(ML_DSA_65); + ADD_TEST_SUITE(ML_DSA_87); +#endif /* OPENSSL_ML_DSA */ } return 1; @@ -1522,6 +1544,12 @@ void cleanup_tests(void) FREE_KEYS(RSA); FREE_KEYS(RSA_PSS); +#ifndef OPENSSL_ML_DSA + FREE_KEYS(ML_DSA_44); + FREE_KEYS(ML_DSA_65); + FREE_KEYS(ML_DSA_87); +#endif /* OPENSSL_ML_DSA */ + OSSL_PROVIDER_unload(nullprov); OSSL_PROVIDER_unload(deflprov); OSSL_PROVIDER_unload(keyprov); -- 2.47.2