From 55c9aaf39db21e2c914bb7163e5cbc19b5994379 Mon Sep 17 00:00:00 2001 From: olszomal Date: Mon, 3 Aug 2026 08:57:12 +0200 Subject: [PATCH] Refactor raw public key handling and legacy EVP_PKEY methods --- src/Makefile.am | 6 +- src/Makefile.mak | 4 +- src/libp11-int.h | 60 ++- src/p11_ec.c | 20 +- src/p11_ecx.c | 435 ++++++++++++++++++ src/p11_eddsa.c | 968 ----------------------------------------- src/p11_falcon.c | 303 ------------- src/p11_key.c | 339 ++------------- src/p11_load.c | 15 +- src/p11_mldsa.c | 364 ---------------- src/p11_pkey.c | 590 +++++++++++++++++++------ src/p11_rawkey.c | 501 +++++++++++++++++++++ src/p11_rsa.c | 70 +-- src/p11_slhdsa.c | 417 ------------------ tests/ed25519-keygen.c | 7 +- tests/ed448-keygen.c | 7 +- 16 files changed, 1496 insertions(+), 2610 deletions(-) create mode 100644 src/p11_ecx.c delete mode 100644 src/p11_eddsa.c delete mode 100644 src/p11_falcon.c delete mode 100644 src/p11_mldsa.c create mode 100644 src/p11_rawkey.c delete mode 100644 src/p11_slhdsa.c diff --git a/src/Makefile.am b/src/Makefile.am index 549ac673..52af4bd7 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -62,9 +62,9 @@ libeng_err_la_CFLAGS = $(AM_CFLAGS) $(OPENSSL_EXTRA_CFLAGS) $(OPENSSL_CFLAGS) \ # because it is compiled as part of libp11_err.la (above). # ---------------------------------------------------------- libp11_la_SOURCES = libpkcs11.c p11_attr.c p11_cert.c p11_ckr.c \ - p11_key.c p11_load.c p11_misc.c p11_rsa.c p11_ec.c p11_eddsa.c \ - p11_mldsa.c p11_slhdsa.c p11_falcon.c \ - p11_pkey.c p11_slot.c p11_front.c p11_atfork.c libp11.exports + p11_key.c p11_load.c p11_misc.c p11_rsa.c p11_ec.c p11_ecx.c \ + p11_rawkey.c p11_pkey.c p11_slot.c p11_front.c p11_atfork.c \ + libp11.exports # Compiler flags for libp11 libp11_la_CFLAGS = $(AM_CFLAGS) $(OPENSSL_CFLAGS) diff --git a/src/Makefile.mak b/src/Makefile.mak index 3cb7d481..833d5e19 100644 --- a/src/Makefile.mak +++ b/src/Makefile.mak @@ -4,8 +4,8 @@ TOPDIR = .. LIBP11_OBJECTS = libpkcs11.obj p11_attr.obj p11_cert.obj \ p11_err.obj p11_ckr.obj p11_key.obj p11_load.obj p11_misc.obj \ - p11_rsa.obj p11_ec.obj p11_pkey.obj p11_slot.obj p11_front.obj \ - p11_atfork.obj p11_eddsa.obj p11_mldsa.obj p11_slhdsa.obj p11_falcon.obj + p11_rsa.obj p11_ec.obj p11_ecx.obj p11_rawkey.obj p11_pkey.obj \ + p11_slot.obj p11_front.obj p11_atfork.obj LIBP11_LIB = libp11.lib LIBP11_TARGET = libp11.dll diff --git a/src/libp11-int.h b/src/libp11-int.h index 0b7ab81b..36abecd8 100644 --- a/src/libp11-int.h +++ b/src/libp11-int.h @@ -44,6 +44,13 @@ #include "p11_pthread.h" +#if OPENSSL_VERSION_NUMBER >= 0x30000000L && \ + OPENSSL_VERSION_NUMBER < 0x40000000L && \ + !defined(OPENSSL_NO_DEPRECATED_3_0) && \ + !defined(OPENSSL_NO_ECX) +#define LIBP11_HAVE_ECX_METHODS +#endif + #if OPENSSL_VERSION_NUMBER >= 0x30000000L #define EVP_PKEY_FALCON512 0x10001 #define EVP_PKEY_FALCON1024 0x10002 @@ -364,6 +371,12 @@ extern PKCS11_KEY *pkcs11_find_key(PKCS11_OBJECT_private *cert); #if OPENSSL_VERSION_NUMBER >= 0x30000000L /* Return the borrowed PKCS#11 object associated with the EVP_PKEY */ extern PKCS11_OBJECT_private *pkcs11_get_ex_data_object(const EVP_PKEY *pk); + +/* Return the borrowed private PKCS#11 object for EVP_PKEY_METHOD operations */ +extern PKCS11_OBJECT_private *pkcs11_get_legacy_pkey_object(const EVP_PKEY *pkey); + +/* Free the allocated EVP_PKEY ex_data index. */ +extern void free_evp_pkey_ex_index(void); #endif /* OPENSSL_VERSION_NUMBER >= 0x30000000L */ /* Get a list of all certificates matching with template associated with this token */ @@ -581,25 +594,6 @@ extern PKCS11_OBJECT_private *pkcs11_get_ex_data_ec(const EC_KEY *ec); extern void pkcs11_set_ex_data_ec(EC_KEY *ec, PKCS11_OBJECT_private *key); #endif /* OPENSSL_NO_EC */ -# if OPENSSL_VERSION_NUMBER >= 0x30000000L && OPENSSL_VERSION_NUMBER < 0x40000000L -/* Set PKCS11_OBJECT_private for an EVP_PKEY */ -extern void pkcs11_set_ex_data_pkey(EVP_PKEY *pkey, PKCS11_OBJECT_private *key); - -/* Retrieve PKCS11_OBJECT_private from an EVP_PKEY */ -extern PKCS11_OBJECT_private *pkcs11_get_ex_data_pkey(const EVP_PKEY *pkey); - -/* Allocate a global EVP_PKEY ex_data index */ -extern void alloc_pkey_ex_index(void); - -/* Free the allocated EVP_PKEY ex_data index. */ -extern void free_pkey_ex_index(void); -# endif /* OPENSSL_VERSION_NUMBER >= 0x30000000L && OPENSSL_VERSION_NUMBER < 0x40000000L */ - -#if OPENSSL_VERSION_NUMBER >= 0x30000000L -/* Free the allocated EVP_PKEY ex_data index. */ -extern void free_evp_pkey_ex_index(void); -#endif /* OPENSSL_VERSION_NUMBER >= 0x30000000L */ - /* Convert an OpenSSL digest (EVP_MD) to the corresponding PKCS#11 hash mechanism */ extern CK_MECHANISM_TYPE pkcs11_md2ckm(const EVP_MD *md); @@ -618,18 +612,21 @@ extern void pkcs11_ecdsa_method_free(void); /* Free the global ECDH_METHOD */ extern void pkcs11_ecdh_method_free(void); -#if !defined(OPENSSL_NO_ECX) && OPENSSL_VERSION_NUMBER >= 0x30000000L && OPENSSL_VERSION_NUMBER < 0x40000000L -/* Free the global ED25519/ED448 EVP_PKEY_METHOD */ -extern void pkcs11_ed_key_method_free(void); +#ifdef LIBP11_HAVE_ECX_METHODS +/* Enable an ECX EVP_PKEY_METHOD wrapper for a PKCS#11 private key */ +extern int pkcs11_ecx_method_enable(PKCS11_OBJECT_private *key, int type); -/* Free the global X25519/X448 EVP_PKEY_METHOD */ -extern void pkcs11_xdh_key_method_free(void); -#endif /* !defined(OPENSSL_NO_ECX) && OPENSSL_VERSION_NUMBER >= 0x30000000L && OPENSSL_VERSION_NUMBER < 0x40000000L */ +/* Remove and free all registered ECX EVP_PKEY_METHOD wrappers */ +extern void pkcs11_ecx_methods_free(void); +#endif /* LIBP11_HAVE_ECX_METHODS */ -#if OPENSSL_VERSION_NUMBER >= 0x30000000L && OPENSSL_VERSION_NUMBER < 0x40000000L -/* Free the global RSA EVP_PKEY_METHOD */ -extern void pkcs11_rsa_key_method_free(void); -# endif /* OPENSSL_VERSION_NUMBER >= 0x30000000L && OPENSSL_VERSION_NUMBER < 0x40000000L */ +#if OPENSSL_VERSION_NUMBER < 0x40000000L +/* Enable the RSA EVP_PKEY_METHOD wrapper for a PKCS#11 private key. */ +extern int pkcs11_rsa_method_enable(PKCS11_OBJECT_private *key); + +/* Remove and free the registered RSA EVP_PKEY_METHOD wrapper. */ +extern void pkcs11_rsa_pkey_method_free(void); +#endif /* OPENSSL_VERSION_NUMBER < 0x40000000L */ #if OPENSSL_VERSION_NUMBER < 0x100020d0L || defined(LIBRESSL_VERSION_NUMBER) /* Get sign_init and sign callbacks from EVP_PKEY_METHOD */ @@ -640,11 +637,6 @@ extern void EVP_PKEY_meth_get_sign(EVP_PKEY_METHOD *pmeth, const unsigned char *tbs, size_t tbslen)); #endif /* OPENSSL_VERSION_NUMBER < 0x100020d0L || defined(LIBRESSL_VERSION_NUMBER) */ -#if OPENSSL_VERSION_NUMBER < 0x40000000L -/* Attempt to sign using the PKCS#11-backed RSA implementation */ -extern EVP_PKEY_METHOD *pkcs11_pkey_method_rsa(void); -#endif /* OPENSSL_VERSION_NUMBER < 0x40000000L */ - #endif /* _LIBP11_INT_H */ /* vim: set noexpandtab: */ diff --git a/src/p11_ec.c b/src/p11_ec.c index 1a8e2041..f29ed942 100644 --- a/src/p11_ec.c +++ b/src/p11_ec.c @@ -387,11 +387,10 @@ static EVP_PKEY *pkcs11_get_evp_key_ec(PKCS11_OBJECT_private *key) ec = pkcs11_get_ec(key); if (!ec) return NULL; + pk = EVP_PKEY_new(); - if (!pk) { - EC_KEY_free(ec); - return NULL; - } + if (!pk) + goto error; if (key->object_class == CKO_PRIVATE_KEY) { #if OPENSSL_VERSION_NUMBER >= 0x10100000L && !defined(LIBRESSL_VERSION_NUMBER) @@ -400,17 +399,24 @@ static EVP_PKEY *pkcs11_get_evp_key_ec(PKCS11_OBJECT_private *key) ECDSA_set_method(ec, PKCS11_get_ecdsa_method()); ECDH_set_method(ec, PKCS11_get_ecdh_method()); #endif - /* This creates a new EC_KEY object which requires its own key object reference */ + /* The EC_KEY object owns the reference stored in its ex_data, + * released by pkcs11_ec_finish() */ key = pkcs11_object_ref(key); pkcs11_set_ex_data_ec(ec, key); } /* TODO: Retrieve the ECDSA private key object attributes instead, * unless the key has the "sensitive" attribute set */ - EVP_PKEY_set1_EC_KEY(pk, ec); /* Also increments the ec ref count */ - EC_KEY_free(ec); /* Drops our reference to it */ + if (EVP_PKEY_set1_EC_KEY(pk, ec) != 1) /* Also increments the ec ref count */ + goto error; + EC_KEY_free(ec); /* Drops our reference to it */ return pk; + +error: + EVP_PKEY_free(pk); + EC_KEY_free(ec); + return NULL; } /********** ECDSA signing */ diff --git a/src/p11_ecx.c b/src/p11_ecx.c new file mode 100644 index 00000000..a4f3f0a6 --- /dev/null +++ b/src/p11_ecx.c @@ -0,0 +1,435 @@ +/* + * Copyright © 2026 Mobi - Com Polska Sp. z o.o. + * Author: Małgorzata Olszówka + * All rights reserved. + * + * OpenSSL 3.x ENGINE compatibility wrappers for Ed25519, Ed448, + * X25519 and X448 PKCS#11 keys. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include "libp11-int.h" + +#ifdef LIBP11_HAVE_ECX_METHODS + +#define ED25519_SIG_LEN 64 +#define ED448_SIG_LEN 114 +#define X25519_PUB_LEN 32 +#define X448_PUB_LEN 56 + +/* + * EVP_PKEY_METHOD is retained only for the OpenSSL 3.x ENGINE compatibility + * path. OpenSSL 4.x uses the provider implementations exclusively. + */ +typedef int (*P11_ECX_INIT_FN)(EVP_PKEY_CTX *); +typedef int (*P11_ECX_SIGN_FN)(EVP_PKEY_CTX *, unsigned char *, size_t *, + const unsigned char *, size_t); +typedef int (*P11_ECX_DIGESTSIGN_FN)(EVP_MD_CTX *, unsigned char *, size_t *, + const unsigned char *, size_t); +typedef int (*P11_ECX_DERIVE_FN)(EVP_PKEY_CTX *, unsigned char *, size_t *); + +typedef enum { + P11_ECX_EDDSA, + P11_ECX_XDH +} P11_ECX_KIND; + +typedef struct { + int type; + P11_ECX_KIND kind; + EVP_PKEY_METHOD *method; + P11_ECX_INIT_FN original_init; + P11_ECX_SIGN_FN original_sign; + P11_ECX_DIGESTSIGN_FN original_digestsign; + P11_ECX_DERIVE_FN original_derive; +} P11_ECX_METHOD; + +static P11_ECX_METHOD ecx_methods[] = { + { .type = EVP_PKEY_ED25519, .kind = P11_ECX_EDDSA }, + { .type = EVP_PKEY_ED448, .kind = P11_ECX_EDDSA }, + { .type = EVP_PKEY_X25519, .kind = P11_ECX_XDH }, + { .type = EVP_PKEY_X448, .kind = P11_ECX_XDH } +}; + +/* Return the ECX method entry for the specified EVP_PKEY type. */ +static P11_ECX_METHOD *ecx_method_by_type(int type) +{ + size_t i; + + for (i = 0; i < sizeof(ecx_methods) / sizeof(ecx_methods[0]); i++) { + if (ecx_methods[i].type == type) + return &ecx_methods[i]; + } + return NULL; +} + +/* + * Sign input with a PKCS#11-backed Ed25519 or Ed448 key. + * If sig is NULL, return the required signature size. + */ +static int pkcs11_eddsa_pmeth_sign(EVP_PKEY_CTX *ctx, unsigned char *sig, + size_t *siglen, const unsigned char *tbs, size_t tbslen) +{ + EVP_PKEY *pkey; + PKCS11_OBJECT_private *key; + size_t required; + + if (ctx == NULL) + return 0; + + pkey = EVP_PKEY_CTX_get0_pkey(ctx); + if (pkey == NULL) + return 0; + + key = pkcs11_get_ex_data_object(pkey); + if (key == NULL) + return -1; /* foreign key: use the original OpenSSL method */ + + if (key->object_class != CKO_PRIVATE_KEY) + return -1; + + if (key->slot == NULL || key->slot->ctx == NULL) + return 0; + + if ((key->slot->ctx->flags & PKCS11_FLAG_NO_METHODS) != 0) + return -1; + + if (check_object_fork(key) < 0) + return 0; + + if (siglen == NULL || tbs == NULL) + return 0; + + switch (EVP_PKEY_get_id(pkey)) { + case EVP_PKEY_ED25519: + required = ED25519_SIG_LEN; + break; + case EVP_PKEY_ED448: + required = ED448_SIG_LEN; + break; + default: + return 0; + } + + if (sig == NULL) { + *siglen = required; + return 1; + } + if (*siglen < required) { + *siglen = required; + return 0; + } + + return pkcs11_evp_pkey_eddsa_sign(key, sig, siglen, + tbs, tbslen) > 0 ? 1 : 0; +} + +/* Sign with a PKCS#11-backed EdDSA key using EVP_DigestSign. */ +static int pkcs11_eddsa_pmeth_digestsign(EVP_MD_CTX *mdctx, + unsigned char *sig, size_t *siglen, + const unsigned char *tbs, size_t tbslen) +{ + EVP_PKEY_CTX *ctx; + + if (mdctx == NULL) + return 0; + + ctx = EVP_MD_CTX_pkey_ctx(mdctx); + if (ctx == NULL) + return 0; + + return pkcs11_eddsa_pmeth_sign(ctx, sig, siglen, tbs, tbslen); +} + +/* + * Derive a shared secret with a PKCS#11-backed X25519 or X448 key. + * If secret is NULL, return the required secret length. + */ +static int pkcs11_xdh_pmeth_derive(EVP_PKEY_CTX *ctx, + unsigned char *secret, size_t *secretlen) +{ + EVP_PKEY *pkey; + EVP_PKEY *peerkey; + PKCS11_OBJECT_private *key; + unsigned char peer_public[X448_PUB_LEN]; + size_t peer_public_len = sizeof(peer_public); + size_t required; + + if (ctx == NULL) + return 0; + + pkey = EVP_PKEY_CTX_get0_pkey(ctx); + if (pkey == NULL) + return 0; + + key = pkcs11_get_ex_data_object(pkey); + if (key == NULL) + return -1; /* foreign key: use the original OpenSSL method */ + + if (key->object_class != CKO_PRIVATE_KEY) + return -1; + + if (key->slot == NULL || key->slot->ctx == NULL) + return 0; + + if ((key->slot->ctx->flags & PKCS11_FLAG_NO_METHODS) != 0) + return -1; + + if (check_object_fork(key) < 0) + return 0; + + if (secretlen == NULL) + return 0; + + switch (EVP_PKEY_get_id(pkey)) { + case EVP_PKEY_X25519: + required = X25519_PUB_LEN; + break; + case EVP_PKEY_X448: + required = X448_PUB_LEN; + break; + default: + return 0; + } + + if (secret == NULL) { + *secretlen = required; + return 1; + } + if (*secretlen < required) { + *secretlen = required; + return 0; + } + + peerkey = EVP_PKEY_CTX_get0_peerkey(ctx); + if (peerkey == NULL) + return 0; + + if (EVP_PKEY_get_raw_public_key(peerkey, NULL, + &peer_public_len) != 1 || + peer_public_len != required || + peer_public_len > sizeof(peer_public)) + return 0; + + if (EVP_PKEY_get_raw_public_key(peerkey, peer_public, + &peer_public_len) != 1) + return 0; + + return pkcs11_evp_pkey_xdh_derive(key, peer_public, + peer_public_len, secret, secretlen) > 0 ? 1 : 0; +} + +/* Sign with libp11, falling back to OpenSSL for a foreign EdDSA key. */ +static int pkcs11_ecx_sign(EVP_PKEY_CTX *ctx, unsigned char *sig, + size_t *siglen, const unsigned char *tbs, size_t tbslen) +{ + EVP_PKEY *pkey = NULL; + P11_ECX_METHOD *state = NULL; + int ret; + + if (ctx != NULL) + pkey = EVP_PKEY_CTX_get0_pkey(ctx); + if (pkey != NULL) + state = ecx_method_by_type(EVP_PKEY_get_id(pkey)); + + if (state == NULL || state->kind != P11_ECX_EDDSA) + return 0; + + ret = pkcs11_eddsa_pmeth_sign(ctx, sig, siglen, tbs, tbslen); + if (ret < 0 && state->original_sign != NULL) + ret = state->original_sign(ctx, sig, siglen, tbs, tbslen); + + return ret; +} + +/* + * Sign with libp11 using EVP_DigestSign, falling back to OpenSSL for a + * foreign EdDSA key. + */ +static int pkcs11_ecx_digestsign(EVP_MD_CTX *mdctx, + unsigned char *sig, size_t *siglen, + const unsigned char *tbs, size_t tbslen) +{ + EVP_PKEY_CTX *ctx = NULL; + EVP_PKEY *pkey = NULL; + P11_ECX_METHOD *state = NULL; + int ret; + + if (mdctx != NULL) + ctx = EVP_MD_CTX_pkey_ctx(mdctx); + if (ctx != NULL) + pkey = EVP_PKEY_CTX_get0_pkey(ctx); + if (pkey != NULL) + state = ecx_method_by_type(EVP_PKEY_get_id(pkey)); + + if (state == NULL || state->kind != P11_ECX_EDDSA) + return 0; + + ret = pkcs11_eddsa_pmeth_digestsign(mdctx, sig, siglen, + tbs, tbslen); + if (ret < 0 && state->original_digestsign != NULL) + ret = state->original_digestsign(mdctx, sig, siglen, + tbs, tbslen); + + return ret; +} + +/* Derive with libp11, falling back to OpenSSL for a foreign XDH key. */ +static int pkcs11_ecx_derive(EVP_PKEY_CTX *ctx, + unsigned char *secret, size_t *secretlen) +{ + EVP_PKEY *pkey = NULL; + P11_ECX_METHOD *state = NULL; + int ret; + + if (ctx != NULL) + pkey = EVP_PKEY_CTX_get0_pkey(ctx); + if (pkey != NULL) + state = ecx_method_by_type(EVP_PKEY_get_id(pkey)); + + if (state == NULL || state->kind != P11_ECX_XDH) + return 0; + + ret = pkcs11_xdh_pmeth_derive(ctx, secret, secretlen); + if (ret < 0 && state->original_derive != NULL) + ret = state->original_derive(ctx, secret, secretlen); + + return ret; +} + +/* Reset the cached ECX method state while preserving its type and kind. */ +static void ecx_method_reset(P11_ECX_METHOD *state) +{ + state->method = NULL; + state->original_init = NULL; + state->original_sign = NULL; + state->original_digestsign = NULL; + state->original_derive = NULL; +} + +/* Remove and free all registered ECX EVP_PKEY_METHOD wrappers. */ +void pkcs11_ecx_methods_free(void) +{ + size_t i; + + for (i = 0; i < sizeof(ecx_methods) / sizeof(ecx_methods[0]); i++) { + P11_ECX_METHOD *state = &ecx_methods[i]; + + if (state->method == NULL) + continue; + + EVP_PKEY_meth_remove(state->method); + EVP_PKEY_meth_free(state->method); + ecx_method_reset(state); + } +} + +/* Create and register an ECX EVP_PKEY_METHOD wrapper. */ +static int pkcs11_ecx_method_new(P11_ECX_METHOD *state) +{ + const EVP_PKEY_METHOD *original; + int original_type; + int original_flags; + + if (state == NULL) + return 0; + + if (state->method != NULL) + return 1; + + original = EVP_PKEY_meth_find(state->type); + if (original == NULL) + return 0; + + EVP_PKEY_meth_get0_info(&original_type, &original_flags, original); + if (original_type != state->type) + return 0; + + if (state->kind == P11_ECX_EDDSA && + !(original_flags & EVP_PKEY_FLAG_SIGCTX_CUSTOM)) + return 0; + + state->method = EVP_PKEY_meth_new(state->type, original_flags); + if (state->method == NULL) + return 0; + + EVP_PKEY_meth_copy(state->method, original); + + switch (state->kind) { + case P11_ECX_EDDSA: + EVP_PKEY_meth_get_sign(original, + &state->original_init, &state->original_sign); + EVP_PKEY_meth_get_digestsign(original, + &state->original_digestsign); + if (state->original_digestsign == NULL) + goto error; + EVP_PKEY_meth_set_sign(state->method, + state->original_init, pkcs11_ecx_sign); + EVP_PKEY_meth_set_digestsign(state->method, + pkcs11_ecx_digestsign); + break; + case P11_ECX_XDH: + EVP_PKEY_meth_get_derive(original, + &state->original_init, &state->original_derive); + if (state->original_derive == NULL) + goto error; + EVP_PKEY_meth_set_derive(state->method, + state->original_init, pkcs11_ecx_derive); + break; + default: + goto error; + } + + /* Register the method globally */ + if (!EVP_PKEY_meth_add0(state->method)) + goto error; + + return 1; + +error: + EVP_PKEY_meth_free(state->method); + ecx_method_reset(state); + return 0; +} + +/* Enable an ECX EVP_PKEY_METHOD wrapper for a PKCS#11 private key. */ +int pkcs11_ecx_method_enable(PKCS11_OBJECT_private *key, int type) +{ + P11_ECX_METHOD *state; + + if (key == NULL || key->slot == NULL || key->slot->ctx == NULL) + return 0; + + if (key->object_class != CKO_PRIVATE_KEY || + (key->slot->ctx->flags & PKCS11_FLAG_NO_METHODS) != 0) + return 1; + + state = ecx_method_by_type(type); + if (state == NULL) + return 1; + + return pkcs11_ecx_method_new(state); +} + +#endif /* LIBP11_HAVE_ECX_METHODS */ + +/* vim: set noexpandtab: */ diff --git a/src/p11_eddsa.c b/src/p11_eddsa.c deleted file mode 100644 index f89c81c5..00000000 --- a/src/p11_eddsa.c +++ /dev/null @@ -1,968 +0,0 @@ -/* - * Copyright © 2025 Mobi - Com Polska Sp. z o.o. - * Author: Małgorzata Olszówka - * All rights reserved. - * - * This file implements the handling of EdDSA and XDH keys stored on a PKCS11 token. - * Inside EVP_PKEY, Ed25519/Ed448 and X25519/X448 keys are stored in an ECX_KEY - * structure. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - */ - -#include "libp11-int.h" -#include - -#if !defined(OPENSSL_NO_ECX) && OPENSSL_VERSION_NUMBER >= 0x30000000L -#include -#include - -#if OPENSSL_VERSION_NUMBER < 0x40000000L -static EVP_PKEY_METHOD *pkcs11_ed25519_method = NULL; -static EVP_PKEY_METHOD *pkcs11_ed448_method = NULL; -static const EVP_PKEY_METHOD *orig_ed25519_method = NULL; -static const EVP_PKEY_METHOD *orig_ed448_method = NULL; - -static EVP_PKEY_METHOD *pkcs11_x25519_method = NULL; -static EVP_PKEY_METHOD *pkcs11_x448_method = NULL; -static const EVP_PKEY_METHOD *orig_x25519_method = NULL; -static const EVP_PKEY_METHOD *orig_x448_method = NULL; -#endif /* OPENSSL_VERSION_NUMBER < 0x40000000L */ - -int (*orig_ed25519_digestsign)(EVP_MD_CTX *ctx, unsigned char *sig, size_t *siglen, - const unsigned char *tbs, size_t tbslen); - -int (*orig_ed448_digestsign)(EVP_MD_CTX *ctx, unsigned char *sig, size_t *siglen, - const unsigned char *tbs, size_t tbslen); - -int (*orig_x25519_derive)(EVP_PKEY_CTX *ctx, unsigned char *secret, size_t *secretlen); - -int (*orig_x448_derive)(EVP_PKEY_CTX *ctx, unsigned char *secret, size_t *secretlen); - -#if OPENSSL_VERSION_NUMBER < 0x40000000L - -/******************************************************************************/ -/* EdDSA methods */ -/******************************************************************************/ - -/* - * EVP_PKEY method sign wrapper for EdDSA. - * This function is invoked internally by EVP_PKEY_sign(). - * If the key belongs to PKCS#11, perform signing via pkcs11_evp_pkey_eddsa_sign(). - */ -static int pkcs11_eddsa_pmeth_sign(EVP_PKEY_CTX *ctx, unsigned char *sig, - size_t *siglen, const unsigned char *tbs, size_t tbslen) -{ - EVP_PKEY *pkey; - PKCS11_OBJECT_private *key; - PKCS11_SLOT_private *slot; - CK_SESSION_HANDLE session; - - if (siglen == NULL || *siglen > UINT_MAX) - return 0; - - pkey = EVP_PKEY_CTX_get0_pkey(ctx); - if (!pkey) - return 0; - - key = pkcs11_get_ex_data_pkey(pkey); - if (!key) - return 0; - - slot = key->slot; - if (!slot) - return 0; - - if (pkcs11_session_pool_acquire(slot, 0, &session)) - return 0; - - pkcs11_session_pool_release(slot, session); - - if (!pkcs11_evp_pkey_eddsa_sign(key, sig, siglen, tbs, tbslen)) - return 0; - - return 1; -} - -/* - * Custom EVP_PKEY_METHOD digestsign implementation for EdDSA (Ed25519/Ed448) - * - * This function supports the two-step signing process used by EVP_DigestSign*(): - * 1. Query the required signature length (sig == NULL). - * 2. Perform the actual signing when a buffer is provided (sig != NULL). - * - * If the key is managed by PKCS#11, the signing is performed via pkcs11_evp_pkey_eddsa_sign(). - * Otherwise, the call is delegated to the original OpenSSL Ed25519/Ed448 implementation. - */ -static int pkcs11_eddsa_pmeth_digestsign(EVP_MD_CTX *ctx, unsigned char *sig, - size_t *siglen, const unsigned char *tbs, size_t tbslen) -{ - EVP_PKEY *pkey; - PKCS11_OBJECT_private *key; - PKCS11_SLOT_private *slot; - CK_SESSION_HANDLE session; - - if (siglen == NULL) - return -1; - - pkey = EVP_PKEY_CTX_get0_pkey(EVP_MD_CTX_pkey_ctx(ctx)); - if (!pkey) - return -1; - - key = pkcs11_get_ex_data_pkey(pkey); - if (!key) - return -1; - - slot = key->slot; - if (!slot) - return -1; - - if (pkcs11_session_pool_acquire(slot, 0, &session)) - return -1; - - pkcs11_session_pool_release(slot, session); - - /* Step 1: caller asks for signature length only */ - if (sig == NULL) { - if (EVP_PKEY_id(pkey) == EVP_PKEY_ED25519) - *siglen = 64; /* fixed size for Ed25519 */ - else if (EVP_PKEY_id(pkey) == EVP_PKEY_ED448) - *siglen = 114; /* fixed size for Ed448 */ - else - return -1; - /* success: report the expected signature length only, - * no signing is performed in this call */ - return 1; - } - - /* Step 2: actual signing */ - if (!pkcs11_evp_pkey_eddsa_sign(key, sig, siglen, tbs, tbslen)) - return -1; - - return 1; -} - - -static int pkcs11_pkey_ed25519_digestsign(EVP_MD_CTX *ctx, - unsigned char *sig, size_t *siglen, - const unsigned char *tbs, size_t tbslen) -{ - int ret; - - ret = pkcs11_eddsa_pmeth_digestsign(ctx, sig, siglen, tbs, tbslen); - if (ret < 0) - /* assume a foreign key */ - ret = (*orig_ed25519_digestsign)(ctx, sig, siglen, tbs, tbslen); - return ret; -} - -static int pkcs11_pkey_ed448_digestsign(EVP_MD_CTX *ctx, - unsigned char *sig, size_t *siglen, - const unsigned char *tbs, size_t tbslen) -{ - int ret; - - ret = pkcs11_eddsa_pmeth_digestsign(ctx, sig, siglen, tbs, tbslen); - if (ret < 0) - ret = (*orig_ed448_digestsign)(ctx, sig, siglen, tbs, tbslen); - return ret; -} - -/* - * For Ed25519/Ed448, no digest algorithm can be set. - * The only valid value here is NULL (PureEdDSA). - * OpenSSL calls this during DigestSignInit() to check whether the digest is acceptable. - */ -static int pkcs11_eddsa_pmeth_ctrl(EVP_PKEY_CTX *ctx, int type, int p1, void *p2) -{ - (void)ctx; - (void)p1; - switch (type) { - case EVP_PKEY_CTRL_MD: - if (p2 == NULL) - return 1; /* Accept NULL digest */ - return 0; /* Reject if caller tries to set a digest */ - default: - return -2; /* command not supported */ - } -} - -/* Global initialize ED25519 EVP_PKEY_METHOD */ -static int pkcs11_ed25519_method_new(void) -{ - int orig_id, orig_flags; - - if (pkcs11_ed25519_method) - return 1; /* EVP_PKEY_ED25519 method already initialized */ - - orig_ed25519_method = EVP_PKEY_meth_find(EVP_PKEY_ED25519); - if (!orig_ed25519_method) - return 0; - - EVP_PKEY_meth_get0_info(&orig_id, &orig_flags, orig_ed25519_method); - if (orig_id != EVP_PKEY_ED25519 || !(orig_flags & EVP_PKEY_FLAG_SIGCTX_CUSTOM)) - return 0; - - /* The digestsign() method is used to generate a signature in a one-shot mode */ - EVP_PKEY_meth_get_digestsign(orig_ed25519_method, &orig_ed25519_digestsign); - if (!orig_ed25519_digestsign) - return 0; - - /* Don't assume any digest related defaults */ - pkcs11_ed25519_method = EVP_PKEY_meth_new(EVP_PKEY_ED25519, EVP_PKEY_FLAG_SIGCTX_CUSTOM); - if (!pkcs11_ed25519_method) - return 0; - - /* Duplicate the original method */ - EVP_PKEY_meth_copy(pkcs11_ed25519_method, orig_ed25519_method); - - /* Override selected ED25519 method callbacks with PKCS#11 implementations */ - EVP_PKEY_meth_set_sign(pkcs11_ed25519_method, NULL, pkcs11_eddsa_pmeth_sign); - EVP_PKEY_meth_set_digestsign(pkcs11_ed25519_method, pkcs11_pkey_ed25519_digestsign); - EVP_PKEY_meth_set_ctrl(pkcs11_ed25519_method, pkcs11_eddsa_pmeth_ctrl, NULL); - - /* Register the method globally */ - if (!EVP_PKEY_meth_add0(pkcs11_ed25519_method)) { - EVP_PKEY_meth_free(pkcs11_ed25519_method); - pkcs11_ed25519_method = NULL; - return 0; - } - return 1; -} - -/* Global initialize ED448 EVP_PKEY_METHOD */ -static int pkcs11_ed448_method_new(void) -{ - int orig_id, orig_flags; - - if (pkcs11_ed448_method) - return 1; /* EVP_PKEY_ED448 method already initialized */ - - orig_ed448_method = EVP_PKEY_meth_find(EVP_PKEY_ED448); - if (!orig_ed448_method) - return 0; - - EVP_PKEY_meth_get0_info(&orig_id, &orig_flags, orig_ed448_method); - if (orig_id != EVP_PKEY_ED448 || !(orig_flags & EVP_PKEY_FLAG_SIGCTX_CUSTOM)) - return 0; - - /* The digestsign() method is used to generate a signature in a one-shot mode */ - EVP_PKEY_meth_get_digestsign(orig_ed448_method, &orig_ed448_digestsign); - if (!orig_ed448_digestsign) - return 0; - - /* Don't assume any digest related defaults */ - pkcs11_ed448_method = EVP_PKEY_meth_new(EVP_PKEY_ED448, EVP_PKEY_FLAG_SIGCTX_CUSTOM); - if (!pkcs11_ed448_method) - return 0; - - /* Duplicate the original method */ - EVP_PKEY_meth_copy(pkcs11_ed448_method, orig_ed448_method); - - /* Override selected ED448 method callbacks with PKCS#11 implementations */ - EVP_PKEY_meth_set_sign(pkcs11_ed448_method, NULL, pkcs11_eddsa_pmeth_sign); - EVP_PKEY_meth_set_digestsign(pkcs11_ed448_method, pkcs11_pkey_ed448_digestsign); - EVP_PKEY_meth_set_ctrl(pkcs11_ed448_method, pkcs11_eddsa_pmeth_ctrl, NULL); - - /* Register the method globally */ - if (!EVP_PKEY_meth_add0(pkcs11_ed448_method)) { - EVP_PKEY_meth_free(pkcs11_ed448_method); - pkcs11_ed448_method = NULL; - return 0; - } - return 1; -} - -void pkcs11_ed25519_method_free(void) -{ - if (pkcs11_ed25519_method) { - free_pkey_ex_index(); - /* Remove an EVP_PKEY_METHOD object added by EVP_PKEY_meth_add0() */ - EVP_PKEY_meth_remove(pkcs11_ed25519_method); - EVP_PKEY_meth_free(pkcs11_ed25519_method); - pkcs11_ed25519_method = NULL; - } -} - -void pkcs11_ed448_method_free(void) -{ - if (pkcs11_ed448_method) { - free_pkey_ex_index(); - EVP_PKEY_meth_remove(pkcs11_ed448_method); - EVP_PKEY_meth_free(pkcs11_ed448_method); - pkcs11_ed448_method = NULL; - } -} - -void pkcs11_ed_key_method_free(void) -{ - pkcs11_ed25519_method_free(); - pkcs11_ed448_method_free(); -} - - -/******************************************************************************/ -/* XDH methods */ -/******************************************************************************/ - -static int pkcs11_xdh_pmeth_derive(EVP_PKEY_CTX *ctx, unsigned char *secret, - size_t *secretlen) -{ - EVP_PKEY *pkey, *peerkey; - PKCS11_OBJECT_private *key; - PKCS11_SLOT_private *slot; - CK_SESSION_HANDLE session; - unsigned char peer_pub[56]; - size_t peer_pub_len = sizeof(peer_pub); - size_t expected_len; - int type; - - if (ctx == NULL || secretlen == NULL) - return 0; - - pkey = EVP_PKEY_CTX_get0_pkey(ctx); - if (!pkey) - return 0; - - key = pkcs11_get_ex_data_pkey(pkey); - if (!key) - return -1; - - slot = key->slot; - if (!slot) - return -1; - - if (pkcs11_session_pool_acquire(slot, 0, &session)) - return -1; - - pkcs11_session_pool_release(slot, session); - - type = EVP_PKEY_id(pkey); - switch (type) { - case EVP_PKEY_X25519: - expected_len = 32; - break; - case EVP_PKEY_X448: - expected_len = 56; - break; - default: - return -1; /* not an XDH key handled by this wrapper */ - } - - if (secret == NULL) { - *secretlen = expected_len; - return 1; - } - - if (*secretlen < expected_len) { - *secretlen = expected_len; - return 0; - } - - peerkey = EVP_PKEY_CTX_get0_peerkey(ctx); - if (peerkey == NULL) - return 0; - - if (EVP_PKEY_get_raw_public_key(peerkey, NULL, &peer_pub_len) <= 0) - return 0; - - if (peer_pub_len != expected_len) - return 0; - - if (EVP_PKEY_get_raw_public_key(peerkey, peer_pub, &peer_pub_len) <= 0) - return 0; - - if (!pkcs11_evp_pkey_xdh_derive(key, peer_pub, peer_pub_len, - secret, secretlen)) - return 0; - - return 1; -} - -static int pkcs11_pkey_x25519_derive(EVP_PKEY_CTX *ctx, unsigned char *secret, - size_t *secretlen) -{ - int ret; - - ret = pkcs11_xdh_pmeth_derive(ctx, secret, secretlen); - if (ret < 0) - /* assume a foreign key */ - ret = (*orig_x25519_derive)(ctx, secret, secretlen); - return ret; -} - -static int pkcs11_pkey_x448_derive(EVP_PKEY_CTX *ctx, unsigned char *secret, - size_t *secretlen) -{ - int ret; - - ret = pkcs11_xdh_pmeth_derive(ctx, secret, secretlen); - if (ret < 0) - ret = (*orig_x448_derive)(ctx, secret, secretlen); - return ret; -} - -/* Global initialize X25519 EVP_PKEY_METHOD */ -static int pkcs11_x25519_method_new(void) -{ - int orig_id, orig_flags; - - if (pkcs11_x25519_method) - return 1; /* EVP_PKEY_X25519 method already initialized */ - - orig_x25519_method = EVP_PKEY_meth_find(EVP_PKEY_X25519); - if (!orig_x25519_method) - return 0; - - EVP_PKEY_meth_get0_info(&orig_id, &orig_flags, orig_x25519_method); - if (orig_id != EVP_PKEY_X25519 || !(orig_flags & EVP_PKEY_FLAG_SIGCTX_CUSTOM)) - return 0; - - EVP_PKEY_meth_get_derive(orig_x25519_method, NULL, &orig_x25519_derive); - if (!orig_x25519_derive) - return 0; - - pkcs11_x25519_method = EVP_PKEY_meth_new(EVP_PKEY_X25519, EVP_PKEY_FLAG_AUTOARGLEN); - if (!pkcs11_x25519_method) - return 0; - - /* Duplicate the original method */ - EVP_PKEY_meth_copy(pkcs11_x25519_method, orig_x25519_method); - - /* Override selected X25519 method callbacks with PKCS#11 implementations */ - EVP_PKEY_meth_set_derive(pkcs11_x25519_method, NULL, pkcs11_pkey_x25519_derive); - - /* Register the method globally */ - if (!EVP_PKEY_meth_add0(pkcs11_x25519_method)) { - EVP_PKEY_meth_free(pkcs11_x25519_method); - pkcs11_x25519_method = NULL; - return 0; - } - return 1; -} - -/* Global initialize X448 EVP_PKEY_METHOD */ -static int pkcs11_x448_method_new(void) -{ - int orig_id, orig_flags; - - if (pkcs11_x448_method) - return 1; /* EVP_PKEY_X448 method already initialized */ - - orig_x448_method = EVP_PKEY_meth_find(EVP_PKEY_X448); - if (!orig_x448_method) - return 0; - - EVP_PKEY_meth_get0_info(&orig_id, &orig_flags, orig_x448_method); - if (orig_id != EVP_PKEY_X448 || !(orig_flags & EVP_PKEY_FLAG_SIGCTX_CUSTOM)) - return 0; - - EVP_PKEY_meth_get_derive(orig_x448_method, NULL, &orig_x448_derive); - if (!orig_x448_derive) - return 0; - - pkcs11_x448_method = EVP_PKEY_meth_new(EVP_PKEY_X448, EVP_PKEY_FLAG_AUTOARGLEN); - if (!pkcs11_x448_method) - return 0; - - /* Duplicate the original method */ - EVP_PKEY_meth_copy(pkcs11_x448_method, orig_x448_method); - - /* Override selected X448 method callbacks with PKCS#11 implementations */ - EVP_PKEY_meth_set_derive(pkcs11_x448_method, NULL, pkcs11_pkey_x448_derive); - - /* Register the method globally */ - if (!EVP_PKEY_meth_add0(pkcs11_x448_method)) { - EVP_PKEY_meth_free(pkcs11_x448_method); - pkcs11_x448_method = NULL; - return 0; - } - return 1; -} - -void pkcs11_x25519_method_free(void) -{ - if (pkcs11_x25519_method) { - free_pkey_ex_index(); - /* Remove an EVP_PKEY_METHOD object added by EVP_PKEY_meth_add0() */ - EVP_PKEY_meth_remove(pkcs11_x25519_method); - EVP_PKEY_meth_free(pkcs11_x25519_method); - pkcs11_x25519_method = NULL; - } -} - -void pkcs11_x448_method_free(void) -{ - if (pkcs11_x448_method) { - free_pkey_ex_index(); - EVP_PKEY_meth_remove(pkcs11_x448_method); - EVP_PKEY_meth_free(pkcs11_x448_method); - pkcs11_x448_method = NULL; - } -} - -void pkcs11_xdh_key_method_free(void) -{ - pkcs11_x25519_method_free(); - pkcs11_x448_method_free(); -} - -#endif /* OPENSSL_VERSION_NUMBER < 0x40000000L */ - - -/******************************************************************************/ -/* Get EVP_PKEY from raw public key bytes */ -/******************************************************************************/ - -/* - * Decode a DER OCTET STRING and return its contents. - * If decoding fails, duplicate the input buffer unchanged. - * Returns 1 on success, 0 on error. - */ -static int strip_der_octet_string_alloc(const unsigned char *in, size_t inlen, - unsigned char **out, size_t *outlen) -{ - const unsigned char *p = in; - unsigned char *buf; - ASN1_OCTET_STRING *os = NULL; - - if (out == NULL || outlen == NULL || in == NULL || inlen == 0) - return 0; - - *out = NULL; - *outlen = 0; - - /* For EdDSA (RFC8032) the CKA_EC_POINT attribute may be - * encoded as a DER OCTET STRING */ - os = d2i_ASN1_OCTET_STRING(NULL, &p, (long)inlen); - if (os != NULL && p == in + inlen) { - const unsigned char *data = ASN1_STRING_get0_data(os); - int len = ASN1_STRING_length(os); - - buf = OPENSSL_malloc((size_t)len); - if (buf == NULL) { - ASN1_OCTET_STRING_free(os); - return 0; - } - memcpy(buf, data, (size_t)len); - *out = buf; - *outlen = (size_t)len; - ASN1_OCTET_STRING_free(os); - return 1; - } - ASN1_OCTET_STRING_free(os); - - /* Not DER OCTET STRING -> copy as-is */ - buf = OPENSSL_malloc(inlen); - if (buf == NULL) - return 0; - memcpy(buf, in, inlen); - *out = buf; - *outlen = inlen; - return 1; -} - -/* - * Parse a DER X.509 certificate and return raw Ed25519, Ed448, X25519 or X448 - * public key bytes. - * Returns 1 on success, 0 on error. - */ -static int extract_ecx_raw_from_x509_der(const unsigned char *der, size_t derlen, - unsigned char **raw, size_t *rawlen) -{ - const unsigned char *p = der; - X509 *cert = NULL; - EVP_PKEY *pkey = NULL; - unsigned char *buf = NULL; - size_t len = 0; - int type, ok = 0; - - if (raw == NULL || rawlen == NULL || der == NULL || derlen == 0) - return 0; - - *raw = NULL; - *rawlen = 0; - - cert = d2i_X509(NULL, &p, (long)derlen); - if (cert == NULL) - goto end; - - pkey = X509_get_pubkey(cert); - if (pkey == NULL) - goto end; - - /* Ensure it is an ECX key with raw public-key support. */ - type = EVP_PKEY_id(pkey); - if (type != EVP_PKEY_ED25519 && type != EVP_PKEY_ED448 && - type != EVP_PKEY_X25519 && type != EVP_PKEY_X448) - goto end; - - - /* Query required size */ - if (EVP_PKEY_get_raw_public_key(pkey, NULL, &len) != 1 || len == 0) - goto end; - - buf = OPENSSL_malloc(len); - if (buf == NULL) - goto end; - - /* Fetch raw public key bytes */ - if (EVP_PKEY_get_raw_public_key(pkey, buf, &len) != 1) { - OPENSSL_free(buf); - buf = NULL; - goto end; - } - - *raw = buf; - *rawlen = len; - buf = NULL; - ok = 1; - -end: - OPENSSL_free(buf); - EVP_PKEY_free(pkey); - X509_free(cert); - return ok; -} - - -/* - * Extract raw EdDSA public key bytes from a CKO_PUBLIC_KEY object using CKA_EC_POINT. - * Returns 1 on success, 0 on failure. - */ -static int extract_pub_from_public_key_obj(PKCS11_CTX_private *ctx, CK_SESSION_HANDLE session, - CK_OBJECT_HANDLE obj, unsigned char **raw, size_t *rawlen) -{ - unsigned char *ecpt = NULL; - size_t ecptlen = 0; - int ok = 0; - - if (pkcs11_getattr_alloc(ctx, session, obj, CKA_EC_POINT, &ecpt, &ecptlen)) - return 0; - - ok = strip_der_octet_string_alloc(ecpt, ecptlen, raw, rawlen); - OPENSSL_free(ecpt); - return ok; -} - -/* - * Extract raw ECX public key bytes from a CKO_CERTIFICATE object. - * The certificate is read from CKA_VALUE (DER-encoded X.509) and - * the public key is obtained via X.509 parsing. - * Returns 1 on success, 0 on failure. - */ -static int extract_pub_from_cert_obj(PKCS11_CTX_private *ctx, CK_SESSION_HANDLE session, - CK_OBJECT_HANDLE obj, unsigned char **raw, size_t *rawlen) -{ - unsigned char *der = NULL; - size_t derlen = 0; - int ok = 0; - - if (pkcs11_getattr_alloc(ctx, session, obj, CKA_VALUE, &der, &derlen)) - return 0; - - ok = extract_ecx_raw_from_x509_der(der, derlen, raw, rawlen); - OPENSSL_free(der); - return ok; -} - -/* - * Select an object that can provide public key material. - * - * For a private key object, try to locate a matching CKO_PUBLIC_KEY - * (same CKA_ID). If not found, fall back to CKO_CERTIFICATE. - * - * On success, returns a PKCS11_OBJECT_private pointer. - * If a new object is returned, *needs_free is set to 1 and the caller - * must free it with pkcs11_object_free(). - * - * Returns NULL on failure. - */ -static PKCS11_OBJECT_private *pkcs11_choose_public_source(PKCS11_OBJECT_private *key, - CK_SESSION_HANDLE session, int *needs_free) -{ - PKCS11_OBJECT_private *obj; - - *needs_free = 0; - - if (key->object_class != CKO_PRIVATE_KEY) - return key; - - obj = pkcs11_object_from_object(key, session, CKO_PUBLIC_KEY); - if (obj != NULL && obj->object != CK_INVALID_HANDLE) { - *needs_free = 1; - return obj; - } - if (obj != NULL) - pkcs11_object_free(obj); - - obj = pkcs11_object_from_object(key, session, CKO_CERTIFICATE); - if (obj != NULL && obj->object != CK_INVALID_HANDLE) { - *needs_free = 1; - return obj; - } - if (obj != NULL) - pkcs11_object_free(obj); - - return NULL; -} - -/* - * Retrieve raw EdDSA public key bytes. - * - * Preference order: - * 1. CKO_PUBLIC_KEY -> CKA_EC_POINT - * 2. CKO_CERTIFICATE -> CKA_VALUE (DER) + X.509 parsing - * - * The returned buffer is allocated with OPENSSL_malloc() - * and must be freed by the caller. - * - * Returns 0 on success, -1 on failure. - */ -static int pkcs11_get_raw_public_key(PKCS11_OBJECT_private *key, - unsigned char **raw, size_t *rawlen) -{ - PKCS11_SLOT_private *slot; - PKCS11_CTX_private *ctx; - CK_SESSION_HANDLE session = CK_INVALID_HANDLE; - PKCS11_OBJECT_private *obj = NULL; - int obj_needs_free = 0, ok = 0; - - if (key == NULL || key->slot == NULL || raw == NULL || rawlen == NULL) - return -1; - - *raw = NULL; - *rawlen = 0; - slot = key->slot; - ctx = slot->ctx; - - if (pkcs11_session_pool_acquire(slot, 0, &session)) - return -1; - - obj = pkcs11_choose_public_source(key, session, &obj_needs_free); - if (obj == NULL || obj->object == CK_INVALID_HANDLE) - goto end; - - switch (obj->object_class) { - case CKO_PUBLIC_KEY: - ok = extract_pub_from_public_key_obj(ctx, session, obj->object, raw, rawlen); - break; - case CKO_CERTIFICATE: - ok = extract_pub_from_cert_obj(ctx, session, obj->object, raw, rawlen); - break; - default: - ok = 0; - break; - } - -end: - pkcs11_session_pool_release(slot, session); - - if (!ok) { - OPENSSL_free(*raw); - *raw = NULL; - *rawlen = 0; - } - if (obj_needs_free && obj != NULL) - pkcs11_object_free(obj); - - return ok ? 0 : -1; -} - -static EVP_PKEY *pkcs11_get_evp_key_ed25519(PKCS11_OBJECT_private *key) -{ - EVP_PKEY *pkey = NULL; - unsigned char *raw = NULL; - size_t rawlen = 0; - - /* Retrieve the public key in raw format from PKCS#11 */ - if (pkcs11_get_raw_public_key(key, &raw, &rawlen) < 0) - return NULL; - - /* Build a EVP_PKEY from the raw public key, used only as software public key */ - pkey = EVP_PKEY_new_raw_public_key_ex(NULL, "ED25519", NULL, raw, rawlen); - OPENSSL_free(raw); - - if (!pkey) - return NULL; - -#if OPENSSL_VERSION_NUMBER < 0x40000000L - if (key->object_class == CKO_PRIVATE_KEY) { - if ((key->slot->ctx->flags & PKCS11_FLAG_NO_METHODS) == 0) { - /* global initialize ED25519 EVP_PKEY_METHOD */ - if (!pkcs11_ed25519_method_new()) { - EVP_PKEY_free(pkey); - return NULL; - } - /* The cached EVP_PKEY borrows the object. Returned keys get - * an owning reference in pkcs11_get_key(). */ - alloc_pkey_ex_index(); - pkcs11_set_ex_data_pkey(pkey, key); - atexit(pkcs11_ed25519_method_free); - } - } -#endif /* OPENSSL_VERSION_NUMBER < 0x40000000L */ - return pkey; -} - -static EVP_PKEY *pkcs11_get_evp_key_ed448(PKCS11_OBJECT_private *key) -{ - EVP_PKEY *pkey = NULL; - unsigned char *raw = NULL; - size_t rawlen = 0; - - /* Retrieve the public key in raw format from PKCS#11 */ - if (pkcs11_get_raw_public_key(key, &raw, &rawlen) < 0) - return NULL; - - /* Build a EVP_PKEY from the raw public key, used only as software public key */ - pkey = EVP_PKEY_new_raw_public_key_ex(NULL, "ED448", NULL, raw, rawlen); - OPENSSL_free(raw); - - if (!pkey) - return NULL; - -#if OPENSSL_VERSION_NUMBER < 0x40000000L - if (key->object_class == CKO_PRIVATE_KEY) { - if ((key->slot->ctx->flags & PKCS11_FLAG_NO_METHODS) == 0) { - /* global initialize ED448 EVP_PKEY_METHOD */ - if (!pkcs11_ed448_method_new()) { - EVP_PKEY_free(pkey); - return NULL; - } - /* The cached EVP_PKEY borrows the object. Returned keys get - * an owning reference in pkcs11_get_key(). */ - alloc_pkey_ex_index(); - pkcs11_set_ex_data_pkey(pkey, key); - atexit(pkcs11_ed448_method_free); - } - } -#endif /* OPENSSL_VERSION_NUMBER < 0x40000000L */ - return pkey; -} - -static EVP_PKEY *pkcs11_get_evp_key_x25519(PKCS11_OBJECT_private *key) -{ - EVP_PKEY *pkey = NULL; - unsigned char *raw = NULL; - size_t rawlen = 0; - - /* Retrieve the public key in raw format from PKCS#11 */ - if (pkcs11_get_raw_public_key(key, &raw, &rawlen) < 0) - return NULL; - - /* Build a EVP_PKEY from the raw public key, used only as software public key */ - pkey = EVP_PKEY_new_raw_public_key_ex(NULL, "X25519", NULL, raw, rawlen); - OPENSSL_free(raw); - - if (!pkey) - return NULL; - -#if OPENSSL_VERSION_NUMBER < 0x40000000L - if (key->object_class == CKO_PRIVATE_KEY) { - if ((key->slot->ctx->flags & PKCS11_FLAG_NO_METHODS) == 0) { - /* global initialize X25519 EVP_PKEY_METHOD */ - if (!pkcs11_x25519_method_new()) { - EVP_PKEY_free(pkey); - return NULL; - } - /* The cached EVP_PKEY borrows the object. Returned keys get - * an owning reference in pkcs11_get_key(). */ - alloc_pkey_ex_index(); - pkcs11_set_ex_data_pkey(pkey, key); - atexit(pkcs11_x25519_method_free); - } - } -#endif /* OPENSSL_VERSION_NUMBER < 0x40000000L */ - return pkey; -} - -static EVP_PKEY *pkcs11_get_evp_key_x448(PKCS11_OBJECT_private *key) -{ - EVP_PKEY *pkey = NULL; - unsigned char *raw = NULL; - size_t rawlen = 0; - - /* Retrieve the public key in raw format from PKCS#11 */ - if (pkcs11_get_raw_public_key(key, &raw, &rawlen) < 0) - return NULL; - - /* Build a EVP_PKEY from the raw public key, used only as software public key */ - pkey = EVP_PKEY_new_raw_public_key_ex(NULL, "X448", NULL, raw, rawlen); - OPENSSL_free(raw); - - if (!pkey) - return NULL; - -#if OPENSSL_VERSION_NUMBER < 0x40000000L - if (key->object_class == CKO_PRIVATE_KEY) { - if ((key->slot->ctx->flags & PKCS11_FLAG_NO_METHODS) == 0) { - /* global initialize X448 EVP_PKEY_METHOD */ - if (!pkcs11_x448_method_new()) { - EVP_PKEY_free(pkey); - return NULL; - } - /* The cached EVP_PKEY borrows the object. Returned keys get - * an owning reference in pkcs11_get_key(). */ - alloc_pkey_ex_index(); - pkcs11_set_ex_data_pkey(pkey, key); - atexit(pkcs11_x448_method_free); - } - } -#endif /* OPENSSL_VERSION_NUMBER < 0x40000000L */ - return pkey; -} - - -PKCS11_OBJECT_ops pkcs11_ed25519_ops = { - EVP_PKEY_ED25519, - pkcs11_get_evp_key_ed25519, -}; - -PKCS11_OBJECT_ops pkcs11_ed448_ops = { - EVP_PKEY_ED448, - pkcs11_get_evp_key_ed448, -}; - -PKCS11_OBJECT_ops pkcs11_x25519_ops = { - EVP_PKEY_X25519, - pkcs11_get_evp_key_x25519, -}; - -PKCS11_OBJECT_ops pkcs11_x448_ops = { - EVP_PKEY_X448, - pkcs11_get_evp_key_x448, -}; - -#else /* !defined(OPENSSL_NO_ECX) && OPENSSL_VERSION_NUMBER >= 0x30000000L */ - -/* - * EdDSA (Ed25519/Ed448) and XDH (X5519/X448) support are not available: - * - either OpenSSL was built without ECX (no-ecx), or - * - OpenSSL version is older than 3.0. - * - * Provide stubs so engine_pkcs11 can still be built without - * requiring conditional changes elsewhere. - */ -#warning "EdDSA support not built with libp11" - -#endif /* !defined(OPENSSL_NO_ECX) && OPENSSL_VERSION_NUMBER >= 0x30000000L */ diff --git a/src/p11_falcon.c b/src/p11_falcon.c deleted file mode 100644 index 54cc1aad..00000000 --- a/src/p11_falcon.c +++ /dev/null @@ -1,303 +0,0 @@ -/* - * Copyright © 2026 Mobi - Com Polska Sp. z o.o. - * Author: Małgorzata Olszówka - * All rights reserved. - * - * This file implements the handling of ML-DSA keys stored on a PKCS11 token. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - */ - -#include "libp11-int.h" -#include - -#if OPENSSL_VERSION_NUMBER >= 0x30000000L - -#define FALCON_512_PUB_LEN 897 -#define FALCON_1024_PUB_LEN 1793 - -/* - * Extract raw PQC FALCON public key bytes from a CKO_PUBLIC_KEY object using CKA_VALUE. - * Returns 1 on success, 0 on failure. - */ -static int extract_pub_from_public_key_obj(PKCS11_CTX_private *ctx, - CK_SESSION_HANDLE session, CK_OBJECT_HANDLE obj, - size_t expected_len, unsigned char **raw, size_t *rawlen) -{ - unsigned char *value = NULL; - size_t value_len = 0; - - if (ctx == NULL || raw == NULL || rawlen == NULL || - obj == CK_INVALID_HANDLE || expected_len == 0) - return 0; - - *raw = NULL; - *rawlen = 0; - - if (pkcs11_getattr_alloc(ctx, session, obj, CKA_VALUE, &value, &value_len)) { - pkcs11_log(ctx, LOG_DEBUG, - "Missing CKA_VALUE attribute on PQC FALCON public key\n"); - return 0; - } - - if (value_len != expected_len) { - pkcs11_log(ctx, LOG_DEBUG, - "Unexpected PQC FALCON public key size: got %lu, expected %lu\n", - (unsigned long)value_len, (unsigned long)expected_len); - OPENSSL_free(value); - return 0; - } - - *raw = value; - *rawlen = value_len; - return 1; -} - -/* - * Extract raw PQC FALCON public key bytes from a CKO_CERTIFICATE object. - * The certificate is read from CKA_VALUE (DER-encoded X.509) and - * the public key is obtained via X.509 parsing. - * Returns 1 on success, 0 on failure. - */ -static int extract_pub_from_cert_obj(PKCS11_CTX_private *ctx, - CK_SESSION_HANDLE session, CK_OBJECT_HANDLE obj, - size_t expected_len, const char *algname, - unsigned char **raw, size_t *rawlen) -{ - const unsigned char *p; - unsigned char *der = NULL; - unsigned char *buf = NULL; - size_t derlen = 0; - size_t len = 0; - X509 *cert = NULL; - EVP_PKEY *pkey = NULL; - int ok = 0; - - if (ctx == NULL || raw == NULL || rawlen == NULL || - obj == CK_INVALID_HANDLE || expected_len == 0 || algname == NULL) - return 0; - - *raw = NULL; - *rawlen = 0; - - if (pkcs11_getattr_alloc(ctx, session, obj, CKA_VALUE, &der, &derlen)) - return 0; - - if (derlen == 0 || derlen > LONG_MAX) - goto end; - - p = der; - cert = d2i_X509(NULL, &p, (long)derlen); - if (cert == NULL || p != der + derlen) - goto end; - - pkey = X509_get_pubkey(cert); - if (pkey == NULL) - goto end; - - if (!EVP_PKEY_is_a(pkey, algname)) - goto end; - - if (EVP_PKEY_get_raw_public_key(pkey, NULL, &len) != 1) - goto end; - - if (len != expected_len) - goto end; - - buf = OPENSSL_malloc(len); - if (buf == NULL) - goto end; - - if (EVP_PKEY_get_raw_public_key(pkey, buf, &len) != 1) - goto end; - - if (len != expected_len) - goto end; - - *raw = buf; - *rawlen = len; - buf = NULL; - ok = 1; - -end: - OPENSSL_free(buf); - OPENSSL_free(der); - EVP_PKEY_free(pkey); - X509_free(cert); - return ok; -} - -/* - * Select an object that can provide public key material. - * - * For a private key object, try to locate a matching CKO_PUBLIC_KEY - * (same CKA_ID). If not found, fall back to CKO_CERTIFICATE. - * - * On success, returns a PKCS11_OBJECT_private pointer. - * If a new object is returned, *needs_free is set to 1 and the caller - * must free it with pkcs11_object_free(). - * - * Returns NULL on failure. - */ -static PKCS11_OBJECT_private *pkcs11_choose_public_source(PKCS11_OBJECT_private *key, - CK_SESSION_HANDLE session, int *needs_free) -{ - PKCS11_OBJECT_private *obj; - - *needs_free = 0; - - if (key->object_class != CKO_PRIVATE_KEY) - return key; - - obj = pkcs11_object_from_object(key, session, CKO_PUBLIC_KEY); - if (obj != NULL && obj->object != CK_INVALID_HANDLE) { - *needs_free = 1; - return obj; - } - if (obj != NULL) - pkcs11_object_free(obj); - - obj = pkcs11_object_from_object(key, session, CKO_CERTIFICATE); - if (obj != NULL && obj->object != CK_INVALID_HANDLE) { - *needs_free = 1; - return obj; - } - if (obj != NULL) - pkcs11_object_free(obj); - - return NULL; -} - -/* - * Retrieve raw PQC FALCON public key bytes. - * - * Preference order: - * 1. CKO_PUBLIC_KEY -> CKA_VALUE - * 2. CKO_CERTIFICATE -> CKA_VALUE (DER) + X.509 parsing - * - * The returned buffer is allocated with OPENSSL_malloc() - * and must be freed by the caller. - * - * Returns 0 on success, -1 on failure. - */ -static int pkcs11_get_raw_public_key(PKCS11_OBJECT_private *key, - size_t expected_len, const char *algname, - unsigned char **raw, size_t *rawlen) -{ - PKCS11_SLOT_private *slot; - PKCS11_CTX_private *ctx; - CK_SESSION_HANDLE session = CK_INVALID_HANDLE; - PKCS11_OBJECT_private *obj = NULL; - int obj_needs_free = 0, ok = 0; - - if (key == NULL || key->slot == NULL || raw == NULL || rawlen == NULL) - return -1; - - *raw = NULL; - *rawlen = 0; - slot = key->slot; - ctx = slot->ctx; - - if (pkcs11_session_pool_acquire(slot, 0, &session)) - return -1; - - obj = pkcs11_choose_public_source(key, session, &obj_needs_free); - if (obj == NULL || obj->object == CK_INVALID_HANDLE) - goto end; - - switch (obj->object_class) { - case CKO_PUBLIC_KEY: - ok = extract_pub_from_public_key_obj(ctx, session, obj->object, - expected_len, raw, rawlen); - break; - case CKO_CERTIFICATE: - ok = extract_pub_from_cert_obj(ctx, session, obj->object, - expected_len, algname, raw, rawlen); - break; - default: - ok = 0; - break; - } - -end: - pkcs11_session_pool_release(slot, session); - - if (!ok) { - OPENSSL_free(*raw); - *raw = NULL; - *rawlen = 0; - } - if (obj_needs_free && obj != NULL) - pkcs11_object_free(obj); - - return ok ? 0 : -1; -} - -static EVP_PKEY *pkcs11_get_evp_key_falcon(PKCS11_OBJECT_private *key, - size_t publen, const char *algname) -{ - EVP_PKEY *pkey = NULL; - unsigned char *raw = NULL; - size_t rawlen = 0; - - /* Retrieve the public key in raw format from PKCS#11 */ - if (pkcs11_get_raw_public_key(key, publen, algname, &raw, &rawlen) < 0) - return NULL; - - /* Build an EVP_PKEY from the raw public key using pkcs11prov keymgmt. - * The resulting key is used for provider-side public key operations. */ - pkey = EVP_PKEY_new_raw_public_key_ex(NULL, algname, "provider=pkcs11prov", raw, rawlen); - OPENSSL_free(raw); - return pkey; -} - -static EVP_PKEY *pkcs11_get_evp_key_falcon512(PKCS11_OBJECT_private *key) -{ - return pkcs11_get_evp_key_falcon(key, FALCON_512_PUB_LEN, "FALCON-512"); -} - -static EVP_PKEY *pkcs11_get_evp_key_falcon1024(PKCS11_OBJECT_private *key) -{ - return pkcs11_get_evp_key_falcon(key, FALCON_1024_PUB_LEN, "FALCON-1024"); -} - - -PKCS11_OBJECT_ops pkcs11_falcon512_ops = { - EVP_PKEY_FALCON512, - pkcs11_get_evp_key_falcon512, -}; - -PKCS11_OBJECT_ops pkcs11_falcon1024_ops = { - EVP_PKEY_FALCON1024, - pkcs11_get_evp_key_falcon1024, -}; - -#else /* OPENSSL_VERSION_NUMBER >= 0x30000000L */ -/* - * PQC FALCON support is not available: - * - OpenSSL version is older than 3.0. - */ -#warning "PQC FALCON support not built with libp11" - -#endif /* OPENSSL_VERSION_NUMBER >= 0x30000000L */ - -/* vim: set noexpandtab: */ diff --git a/src/p11_key.c b/src/p11_key.c index b72a4b40..79cc6972 100644 --- a/src/p11_key.c +++ b/src/p11_key.c @@ -30,27 +30,6 @@ static int evp_pkey_ex_index = 0; #endif /* OPENSSL_VERSION_NUMBER >= 0x30000000L */ -#if OPENSSL_VERSION_NUMBER < 0x40000000L -# if OPENSSL_VERSION_NUMBER >= 0x30000000L -static int pkey_ex_index = 0; -# endif /* OPENSSL_VERSION_NUMBER >= 0x30000000L*/ - -#if OPENSSL_VERSION_NUMBER < 0x10101000L || defined(LIBRESSL_VERSION_NUMBER) -static EVP_PKEY_METHOD *orig_method_rsa = NULL; -#else -static const EVP_PKEY_METHOD *orig_method_rsa = NULL; -#endif /* OPENSSL_VERSION_NUMBER < 0x10101000L || defined(LIBRESSL_VERSION_NUMBER) */ - -static int (*orig_pkey_rsa_sign_init) (EVP_PKEY_CTX *ctx); -static int (*orig_pkey_rsa_sign) (EVP_PKEY_CTX *ctx, - unsigned char *sig, size_t *siglen, - const unsigned char *tbs, size_t tbslen); -static int (*orig_pkey_rsa_decrypt_init) (EVP_PKEY_CTX *ctx); -static int (*orig_pkey_rsa_decrypt) (EVP_PKEY_CTX *ctx, - unsigned char *out, size_t *outlen, - const unsigned char *in, size_t inlen); -#endif /* OPENSSL_VERSION_NUMBER < 0x40000000L */ - #if !defined(OPENSSL_NO_ECX) && OPENSSL_VERSION_NUMBER >= 0x30000000L /* DER OIDs */ static const unsigned char OID_ED25519[] = { 0x06, 0x03, 0x2B, 0x65, 0x70 }; @@ -1379,6 +1358,26 @@ PKCS11_OBJECT_private *pkcs11_get_ex_data_object(const EVP_PKEY *pk) { return pkcs11_get_ex_data_evp_pkey(pk); } + +/* + * Return the borrowed private PKCS#11 object for legacy EVP_PKEY_METHOD + * operations, or NULL if unavailable or disabled. + */ +PKCS11_OBJECT_private *pkcs11_get_legacy_pkey_object(const EVP_PKEY *pkey) +{ + PKCS11_OBJECT_private *key; + + key = pkcs11_get_ex_data_object(pkey); + if (key == NULL) + return NULL; + + if (key->object_class != CKO_PRIVATE_KEY || + key->slot == NULL || key->slot->ctx == NULL || + (key->slot->ctx->flags & PKCS11_FLAG_NO_METHODS) != 0) + return NULL; + + return key; +} #endif /* OPENSSL_VERSION_NUMBER >= 0x30000000L */ /* @@ -1773,12 +1772,10 @@ static PKCS11_OBJECT_ops *pkcs11_slhdsa_ops_from_param(CK_ULONG param_set) static EVP_PKEY *pkcs11_dup_raw_public_key(EVP_PKEY *pkey) { const char *name; + const char *properties = NULL; unsigned char *pub = NULL; size_t publen = 0; EVP_PKEY *ret = NULL; -#if OPENSSL_VERSION_NUMBER < 0x40000000L - PKCS11_OBJECT_private *obj; -#endif /* OPENSSL_VERSION_NUMBER < 0x40000000L */ if (pkey == NULL) return NULL; @@ -1787,6 +1784,9 @@ static EVP_PKEY *pkcs11_dup_raw_public_key(EVP_PKEY *pkey) if (name == NULL) return NULL; + if (!strcmp(name, "FALCON-512") || !strcmp(name, "FALCON-1024")) + properties = "provider=pkcs11prov"; + if (!EVP_PKEY_get_raw_public_key(pkey, NULL, &publen) || publen == 0) return NULL; @@ -1798,16 +1798,10 @@ static EVP_PKEY *pkcs11_dup_raw_public_key(EVP_PKEY *pkey) if (!EVP_PKEY_get_raw_public_key(pkey, pub, &publen)) goto end; - ret = EVP_PKEY_new_raw_public_key_ex(NULL, name, NULL, pub, publen); + ret = EVP_PKEY_new_raw_public_key_ex(NULL, name, properties, pub, publen); if (ret == NULL) goto end; -#if OPENSSL_VERSION_NUMBER < 0x40000000L - /* Preserve the ex_data association used by legacy EVP_PKEY_METHOD wrappers. */ - obj = pkcs11_get_ex_data_pkey(pkey); - if (obj != NULL) - pkcs11_set_ex_data_pkey(ret, obj); -#endif /* OPENSSL_VERSION_NUMBER < 0x40000000L */ end: OPENSSL_free(pub); return ret; @@ -1954,287 +1948,4 @@ void free_evp_pkey_ex_index(void) } #endif /* OPENSSL_VERSION_NUMBER >= 0x30000000L */ - -#if OPENSSL_VERSION_NUMBER < 0x40000000L -# if OPENSSL_VERSION_NUMBER >= 0x30000000L -void pkcs11_set_ex_data_pkey(EVP_PKEY *pkey, PKCS11_OBJECT_private *key) -{ - EVP_PKEY_set_ex_data(pkey, pkey_ex_index, key); -} - -PKCS11_OBJECT_private *pkcs11_get_ex_data_pkey(const EVP_PKEY *pkey) -{ - return EVP_PKEY_get_ex_data(pkey, pkey_ex_index); -} - -void alloc_pkey_ex_index(void) -{ - if (pkey_ex_index == 0) { - while (pkey_ex_index == 0) /* Workaround for OpenSSL RT3710 */ - pkey_ex_index = EVP_PKEY_get_ex_new_index(0, "libp11 PKCS11_KEY", - NULL, NULL, NULL); - if (pkey_ex_index < 0) - pkey_ex_index = 0; /* Fallback to app_data */ - } -} - -void free_pkey_ex_index(void) -{ - if (pkey_ex_index > 0) { - CRYPTO_free_ex_index(CRYPTO_EX_INDEX_EVP_PKEY, pkey_ex_index); - pkey_ex_index = 0; - } -} -# endif /* OPENSSL_VERSION_NUMBER >= 0x30000000L */ - -/* Attempt to sign using the PKCS#11-backed RSA implementation */ -static int pkcs11_try_pkey_rsa_sign(EVP_PKEY_CTX *evp_pkey_ctx, - unsigned char *sig, size_t *siglen, - const unsigned char *tbs, size_t tbslen) -{ - EVP_PKEY *pkey; - RSA *rsa; - int padding; - PKCS11_OBJECT_private *key; - PKCS11_SLOT_private *slot; - const EVP_MD *md, *mgf1_md; - CK_SESSION_HANDLE session; - const char *mdname, *mgf1_mdname; - int salt_len; - - /* RSA method has EVP_PKEY_FLAG_AUTOARGLEN set. OpenSSL core will handle - * the size inquiry internally. */ - if (!sig) - return -1; - - if (!evp_pkey_ctx) - return -1; - - pkey = EVP_PKEY_CTX_get0_pkey(evp_pkey_ctx); - if (!pkey) - return -1; - - rsa = (RSA *)EVP_PKEY_get0_RSA(pkey); - if (!rsa) - return -1; - - key = pkcs11_get_ex_data_rsa(rsa); - if (check_object_fork(key) < 0) - return -1; - - slot = key->slot; - if (!slot) - return -1; - - if (pkcs11_session_pool_acquire(slot, 0, &session)) - return -1; - - pkcs11_session_pool_release(slot, session); - - /* retrieve PSS parameters */ - if (EVP_PKEY_CTX_get_rsa_padding(evp_pkey_ctx, &padding) <= 0) - return -1; - - if (padding != RSA_PKCS1_PSS_PADDING) - return -1; /* unsupported */ - - if (EVP_PKEY_CTX_get_signature_md(evp_pkey_ctx, &md) <= 0) - return -1; - - if (tbslen != (size_t)EVP_MD_size(md)) - return -1; - - if (EVP_PKEY_CTX_get_rsa_mgf1_md(evp_pkey_ctx, &mgf1_md) <= 0) - return -1; - - if (EVP_PKEY_CTX_get_rsa_pss_saltlen(evp_pkey_ctx, &salt_len) == 0) - return -1; - - mdname = EVP_MD_name(md); - mgf1_mdname = EVP_MD_name(mgf1_md); - - return pkcs11_evp_pkey_rsa_sign(key, pkey, mdname, padding, - salt_len, mgf1_mdname, sig, siglen, tbs, tbslen); -} - -/* Attempt to decrypt using the PKCS#11-backed RSA implementation */ -static int pkcs11_try_pkey_rsa_decrypt(EVP_PKEY_CTX *evp_pkey_ctx, - unsigned char *out, size_t *outlen, - const unsigned char *in, size_t inlen) -{ - EVP_PKEY *pkey; - RSA *rsa; - int padding; - PKCS11_OBJECT_private *key; - PKCS11_SLOT_private *slot; - CK_SESSION_HANDLE session; - const EVP_MD *md, *mgf1_md; - const char *mdname = NULL, *mgf1_mdname = NULL; - unsigned char *oaep_label = NULL; - int oaep_labellen = 0; - - /* RSA method has EVP_PKEY_FLAG_AUTOARGLEN set. OpenSSL core will handle - * the size inquiry internally. */ - if (!out) - return -1; - - if (!evp_pkey_ctx) - return -1; - - pkey = EVP_PKEY_CTX_get0_pkey(evp_pkey_ctx); - if (!pkey) - return -1; - - rsa = (RSA *)EVP_PKEY_get0_RSA(pkey); - if (!rsa) - return -1; - - key = pkcs11_get_ex_data_rsa(rsa); - if (check_object_fork(key) < 0) - return -1; - - /* check RSA padding */ - if (EVP_PKEY_CTX_get_rsa_padding(evp_pkey_ctx, &padding) <= 0) - return -1; - - slot = key->slot; - if (!slot) - return -1; - - if (pkcs11_session_pool_acquire(slot, 0, &session)) - return -1; - - pkcs11_session_pool_release(slot, session); - - switch (padding) { - case RSA_PKCS1_PADDING: - break; - - case RSA_PKCS1_OAEP_PADDING: - /* retrieve OAEP parameters */ - if (EVP_PKEY_CTX_get_rsa_oaep_md(evp_pkey_ctx, &md) <= 0 || - md == NULL) - return -1; - - if (EVP_PKEY_CTX_get_rsa_mgf1_md(evp_pkey_ctx, &mgf1_md) <= 0 || - mgf1_md == NULL) - return -1; - - mdname = EVP_MD_name(md); - mgf1_mdname = EVP_MD_name(mgf1_md); - - oaep_labellen = EVP_PKEY_CTX_get0_rsa_oaep_label(evp_pkey_ctx, - &oaep_label); - if (oaep_labellen < 0) { - oaep_labellen = 0; - oaep_label = NULL; - } - break; - - default: - return -1; - } - - return pkcs11_evp_pkey_rsa_decrypt(key, mdname, padding, mgf1_mdname, - oaep_label, oaep_labellen, out, outlen, in, inlen); -} - -static int pkcs11_pkey_rsa_sign(EVP_PKEY_CTX *evp_pkey_ctx, - unsigned char *sig, size_t *siglen, - const unsigned char *tbs, size_t tbslen) -{ - int ret; - - ret = pkcs11_try_pkey_rsa_sign(evp_pkey_ctx, sig, siglen, tbs, tbslen); - if (ret < 0) - ret = (*orig_pkey_rsa_sign)(evp_pkey_ctx, sig, siglen, tbs, tbslen); - return ret; -} - -static int pkcs11_pkey_rsa_decrypt(EVP_PKEY_CTX *evp_pkey_ctx, - unsigned char *out, size_t *outlen, - const unsigned char *in, size_t inlen) -{ - int ret; - - ret = pkcs11_try_pkey_rsa_decrypt(evp_pkey_ctx, out, outlen, in, inlen); - if (ret < 0) - ret = (*orig_pkey_rsa_decrypt)(evp_pkey_ctx, out, outlen, in, inlen); - return ret; -} - -#if OPENSSL_VERSION_NUMBER < 0x100020d0L || defined(LIBRESSL_VERSION_NUMBER) -void EVP_PKEY_meth_get_sign(EVP_PKEY_METHOD *pmeth, - int (**psign_init) (EVP_PKEY_CTX *ctx), - int (**psign) (EVP_PKEY_CTX *ctx, - unsigned char *sig, size_t *siglen, - const unsigned char *tbs, size_t tbslen)) -{ - if (psign_init) - *psign_init = pmeth->sign_init; - if (psign) - *psign = pmeth->sign; -} - -static void EVP_PKEY_meth_get_decrypt(EVP_PKEY_METHOD *pmeth, - int (**pdecrypt_init) (EVP_PKEY_CTX *ctx), - int (**pdecrypt) (EVP_PKEY_CTX *ctx, - unsigned char *out, - size_t *outlen, - const unsigned char *in, - size_t inlen)) -{ - if (pdecrypt_init) - *pdecrypt_init = pmeth->decrypt_init; - if (pdecrypt) - *pdecrypt = pmeth->decrypt; -} -#endif - -EVP_PKEY_METHOD *pkcs11_pkey_method_rsa(void) -{ - EVP_PKEY_METHOD *new_meth_rsa = NULL; - int orig_id; - - /* Cache the original EVP_PKEY_RSA method (once) */ - if (!orig_method_rsa) -#if OPENSSL_VERSION_NUMBER < 0x10101000L || defined(LIBRESSL_VERSION_NUMBER) - orig_method_rsa = (EVP_PKEY_METHOD *)EVP_PKEY_meth_find(EVP_PKEY_RSA); -#else - orig_method_rsa = EVP_PKEY_meth_find(EVP_PKEY_RSA); -#endif /* OPENSSL_VERSION_NUMBER < 0x10101000L || defined(LIBRESSL_VERSION_NUMBER) */ - - if (!orig_method_rsa) - return NULL; - - EVP_PKEY_meth_get0_info(&orig_id, NULL, orig_method_rsa); - if (orig_id != EVP_PKEY_RSA) - return NULL; - - EVP_PKEY_meth_get_sign(orig_method_rsa, - &orig_pkey_rsa_sign_init, &orig_pkey_rsa_sign); - if (!orig_pkey_rsa_sign) - return NULL; - - EVP_PKEY_meth_get_decrypt(orig_method_rsa, - &orig_pkey_rsa_decrypt_init, &orig_pkey_rsa_decrypt); - if (!orig_pkey_rsa_decrypt) - return NULL; - - new_meth_rsa = EVP_PKEY_meth_new(EVP_PKEY_RSA, EVP_PKEY_FLAG_AUTOARGLEN); - if (!new_meth_rsa) - return NULL; - - /* Duplicate the original method */ - EVP_PKEY_meth_copy(new_meth_rsa, orig_method_rsa); - - EVP_PKEY_meth_set_sign(new_meth_rsa, - orig_pkey_rsa_sign_init, pkcs11_pkey_rsa_sign); - EVP_PKEY_meth_set_decrypt(new_meth_rsa, - orig_pkey_rsa_decrypt_init, pkcs11_pkey_rsa_decrypt); - - return new_meth_rsa; -} -#endif /* OPENSSL_VERSION_NUMBER < 0x40000000L */ - /* vim: set noexpandtab: */ diff --git a/src/p11_load.c b/src/p11_load.c index bd757827..78800b00 100644 --- a/src/p11_load.c +++ b/src/p11_load.c @@ -38,17 +38,12 @@ static void libp11_global_free(void) #endif /* OPENSSL_VERSION_NUMBER >= 0x30000000L */ #ifndef OPENSSL_NO_RSA +#if OPENSSL_VERSION_NUMBER < 0x40000000L + pkcs11_rsa_pkey_method_free(); +#endif /* OPENSSL_VERSION_NUMBER < 0x40000000L */ pkcs11_rsa_method_free(); -# if OPENSSL_VERSION_NUMBER >= 0x30000000L && OPENSSL_VERSION_NUMBER < 0x40000000L - pkcs11_rsa_key_method_free(); -# endif /* OPENSSL_VERSION_NUMBER >= 0x30000000L && OPENSSL_VERSION_NUMBER < 0x40000000L */ #endif /* OPENSSL_NO_RSA */ -#if !defined(OPENSSL_NO_ECX) && OPENSSL_VERSION_NUMBER >= 0x30000000L && OPENSSL_VERSION_NUMBER < 0x40000000L - pkcs11_ed_key_method_free(); - pkcs11_xdh_key_method_free(); -#endif /* !defined(OPENSSL_NO_ECX) && OPENSSL_VERSION_NUMBER >= 0x30000000L && OPENSSL_VERSION_NUMBER < 0x40000000L */ - #if OPENSSL_VERSION_NUMBER >= 0x10100002L #ifndef OPENSSL_NO_EC pkcs11_ec_key_method_free(); @@ -65,6 +60,10 @@ static void libp11_global_free(void) #endif /* OPENSSL_NO_ECDH */ #endif /* OPENSSL_VERSION_NUMBER >= 0x10100002L */ +#ifdef LIBP11_HAVE_ECX_METHODS + pkcs11_ecx_methods_free(); +#endif /* LIBP11_HAVE_ECX_METHODS */ + ERR_unload_CKR_strings(); ERR_unload_P11_strings(); } diff --git a/src/p11_mldsa.c b/src/p11_mldsa.c deleted file mode 100644 index 9bc87d1c..00000000 --- a/src/p11_mldsa.c +++ /dev/null @@ -1,364 +0,0 @@ -/* - * Copyright © 2026 Mobi - Com Polska Sp. z o.o. - * Author: Małgorzata Olszówka - * All rights reserved. - * - * This file implements the handling of ML-DSA and ML-KEM keys - * stored on a PKCS#11 token. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - */ - -#include "libp11-int.h" -#include - -#if OPENSSL_VERSION_NUMBER >= 0x30500000L - -#ifndef OPENSSL_NO_ML_DSA -#define ML_DSA_44_PUB_LEN 1312 -#define ML_DSA_65_PUB_LEN 1952 -#define ML_DSA_87_PUB_LEN 2592 -#endif /* OPENSSL_NO_ML_DSA */ - -#ifndef OPENSSL_NO_ML_KEM -#define ML_KEM_512_PUB_LEN 800 -#define ML_KEM_768_PUB_LEN 1184 -#define ML_KEM_1024_PUB_LEN 1568 -#endif /* OPENSSL_NO_ML_KEM */ - -#if !defined(OPENSSL_NO_ML_DSA) || !defined(OPENSSL_NO_ML_KEM) -/* - * Extract raw ML-DSA or ML-KEM public key bytes from a - * CKO_PUBLIC_KEY object using CKA_VALUE. - * Returns 1 on success, 0 on failure. - */ -static int extract_pub_from_public_key_obj(PKCS11_CTX_private *ctx, - CK_SESSION_HANDLE session, CK_OBJECT_HANDLE obj, const char *algname, - size_t expected_len, unsigned char **raw, size_t *rawlen) -{ - unsigned char *value = NULL; - size_t value_len = 0; - - if (ctx == NULL || raw == NULL || rawlen == NULL || - obj == CK_INVALID_HANDLE || expected_len == 0) - return 0; - - *raw = NULL; - *rawlen = 0; - - if (pkcs11_getattr_alloc(ctx, session, obj, CKA_VALUE, &value, &value_len)) { - pkcs11_log(ctx, LOG_DEBUG, - "Missing CKA_VALUE attribute on %s public key\n", algname); - return 0; - } - - if (value_len != expected_len) { - pkcs11_log(ctx, LOG_DEBUG, - "Unexpected %s public key size: got %lu, expected %lu\n", - algname, (unsigned long)value_len, - (unsigned long)expected_len); - OPENSSL_free(value); - return 0; - } - - *raw = value; - *rawlen = value_len; - return 1; -} - -/* - * Extract raw ML-DSA or ML-KEM public key bytes from a - * CKO_CERTIFICATE object. - * The certificate is read from CKA_VALUE (DER-encoded X.509) and - * the public key is obtained via X.509 parsing. - * Returns 1 on success, 0 on failure. - */ -static int extract_pub_from_cert_obj(PKCS11_CTX_private *ctx, - CK_SESSION_HANDLE session, CK_OBJECT_HANDLE obj, - size_t expected_len, const char *algname, - unsigned char **raw, size_t *rawlen) -{ - const unsigned char *p; - unsigned char *der = NULL; - unsigned char *buf = NULL; - size_t derlen = 0; - size_t len = 0; - X509 *cert = NULL; - EVP_PKEY *pkey = NULL; - int ok = 0; - - if (ctx == NULL || raw == NULL || rawlen == NULL || - obj == CK_INVALID_HANDLE || expected_len == 0 || algname == NULL) - return 0; - - *raw = NULL; - *rawlen = 0; - - if (pkcs11_getattr_alloc(ctx, session, obj, CKA_VALUE, &der, &derlen)) - return 0; - - if (derlen == 0 || derlen > LONG_MAX) - goto end; - - p = der; - cert = d2i_X509(NULL, &p, (long)derlen); - if (cert == NULL || p != der + derlen) - goto end; - - pkey = X509_get_pubkey(cert); - if (pkey == NULL) - goto end; - - if (!EVP_PKEY_is_a(pkey, algname)) - goto end; - - if (EVP_PKEY_get_raw_public_key(pkey, NULL, &len) != 1) - goto end; - - if (len != expected_len) - goto end; - - buf = OPENSSL_malloc(len); - if (buf == NULL) - goto end; - - if (EVP_PKEY_get_raw_public_key(pkey, buf, &len) != 1) - goto end; - - if (len != expected_len) - goto end; - - *raw = buf; - *rawlen = len; - buf = NULL; - ok = 1; - -end: - OPENSSL_free(buf); - OPENSSL_free(der); - EVP_PKEY_free(pkey); - X509_free(cert); - return ok; -} - -/* - * Select an object that can provide public key material. - * - * For a private key object, try to locate a matching CKO_PUBLIC_KEY - * (same CKA_ID). If not found, fall back to CKO_CERTIFICATE. - * - * On success, returns a PKCS11_OBJECT_private pointer. - * If a new object is returned, *needs_free is set to 1 and the caller - * must free it with pkcs11_object_free(). - * - * Returns NULL on failure. - */ -static PKCS11_OBJECT_private *pkcs11_choose_public_source(PKCS11_OBJECT_private *key, - CK_SESSION_HANDLE session, int *needs_free) -{ - PKCS11_OBJECT_private *obj; - - *needs_free = 0; - - if (key->object_class != CKO_PRIVATE_KEY) - return key; - - obj = pkcs11_object_from_object(key, session, CKO_PUBLIC_KEY); - if (obj != NULL && obj->object != CK_INVALID_HANDLE) { - *needs_free = 1; - return obj; - } - if (obj != NULL) - pkcs11_object_free(obj); - - obj = pkcs11_object_from_object(key, session, CKO_CERTIFICATE); - if (obj != NULL && obj->object != CK_INVALID_HANDLE) { - *needs_free = 1; - return obj; - } - if (obj != NULL) - pkcs11_object_free(obj); - - return NULL; -} - -/* - * Retrieve raw ML-DSA or ML-KEM public key bytes. - * - * Preference order: - * 1. CKO_PUBLIC_KEY -> CKA_VALUE - * 2. CKO_CERTIFICATE -> CKA_VALUE (DER) + X.509 parsing - * - * The returned buffer is allocated with OPENSSL_malloc() - * and must be freed by the caller. - * - * Returns 0 on success, -1 on failure. - */ -static int pkcs11_get_raw_public_key(PKCS11_OBJECT_private *key, - size_t expected_len, const char *algname, - unsigned char **raw, size_t *rawlen) -{ - PKCS11_SLOT_private *slot; - PKCS11_CTX_private *ctx; - CK_SESSION_HANDLE session = CK_INVALID_HANDLE; - PKCS11_OBJECT_private *obj = NULL; - int obj_needs_free = 0, ok = 0; - - if (key == NULL || key->slot == NULL || raw == NULL || rawlen == NULL) - return -1; - - *raw = NULL; - *rawlen = 0; - slot = key->slot; - ctx = slot->ctx; - - if (pkcs11_session_pool_acquire(slot, 0, &session)) - return -1; - - obj = pkcs11_choose_public_source(key, session, &obj_needs_free); - if (obj == NULL || obj->object == CK_INVALID_HANDLE) - goto end; - - switch (obj->object_class) { - case CKO_PUBLIC_KEY: - ok = extract_pub_from_public_key_obj(ctx, session, obj->object, - algname, expected_len, raw, rawlen); - break; - case CKO_CERTIFICATE: - ok = extract_pub_from_cert_obj(ctx, session, obj->object, - expected_len, algname, raw, rawlen); - break; - default: - ok = 0; - break; - } - -end: - pkcs11_session_pool_release(slot, session); - - if (!ok) { - OPENSSL_free(*raw); - *raw = NULL; - *rawlen = 0; - } - if (obj_needs_free && obj != NULL) - pkcs11_object_free(obj); - - return ok ? 0 : -1; -} - -static EVP_PKEY *pkcs11_get_evp_key_ml(PKCS11_OBJECT_private *key, - size_t publen, const char *algname) -{ - EVP_PKEY *pkey = NULL; - unsigned char *raw = NULL; - size_t rawlen = 0; - - /* Retrieve the public key in raw format from PKCS#11 */ - if (pkcs11_get_raw_public_key(key, publen, algname, &raw, &rawlen) < 0) - return NULL; - - /* Build an EVP_PKEY used only as a software public key */ - pkey = EVP_PKEY_new_raw_public_key_ex(NULL, algname, NULL, raw, rawlen); - OPENSSL_free(raw); - - return pkey; -} - -#endif /* !defined(OPENSSL_NO_ML_DSA) || !defined(OPENSSL_NO_ML_KEM) */ - -#ifndef OPENSSL_NO_ML_DSA -static EVP_PKEY *pkcs11_get_evp_key_mldsa44(PKCS11_OBJECT_private *key) -{ - return pkcs11_get_evp_key_ml(key, ML_DSA_44_PUB_LEN, "ML-DSA-44"); -} - -static EVP_PKEY *pkcs11_get_evp_key_mldsa65(PKCS11_OBJECT_private *key) -{ - return pkcs11_get_evp_key_ml(key, ML_DSA_65_PUB_LEN, "ML-DSA-65"); -} - -static EVP_PKEY *pkcs11_get_evp_key_mldsa87(PKCS11_OBJECT_private *key) -{ - return pkcs11_get_evp_key_ml(key, ML_DSA_87_PUB_LEN, "ML-DSA-87"); -} - -PKCS11_OBJECT_ops pkcs11_mldsa44_ops = { - EVP_PKEY_ML_DSA_44, - pkcs11_get_evp_key_mldsa44, -}; - -PKCS11_OBJECT_ops pkcs11_mldsa65_ops = { - EVP_PKEY_ML_DSA_65, - pkcs11_get_evp_key_mldsa65, -}; - -PKCS11_OBJECT_ops pkcs11_mldsa87_ops = { - EVP_PKEY_ML_DSA_87, - pkcs11_get_evp_key_mldsa87, -}; -#else /* OPENSSL_NO_ML_DSA */ -#warning "ML-DSA support not built with libp11" -#endif /* OPENSSL_NO_ML_DSA */ - -#ifndef OPENSSL_NO_ML_KEM -static EVP_PKEY *pkcs11_get_evp_key_mlkem512(PKCS11_OBJECT_private *key) -{ - return pkcs11_get_evp_key_ml(key, ML_KEM_512_PUB_LEN, "ML-KEM-512"); -} - -static EVP_PKEY *pkcs11_get_evp_key_mlkem768(PKCS11_OBJECT_private *key) -{ - return pkcs11_get_evp_key_ml(key, ML_KEM_768_PUB_LEN, "ML-KEM-768"); -} - -static EVP_PKEY *pkcs11_get_evp_key_mlkem1024(PKCS11_OBJECT_private *key) -{ - return pkcs11_get_evp_key_ml(key, ML_KEM_1024_PUB_LEN, "ML-KEM-1024"); -} - -PKCS11_OBJECT_ops pkcs11_mlkem512_ops = { - EVP_PKEY_ML_KEM_512, - pkcs11_get_evp_key_mlkem512, -}; - -PKCS11_OBJECT_ops pkcs11_mlkem768_ops = { - EVP_PKEY_ML_KEM_768, - pkcs11_get_evp_key_mlkem768, -}; - -PKCS11_OBJECT_ops pkcs11_mlkem1024_ops = { - EVP_PKEY_ML_KEM_1024, - pkcs11_get_evp_key_mlkem1024, -}; -#else /* OPENSSL_NO_ML_KEM */ -#warning "ML-KEM support not built with libp11" -#endif /* OPENSSL_NO_ML_KEM */ - -#else /* OPENSSL_VERSION_NUMBER >= 0x30500000L */ - -#warning "ML-DSA support not built with libp11" -#warning "ML-KEM support not built with libp11" - -#endif /* OPENSSL_VERSION_NUMBER >= 0x30500000L */ - -/* vim: set noexpandtab: */ diff --git a/src/p11_pkey.c b/src/p11_pkey.c index b0a72b12..173e016c 100644 --- a/src/p11_pkey.c +++ b/src/p11_pkey.c @@ -35,6 +35,23 @@ #endif #if OPENSSL_VERSION_NUMBER < 0x40000000L +#if OPENSSL_VERSION_NUMBER < 0x10101000L || defined(LIBRESSL_VERSION_NUMBER) +static EVP_PKEY_METHOD *orig_method_rsa = NULL; +#else +static const EVP_PKEY_METHOD *orig_method_rsa = NULL; +#endif /* OPENSSL_VERSION_NUMBER < 0x10101000L || defined(LIBRESSL_VERSION_NUMBER) */ + +static EVP_PKEY_METHOD *rsa_pkey_method = NULL; + +static int (*orig_pkey_rsa_sign_init) (EVP_PKEY_CTX *ctx); +static int (*orig_pkey_rsa_sign) (EVP_PKEY_CTX *ctx, + unsigned char *sig, size_t *siglen, + const unsigned char *tbs, size_t tbslen); +static int (*orig_pkey_rsa_decrypt_init) (EVP_PKEY_CTX *ctx); +static int (*orig_pkey_rsa_decrypt) (EVP_PKEY_CTX *ctx, + unsigned char *out, size_t *outlen, + const unsigned char *in, size_t inlen); + # ifndef OPENSSL_NO_EC static int (*orig_pkey_ec_sign_init) (EVP_PKEY_CTX *ctx); static int (*orig_pkey_ec_sign) (EVP_PKEY_CTX *ctx, @@ -495,6 +512,131 @@ static int pkcs11_verify_with_mechanism(PKCS11_OBJECT_private *key, pkcs11_session_pool_release(slot, session); return rv; } + +/* + * Execute a PKCS#11 decapsulation operation using the specified mechanism. + * + * The resulting secret key is created as an extractable session object, + * read through CKA_VALUE and destroyed before the session is released. + * + * Returns CKR_OK on success or a PKCS#11/vendor-defined error code on failure. + */ +static CK_RV pkcs11_decapsulate_with_mechanism( + PKCS11_OBJECT_private *key, CK_MECHANISM *mechanism, + unsigned char *out, size_t *outlen, + const unsigned char *in, size_t inlen) +{ + CK_RV rv = CKR_GENERAL_ERROR; + PKCS11_SLOT_private *slot; + PKCS11_CTX_private *ctx; + CK_SESSION_HANDLE session; + CK_OBJECT_HANDLE newkey = CK_INVALID_HANDLE; + CK_OBJECT_CLASS newkey_class = CKO_SECRET_KEY; + CK_KEY_TYPE newkey_type = CKK_GENERIC_SECRET; + CK_BBOOL ck_false = CK_FALSE; + CK_BBOOL ck_true = CK_TRUE; + CK_ULONG newkey_len = 0; + CK_ULONG ck_inlen; + unsigned char *value = NULL; + size_t len, value_len_alloc = 0; + CK_ATTRIBUTE newkey_template[] = { + {CKA_TOKEN, &ck_false, sizeof(ck_false)}, /* session only object */ + {CKA_CLASS, &newkey_class, sizeof(newkey_class)}, + {CKA_KEY_TYPE, &newkey_type, sizeof(newkey_type)}, + {CKA_VALUE_LEN, &newkey_len, sizeof(newkey_len)}, + {CKA_SENSITIVE, &ck_false, sizeof(ck_false)}, + {CKA_EXTRACTABLE, &ck_true, sizeof(ck_true)}, + }; + + if (key == NULL || mechanism == NULL || out == NULL || + outlen == NULL || *outlen == 0 || in == NULL) + return CKR_ARGUMENTS_BAD; + + if (*outlen > (size_t)(CK_ULONG)-1 || + inlen > (size_t)(CK_ULONG)-1) + return CKR_ARGUMENTS_BAD; + + slot = key->slot; + if (slot == NULL) + return CKR_GENERAL_ERROR; + + ctx = slot->ctx; + if (ctx == NULL) + return CKR_GENERAL_ERROR; + +#ifdef DEBUG + pkcs11_log(ctx, LOG_DEBUG, + "%s:%d pkcs11_decapsulate_with_mechanism() " + "%s out=%p *outlen=%lu in=%p inlen=%lu\n", + __FILE__, __LINE__, + pkcs11_mechanism_name(mechanism), + out, (unsigned long)*outlen, + in, (unsigned long)inlen); +#endif + + len = *outlen; + newkey_len = (CK_ULONG)len; + ck_inlen = (CK_ULONG)inlen; + + if (pkcs11_session_pool_acquire(slot, 0, &session)) + return CKR_GENERAL_ERROR; + + if (key->always_authenticate == CK_TRUE) { + rv = pkcs11_authenticate(key, session); + if (rv != CKR_OK) + goto end; + } + + if (ctx->method_3_2 == NULL || + ctx->method_3_2->C_DecapsulateKey == NULL) { + pkcs11_log(ctx, LOG_DEBUG, + "PKCS#11 3.2 C_DecapsulateKey is not available\n"); + rv = CKR_FUNCTION_NOT_SUPPORTED; + goto end; + } + + rv = CRYPTOKI_call_3_2(ctx, + C_DecapsulateKey(session, mechanism, key->object, + newkey_template, + sizeof(newkey_template) / sizeof(*newkey_template), + (CK_BYTE_PTR)in, ck_inlen, &newkey)); + if (rv != CKR_OK) { + pkcs11_log(ctx, LOG_DEBUG, + "%s:%d C_DecapsulateKey rv=0x%08lX (%lu)\n", + __FILE__, __LINE__, + (unsigned long)rv, (unsigned long)rv); + goto end; + } + + if (newkey == CK_INVALID_HANDLE) { + rv = CKR_GENERAL_ERROR; + goto end; + } + + if (pkcs11_getattr_alloc(ctx, session, newkey, CKA_VALUE, + &value, &value_len_alloc)) { + rv = CKR_GENERAL_ERROR; + goto end; + } + + if (value_len_alloc > len) { + *outlen = value_len_alloc; + rv = CKR_BUFFER_TOO_SMALL; + goto end; + } + + memcpy(out, value, value_len_alloc); + *outlen = value_len_alloc; + rv = CKR_OK; + +end: + if (newkey != CK_INVALID_HANDLE) + CRYPTOKI_call(ctx, C_DestroyObject(session, newkey)); + + OPENSSL_clear_free(value, value_len_alloc); + pkcs11_session_pool_release(slot, session); + return rv; +} #endif /* OPENSSL_VERSION_NUMBER >= 0x30000000L */ /* @@ -663,133 +805,6 @@ static CK_RV pkcs11_derive_with_mechanism(PKCS11_OBJECT_private *key, return rv; } -#if OPENSSL_VERSION_NUMBER >= 0x30000000L -/* - * Execute a PKCS#11 decapsulation operation using the specified mechanism. - * - * The resulting secret key is created as an extractable session object, - * read through CKA_VALUE and destroyed before the session is released. - * - * Returns CKR_OK on success or a PKCS#11/vendor-defined error code on failure. - */ -static CK_RV pkcs11_decapsulate_with_mechanism( - PKCS11_OBJECT_private *key, CK_MECHANISM *mechanism, - unsigned char *out, size_t *outlen, - const unsigned char *in, size_t inlen) -{ - CK_RV rv = CKR_GENERAL_ERROR; - PKCS11_SLOT_private *slot; - PKCS11_CTX_private *ctx; - CK_SESSION_HANDLE session; - CK_OBJECT_HANDLE newkey = CK_INVALID_HANDLE; - CK_OBJECT_CLASS newkey_class = CKO_SECRET_KEY; - CK_KEY_TYPE newkey_type = CKK_GENERIC_SECRET; - CK_BBOOL ck_false = CK_FALSE; - CK_BBOOL ck_true = CK_TRUE; - CK_ULONG newkey_len = 0; - CK_ULONG ck_inlen; - unsigned char *value = NULL; - size_t len, value_len_alloc = 0; - CK_ATTRIBUTE newkey_template[] = { - {CKA_TOKEN, &ck_false, sizeof(ck_false)}, /* session only object */ - {CKA_CLASS, &newkey_class, sizeof(newkey_class)}, - {CKA_KEY_TYPE, &newkey_type, sizeof(newkey_type)}, - {CKA_VALUE_LEN, &newkey_len, sizeof(newkey_len)}, - {CKA_SENSITIVE, &ck_false, sizeof(ck_false)}, - {CKA_EXTRACTABLE, &ck_true, sizeof(ck_true)}, - }; - - if (key == NULL || mechanism == NULL || out == NULL || - outlen == NULL || *outlen == 0 || in == NULL) - return CKR_ARGUMENTS_BAD; - - if (*outlen > (size_t)(CK_ULONG)-1 || - inlen > (size_t)(CK_ULONG)-1) - return CKR_ARGUMENTS_BAD; - - slot = key->slot; - if (slot == NULL) - return CKR_GENERAL_ERROR; - - ctx = slot->ctx; - if (ctx == NULL) - return CKR_GENERAL_ERROR; - -#ifdef DEBUG - pkcs11_log(ctx, LOG_DEBUG, - "%s:%d pkcs11_decapsulate_with_mechanism() " - "%s out=%p *outlen=%lu in=%p inlen=%lu\n", - __FILE__, __LINE__, - pkcs11_mechanism_name(mechanism), - out, (unsigned long)*outlen, - in, (unsigned long)inlen); -#endif - - len = *outlen; - newkey_len = (CK_ULONG)len; - ck_inlen = (CK_ULONG)inlen; - - if (pkcs11_session_pool_acquire(slot, 0, &session)) - return CKR_GENERAL_ERROR; - - if (key->always_authenticate == CK_TRUE) { - rv = pkcs11_authenticate(key, session); - if (rv != CKR_OK) - goto end; - } - - if (ctx->method_3_2 == NULL || - ctx->method_3_2->C_DecapsulateKey == NULL) { - pkcs11_log(ctx, LOG_DEBUG, - "PKCS#11 3.2 C_DecapsulateKey is not available\n"); - rv = CKR_FUNCTION_NOT_SUPPORTED; - goto end; - } - - rv = CRYPTOKI_call_3_2(ctx, - C_DecapsulateKey(session, mechanism, key->object, - newkey_template, - sizeof(newkey_template) / sizeof(*newkey_template), - (CK_BYTE_PTR)in, ck_inlen, &newkey)); - if (rv != CKR_OK) { - pkcs11_log(ctx, LOG_DEBUG, - "%s:%d C_DecapsulateKey rv=0x%08lX (%lu)\n", - __FILE__, __LINE__, - (unsigned long)rv, (unsigned long)rv); - goto end; - } - - if (newkey == CK_INVALID_HANDLE) { - rv = CKR_GENERAL_ERROR; - goto end; - } - - if (pkcs11_getattr_alloc(ctx, session, newkey, CKA_VALUE, - &value, &value_len_alloc)) { - rv = CKR_GENERAL_ERROR; - goto end; - } - - if (value_len_alloc > len) { - *outlen = value_len_alloc; - rv = CKR_BUFFER_TOO_SMALL; - goto end; - } - - memcpy(out, value, value_len_alloc); - *outlen = value_len_alloc; - rv = CKR_OK; - -end: - if (newkey != CK_INVALID_HANDLE) - CRYPTOKI_call(ctx, C_DestroyObject(session, newkey)); - - OPENSSL_clear_free(value, value_len_alloc); - pkcs11_session_pool_release(slot, session); - return rv; -} -#endif /* OPENSSL_VERSION_NUMBER >= 0x30000000L */ - /* DER-encode data as an ASN.1 OCTET STRING. */ static unsigned char *der_encode_octet_string(const unsigned char *data, size_t data_len, size_t *der_len) @@ -1478,6 +1493,9 @@ static int pkcs11_try_pkey_ec_sign(EVP_PKEY_CTX *evp_pkey_ctx, return -1; /* buffer too small */ key = pkcs11_get_ex_data_ec(eckey); + if (!key) + return -1; + if (check_object_fork(key) < 0) return -1; @@ -1537,7 +1555,7 @@ static int pkcs11_eddsa_pmeth_sign(EVP_PKEY_CTX *evp_pkey_ctx, if (!pkey) return -1; - key = pkcs11_get_ex_data_pkey(pkey); + key = pkcs11_get_legacy_pkey_object(pkey); if (!key) return -1; @@ -1572,7 +1590,7 @@ static int pkcs11_eddsa_pmeth_digestsign(EVP_MD_CTX *ctx, unsigned char *sig, if (!pkey) return -1; - key = pkcs11_get_ex_data_pkey(pkey); + key = pkcs11_get_legacy_pkey_object(pkey); if (!key) return -1; @@ -1646,6 +1664,7 @@ static int pkcs11_pkey_ec_sign(EVP_PKEY_CTX *evp_pkey_ctx, ret = pkcs11_try_pkey_ec_sign(evp_pkey_ctx, sig, siglen, tbs, tbslen); if (ret < 0) ret = (*orig_pkey_ec_sign)(evp_pkey_ctx, sig, siglen, tbs, tbslen); + return ret; } @@ -1726,6 +1745,319 @@ static EVP_PKEY_METHOD *pkcs11_pkey_method_ed448(void) } #endif /* !defined(OPENSSL_NO_ECX) && OPENSSL_VERSION_NUMBER >= 0x30000000L */ + +/* Attempt to sign using the PKCS#11-backed RSA implementation */ +static int pkcs11_try_pkey_rsa_sign(EVP_PKEY_CTX *evp_pkey_ctx, + unsigned char *sig, size_t *siglen, + const unsigned char *tbs, size_t tbslen) +{ + EVP_PKEY *pkey; + RSA *rsa; + int padding; + PKCS11_OBJECT_private *key; + PKCS11_SLOT_private *slot; + const EVP_MD *md, *mgf1_md; + CK_SESSION_HANDLE session; + const char *mdname, *mgf1_mdname; + int salt_len; + + /* RSA method has EVP_PKEY_FLAG_AUTOARGLEN set. OpenSSL core will handle + * the size inquiry internally. */ + if (!sig) + return -1; + + if (!evp_pkey_ctx) + return -1; + + pkey = EVP_PKEY_CTX_get0_pkey(evp_pkey_ctx); + if (!pkey) + return -1; + + rsa = (RSA *)EVP_PKEY_get0_RSA(pkey); + if (!rsa) + return -1; + + key = pkcs11_get_ex_data_rsa(rsa); + if (!key) + return -1; + + if (check_object_fork(key) < 0) + return -1; + + slot = key->slot; + if (!slot) + return -1; + + if (pkcs11_session_pool_acquire(slot, 0, &session)) + return -1; + + pkcs11_session_pool_release(slot, session); + + /* retrieve PSS parameters */ + if (EVP_PKEY_CTX_get_rsa_padding(evp_pkey_ctx, &padding) <= 0) + return -1; + + if (padding != RSA_PKCS1_PSS_PADDING) + return -1; /* unsupported */ + + if (EVP_PKEY_CTX_get_signature_md(evp_pkey_ctx, &md) <= 0) + return -1; + + if (tbslen != (size_t)EVP_MD_size(md)) + return -1; + + if (EVP_PKEY_CTX_get_rsa_mgf1_md(evp_pkey_ctx, &mgf1_md) <= 0) + return -1; + + if (EVP_PKEY_CTX_get_rsa_pss_saltlen(evp_pkey_ctx, &salt_len) == 0) + return -1; + + mdname = EVP_MD_name(md); + mgf1_mdname = EVP_MD_name(mgf1_md); + + return pkcs11_evp_pkey_rsa_sign(key, pkey, mdname, padding, + salt_len, mgf1_mdname, sig, siglen, tbs, tbslen); +} + +/* Attempt to decrypt using the PKCS#11-backed RSA implementation */ +static int pkcs11_try_pkey_rsa_decrypt(EVP_PKEY_CTX *evp_pkey_ctx, + unsigned char *out, size_t *outlen, + const unsigned char *in, size_t inlen) +{ + EVP_PKEY *pkey; + RSA *rsa; + int padding; + PKCS11_OBJECT_private *key; + PKCS11_SLOT_private *slot; + CK_SESSION_HANDLE session; + const EVP_MD *md, *mgf1_md; + const char *mdname = NULL, *mgf1_mdname = NULL; + unsigned char *oaep_label = NULL; + int oaep_labellen = 0; + + /* RSA method has EVP_PKEY_FLAG_AUTOARGLEN set. OpenSSL core will handle + * the size inquiry internally. */ + if (!out) + return -1; + + if (!evp_pkey_ctx) + return -1; + + pkey = EVP_PKEY_CTX_get0_pkey(evp_pkey_ctx); + if (!pkey) + return -1; + + rsa = (RSA *)EVP_PKEY_get0_RSA(pkey); + if (!rsa) + return -1; + + key = pkcs11_get_ex_data_rsa(rsa); + if (!key) + return -1; + + if (check_object_fork(key) < 0) + return -1; + + /* check RSA padding */ + if (EVP_PKEY_CTX_get_rsa_padding(evp_pkey_ctx, &padding) <= 0) + return -1; + + slot = key->slot; + if (!slot) + return -1; + + if (pkcs11_session_pool_acquire(slot, 0, &session)) + return -1; + + pkcs11_session_pool_release(slot, session); + + switch (padding) { + case RSA_PKCS1_PADDING: + break; + + case RSA_PKCS1_OAEP_PADDING: + /* retrieve OAEP parameters */ + if (EVP_PKEY_CTX_get_rsa_oaep_md(evp_pkey_ctx, &md) <= 0 || + md == NULL) + return -1; + + if (EVP_PKEY_CTX_get_rsa_mgf1_md(evp_pkey_ctx, &mgf1_md) <= 0 || + mgf1_md == NULL) + return -1; + + mdname = EVP_MD_name(md); + mgf1_mdname = EVP_MD_name(mgf1_md); + + oaep_labellen = EVP_PKEY_CTX_get0_rsa_oaep_label(evp_pkey_ctx, + &oaep_label); + if (oaep_labellen < 0) { + oaep_labellen = 0; + oaep_label = NULL; + } + break; + + default: + return -1; + } + + return pkcs11_evp_pkey_rsa_decrypt(key, mdname, padding, mgf1_mdname, + oaep_label, oaep_labellen, out, outlen, in, inlen); +} + +static int pkcs11_pkey_rsa_sign(EVP_PKEY_CTX *evp_pkey_ctx, + unsigned char *sig, size_t *siglen, + const unsigned char *tbs, size_t tbslen) +{ + int ret; + + ret = pkcs11_try_pkey_rsa_sign(evp_pkey_ctx, sig, siglen, tbs, tbslen); + if (ret < 0) + ret = (*orig_pkey_rsa_sign)(evp_pkey_ctx, sig, siglen, tbs, tbslen); + + return ret; +} + +static int pkcs11_pkey_rsa_decrypt(EVP_PKEY_CTX *evp_pkey_ctx, + unsigned char *out, size_t *outlen, + const unsigned char *in, size_t inlen) +{ + int ret; + + ret = pkcs11_try_pkey_rsa_decrypt(evp_pkey_ctx, out, outlen, in, inlen); + if (ret < 0) + ret = (*orig_pkey_rsa_decrypt)(evp_pkey_ctx, out, outlen, in, inlen); + + + return ret; +} + +#if OPENSSL_VERSION_NUMBER < 0x100020d0L || defined(LIBRESSL_VERSION_NUMBER) +void EVP_PKEY_meth_get_sign(EVP_PKEY_METHOD *pmeth, + int (**psign_init) (EVP_PKEY_CTX *ctx), + int (**psign) (EVP_PKEY_CTX *ctx, + unsigned char *sig, size_t *siglen, + const unsigned char *tbs, size_t tbslen)) +{ + if (psign_init) + *psign_init = pmeth->sign_init; + if (psign) + *psign = pmeth->sign; +} + +static void EVP_PKEY_meth_get_decrypt(EVP_PKEY_METHOD *pmeth, + int (**pdecrypt_init) (EVP_PKEY_CTX *ctx), + int (**pdecrypt) (EVP_PKEY_CTX *ctx, + unsigned char *out, + size_t *outlen, + const unsigned char *in, + size_t inlen)) +{ + if (pdecrypt_init) + *pdecrypt_init = pmeth->decrypt_init; + if (pdecrypt) + *pdecrypt = pmeth->decrypt; +} +#endif /* OPENSSL_VERSION_NUMBER < 0x100020d0L || defined(LIBRESSL_VERSION_NUMBER) */ + +/* Attempt to sign using the PKCS#11-backed RSA implementation */ +static EVP_PKEY_METHOD *pkcs11_pkey_method_rsa(void) +{ + EVP_PKEY_METHOD *new_meth_rsa = NULL; + int orig_id; + + /* Cache the original EVP_PKEY_RSA method (once) */ + if (!orig_method_rsa) +#if OPENSSL_VERSION_NUMBER < 0x10101000L || defined(LIBRESSL_VERSION_NUMBER) + orig_method_rsa = (EVP_PKEY_METHOD *)EVP_PKEY_meth_find(EVP_PKEY_RSA); +#else + orig_method_rsa = EVP_PKEY_meth_find(EVP_PKEY_RSA); +#endif /* OPENSSL_VERSION_NUMBER < 0x10101000L || defined(LIBRESSL_VERSION_NUMBER) */ + + if (!orig_method_rsa) + return NULL; + + EVP_PKEY_meth_get0_info(&orig_id, NULL, orig_method_rsa); + if (orig_id != EVP_PKEY_RSA) + return NULL; + + EVP_PKEY_meth_get_sign(orig_method_rsa, + &orig_pkey_rsa_sign_init, &orig_pkey_rsa_sign); + if (!orig_pkey_rsa_sign) + return NULL; + + EVP_PKEY_meth_get_decrypt(orig_method_rsa, + &orig_pkey_rsa_decrypt_init, &orig_pkey_rsa_decrypt); + if (!orig_pkey_rsa_decrypt) + return NULL; + + new_meth_rsa = EVP_PKEY_meth_new(EVP_PKEY_RSA, EVP_PKEY_FLAG_AUTOARGLEN); + if (!new_meth_rsa) + return NULL; + + /* Duplicate the original method */ + EVP_PKEY_meth_copy(new_meth_rsa, orig_method_rsa); + + EVP_PKEY_meth_set_sign(new_meth_rsa, + orig_pkey_rsa_sign_init, pkcs11_pkey_rsa_sign); + EVP_PKEY_meth_set_decrypt(new_meth_rsa, + orig_pkey_rsa_decrypt_init, pkcs11_pkey_rsa_decrypt); + + return new_meth_rsa; +} + +/* Remove and free the registered RSA EVP_PKEY_METHOD wrapper. */ +void pkcs11_rsa_pkey_method_free(void) +{ +#if OPENSSL_VERSION_NUMBER >= 0x10101000L + if (rsa_pkey_method == NULL) + return; + + if (EVP_PKEY_meth_remove(rsa_pkey_method) != 1) + return; + + EVP_PKEY_meth_free(rsa_pkey_method); + rsa_pkey_method = NULL; +#else + /* + * OpenSSL 1.1.0 provides EVP_PKEY_meth_add0(), but no public API + * for unregistering a method. Keep the registered method alive + * until process termination. + */ +#endif +} + +/* Create and register the RSA EVP_PKEY_METHOD wrapper. */ +static int pkcs11_rsa_pkey_method_new(void) +{ + if (rsa_pkey_method != NULL) + return 1; + + rsa_pkey_method = pkcs11_pkey_method_rsa(); + if (rsa_pkey_method == NULL) + return 0; + + if (!EVP_PKEY_meth_add0(rsa_pkey_method)) { + EVP_PKEY_meth_free(rsa_pkey_method); + rsa_pkey_method = NULL; + return 0; + } + + return 1; +} + +/* Enable the RSA EVP_PKEY_METHOD wrapper for a PKCS#11 private key. */ +int pkcs11_rsa_method_enable(PKCS11_OBJECT_private *key) +{ + if (key == NULL || key->slot == NULL || key->slot->ctx == NULL) + return 0; + + if (key->object_class != CKO_PRIVATE_KEY || + (key->slot->ctx->flags & PKCS11_FLAG_NO_METHODS) != 0) + return 1; + + return pkcs11_rsa_pkey_method_new(); +} + int PKCS11_pkey_meths(ENGINE *e, EVP_PKEY_METHOD **pmeth, const int **nids, int nid) { diff --git a/src/p11_rawkey.c b/src/p11_rawkey.c new file mode 100644 index 00000000..e220f5b2 --- /dev/null +++ b/src/p11_rawkey.c @@ -0,0 +1,501 @@ +/* + * Copyright © 2026 Mobi - Com Polska Sp. z o.o. + * Author: Małgorzata Olszówka + * All rights reserved. + * + * Common handling of algorithms using raw public key representations: + * Ed25519, Ed448, X25519, X448, ML-DSA, ML-KEM, SLH-DSA and Falcon. + * + * Public key material is retrieved from a matching public key object or + * certificate and used to construct an OpenSSL EVP_PKEY. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include "libp11-int.h" + +#include + +#include +#include +#include + +#if OPENSSL_VERSION_NUMBER >= 0x30000000L + +#define FALCON_512_PUB_LEN 897 +#define FALCON_1024_PUB_LEN 1793 + +#ifndef OPENSSL_NO_ECX +#define ED25519_PUB_LEN 32 +#define ED448_PUB_LEN 57 +#define X25519_PUB_LEN 32 +#define X448_PUB_LEN 56 +#endif /* OPENSSL_NO_ECX */ + +#if OPENSSL_VERSION_NUMBER >= 0x30500000L +#ifndef OPENSSL_NO_ML_DSA +#define ML_DSA_44_PUB_LEN 1312 +#define ML_DSA_65_PUB_LEN 1952 +#define ML_DSA_87_PUB_LEN 2592 +#endif /* OPENSSL_NO_ML_DSA */ + +#ifndef OPENSSL_NO_ML_KEM +#define ML_KEM_512_PUB_LEN 800 +#define ML_KEM_768_PUB_LEN 1184 +#define ML_KEM_1024_PUB_LEN 1568 +#endif /* OPENSSL_NO_ML_KEM */ + +#ifndef OPENSSL_NO_SLH_DSA +#define SLH_DSA_128_PUB_LEN 32 +#define SLH_DSA_192_PUB_LEN 48 +#define SLH_DSA_256_PUB_LEN 64 +#endif /* OPENSSL_NO_SLH_DSA */ +#endif /* OPENSSL_VERSION_NUMBER >= 0x30500000L */ + +typedef enum { + P11_RAW_FROM_VALUE, + P11_RAW_FROM_EC_POINT +} P11_RAW_SOURCE; + +typedef struct { + int type; + const char *name; + size_t public_len; + P11_RAW_SOURCE public_source; +} P11_RAW_ALGORITHM; + + +/* + * CKA_EC_POINT is normally a DER OCTET STRING, but some modules return its + * contents directly. Accept both representations. + * Returns 1 on success, 0 on error. + */ +static int strip_der_octet_string_alloc(const unsigned char *in, size_t inlen, + unsigned char **out, size_t *outlen) +{ + const unsigned char *p = in; + unsigned char *buf; + ASN1_OCTET_STRING *os = NULL; + + if (out == NULL || outlen == NULL || in == NULL || inlen == 0) + return 0; + + *out = NULL; + *outlen = 0; + + if (inlen <= LONG_MAX) { + /* Discard ASN.1 errors when the input is valid raw data */ + ERR_set_mark(); + + /* For EdDSA (RFC8032) the CKA_EC_POINT attribute may be + * encoded as a DER OCTET STRING */ + os = d2i_ASN1_OCTET_STRING(NULL, &p, (long)inlen); + if (os != NULL && p == in + inlen) { + const unsigned char *data = ASN1_STRING_get0_data(os); + int len = ASN1_STRING_length(os); + + if (len <= 0 || data == NULL) { + ASN1_OCTET_STRING_free(os); + ERR_pop_to_mark(); + return 0; + } + + buf = OPENSSL_memdup(data, (size_t)len); + ASN1_OCTET_STRING_free(os); + ERR_pop_to_mark(); + if (buf == NULL) + return 0; + + *out = buf; + *outlen = (size_t)len; + return 1; + } + ASN1_OCTET_STRING_free(os); + ERR_pop_to_mark(); + } + + /* Not DER OCTET STRING -> copy as-is */ + buf = OPENSSL_memdup(in, inlen); + if (buf == NULL) + return 0; + + *out = buf; + *outlen = inlen; + return 1; +} + +/* + * Extract raw public key bytes from a CKO_PUBLIC_KEY object using + * the attribute configured for the algorithm. + * Returns 1 on success, 0 on failure. + */ +static int extract_pub_from_public_key_obj(PKCS11_CTX_private *ctx, + CK_SESSION_HANDLE session, CK_OBJECT_HANDLE object, + const P11_RAW_ALGORITHM *alg, unsigned char **raw, size_t *rawlen) +{ + CK_ATTRIBUTE_TYPE attr; + const char *attr_name; + unsigned char *value = NULL; + unsigned char *decoded = NULL; + size_t value_len = 0; + size_t decoded_len = 0; + int ok = 0; + + if (ctx == NULL || alg == NULL || raw == NULL || rawlen == NULL || + object == CK_INVALID_HANDLE || alg->public_len == 0) + return 0; + + *raw = NULL; + *rawlen = 0; + + if (alg->public_source == P11_RAW_FROM_EC_POINT) { + attr = CKA_EC_POINT; + attr_name = "CKA_EC_POINT"; + } else { + attr = CKA_VALUE; + attr_name = "CKA_VALUE"; + } + + if (pkcs11_getattr_alloc(ctx, session, object, attr, &value, &value_len)) { + pkcs11_log(ctx, LOG_DEBUG, + "Missing %s attribute on %s public key\n", + attr_name, alg->name); + return 0; + } + + if (alg->public_source == P11_RAW_FROM_EC_POINT) { + if (!strip_der_octet_string_alloc(value, value_len, + &decoded, &decoded_len)) + goto end; + } else { + decoded = value; + decoded_len = value_len; + value = NULL; + } + + if (decoded_len != alg->public_len) { + pkcs11_log(ctx, LOG_DEBUG, + "Unexpected %s public key size: got %lu, expected %lu\n", + alg->name, (unsigned long)decoded_len, + (unsigned long)alg->public_len); + goto end; + } + + *raw = decoded; + *rawlen = decoded_len; + decoded = NULL; + ok = 1; + +end: + OPENSSL_free(decoded); + OPENSSL_free(value); + return ok; +} + +/* + * Extract raw public key bytes from a CKO_CERTIFICATE object. + * The certificate is read from CKA_VALUE (DER-encoded X.509) and + * the public key is obtained via X.509 parsing. + * Returns 1 on success, 0 on failure. + */ +static int extract_pub_from_cert_obj(PKCS11_CTX_private *ctx, + CK_SESSION_HANDLE session, CK_OBJECT_HANDLE object, + const P11_RAW_ALGORITHM *alg, unsigned char **raw, size_t *rawlen) +{ + const unsigned char *p; + unsigned char *der = NULL; + unsigned char *buf = NULL; + size_t derlen = 0; + size_t len = 0; + X509 *cert = NULL; + EVP_PKEY *pkey = NULL; + int ok = 0; + + if (ctx == NULL || alg == NULL || raw == NULL || rawlen == NULL || + object == CK_INVALID_HANDLE || alg->public_len == 0) + return 0; + + *raw = NULL; + *rawlen = 0; + + if (pkcs11_getattr_alloc(ctx, session, object, CKA_VALUE, &der, &derlen)) + return 0; + + if (derlen == 0 || derlen > LONG_MAX) + goto end; + + p = der; + cert = d2i_X509(NULL, &p, (long)derlen); + if (cert == NULL || p != der + derlen) + goto end; + + pkey = X509_get_pubkey(cert); + if (pkey == NULL || !EVP_PKEY_is_a(pkey, alg->name)) + goto end; + + if (EVP_PKEY_get_raw_public_key(pkey, NULL, &len) != 1 || + len != alg->public_len) + goto end; + + buf = OPENSSL_malloc(len); + if (buf == NULL) + goto end; + + if (EVP_PKEY_get_raw_public_key(pkey, buf, &len) != 1 || + len != alg->public_len) + goto end; + + *raw = buf; + *rawlen = len; + buf = NULL; + ok = 1; + +end: + OPENSSL_free(buf); + OPENSSL_free(der); + EVP_PKEY_free(pkey); + X509_free(cert); + return ok; +} + +/* + * Select an object that can provide public key material. + * + * For a private key object, try to locate a matching CKO_PUBLIC_KEY + * (same CKA_ID). If not found, fall back to CKO_CERTIFICATE. + * + * On success, returns a PKCS11_OBJECT_private pointer. + * If a new object is returned, *needs_free is set to 1 and the caller + * must free it with pkcs11_object_free(). + * + * Returns NULL on failure. + */ +static PKCS11_OBJECT_private *pkcs11_choose_public_source(PKCS11_OBJECT_private *key, + CK_SESSION_HANDLE session, int *needs_free) +{ + PKCS11_OBJECT_private *object; + + if (key == NULL || needs_free == NULL) + return NULL; + + *needs_free = 0; + + if (key->object_class != CKO_PRIVATE_KEY) + return key; + + object = pkcs11_object_from_object(key, session, CKO_PUBLIC_KEY); + if (object != NULL && object->object != CK_INVALID_HANDLE) { + *needs_free = 1; + return object; + } + pkcs11_object_free(object); + + object = pkcs11_object_from_object(key, session, CKO_CERTIFICATE); + if (object != NULL && object->object != CK_INVALID_HANDLE) { + *needs_free = 1; + return object; + } + pkcs11_object_free(object); + + return NULL; +} + +/* + * Retrieve raw public key bytes. + * + * For a private key, prefer a matching CKO_PUBLIC_KEY object and fall back + * to a matching CKO_CERTIFICATE. Public-key material is read from the + * algorithm-specific attribute or extracted from the certificate. + * + * The returned buffer is allocated with OPENSSL_malloc() and must be freed + * by the caller. + * + * Returns 0 on success, -1 on failure. + */ +static int get_raw_public_key(PKCS11_OBJECT_private *key, + const P11_RAW_ALGORITHM *alg, unsigned char **raw, size_t *rawlen) +{ + PKCS11_SLOT_private *slot; + PKCS11_CTX_private *ctx; + PKCS11_OBJECT_private *obj = NULL; + CK_SESSION_HANDLE session = CK_INVALID_HANDLE; + int object_needs_free = 0; + int ok = 0; + + if (key == NULL || key->slot == NULL || alg == NULL || + raw == NULL || rawlen == NULL) + return -1; + + *raw = NULL; + *rawlen = 0; + slot = key->slot; + ctx = slot->ctx; + if (ctx == NULL) + return -1; + + if (pkcs11_session_pool_acquire(slot, 0, &session)) + return -1; + + obj = pkcs11_choose_public_source(key, session, &object_needs_free); + if (obj == NULL || obj->object == CK_INVALID_HANDLE) + goto end; + + switch (obj->object_class) { + case CKO_PUBLIC_KEY: + ok = extract_pub_from_public_key_obj(ctx, session, obj->object, + alg, raw, rawlen); + break; + case CKO_CERTIFICATE: + ok = extract_pub_from_cert_obj(ctx, session, obj->object, + alg, raw, rawlen); + break; + default: + break; + } + +end: + if (object_needs_free) + pkcs11_object_free(obj); + pkcs11_session_pool_release(slot, session); + + if (!ok) { + OPENSSL_free(*raw); + *raw = NULL; + *rawlen = 0; + return -1; + } + return 0; +} + +/* Create an EVP_PKEY from raw public key material. */ +static EVP_PKEY *pkcs11_get_evp_raw_key(PKCS11_OBJECT_private *key, + const P11_RAW_ALGORITHM *alg) +{ + EVP_PKEY *pkey; + const char *properties = NULL; + unsigned char *raw = NULL; + size_t rawlen = 0; + + if (get_raw_public_key(key, alg, &raw, &rawlen) < 0) + return NULL; + + /* Falcon KEYMGMT is provided by pkcs11prov. Other algorithms use + * the default OpenSSL provider selection. */ + if (alg->type == EVP_PKEY_FALCON512 || alg->type == EVP_PKEY_FALCON1024) + properties = "provider=pkcs11prov"; + + /* Create a public-only EVP_PKEY using the selected KEYMGMT. */ + pkey = EVP_PKEY_new_raw_public_key_ex(NULL, alg->name, properties, + raw, rawlen); + OPENSSL_free(raw); + if (pkey == NULL) + return NULL; + +#ifdef LIBP11_HAVE_ECX_METHODS + if (!pkcs11_ecx_method_enable(key, alg->type)) { + EVP_PKEY_free(pkey); + return NULL; + } +#endif /* LIBP11_HAVE_ECX_METHODS */ + return pkey; +} + +#define DEFINE_RAW_OBJECT_OPS(symbol, pkey_type, alg_name, pub_len, source) \ + static const P11_RAW_ALGORITHM symbol##_algorithm = { \ + pkey_type, alg_name, pub_len, source \ + }; \ + static EVP_PKEY *pkcs11_get_evp_key_##symbol(PKCS11_OBJECT_private *key) \ + { \ + return pkcs11_get_evp_raw_key(key, &symbol##_algorithm); \ + } \ + PKCS11_OBJECT_ops pkcs11_##symbol##_ops = { \ + pkey_type, pkcs11_get_evp_key_##symbol \ + } + +/* Falcon is implemented by pkcs11prov rather than the OpenSSL default provider. */ +DEFINE_RAW_OBJECT_OPS(falcon512, EVP_PKEY_FALCON512, "FALCON-512", + FALCON_512_PUB_LEN, P11_RAW_FROM_VALUE); +DEFINE_RAW_OBJECT_OPS(falcon1024, EVP_PKEY_FALCON1024, "FALCON-1024", + FALCON_1024_PUB_LEN, P11_RAW_FROM_VALUE); + +#ifndef OPENSSL_NO_ECX +DEFINE_RAW_OBJECT_OPS(ed25519, EVP_PKEY_ED25519, "ED25519", + ED25519_PUB_LEN, P11_RAW_FROM_EC_POINT); +DEFINE_RAW_OBJECT_OPS(ed448, EVP_PKEY_ED448, "ED448", + ED448_PUB_LEN, P11_RAW_FROM_EC_POINT); +DEFINE_RAW_OBJECT_OPS(x25519, EVP_PKEY_X25519, "X25519", + X25519_PUB_LEN, P11_RAW_FROM_EC_POINT); +DEFINE_RAW_OBJECT_OPS(x448, EVP_PKEY_X448, "X448", + X448_PUB_LEN, P11_RAW_FROM_EC_POINT); +#endif /* OPENSSL_NO_ECX */ + +#if OPENSSL_VERSION_NUMBER >= 0x30500000L +#ifndef OPENSSL_NO_ML_DSA +DEFINE_RAW_OBJECT_OPS(mldsa44, EVP_PKEY_ML_DSA_44, "ML-DSA-44", + ML_DSA_44_PUB_LEN, P11_RAW_FROM_VALUE); +DEFINE_RAW_OBJECT_OPS(mldsa65, EVP_PKEY_ML_DSA_65, "ML-DSA-65", + ML_DSA_65_PUB_LEN, P11_RAW_FROM_VALUE); +DEFINE_RAW_OBJECT_OPS(mldsa87, EVP_PKEY_ML_DSA_87, "ML-DSA-87", + ML_DSA_87_PUB_LEN, P11_RAW_FROM_VALUE); +#endif /* OPENSSL_NO_ML_DSA */ + +#ifndef OPENSSL_NO_ML_KEM +DEFINE_RAW_OBJECT_OPS(mlkem512, EVP_PKEY_ML_KEM_512, "ML-KEM-512", + ML_KEM_512_PUB_LEN, P11_RAW_FROM_VALUE); +DEFINE_RAW_OBJECT_OPS(mlkem768, EVP_PKEY_ML_KEM_768, "ML-KEM-768", + ML_KEM_768_PUB_LEN, P11_RAW_FROM_VALUE); +DEFINE_RAW_OBJECT_OPS(mlkem1024, EVP_PKEY_ML_KEM_1024, "ML-KEM-1024", + ML_KEM_1024_PUB_LEN, P11_RAW_FROM_VALUE); +#endif /* OPENSSL_NO_ML_KEM */ + +#ifndef OPENSSL_NO_SLH_DSA +DEFINE_RAW_OBJECT_OPS(slhdsa_sha2_128s, EVP_PKEY_SLH_DSA_SHA2_128S, + "SLH-DSA-SHA2-128s", SLH_DSA_128_PUB_LEN, P11_RAW_FROM_VALUE); +DEFINE_RAW_OBJECT_OPS(slhdsa_sha2_128f, EVP_PKEY_SLH_DSA_SHA2_128F, + "SLH-DSA-SHA2-128f", SLH_DSA_128_PUB_LEN, P11_RAW_FROM_VALUE); +DEFINE_RAW_OBJECT_OPS(slhdsa_sha2_192s, EVP_PKEY_SLH_DSA_SHA2_192S, + "SLH-DSA-SHA2-192s", SLH_DSA_192_PUB_LEN, P11_RAW_FROM_VALUE); +DEFINE_RAW_OBJECT_OPS(slhdsa_sha2_192f, EVP_PKEY_SLH_DSA_SHA2_192F, + "SLH-DSA-SHA2-192f", SLH_DSA_192_PUB_LEN, P11_RAW_FROM_VALUE); +DEFINE_RAW_OBJECT_OPS(slhdsa_sha2_256s, EVP_PKEY_SLH_DSA_SHA2_256S, + "SLH-DSA-SHA2-256s", SLH_DSA_256_PUB_LEN, P11_RAW_FROM_VALUE); +DEFINE_RAW_OBJECT_OPS(slhdsa_sha2_256f, EVP_PKEY_SLH_DSA_SHA2_256F, + "SLH-DSA-SHA2-256f", SLH_DSA_256_PUB_LEN, P11_RAW_FROM_VALUE); +DEFINE_RAW_OBJECT_OPS(slhdsa_shake_128s, EVP_PKEY_SLH_DSA_SHAKE_128S, + "SLH-DSA-SHAKE-128s", SLH_DSA_128_PUB_LEN, P11_RAW_FROM_VALUE); +DEFINE_RAW_OBJECT_OPS(slhdsa_shake_128f, EVP_PKEY_SLH_DSA_SHAKE_128F, + "SLH-DSA-SHAKE-128f", SLH_DSA_128_PUB_LEN, P11_RAW_FROM_VALUE); +DEFINE_RAW_OBJECT_OPS(slhdsa_shake_192s, EVP_PKEY_SLH_DSA_SHAKE_192S, + "SLH-DSA-SHAKE-192s", SLH_DSA_192_PUB_LEN, P11_RAW_FROM_VALUE); +DEFINE_RAW_OBJECT_OPS(slhdsa_shake_192f, EVP_PKEY_SLH_DSA_SHAKE_192F, + "SLH-DSA-SHAKE-192f", SLH_DSA_192_PUB_LEN, P11_RAW_FROM_VALUE); +DEFINE_RAW_OBJECT_OPS(slhdsa_shake_256s, EVP_PKEY_SLH_DSA_SHAKE_256S, + "SLH-DSA-SHAKE-256s", SLH_DSA_256_PUB_LEN, P11_RAW_FROM_VALUE); +DEFINE_RAW_OBJECT_OPS(slhdsa_shake_256f, EVP_PKEY_SLH_DSA_SHAKE_256F, + "SLH-DSA-SHAKE-256f", SLH_DSA_256_PUB_LEN, P11_RAW_FROM_VALUE); +#endif /* OPENSSL_NO_SLH_DSA */ +#endif /* OPENSSL_VERSION_NUMBER >= 0x30500000L */ + +#undef DEFINE_RAW_OBJECT_OPS +#endif /* OPENSSL_VERSION_NUMBER >= 0x30000000L */ + +/* vim: set noexpandtab: */ diff --git a/src/p11_rsa.c b/src/p11_rsa.c index 58a80a2d..d50af46b 100644 --- a/src/p11_rsa.c +++ b/src/p11_rsa.c @@ -29,10 +29,6 @@ static int rsa_ex_index = 0; static RSA_METHOD *pkcs11_rsa_method = NULL; -#if OPENSSL_VERSION_NUMBER >= 0x30000000L && OPENSSL_VERSION_NUMBER < 0x40000000L -static EVP_PKEY_METHOD *pkey_method_rsa = NULL; -#endif /* OPENSSL_VERSION_NUMBER >= 0x30000000L && OPENSSL_VERSION_NUMBER < 0x40000000L */ - static RSA *pkcs11_get1_rsa(PKCS11_OBJECT_private *key) { EVP_PKEY *evp_key = pkcs11_get_key(key, key->object_class); @@ -235,39 +231,6 @@ void pkcs11_set_ex_data_rsa(RSA *rsa, PKCS11_OBJECT_private *key) RSA_set_ex_data(rsa, rsa_ex_index, key); } -#if OPENSSL_VERSION_NUMBER >= 0x30000000L && OPENSSL_VERSION_NUMBER < 0x40000000L - -/* Global initialize RSA EVP_PKEY_METHOD */ -static int pkcs11_pkey_method_rsa_new(void) -{ - if (pkey_method_rsa) - return 1; /* EVP_PKEY_RSA method already initialized */ - - pkey_method_rsa = pkcs11_pkey_method_rsa(); - if (!pkey_method_rsa) - return 0; - - /* Register the method globally */ - if (!EVP_PKEY_meth_add0(pkey_method_rsa)) { - EVP_PKEY_meth_free(pkey_method_rsa); - pkey_method_rsa = NULL; - return 0; - } - return 1; -} - -void pkcs11_rsa_key_method_free(void) -{ - if (pkey_method_rsa) { - free_pkey_ex_index(); - EVP_PKEY_meth_remove(pkey_method_rsa); - EVP_PKEY_meth_free(pkey_method_rsa); - pkey_method_rsa = NULL; - } -} - -#endif /* OPENSSL_VERSION_NUMBER >= 0x30000000L && OPENSSL_VERSION_NUMBER < 0x40000000L */ - /* * Build an EVP_PKEY object */ @@ -285,22 +248,12 @@ static EVP_PKEY *pkcs11_get_evp_key_rsa(PKCS11_OBJECT_private *key) return NULL; } if (key->object_class == CKO_PRIVATE_KEY) { -#if OPENSSL_VERSION_NUMBER >= 0x30000000L -# if OPENSSL_VERSION_NUMBER < 0x40000000L - if ((key->slot->ctx->flags & PKCS11_FLAG_NO_METHODS) == 0) { - /* global initialize RSA EVP_PKEY_METHOD */ - if (!pkcs11_pkey_method_rsa_new()) { - EVP_PKEY_free(pk); - return NULL; - } - alloc_pkey_ex_index(); - pkcs11_set_ex_data_pkey(pk, key); - atexit(pkcs11_rsa_key_method_free); - } -# endif /* OPENSSL_VERSION_NUMBER < 0x40000000L */ -#endif /* OPENSSL_VERSION_NUMBER >= 0x30000000L */ - - /* The RSA object owns the reference stored in its ex_data. */ +#if OPENSSL_VERSION_NUMBER < 0x40000000L + if (!pkcs11_rsa_method_enable(key)) + goto error; +#endif /* OPENSSL_VERSION_NUMBER < 0x40000000L */ + /* The RSA object owns the reference stored in its ex_data, + * released by pkcs11_rsa_free_method() */ key = pkcs11_object_ref(key); RSA_set_method(rsa, PKCS11_get_rsa_method()); #if OPENSSL_VERSION_NUMBER >= 0x10100005L || ( defined(LIBRESSL_VERSION_NUMBER) && LIBRESSL_VERSION_NUMBER >= 0x3050000fL ) @@ -318,9 +271,16 @@ static EVP_PKEY *pkcs11_get_evp_key_rsa(PKCS11_OBJECT_private *key) #endif pkcs11_set_ex_data_rsa(rsa, key); - EVP_PKEY_set1_RSA(pk, rsa); /* Also increments the rsa ref count */ + if (EVP_PKEY_set1_RSA(pk, rsa) != 1) /* Also increments the rsa ref count */ + goto error; + RSA_free(rsa); /* Drops our reference to it */ return pk; + +error: + EVP_PKEY_free(pk); + RSA_free(rsa); + return NULL; } /* TODO: remove this function in libp11 0.5.0 */ @@ -534,7 +494,7 @@ static int RSA_meth_set_finish(RSA_METHOD *meth, int (*finish)(RSA *rsa)) #endif /* - * Overload the default OpenSSL methods for RSA + * Overload the default OpenSSL methods for RSA_PKCS1_PADDING */ RSA_METHOD *PKCS11_get_rsa_method(void) { diff --git a/src/p11_slhdsa.c b/src/p11_slhdsa.c deleted file mode 100644 index 07161098..00000000 --- a/src/p11_slhdsa.c +++ /dev/null @@ -1,417 +0,0 @@ -/* - * Copyright © 2026 Mobi - Com Polska Sp. z o.o. - * Author: Małgorzata Olszówka - * All rights reserved. - * - * This file implements the handling of SLH-DSA keys stored on a PKCS11 token. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - */ - -#include "libp11-int.h" -#include - -#if !defined(OPENSSL_NO_SLH_DSA) && OPENSSL_VERSION_NUMBER >= 0x30500000L - -#define SLH_DSA_128_PUB_BYTES 32 -#define SLH_DSA_192_PUB_BYTES 48 -#define SLH_DSA_256_PUB_BYTES 64 - -/* - * Extract raw SLH-DSA public key bytes from a CKO_PUBLIC_KEY object using CKA_VALUE. - * Returns 1 on success, 0 on failure. - */ -static int extract_pub_from_public_key_obj(PKCS11_CTX_private *ctx, - CK_SESSION_HANDLE session, CK_OBJECT_HANDLE obj, - size_t expected_len, unsigned char **raw, size_t *rawlen) -{ - unsigned char *value = NULL; - size_t value_len = 0; - - if (ctx == NULL || raw == NULL || rawlen == NULL || - obj == CK_INVALID_HANDLE || expected_len == 0) - return 0; - - *raw = NULL; - *rawlen = 0; - - if (pkcs11_getattr_alloc(ctx, session, obj, CKA_VALUE, &value, &value_len)) { - pkcs11_log(ctx, LOG_DEBUG, - "Missing CKA_VALUE attribute on SLH-DSA public key\n"); - return 0; - } - - if (value_len != expected_len) { - pkcs11_log(ctx, LOG_DEBUG, - "Unexpected SLH-DSA public key size: got %lu, expected %lu\n", - (unsigned long)value_len, (unsigned long)expected_len); - OPENSSL_free(value); - return 0; - } - - *raw = value; - *rawlen = value_len; - return 1; -} - -/* - * Extract raw SLH-DSA public key bytes from a CKO_CERTIFICATE object. - * The certificate is read from CKA_VALUE (DER-encoded X.509) and - * the public key is obtained via X.509 parsing. - * Returns 1 on success, 0 on failure. - */ -static int extract_pub_from_cert_obj(PKCS11_CTX_private *ctx, - CK_SESSION_HANDLE session, CK_OBJECT_HANDLE obj, - size_t expected_len, const char *algname, - unsigned char **raw, size_t *rawlen) -{ - const unsigned char *p; - unsigned char *der = NULL; - unsigned char *buf = NULL; - size_t derlen = 0; - size_t len = 0; - X509 *cert = NULL; - EVP_PKEY *pkey = NULL; - int ok = 0; - - if (ctx == NULL || raw == NULL || rawlen == NULL || - obj == CK_INVALID_HANDLE || expected_len == 0 || algname == NULL) - return 0; - - *raw = NULL; - *rawlen = 0; - - if (pkcs11_getattr_alloc(ctx, session, obj, CKA_VALUE, &der, &derlen)) - return 0; - - if (derlen == 0 || derlen > LONG_MAX) - goto end; - - p = der; - cert = d2i_X509(NULL, &p, (long)derlen); - if (cert == NULL || p != der + derlen) - goto end; - - pkey = X509_get_pubkey(cert); - if (pkey == NULL) - goto end; - - if (!EVP_PKEY_is_a(pkey, algname)) - goto end; - - if (EVP_PKEY_get_raw_public_key(pkey, NULL, &len) != 1) - goto end; - - if (len != expected_len) - goto end; - - buf = OPENSSL_malloc(len); - if (buf == NULL) - goto end; - - if (EVP_PKEY_get_raw_public_key(pkey, buf, &len) != 1) - goto end; - - if (len != expected_len) - goto end; - - *raw = buf; - *rawlen = len; - buf = NULL; - ok = 1; - -end: - OPENSSL_free(buf); - OPENSSL_free(der); - EVP_PKEY_free(pkey); - X509_free(cert); - return ok; -} - -/* - * Select an object that can provide public key material. - * - * For a private key object, try to locate a matching CKO_PUBLIC_KEY - * (same CKA_ID). If not found, fall back to CKO_CERTIFICATE. - * - * On success, returns a PKCS11_OBJECT_private pointer. - * If a new object is returned, *needs_free is set to 1 and the caller - * must free it with pkcs11_object_free(). - * - * Returns NULL on failure. - */ -static PKCS11_OBJECT_private *pkcs11_choose_public_source(PKCS11_OBJECT_private *key, - CK_SESSION_HANDLE session, int *needs_free) -{ - PKCS11_OBJECT_private *obj; - - *needs_free = 0; - - if (key->object_class != CKO_PRIVATE_KEY) - return key; - - obj = pkcs11_object_from_object(key, session, CKO_PUBLIC_KEY); - if (obj != NULL && obj->object != CK_INVALID_HANDLE) { - *needs_free = 1; - return obj; - } - if (obj != NULL) - pkcs11_object_free(obj); - - obj = pkcs11_object_from_object(key, session, CKO_CERTIFICATE); - if (obj != NULL && obj->object != CK_INVALID_HANDLE) { - *needs_free = 1; - return obj; - } - if (obj != NULL) - pkcs11_object_free(obj); - - return NULL; -} - -/* - * Retrieve raw SLH-DSA public key bytes. - * - * Preference order: - * 1. CKO_PUBLIC_KEY -> CKA_VALUE - * 2. CKO_CERTIFICATE -> CKA_VALUE (DER) + X.509 parsing - * - * The returned buffer is allocated with OPENSSL_malloc() - * and must be freed by the caller. - * - * Returns 0 on success, -1 on failure. - */ -static int pkcs11_get_raw_public_key(PKCS11_OBJECT_private *key, - size_t expected_len, const char *algname, - unsigned char **raw, size_t *rawlen) -{ - PKCS11_SLOT_private *slot; - PKCS11_CTX_private *ctx; - CK_SESSION_HANDLE session = CK_INVALID_HANDLE; - PKCS11_OBJECT_private *obj = NULL; - int obj_needs_free = 0, ok = 0; - - if (key == NULL || key->slot == NULL || raw == NULL || rawlen == NULL) - return -1; - - *raw = NULL; - *rawlen = 0; - slot = key->slot; - ctx = slot->ctx; - - if (pkcs11_session_pool_acquire(slot, 0, &session)) - return -1; - - obj = pkcs11_choose_public_source(key, session, &obj_needs_free); - if (obj == NULL || obj->object == CK_INVALID_HANDLE) - goto end; - - switch (obj->object_class) { - case CKO_PUBLIC_KEY: - ok = extract_pub_from_public_key_obj(ctx, session, obj->object, - expected_len, raw, rawlen); - break; - case CKO_CERTIFICATE: - ok = extract_pub_from_cert_obj(ctx, session, obj->object, - expected_len, algname, raw, rawlen); - break; - default: - ok = 0; - break; - } - -end: - pkcs11_session_pool_release(slot, session); - - if (!ok) { - OPENSSL_free(*raw); - *raw = NULL; - *rawlen = 0; - } - if (obj_needs_free && obj != NULL) - pkcs11_object_free(obj); - - return ok ? 0 : -1; -} - -static EVP_PKEY *pkcs11_get_evp_key_slhdsa(PKCS11_OBJECT_private *key, - size_t publen, const char *algname) -{ - EVP_PKEY *pkey = NULL; - unsigned char *raw = NULL; - size_t rawlen = 0; - - /* Retrieve the public key in raw format from PKCS#11 */ - if (pkcs11_get_raw_public_key(key, publen, algname, &raw, &rawlen) < 0) - return NULL; - - /* Build a EVP_PKEY from the raw public key, used only as software public key */ - pkey = EVP_PKEY_new_raw_public_key_ex(NULL, algname, NULL, raw, rawlen); - OPENSSL_free(raw); - return pkey; -} - -static EVP_PKEY *pkcs11_get_evp_key_slhdsa_sha2_128s(PKCS11_OBJECT_private *key) -{ - return pkcs11_get_evp_key_slhdsa(key, SLH_DSA_128_PUB_BYTES, - "SLH-DSA-SHA2-128s"); -} - -static EVP_PKEY *pkcs11_get_evp_key_slhdsa_sha2_128f(PKCS11_OBJECT_private *key) -{ - return pkcs11_get_evp_key_slhdsa(key, SLH_DSA_128_PUB_BYTES, - "SLH-DSA-SHA2-128f"); -} - -static EVP_PKEY *pkcs11_get_evp_key_slhdsa_sha2_192s(PKCS11_OBJECT_private *key) -{ - return pkcs11_get_evp_key_slhdsa(key, SLH_DSA_192_PUB_BYTES, - "SLH-DSA-SHA2-192s"); -} - -static EVP_PKEY *pkcs11_get_evp_key_slhdsa_sha2_192f(PKCS11_OBJECT_private *key) -{ - return pkcs11_get_evp_key_slhdsa(key, SLH_DSA_192_PUB_BYTES, - "SLH-DSA-SHA2-192f"); -} - -static EVP_PKEY *pkcs11_get_evp_key_slhdsa_sha2_256s(PKCS11_OBJECT_private *key) -{ - return pkcs11_get_evp_key_slhdsa(key, SLH_DSA_256_PUB_BYTES, - "SLH-DSA-SHA2-256s"); -} - -static EVP_PKEY *pkcs11_get_evp_key_slhdsa_sha2_256f(PKCS11_OBJECT_private *key) -{ - return pkcs11_get_evp_key_slhdsa(key, SLH_DSA_256_PUB_BYTES, - "SLH-DSA-SHA2-256f"); -} - -static EVP_PKEY *pkcs11_get_evp_key_slhdsa_shake_128s(PKCS11_OBJECT_private *key) -{ - return pkcs11_get_evp_key_slhdsa(key, SLH_DSA_128_PUB_BYTES, - "SLH-DSA-SHAKE-128s"); -} - -static EVP_PKEY *pkcs11_get_evp_key_slhdsa_shake_128f(PKCS11_OBJECT_private *key) -{ - return pkcs11_get_evp_key_slhdsa(key, SLH_DSA_128_PUB_BYTES, - "SLH-DSA-SHAKE-128f"); -} - -static EVP_PKEY *pkcs11_get_evp_key_slhdsa_shake_192s(PKCS11_OBJECT_private *key) -{ - return pkcs11_get_evp_key_slhdsa(key, SLH_DSA_192_PUB_BYTES, - "SLH-DSA-SHAKE-192s"); -} - -static EVP_PKEY *pkcs11_get_evp_key_slhdsa_shake_192f(PKCS11_OBJECT_private *key) -{ - return pkcs11_get_evp_key_slhdsa(key, SLH_DSA_192_PUB_BYTES, - "SLH-DSA-SHAKE-192f"); -} - -static EVP_PKEY *pkcs11_get_evp_key_slhdsa_shake_256s(PKCS11_OBJECT_private *key) -{ - return pkcs11_get_evp_key_slhdsa(key, SLH_DSA_256_PUB_BYTES, - "SLH-DSA-SHAKE-256s"); -} - -static EVP_PKEY *pkcs11_get_evp_key_slhdsa_shake_256f(PKCS11_OBJECT_private *key) -{ - return pkcs11_get_evp_key_slhdsa(key, SLH_DSA_256_PUB_BYTES, - "SLH-DSA-SHAKE-256f"); -} - - -PKCS11_OBJECT_ops pkcs11_slhdsa_sha2_128s_ops = { - EVP_PKEY_SLH_DSA_SHA2_128S, - pkcs11_get_evp_key_slhdsa_sha2_128s, -}; - -PKCS11_OBJECT_ops pkcs11_slhdsa_sha2_128f_ops = { - EVP_PKEY_SLH_DSA_SHA2_128F, - pkcs11_get_evp_key_slhdsa_sha2_128f, -}; - -PKCS11_OBJECT_ops pkcs11_slhdsa_sha2_192s_ops = { - EVP_PKEY_SLH_DSA_SHA2_192S, - pkcs11_get_evp_key_slhdsa_sha2_192s, -}; - -PKCS11_OBJECT_ops pkcs11_slhdsa_sha2_192f_ops = { - EVP_PKEY_SLH_DSA_SHA2_192F, - pkcs11_get_evp_key_slhdsa_sha2_192f, -}; - -PKCS11_OBJECT_ops pkcs11_slhdsa_sha2_256s_ops = { - EVP_PKEY_SLH_DSA_SHA2_256S, - pkcs11_get_evp_key_slhdsa_sha2_256s, -}; - -PKCS11_OBJECT_ops pkcs11_slhdsa_sha2_256f_ops = { - EVP_PKEY_SLH_DSA_SHA2_256F, - pkcs11_get_evp_key_slhdsa_sha2_256f, -}; - -PKCS11_OBJECT_ops pkcs11_slhdsa_shake_128s_ops = { - EVP_PKEY_SLH_DSA_SHAKE_128S, - pkcs11_get_evp_key_slhdsa_shake_128s, -}; - -PKCS11_OBJECT_ops pkcs11_slhdsa_shake_128f_ops = { - EVP_PKEY_SLH_DSA_SHAKE_128F, - pkcs11_get_evp_key_slhdsa_shake_128f, -}; - -PKCS11_OBJECT_ops pkcs11_slhdsa_shake_192s_ops = { - EVP_PKEY_SLH_DSA_SHAKE_192S, - pkcs11_get_evp_key_slhdsa_shake_192s, -}; - -PKCS11_OBJECT_ops pkcs11_slhdsa_shake_192f_ops = { - EVP_PKEY_SLH_DSA_SHAKE_192F, - pkcs11_get_evp_key_slhdsa_shake_192f, -}; - -PKCS11_OBJECT_ops pkcs11_slhdsa_shake_256s_ops = { - EVP_PKEY_SLH_DSA_SHAKE_256S, - pkcs11_get_evp_key_slhdsa_shake_256s, -}; - -PKCS11_OBJECT_ops pkcs11_slhdsa_shake_256f_ops = { - EVP_PKEY_SLH_DSA_SHAKE_256F, - pkcs11_get_evp_key_slhdsa_shake_256f, -}; - - -#else /* !defined(OPENSSL_NO_SLH_DSA) && OPENSSL_VERSION_NUMBER >= 0x30500000L */ -/* - * SLH-DSA support is not available: - * - either OpenSSL was built without SLH-DSA support, or - * - OpenSSL version is older than 3.5. - */ -#warning "SLH-DSA support not built with libp11" - -#endif /* !defined(OPENSSL_NO_SLH_DSA) && OPENSSL_VERSION_NUMBER >= 0x30500000L */ - -/* vim: set noexpandtab: */ diff --git a/tests/ed25519-keygen.c b/tests/ed25519-keygen.c index 6b718e9d..eeba7874 100644 --- a/tests/ed25519-keygen.c +++ b/tests/ed25519-keygen.c @@ -23,9 +23,10 @@ #include "oneshot_common.h" #if !defined(OPENSSL_NO_ENGINE) && \ - !defined(OPENSSL_NO_ECX) && \ - (OPENSSL_VERSION_NUMBER >= 0x30000000L) && \ - (OPENSSL_VERSION_NUMBER < 0x40000000L) + !defined(OPENSSL_NO_ECX) && \ + (OPENSSL_VERSION_NUMBER >= 0x30000000L) && \ + (OPENSSL_VERSION_NUMBER < 0x40000000L) && \ + !defined(OPENSSL_NO_DEPRECATED_3_0) void display_openssl_errors(void) { diff --git a/tests/ed448-keygen.c b/tests/ed448-keygen.c index 33a87a5f..c9b43343 100644 --- a/tests/ed448-keygen.c +++ b/tests/ed448-keygen.c @@ -23,9 +23,10 @@ #include "oneshot_common.h" #if !defined(OPENSSL_NO_ENGINE) && \ - !defined(OPENSSL_NO_ECX) && \ - (OPENSSL_VERSION_NUMBER >= 0x30000000L) && \ - (OPENSSL_VERSION_NUMBER < 0x40000000L) + !defined(OPENSSL_NO_ECX) && \ + (OPENSSL_VERSION_NUMBER >= 0x30000000L) && \ + (OPENSSL_VERSION_NUMBER < 0x40000000L) && \ + !defined(OPENSSL_NO_DEPRECATED_3_0) void display_openssl_errors(void) {