From: Andreas Steffen Date: Fri, 11 Jul 2025 19:02:58 +0000 (+0200) Subject: openssl: Add ML-DSA support X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=c2ee041489d6a4dd4f1fb4ac9983718437b9f696;p=thirdparty%2Fstrongswan.git openssl: Add ML-DSA support --- diff --git a/src/libstrongswan/plugins/openssl/Makefile.am b/src/libstrongswan/plugins/openssl/Makefile.am index 636851a8ea..30edffa000 100644 --- a/src/libstrongswan/plugins/openssl/Makefile.am +++ b/src/libstrongswan/plugins/openssl/Makefile.am @@ -39,6 +39,8 @@ libstrongswan_openssl_la_SOURCES = \ openssl_x_diffie_hellman.c openssl_x_diffie_hellman.h \ openssl_ed_private_key.c openssl_ed_private_key.h \ openssl_ed_public_key.c openssl_ed_public_key.h \ + openssl_ml_dsa_private_key.c openssl_ml_dsa_private_key.h \ + openssl_ml_dsa_public_key.c openssl_ml_dsa_public_key.h \ openssl_xof.c openssl_xof.h \ openssl_kem.c openssl_kem.h diff --git a/src/libstrongswan/plugins/openssl/openssl_ml_dsa_private_key.c b/src/libstrongswan/plugins/openssl/openssl_ml_dsa_private_key.c new file mode 100644 index 0000000000..036b601409 --- /dev/null +++ b/src/libstrongswan/plugins/openssl/openssl_ml_dsa_private_key.c @@ -0,0 +1,358 @@ +/* + * Copyright (C) 2025 Andreas Steffen + * + * Copyright (C) secunet Security Networks AG + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. See . + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +#include + +#if OPENSSL_VERSION_NUMBER >= 0x30500000L && !defined(OPENSSL_NO_ML_DSA) +#include +#include +#include + +#include "openssl_ml_dsa_private_key.h" +#include "openssl_util.h" + +#include + +typedef struct private_private_key_t private_private_key_t; + +#define ML_DSA_SEED_LEN 32 + +/** + * Private data + */ +struct private_private_key_t { + + /** + * Public interface + */ + private_key_t public; + + /** + * Key object + */ + EVP_PKEY *key; + + /** + * Key type + */ + key_type_t type; + + /** + * reference count + */ + refcount_t ref; +}; + +/* from openssl_ml_dsa public key */ +char* openssl_ml_dsa_alg_name(key_type_t type); +bool openssl_ml_dsa_fingerprint(EVP_PKEY *key, cred_encoding_type_t type, chunk_t *fp); + +METHOD(private_key_t, sign, bool, + private_private_key_t *this, signature_scheme_t scheme, + void *params, chunk_t data, chunk_t *signature) +{ + pqc_params_t pqc_params; + OSSL_PARAM ossl_params[] = { OSSL_PARAM_END, OSSL_PARAM_END, OSSL_PARAM_END}; + EVP_PKEY_CTX *ctx = NULL; + EVP_SIGNATURE *sig_alg = NULL; + int deterministic; + bool success = FALSE; + + if ((this->type == KEY_ML_DSA_44 && scheme != SIGN_ML_DSA_44) || + (this->type == KEY_ML_DSA_65 && scheme != SIGN_ML_DSA_65) || + (this->type == KEY_ML_DSA_87 && scheme != SIGN_ML_DSA_87)) + { + DBG1(DBG_LIB, "signature scheme %N not supported by %N key", + signature_scheme_names, scheme, key_type_names, this->type); + return FALSE; + } + + /* set PQC signature params */ + if (!pqc_params_create(params, &pqc_params)) + { + return FALSE; + } + deterministic = pqc_params.deterministic ? 1 : 0; + ossl_params[0] = OSSL_PARAM_construct_int( + OSSL_SIGNATURE_PARAM_DETERMINISTIC, + &deterministic); + if (pqc_params.ctx.len) + { + ossl_params[1] = OSSL_PARAM_construct_octet_string( + OSSL_SIGNATURE_PARAM_CONTEXT_STRING, + pqc_params.ctx.ptr, pqc_params.ctx.len); + } + + ctx = EVP_PKEY_CTX_new_from_pkey(NULL, this->key, NULL); + if (!ctx) + { + goto error; + } + sig_alg = EVP_SIGNATURE_fetch(NULL, openssl_ml_dsa_alg_name(this->type), NULL); + + if (EVP_PKEY_sign_message_init(ctx, sig_alg, ossl_params) <= 0) + { + goto error; + } + + if (EVP_PKEY_sign(ctx, NULL, &signature->len, data.ptr, data.len) <= 0) + { + goto error; + } + + *signature = chunk_alloc(signature->len); + + if (EVP_PKEY_sign(ctx, signature->ptr, &signature->len, + data.ptr, data.len) <= 0) + { + goto error; + } + success = TRUE; + +error: + pqc_params_free(&pqc_params); + EVP_SIGNATURE_free(sig_alg); + EVP_PKEY_CTX_free(ctx); + + return success; +} + +METHOD(private_key_t, decrypt, bool, + private_private_key_t *this, encryption_scheme_t scheme, + void *params, chunk_t crypto, chunk_t *plain) +{ + DBG1(DBG_LIB, "EdDSA private key decryption not implemented"); + return FALSE; +} + +METHOD(private_key_t, get_keysize, int, + private_private_key_t *this) +{ + return BITS_PER_BYTE * get_public_key_size(this->type); +} + +METHOD(private_key_t, get_type, key_type_t, + private_private_key_t *this) +{ + return this->type; +} + +METHOD(private_key_t, get_public_key, public_key_t*, + private_private_key_t *this) +{ + public_key_t *public; + u_char buf[2592]; + chunk_t key = {buf, sizeof(buf)}; + + if (!EVP_PKEY_get_octet_string_param(this->key, OSSL_PKEY_PARAM_PUB_KEY, + buf, sizeof(buf), &key.len)) + { + return NULL; + } + public = lib->creds->create(lib->creds, CRED_PUBLIC_KEY, this->type, + BUILD_BLOB, key, BUILD_END); + return public; +} + +METHOD(private_key_t, get_fingerprint, bool, + private_private_key_t *this, cred_encoding_type_t type, + chunk_t *fingerprint) +{ + return openssl_ml_dsa_fingerprint(this->key, type, fingerprint); +} + +METHOD(private_key_t, get_encoding, bool, + private_private_key_t *this, cred_encoding_type_t type, chunk_t *encoding) +{ + switch (type) + { + case PRIVKEY_ASN1_DER: + case PRIVKEY_PEM: + { + bool success = TRUE; + + *encoding = openssl_i2chunk(PrivateKey, this->key); + + if (type == PRIVKEY_PEM) + { + chunk_t asn1_encoding = *encoding; + + success = lib->encoding->encode(lib->encoding, PRIVKEY_PEM, + NULL, encoding, CRED_PART_PRIV_ASN1_DER, + asn1_encoding, CRED_PART_END); + chunk_clear(&asn1_encoding); + } + + return success; + } + default: + return FALSE; + } +} + +METHOD(private_key_t, get_ref, private_key_t*, + private_private_key_t *this) +{ + ref_get(&this->ref); + return &this->public; +} + +METHOD(private_key_t, destroy, void, + private_private_key_t *this) +{ + if (ref_put(&this->ref)) + { + lib->encoding->clear_cache(lib->encoding, this->key); + EVP_PKEY_free(this->key); + free(this); + } +} + +/** + * Create an ML-DSA private_key_t instance + */ +static private_key_t *create_instance(key_type_t type, chunk_t keyseed) +{ + private_private_key_t *this = NULL; + EVP_PKEY_CTX *ctx = NULL; + EVP_PKEY *key = NULL; + + ctx = EVP_PKEY_CTX_new_from_name(NULL, openssl_ml_dsa_alg_name(type), NULL); + if (!ctx || EVP_PKEY_keygen_init(ctx) <= 0) + { + DBG1(DBG_LIB, "failed to create ctx"); + goto end; + } + + if (keyseed.ptr) + { + OSSL_PARAM params[] = { + OSSL_PARAM_octet_string(OSSL_PKEY_PARAM_ML_DSA_SEED, + keyseed.ptr, keyseed.len), + OSSL_PARAM_END + }; + + if (!EVP_PKEY_CTX_set_params(ctx, params)) + { + DBG1(DBG_LIB, "failed to set keyseed"); + goto end; + } + } + + if (EVP_PKEY_generate(ctx, &key) <= 0) + { + DBG1(DBG_LIB, "failed to generate ML-DSA private key"); + goto end; + } + + INIT(this, + .public = { + .get_type = _get_type, + .sign = _sign, + .decrypt = _decrypt, + .get_keysize = _get_keysize, + .get_public_key = _get_public_key, + .equals = private_key_equals, + .belongs_to = private_key_belongs_to, + .get_fingerprint = _get_fingerprint, + .has_fingerprint = private_key_has_fingerprint, + .get_encoding = _get_encoding, + .get_ref = _get_ref, + .destroy = _destroy, + }, + .type = type, + .key = key, + .ref = 1, + ); + +end: + EVP_PKEY_CTX_free(ctx); + + return this ? &this->public : NULL; +} + +/* + * Described in header + */ +private_key_t *openssl_ml_dsa_private_key_gen(key_type_t type, va_list args) +{ + if (type != KEY_ML_DSA_44 && type != KEY_ML_DSA_65 && type != KEY_ML_DSA_87) + { + return NULL; + } + while (TRUE) + { + switch (va_arg(args, builder_part_t)) + { + case BUILD_KEY_SIZE: + /* just ignore the key size */ + va_arg(args, u_int); + continue; + case BUILD_END: + break; + default: + return NULL; + } + break; + } + + return create_instance(type, chunk_empty); +} + +/* + * Described in header + */ +private_key_t *openssl_ml_dsa_private_key_load(key_type_t type, va_list args) +{ + chunk_t priv = chunk_empty; + + if (type != KEY_ML_DSA_44 && type != KEY_ML_DSA_65 && type != KEY_ML_DSA_87) + { + return NULL; + } + + while (TRUE) + { + switch (va_arg(args, builder_part_t)) + { + case BUILD_BLOB: + priv = va_arg(args, chunk_t); + continue; + case BUILD_END: + break; + default: + return NULL; + } + break; + } + + if (priv.len == ML_DSA_SEED_LEN + 2 && + priv.ptr[0] == 0x80 && priv.ptr[1] == ML_DSA_SEED_LEN) + { + priv = chunk_skip(priv, 2); + } + if (priv.len != ML_DSA_SEED_LEN) + { + DBG1(DBG_LIB, "error: the size of the loaded ML-DSA private key seed " + "is %u bytes instead of %d bytes", priv.len, ML_DSA_SEED_LEN); + return NULL; + } + + return create_instance(type, priv); +} + + +#endif /* OPENSSL_NO_ML_DSA*/ diff --git a/src/libstrongswan/plugins/openssl/openssl_ml_dsa_private_key.h b/src/libstrongswan/plugins/openssl/openssl_ml_dsa_private_key.h new file mode 100644 index 0000000000..9ff521ac0a --- /dev/null +++ b/src/libstrongswan/plugins/openssl/openssl_ml_dsa_private_key.h @@ -0,0 +1,50 @@ +/* + * Copyright (C) 2025 Andreas Steffen + * + * Copyright (C) secunet Security Networks AG + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. See . + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +/** + * @defgroup openssl_ml_dsa_private_key openssl_ml_dsa_private_key + * @{ @ingroup openssl_p + */ + +#ifndef OPENSSL_ML_DSA_PRIVATE_KEY_H_ +#define OPENSSL_ML_DSA_PRIVATE_KEY_H_ + +#include + +#include +#include + +/** + * Generate an ML-DSA private key using OpenSSL. + * + * @param type type of the key, must be ML_DSA_44, ML_DSA_65 or ML_DSA_87 + * @param args builder_part_t argument list + * @return generated key, NULL on failure + */ +private_key_t *openssl_ml_dsa_private_key_gen(key_type_t type, va_list args); + +/** + * Load an ML-DSA private key using OpenSSL. + * + * Accepts a BUILD_BLOB_ASN1_DER argument. + * + * @param type type of the key, must be ML_DSA_44, ML_DSA_65 or ML_DSA_87 + * @param args builder_part_t argument list + * @return loaded key, NULL on failure + */ +private_key_t *openssl_ml_dsa_private_key_load(key_type_t type, va_list args); + +#endif /** OPENSSL_ML_DSA_PRIVATE_KEY_H_ @}*/ diff --git a/src/libstrongswan/plugins/openssl/openssl_ml_dsa_public_key.c b/src/libstrongswan/plugins/openssl/openssl_ml_dsa_public_key.c new file mode 100644 index 0000000000..f9e818868c --- /dev/null +++ b/src/libstrongswan/plugins/openssl/openssl_ml_dsa_public_key.c @@ -0,0 +1,342 @@ +/* + * Copyright (C) 2025 Andreas Steffen + * + * Copyright (C) secunet Security Networks AG + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. See . + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +#include + +#if OPENSSL_VERSION_NUMBER >= 0x30500000L && !defined(OPENSSL_NO_ML_DSA) +#include +#include +#include + +#include "openssl_ml_dsa_public_key.h" + +#include + +typedef struct private_public_key_t private_public_key_t; + +/** + * Private data + */ +struct private_public_key_t { + + /** + * Public interface + */ + public_key_t public; + + /** + * Key object + */ + EVP_PKEY *key; + + /** + * Key type + */ + key_type_t type; + + /** + * Reference counter + */ + refcount_t ref; +}; + +/** + * Map a key type to an algorithm name + */ +char* openssl_ml_dsa_alg_name(key_type_t type) +{ + switch (type) + { + case KEY_ML_DSA_44: + return "ML-DSA-44"; + case KEY_ML_DSA_65: + return "ML-DSA-65"; + case KEY_ML_DSA_87: + return "ML-DSA-87"; + default: + return NULL; + } +} + +/** + * Map a key type to an EVP key type + */ +int openssl_ml_dsa_key_type(key_type_t type) +{ + switch (type) + { + case KEY_ML_DSA_44: + return EVP_PKEY_ML_DSA_44; + case KEY_ML_DSA_65: + return EVP_PKEY_ML_DSA_65; + case KEY_ML_DSA_87: + return EVP_PKEY_ML_DSA_87; + default: + return 0; + } +} + +METHOD(public_key_t, get_type, key_type_t, + private_public_key_t *this) +{ + return this->type; +} + +METHOD(public_key_t, verify, bool, + private_public_key_t *this, signature_scheme_t scheme, + void *params, chunk_t data, chunk_t signature) +{ + pqc_params_t pqc_params; + EVP_PKEY_CTX *ctx = NULL; + EVP_SIGNATURE *sig_alg = NULL; + OSSL_PARAM ossl_params[] = { OSSL_PARAM_END, OSSL_PARAM_END}; + bool success = FALSE; + + if ((this->type == KEY_ML_DSA_44 && scheme != SIGN_ML_DSA_44) || + (this->type == KEY_ML_DSA_65 && scheme != SIGN_ML_DSA_65) || + (this->type == KEY_ML_DSA_87 && scheme != SIGN_ML_DSA_87)) + { + DBG1(DBG_LIB, "signature scheme %N not supported by %N key", + signature_scheme_names, scheme, key_type_names, this->type); + return FALSE; + } + + /* set PQC signature params */ + if (!pqc_params_create(params, &pqc_params)) + { + return FALSE; + } + if (pqc_params.ctx.len) + { + ossl_params[0] = OSSL_PARAM_construct_octet_string( + OSSL_SIGNATURE_PARAM_CONTEXT_STRING, + pqc_params.ctx.ptr, pqc_params.ctx.len); + } + + ctx = EVP_PKEY_CTX_new_from_pkey(NULL, this->key, NULL); + if (!ctx) + { + goto error; + } + sig_alg = EVP_SIGNATURE_fetch(NULL, openssl_ml_dsa_alg_name(this->type), NULL); + + if (EVP_PKEY_verify_message_init(ctx, sig_alg, ossl_params) <= 0) + { + goto error; + } + + if (EVP_PKEY_verify(ctx, signature.ptr, signature.len, data.ptr, data.len) <= 0) + { + goto error; + } + success = TRUE; + +error: + pqc_params_free(&pqc_params); + EVP_SIGNATURE_free(sig_alg); + EVP_PKEY_CTX_free(ctx); + + return success; +} + +METHOD(public_key_t, encrypt, bool, + private_public_key_t *this, encryption_scheme_t scheme, + void *params, chunk_t crypto, chunk_t *plain) +{ + DBG1(DBG_LIB, "encryption scheme %N not supported", encryption_scheme_names, + scheme); + return FALSE; +} + +METHOD(public_key_t, get_keysize, int, + private_public_key_t *this) +{ + return BITS_PER_BYTE * get_public_key_size(this->type); +} + +/** + * Calculate fingerprint from an EdDSA key, also used in ed private key. + */ +bool openssl_ml_dsa_fingerprint(EVP_PKEY *key, cred_encoding_type_t type, + chunk_t *fp) +{ + hasher_t *hasher; + chunk_t blob; + u_char *p; + + if (lib->encoding->get_cache(lib->encoding, type, key, fp)) + { + return TRUE; + } + switch (type) + { + case KEYID_PUBKEY_SHA1: + if (!EVP_PKEY_get_raw_public_key(key, NULL, &blob.len)) + { + return FALSE; + } + blob = chunk_alloca(blob.len); + if (!EVP_PKEY_get_raw_public_key(key, blob.ptr, &blob.len)) + { + return FALSE; + } + break; + case KEYID_PUBKEY_INFO_SHA1: + blob = chunk_alloca(i2d_PUBKEY(key, NULL)); + p = blob.ptr; + i2d_PUBKEY(key, &p); + break; + default: + return FALSE; + } + hasher = lib->crypto->create_hasher(lib->crypto, HASH_SHA1); + if (!hasher || !hasher->allocate_hash(hasher, blob, fp)) + { + DBG1(DBG_LIB, "SHA1 not supported, fingerprinting failed"); + DESTROY_IF(hasher); + return FALSE; + } + hasher->destroy(hasher); + lib->encoding->cache(lib->encoding, type, key, fp); + return TRUE; +} + +METHOD(public_key_t, get_fingerprint, bool, + private_public_key_t *this, cred_encoding_type_t type, chunk_t *fingerprint) +{ + return openssl_ml_dsa_fingerprint(this->key, type, fingerprint); +} + +METHOD(public_key_t, get_encoding, bool, + private_public_key_t *this, cred_encoding_type_t type, chunk_t *encoding) +{ + bool success = TRUE; + u_char *p; + + *encoding = chunk_alloc(i2d_PUBKEY(this->key, NULL)); + p = encoding->ptr; + i2d_PUBKEY(this->key, &p); + + if (type != PUBKEY_SPKI_ASN1_DER) + { + chunk_t asn1_encoding = *encoding; + + success = lib->encoding->encode(lib->encoding, type, + NULL, encoding, CRED_PART_EDDSA_PUB_ASN1_DER, + asn1_encoding, CRED_PART_END); + chunk_clear(&asn1_encoding); + } + return success; +} + +METHOD(public_key_t, get_ref, public_key_t*, + private_public_key_t *this) +{ + ref_get(&this->ref); + return &this->public; +} + +METHOD(public_key_t, destroy, void, + private_public_key_t *this) +{ + if (ref_put(&this->ref)) + { + lib->encoding->clear_cache(lib->encoding, this->key); + EVP_PKEY_free(this->key); + free(this); + } +} + +/** + * Generic private constructor + */ +static private_public_key_t *create_empty(key_type_t type) +{ + private_public_key_t *this; + + INIT(this, + .public = { + .get_type = _get_type, + .verify = _verify, + .encrypt = _encrypt, + .get_keysize = _get_keysize, + .equals = public_key_equals, + .get_fingerprint = _get_fingerprint, + .has_fingerprint = public_key_has_fingerprint, + .get_encoding = _get_encoding, + .get_ref = _get_ref, + .destroy = _destroy, + }, + .type = type, + .ref = 1, + ); + + return this; +} + +/* + * Described in header + */ +public_key_t *openssl_ml_dsa_public_key_load(key_type_t type, va_list args) +{ + private_public_key_t *this; + chunk_t pkcs1, blob = chunk_empty; + EVP_PKEY *key = NULL; + + while (TRUE) + { + switch (va_arg(args, builder_part_t)) + { + case BUILD_BLOB: + blob = va_arg(args, chunk_t); + continue; + case BUILD_BLOB_ASN1_DER: + pkcs1 = va_arg(args, chunk_t); + type = public_key_info_decode(pkcs1, &blob); + continue; + case BUILD_END: + break; + default: + return NULL; + } + break; + } + + if (blob.len) + { + key = EVP_PKEY_new_raw_public_key(openssl_ml_dsa_key_type(type), NULL, + blob.ptr, blob.len); + } + else if (pkcs1.len) + { + key = d2i_PUBKEY(NULL, (const u_char**)&pkcs1.ptr, pkcs1.len); + if (key && EVP_PKEY_base_id(key) != openssl_ml_dsa_key_type(type)) + { + EVP_PKEY_free(key); + return NULL; + } + } + if (!key) + { + return NULL; + } + this = create_empty(type); + this->key = key; + + return &this->public; +} + +#endif /* OPENSSL_VERSION_NUMBER */ diff --git a/src/libstrongswan/plugins/openssl/openssl_ml_dsa_public_key.h b/src/libstrongswan/plugins/openssl/openssl_ml_dsa_public_key.h new file mode 100644 index 0000000000..223830c5af --- /dev/null +++ b/src/libstrongswan/plugins/openssl/openssl_ml_dsa_public_key.h @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2025 Andreas Steffen + * + * Copyright (C) secunet Security Networks AG + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. See . + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +/** + * @defgroup openssl_ml_dsa_public_key openssl_ml_dsa_public_key + * @{ @ingroup openssl_p + */ + +#ifndef OPENSSL_ML_DSA_PUBLIC_KEY_H_ +#define OPENSSL_ML_DSA_PUBLIC_KEY_H_ + +#include +#include + +/** + * Load an ML-DSA public key using OpenSSL. + * + * Accepts a BUILD_BLOB_ASN1_DER argument. + * + * @param type type of the key, must be ML_DSA_44, ML_DSA_65 or ML_DSA_87 + * @param args builder_part_t argument list + * @return loaded key, NULL on failure + */ +public_key_t *openssl_ml_dsa_public_key_load(key_type_t type, va_list args); + +#endif /** OPENSSL_ML_DSA_PUBLIC_KEY_H_ @}*/ diff --git a/src/libstrongswan/plugins/openssl/openssl_plugin.c b/src/libstrongswan/plugins/openssl/openssl_plugin.c index e5d2022aa7..e63032c92b 100644 --- a/src/libstrongswan/plugins/openssl/openssl_plugin.c +++ b/src/libstrongswan/plugins/openssl/openssl_plugin.c @@ -1,6 +1,7 @@ /* * Copyright (C) 2008-2020 Tobias Brunner * Copyright (C) 2008 Martin Willi + * Copyright (C) 2025 Andreas Steffen * * Copyright (C) secunet Security Networks AG * @@ -31,6 +32,7 @@ #endif #if OPENSSL_VERSION_NUMBER >= 0x30000000L #include +#include #endif #include "openssl_plugin.h" @@ -56,6 +58,8 @@ #include "openssl_x_diffie_hellman.h" #include "openssl_ed_public_key.h" #include "openssl_ed_private_key.h" +#include "openssl_ml_dsa_private_key.h" +#include "openssl_ml_dsa_public_key.h" #include "openssl_xof.h" #include "openssl_kem.h" @@ -687,6 +691,28 @@ METHOD(plugin_t, get_features, int, PLUGIN_REGISTER(HASHER, return_null), PLUGIN_PROVIDE(HASHER, HASH_IDENTITY), #endif /* OPENSSL_VERSION_NUMBER && !OPENSSL_NO_EC && !OPENSSL_IS_AWSLC */ +#if (OPENSSL_VERSION_NUMBER >= 0x30500000L && !defined(OPENSSL_NO_ML_DSA)) + /* ML-DSA private/public key loading */ + PLUGIN_REGISTER(PUBKEY, openssl_ml_dsa_public_key_load, TRUE), + PLUGIN_PROVIDE(PUBKEY, KEY_ML_DSA_44), + PLUGIN_PROVIDE(PUBKEY, KEY_ML_DSA_65), + PLUGIN_PROVIDE(PUBKEY, KEY_ML_DSA_87), + PLUGIN_REGISTER(PRIVKEY, openssl_ml_dsa_private_key_load, TRUE), + PLUGIN_PROVIDE(PRIVKEY, KEY_ML_DSA_44), + PLUGIN_PROVIDE(PRIVKEY, KEY_ML_DSA_65), + PLUGIN_PROVIDE(PRIVKEY, KEY_ML_DSA_87), + PLUGIN_REGISTER(PRIVKEY_GEN, openssl_ml_dsa_private_key_gen, FALSE), + PLUGIN_PROVIDE(PRIVKEY_GEN, KEY_ML_DSA_44), + PLUGIN_PROVIDE(PRIVKEY_GEN, KEY_ML_DSA_65), + PLUGIN_PROVIDE(PRIVKEY_GEN, KEY_ML_DSA_87), + PLUGIN_PROVIDE(PRIVKEY_SIGN, SIGN_ML_DSA_44), + PLUGIN_PROVIDE(PRIVKEY_SIGN, SIGN_ML_DSA_65), + PLUGIN_PROVIDE(PRIVKEY_SIGN, SIGN_ML_DSA_87), + PLUGIN_PROVIDE(PUBKEY_VERIFY, SIGN_ML_DSA_44), + PLUGIN_PROVIDE(PUBKEY_VERIFY, SIGN_ML_DSA_65), + PLUGIN_PROVIDE(PUBKEY_VERIFY, SIGN_ML_DSA_87), +#endif /* OPENSSL_NO_ML_DSA */ + /* generic key loader */ PLUGIN_REGISTER(PRIVKEY, openssl_private_key_load, TRUE), PLUGIN_PROVIDE(PRIVKEY, KEY_ANY), @@ -787,6 +813,27 @@ static int concat_ossl_providers(OSSL_PROVIDER *provider, void *cbdata) } #endif +#if (OPENSSL_VERSION_NUMBER >= 0x30500000L && !defined(OPENSSL_NO_ML_DSA)) +/** + * Callback to add ml.dsa parameters to loaded providers + */ +static int add_ml_dsa_ossl_params(OSSL_PROVIDER *provider, void *cbdata) +{ + + if (!OSSL_PROVIDER_add_conf_parameter(provider, + OSSL_PKEY_PARAM_ML_DSA_RETAIN_SEED, "yes") || + !OSSL_PROVIDER_add_conf_parameter(provider, + OSSL_PKEY_PARAM_ML_DSA_PREFER_SEED, "yes") || + !OSSL_PROVIDER_add_conf_parameter(provider, + OSSL_PKEY_PARAM_ML_DSA_OUTPUT_FORMATS, "seed-only")) + { + DBG1(DBG_LIB, "failed to set ml.dsa parameters in default provider"); + return 1; + } + return 0; +} +#endif /* OPENSSL_NO_ML_DSA */ + /* * see header file */ @@ -867,6 +914,9 @@ plugin_t *openssl_plugin_create() OSSL_PROVIDER_do_all(NULL, concat_ossl_providers, &data); dbg(DBG_LIB, strpfx(lib->ns, "charon") ? 1 : 2, "providers loaded by OpenSSL:%s", data.names); +#if (OPENSSL_VERSION_NUMBER >= 0x30500000L && !defined(OPENSSL_NO_ML_DSA)) + OSSL_PROVIDER_do_all(NULL, add_ml_dsa_ossl_params, NULL); +#endif /* OPENSSL_NO_ML_DSA */ #endif /* OPENSSL_VERSION_NUMBER */ #ifdef OPENSSL_FIPS