diff --git a/.gitignore b/.gitignore index 28b7d6b5..2d8c6956 100644 --- a/.gitignore +++ b/.gitignore @@ -95,6 +95,7 @@ tests/rsa-keygen tests/ec-keygen tests/ec-derive tests/ec-derive-prov +tests/ed25519-software-key tests/ed25519-keygen tests/ed448-keygen tests/ed25519-keygen-prov diff --git a/src/Makefile.am b/src/Makefile.am index 549ac673..fa2698d3 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_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..e94933af 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_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 45c60358..ecde9359 100644 --- a/src/libp11-int.h +++ b/src/libp11-int.h @@ -1,6 +1,6 @@ /* libp11, a simple layer on top of PKCS#11 API * Copyright (C) 2005 Olaf Kirch - * Copyright (C) 2015-2025 Michał Trojnara + * Copyright (C) 2015-2026 Michał Trojnara * Copyright © 2025-2026 Mobi - Com Polska Sp. z o.o. * * This library is free software; you can redistribute it and/or @@ -587,20 +587,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); @@ -624,14 +610,6 @@ 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); - -/* 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 */ - #if OPENSSL_VERSION_NUMBER < 0x100020d0L || defined(LIBRESSL_VERSION_NUMBER) /* Get sign_init and sign callbacks from EVP_PKEY_METHOD */ extern void EVP_PKEY_meth_get_sign(EVP_PKEY_METHOD *pmeth, @@ -641,11 +619,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 9e555bd8..670db1ab 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,25 @@ 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 */ + /* Keep the PKCS11_OBJECT_private alive while referenced from EC ex_data. + * With EC_KEY_METHOD, pkcs11_ec_finish() releases this reference; + * legacy ECDSA/ECDH methods have no equivalent finish hook. */ 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_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..3c5c9263 100644 --- a/src/p11_key.c +++ b/src/p11_key.c @@ -1,7 +1,7 @@ /* libp11, a simple layer on top of PKCS#11 API * Copyright (C) 2005 Olaf Kirch - * Copyright (C) 2016-2025 Michał Trojnara - * Copyright © 2025 Mobi - Com Polska Sp. z o.o. + * Copyright (C) 2016-2026 Michał Trojnara + * Copyright © 2026 Mobi - Com Polska Sp. z o.o. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -26,30 +26,15 @@ /* The maximum length of PIN */ #define MAX_PIN_LENGTH 256 +/* EVP_PKEY ex_data used by provider/native key handling. */ #if OPENSSL_VERSION_NUMBER >= 0x30000000L 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 */ +/******************************************************************************/ +/* PKCS#11 key object handling and algorithm-specific key parameters */ +/******************************************************************************/ #if !defined(OPENSSL_NO_ECX) && OPENSSL_VERSION_NUMBER >= 0x30000000L /* DER OIDs */ @@ -80,47 +65,6 @@ static const unsigned char STR_X448[] = { #endif /* !defined(OPENSSL_NO_ECX) && OPENSSL_VERSION_NUMBER >= 0x30000000L */ -#if OPENSSL_VERSION_NUMBER < 0x100020d0L || defined(LIBRESSL_VERSION_NUMBER) -struct evp_pkey_method_st { - int pkey_id; - int flags; - int (*init) (EVP_PKEY_CTX *ctx); - int (*copy) (EVP_PKEY_CTX *dst, EVP_PKEY_CTX *src); - void (*cleanup) (EVP_PKEY_CTX *ctx); - int (*paramgen_init) (EVP_PKEY_CTX *ctx); - int (*paramgen) (EVP_PKEY_CTX *ctx, EVP_PKEY *pkey); - int (*keygen_init) (EVP_PKEY_CTX *ctx); - int (*keygen) (EVP_PKEY_CTX *ctx, EVP_PKEY *pkey); - int (*sign_init) (EVP_PKEY_CTX *ctx); - int (*sign) (EVP_PKEY_CTX *ctx, unsigned char *sig, size_t *siglen, - const unsigned char *tbs, size_t tbslen); - int (*verify_init) (EVP_PKEY_CTX *ctx); - int (*verify) (EVP_PKEY_CTX *ctx, - const unsigned char *sig, size_t siglen, - const unsigned char *tbs, size_t tbslen); - int (*verify_recover_init) (EVP_PKEY_CTX *ctx); - int (*verify_recover) (EVP_PKEY_CTX *ctx, - unsigned char *rout, size_t *routlen, - const unsigned char *sig, size_t siglen); - int (*signctx_init) (EVP_PKEY_CTX *ctx, EVP_MD_CTX *mctx); - int (*signctx) (EVP_PKEY_CTX *ctx, unsigned char *sig, size_t *siglen, - EVP_MD_CTX *mctx); - int (*verifyctx_init) (EVP_PKEY_CTX *ctx, EVP_MD_CTX *mctx); - int (*verifyctx) (EVP_PKEY_CTX *ctx, const unsigned char *sig, int siglen, - EVP_MD_CTX *mctx); - int (*encrypt_init) (EVP_PKEY_CTX *ctx); - int (*encrypt) (EVP_PKEY_CTX *ctx, unsigned char *out, size_t *outlen, - const unsigned char *in, size_t inlen); - int (*decrypt_init) (EVP_PKEY_CTX *ctx); - int (*decrypt) (EVP_PKEY_CTX *ctx, unsigned char *out, size_t *outlen, - const unsigned char *in, size_t inlen); - int (*derive_init) (EVP_PKEY_CTX *ctx); - int (*derive) (EVP_PKEY_CTX *ctx, unsigned char *key, size_t *keylen); - int (*ctrl) (EVP_PKEY_CTX *ctx, int type, int p1, void *p2); - int (*ctrl_str) (EVP_PKEY_CTX *ctx, const char *type, const char *value); -}; /* EVP_PKEY_METHOD */ -#endif - static int pkcs11_find_keys(PKCS11_SLOT_private *, CK_SESSION_HANDLE, unsigned int, PKCS11_TEMPLATE *); static int pkcs11_init_key(PKCS11_SLOT_private *, CK_SESSION_HANDLE session, @@ -568,6 +512,11 @@ int pkcs11_reload_object(PKCS11_OBJECT_private *obj) return 0; } + +/******************************************************************************/ +/* PKCS#11 key generation */ +/******************************************************************************/ + /** * Generate RSA key pair directly on token */ @@ -1159,6 +1108,11 @@ int pkcs11_falcon_keygen(PKCS11_SLOT_private *slot, } #endif /* OPENSSL_VERSION_NUMBER >= 0x30000000L */ + +/******************************************************************************/ +/* PKCS#11 key storage */ +/******************************************************************************/ + /* * Store a private key on the token */ @@ -1272,6 +1226,11 @@ static int pkcs11_store_key(PKCS11_SLOT_private *slot, EVP_PKEY *pk, } + +/******************************************************************************/ +/* EVP_PKEY construction and PKCS#11 object association */ +/******************************************************************************/ + /* * Get the key type */ @@ -1773,12 +1732,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 +1744,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 +1758,8 @@ 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); - if (ret == NULL) - goto end; + ret = EVP_PKEY_new_raw_public_key_ex(NULL, name, properties, pub, publen); -#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; @@ -1880,6 +1832,13 @@ void pkcs11_destroy_keys(PKCS11_SLOT_private *slot, unsigned int type) keys->num = 0; } +/******************************************************************************/ +/* EVP_PKEY ex_data used by provider/native key handling */ +/******************************************************************************/ + +/* + * EVP_PKEY ex_data used by provider/native key handling. + */ #if OPENSSL_VERSION_NUMBER >= 0x30000000L static void pkcs11_set_ex_data_evp_pkey(EVP_PKEY *pkey, PKCS11_OBJECT_private *obj) @@ -1954,287 +1913,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 dfea4035..512abfce 100644 --- a/src/p11_load.c +++ b/src/p11_load.c @@ -41,11 +41,6 @@ static void libp11_global_free(void) pkcs11_rsa_method_free(); #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(); 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 8fb9a71c..6696c3af 100644 --- a/src/p11_pkey.c +++ b/src/p11_pkey.c @@ -1,7 +1,7 @@ /* libp11, a simple layer on top of PKCS#11 API * Copyright (C) 2017 Douglas E. Engert - * Copyright (C) 2017-2025 Michał Trojnara - * Copyright © 2025 Mobi - Com Polska Sp. z o.o. + * Copyright (C) 2017-2026 Michał Trojnara + * Copyright © 2026 Mobi - Com Polska Sp. z o.o. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -34,23 +34,129 @@ #define RSA_PSS_SALTLEN_AUTO_DIGEST_MAX -4 #endif + +/******************************************************************************/ +/* Legacy ENGINE EVP_PKEY_METHOD support */ +/******************************************************************************/ + +/* --- ENGINE method definitions and state ---------------------------------- */ + +#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 + +#ifdef LIBP11_HAVE_ECX_METHODS +#define ED25519_SIG_LEN 64 +#define ED448_SIG_LEN 114 +#define X25519_KEY_LEN 32 +#define X448_KEY_LEN 56 +#endif /* LIBP11_HAVE_ECX_METHODS */ + #if OPENSSL_VERSION_NUMBER < 0x40000000L -# 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, - unsigned char *sig, size_t *siglen, - const unsigned char *tbs, size_t tbslen); -# endif /* OPENSSL_NO_EC */ -#if !defined(OPENSSL_NO_ECX) && OPENSSL_VERSION_NUMBER >= 0x30000000L -static int (*orig_pkey_ed25519_digestsign)(EVP_MD_CTX *ctx, - unsigned char *sig, size_t *siglen, - const unsigned char *tbs, size_t tbslen); -static int (*orig_pkey_ed448_digestsign)(EVP_MD_CTX *ctx, - unsigned char *sig, size_t *siglen, - const unsigned char *tbs, size_t tbslen); -#endif /* !defined(OPENSSL_NO_ECX) && OPENSSL_VERSION_NUMBER >= 0x30000000L */ -#endif /* OPENSSL_VERSION_NUMBER < 0x40000000L */ +typedef int (*P11_PKEY_INIT_FN)(EVP_PKEY_CTX *); +typedef int (*P11_PKEY_SIGN_FN)(EVP_PKEY_CTX *, unsigned char *, size_t *, + const unsigned char *, size_t); +typedef int (*P11_PKEY_DECRYPT_FN)(EVP_PKEY_CTX *, unsigned char *, size_t *, + const unsigned char *, size_t); +typedef int (*P11_PKEY_DIGESTSIGN_FN)(EVP_MD_CTX *, unsigned char *, size_t *, + const unsigned char *, size_t); +typedef int (*P11_PKEY_DERIVE_FN)(EVP_PKEY_CTX *, unsigned char *, size_t *); + +typedef enum { + P11_PKEY_RSA, +#ifndef OPENSSL_NO_EC + P11_PKEY_EC, +#endif /* OPENSSL_NO_EC */ +#ifdef LIBP11_HAVE_ECX_METHODS + P11_PKEY_EDDSA, + P11_PKEY_XDH +#endif /* LIBP11_HAVE_ECX_METHODS */ +} P11_PKEY_KIND; + +typedef struct { + int type; + P11_PKEY_KIND kind; + EVP_PKEY_METHOD *method; + P11_PKEY_INIT_FN original_init; + P11_PKEY_SIGN_FN original_sign; + P11_PKEY_INIT_FN original_decrypt_init; + P11_PKEY_DECRYPT_FN original_decrypt; + P11_PKEY_DIGESTSIGN_FN original_digestsign; + P11_PKEY_DERIVE_FN original_derive; +} P11_PKEY_METHOD; + +static P11_PKEY_METHOD pkey_methods[] = { + { .type = EVP_PKEY_RSA, .kind = P11_PKEY_RSA }, +#ifndef OPENSSL_NO_EC + { .type = EVP_PKEY_EC, .kind = P11_PKEY_EC }, +#endif /* OPENSSL_NO_EC */ +#ifdef LIBP11_HAVE_ECX_METHODS + { .type = EVP_PKEY_ED25519, .kind = P11_PKEY_EDDSA }, + { .type = EVP_PKEY_ED448, .kind = P11_PKEY_EDDSA }, + { .type = EVP_PKEY_X25519, .kind = P11_PKEY_XDH }, + { .type = EVP_PKEY_X448, .kind = P11_PKEY_XDH } +#endif /* LIBP11_HAVE_ECX_METHODS */ +}; + +static P11_PKEY_METHOD *pkey_method_by_type(int type) +{ + size_t i; + + for (i = 0; i < sizeof(pkey_methods) / sizeof(pkey_methods[0]); i++) { + if (pkey_methods[i].type == type) + return &pkey_methods[i]; + } + return NULL; +} +#endif + + +/* --- OpenSSL compatibility helpers ---------------------------------------- */ + +#if OPENSSL_VERSION_NUMBER < 0x100020d0L || defined(LIBRESSL_VERSION_NUMBER) +struct evp_pkey_method_st { + int pkey_id; + int flags; + int (*init) (EVP_PKEY_CTX *ctx); + int (*copy) (EVP_PKEY_CTX *dst, EVP_PKEY_CTX *src); + void (*cleanup) (EVP_PKEY_CTX *ctx); + int (*paramgen_init) (EVP_PKEY_CTX *ctx); + int (*paramgen) (EVP_PKEY_CTX *ctx, EVP_PKEY *pkey); + int (*keygen_init) (EVP_PKEY_CTX *ctx); + int (*keygen) (EVP_PKEY_CTX *ctx, EVP_PKEY *pkey); + int (*sign_init) (EVP_PKEY_CTX *ctx); + int (*sign) (EVP_PKEY_CTX *ctx, unsigned char *sig, size_t *siglen, + const unsigned char *tbs, size_t tbslen); + int (*verify_init) (EVP_PKEY_CTX *ctx); + int (*verify) (EVP_PKEY_CTX *ctx, + const unsigned char *sig, size_t siglen, + const unsigned char *tbs, size_t tbslen); + int (*verify_recover_init) (EVP_PKEY_CTX *ctx); + int (*verify_recover) (EVP_PKEY_CTX *ctx, + unsigned char *rout, size_t *routlen, + const unsigned char *sig, size_t siglen); + int (*signctx_init) (EVP_PKEY_CTX *ctx, EVP_MD_CTX *mctx); + int (*signctx) (EVP_PKEY_CTX *ctx, unsigned char *sig, size_t *siglen, + EVP_MD_CTX *mctx); + int (*verifyctx_init) (EVP_PKEY_CTX *ctx, EVP_MD_CTX *mctx); + int (*verifyctx) (EVP_PKEY_CTX *ctx, const unsigned char *sig, int siglen, + EVP_MD_CTX *mctx); + int (*encrypt_init) (EVP_PKEY_CTX *ctx); + int (*encrypt) (EVP_PKEY_CTX *ctx, unsigned char *out, size_t *outlen, + const unsigned char *in, size_t inlen); + int (*decrypt_init) (EVP_PKEY_CTX *ctx); + int (*decrypt) (EVP_PKEY_CTX *ctx, unsigned char *out, size_t *outlen, + const unsigned char *in, size_t inlen); + int (*derive_init) (EVP_PKEY_CTX *ctx); + int (*derive) (EVP_PKEY_CTX *ctx, unsigned char *key, size_t *keylen); + int (*ctrl) (EVP_PKEY_CTX *ctx, int type, int p1, void *p2); + int (*ctrl_str) (EVP_PKEY_CTX *ctx, const char *type, const char *value); +}; /* EVP_PKEY_METHOD */ +#endif #if OPENSSL_VERSION_NUMBER < 0x10002000L || defined(LIBRESSL_VERSION_NUMBER) @@ -129,6 +235,45 @@ static void EVP_PKEY_meth_copy(EVP_PKEY_METHOD *dst, const EVP_PKEY_METHOD *src) #endif +#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 + + +/******************************************************************************/ +/* PKCS#11 cryptographic operations shared by ENGINE and provider code */ +/******************************************************************************/ + +/* + * Build PKCS#11 RSA-PSS parameters from the OpenSSL signing context. + * + * Resolve the signature and MGF1 digests, normalize OpenSSL salt-length + * values, and fill CK_RSA_PKCS_PSS_PARAMS for the PKCS#11 mechanism. + */ static int pkcs11_params_pss(CK_RSA_PKCS_PSS_PARAMS *pss_params, EVP_PKEY *pkey, int salt_len, const char *mdname, const char *mgf1_mdname, PKCS11_CTX_private *pctx) @@ -193,6 +338,12 @@ static int pkcs11_params_pss(CK_RSA_PKCS_PSS_PARAMS *pss_params, EVP_PKEY *pkey, return 0; } +/* + * Build PKCS#11 RSA-OAEP parameters from the OpenSSL decryption context. + * + * Resolve the OAEP and MGF1 digests, apply the default digest if needed, + * and fill CK_RSA_PKCS_OAEP_PARAMS including the optional OAEP label. + */ static int pkcs11_oaep_param(CK_RSA_PKCS_OAEP_PARAMS *oaep_params, const char *oaep_mdname, const char *mgf1_mdname, unsigned char *oaep_label, size_t oaep_labellen, @@ -208,7 +359,7 @@ static int pkcs11_oaep_param(CK_RSA_PKCS_OAEP_PARAMS *oaep_params, if (oaep_md == NULL) return -1; - /* mgf1 default = signature digest */ + /* mgf1 default = OAEP digest */ if (mgf1_mdname == NULL) mgf1_mdname = oaep_mdname; @@ -358,12 +509,12 @@ const char *pkcs11_mechanism_name(CK_MECHANISM *mechanism) * * Returns: CKR_OK on success or PKCS#11 error code on failure */ -static int pkcs11_sign_with_mechanism(PKCS11_OBJECT_private *key, +static CK_RV pkcs11_sign_with_mechanism(PKCS11_OBJECT_private *key, CK_MECHANISM *mechanism, unsigned char *sig, size_t *siglen, const unsigned char *tbs, size_t tbslen) { - int rv = CKR_GENERAL_ERROR; + CK_RV rv = CKR_GENERAL_ERROR; PKCS11_SLOT_private *slot; PKCS11_CTX_private *ctx; CK_SESSION_HANDLE session; @@ -373,6 +524,9 @@ static int pkcs11_sign_with_mechanism(PKCS11_OBJECT_private *key, if (key == NULL || mechanism == NULL || siglen == NULL || tbs == NULL) return CKR_ARGUMENTS_BAD; + if (*siglen > (size_t)(CK_ULONG)-1 || tbslen > (size_t)(CK_ULONG)-1) + return CKR_ARGUMENTS_BAD; + slot = key->slot; if (slot == NULL) return CKR_GENERAL_ERROR; @@ -413,14 +567,14 @@ static int pkcs11_sign_with_mechanism(PKCS11_OBJECT_private *key, rv = CRYPTOKI_call(ctx, C_Encrypt(session, (CK_BYTE_PTR)tbs, ck_tbslen, sig, &ck_siglen)); if (rv != CKR_OK) { - pkcs11_log(ctx, LOG_DEBUG, "%s:%d C_Encrypt rv=%d\n", - __FILE__, __LINE__, rv); + pkcs11_log(ctx, LOG_DEBUG, "%s:%d C_Encrypt rv=0x%08lX (%lu)\n", + __FILE__, __LINE__, (unsigned long)rv, (unsigned long)rv); goto end; } } if (rv != CKR_OK) { - pkcs11_log(ctx, LOG_DEBUG, "%s:%d C_Sign rv=%d\n", - __FILE__, __LINE__, rv); + pkcs11_log(ctx, LOG_DEBUG, "%s:%d C_Sign rv=0x%08lX (%lu)\n", + __FILE__, __LINE__, (unsigned long)rv, (unsigned long)rv); goto end; } @@ -434,15 +588,14 @@ static int pkcs11_sign_with_mechanism(PKCS11_OBJECT_private *key, #if OPENSSL_VERSION_NUMBER >= 0x30000000L /* * Execute a PKCS#11 verify operation using the specified mechanism. - * * Returns: CKR_OK on success or PKCS#11 error code on failure */ -static int pkcs11_verify_with_mechanism(PKCS11_OBJECT_private *key, +static CK_RV pkcs11_verify_with_mechanism(PKCS11_OBJECT_private *key, CK_MECHANISM *mechanism, const unsigned char *sig, size_t siglen, const unsigned char *tbs, size_t tbslen) { - int rv = CKR_GENERAL_ERROR; + CK_RV rv = CKR_GENERAL_ERROR; PKCS11_SLOT_private *slot; PKCS11_CTX_private *ctx; CK_SESSION_HANDLE session; @@ -452,6 +605,9 @@ static int pkcs11_verify_with_mechanism(PKCS11_OBJECT_private *key, if (key == NULL || mechanism == NULL || sig == NULL || tbs == NULL) return CKR_ARGUMENTS_BAD; + if (siglen > (size_t)(CK_ULONG)-1 || tbslen > (size_t)(CK_ULONG)-1) + return CKR_ARGUMENTS_BAD; + slot = key->slot; if (slot == NULL) return CKR_GENERAL_ERROR; @@ -476,8 +632,8 @@ static int pkcs11_verify_with_mechanism(PKCS11_OBJECT_private *key, rv = CRYPTOKI_call(ctx, C_VerifyInit(session, mechanism, key->object)); if (rv != CKR_OK) { - pkcs11_log(ctx, LOG_DEBUG, "%s:%d C_VerifyInit rv=%d\n", - __FILE__, __LINE__, rv); + pkcs11_log(ctx, LOG_DEBUG, "%s:%d C_VerifyInit rv=0x%08lX (%lu)\n", + __FILE__, __LINE__, (unsigned long)rv, (unsigned long)rv); goto end; } @@ -486,8 +642,8 @@ static int pkcs11_verify_with_mechanism(PKCS11_OBJECT_private *key, (CK_BYTE_PTR)tbs, ck_tbslen, (CK_BYTE_PTR)sig, ck_siglen)); if (rv != CKR_OK) { - pkcs11_log(ctx, LOG_DEBUG, "%s:%d C_Verify rv=%d\n", - __FILE__, __LINE__, rv); + pkcs11_log(ctx, LOG_DEBUG, "%s:%d C_Verify rv=0x%08lX (%lu)\n", + __FILE__, __LINE__, (unsigned long)rv, (unsigned long)rv); goto end; } @@ -501,12 +657,12 @@ static int pkcs11_verify_with_mechanism(PKCS11_OBJECT_private *key, * Execute a PKCS#11 decryption operation using the specified mechanism. * Returns: CKR_OK on success or PKCS#11 error code on failure. */ -static int pkcs11_decrypt_with_mechanism(PKCS11_OBJECT_private *key, +static CK_RV pkcs11_decrypt_with_mechanism(PKCS11_OBJECT_private *key, CK_MECHANISM *mechanism, unsigned char *out, size_t *outlen, const unsigned char *in, size_t inlen) { - int rv = CKR_GENERAL_ERROR; + CK_RV rv = CKR_GENERAL_ERROR; PKCS11_SLOT_private *slot; PKCS11_CTX_private *ctx; CK_SESSION_HANDLE session; @@ -516,6 +672,9 @@ static int pkcs11_decrypt_with_mechanism(PKCS11_OBJECT_private *key, if (key == NULL || mechanism == NULL || outlen == NULL || 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; @@ -539,8 +698,8 @@ static int pkcs11_decrypt_with_mechanism(PKCS11_OBJECT_private *key, rv = CRYPTOKI_call(ctx, C_DecryptInit(session, mechanism, key->object)); if (rv != CKR_OK) { - pkcs11_log(ctx, LOG_DEBUG, "%s:%d C_DecryptInit rv=%d\n", - __FILE__, __LINE__, rv); + pkcs11_log(ctx, LOG_DEBUG, "%s:%d C_DecryptInit rv=0x%08lX (%lu)\n", + __FILE__, __LINE__, (unsigned long)rv, (unsigned long)rv); goto end; } @@ -553,8 +712,8 @@ static int pkcs11_decrypt_with_mechanism(PKCS11_OBJECT_private *key, rv = CRYPTOKI_call(ctx, C_Decrypt(session, (CK_BYTE_PTR)in, ck_inlen, out, &ck_outlen)); if (rv != CKR_OK) { - pkcs11_log(ctx, LOG_DEBUG, "%s:%d C_Decrypt rv=%d\n", - __FILE__, __LINE__, rv); + pkcs11_log(ctx, LOG_DEBUG, "%s:%d C_Decrypt rv=0x%08lX (%lu)\n", + __FILE__, __LINE__, (unsigned long)rv, (unsigned long)rv); goto end; } @@ -565,7 +724,8 @@ static int pkcs11_decrypt_with_mechanism(PKCS11_OBJECT_private *key, return rv; } -#ifndef OPENSSL_NO_EC +#if !defined(OPENSSL_NO_EC) || \ + (!defined(OPENSSL_NO_ECX) && OPENSSL_VERSION_NUMBER >= 0x30000000L) /* * Execute a PKCS#11 derive operation using the specified mechanism. * Returns: CKR_OK on success or PKCS#11/vendor defined error code on failure. @@ -592,7 +752,6 @@ static CK_RV pkcs11_derive_with_mechanism(PKCS11_OBJECT_private *key, {CKA_VALUE_LEN, &newkey_len, sizeof(newkey_len)}, {CKA_SENSITIVE, &ck_false, sizeof(ck_false)}, {CKA_EXTRACTABLE, &ck_true, sizeof(ck_true)}, - {CKA_DERIVE, &ck_true, sizeof(ck_true)}, }; if (key == NULL || mechanism == NULL || secret == NULL || @@ -662,6 +821,8 @@ static CK_RV pkcs11_derive_with_mechanism(PKCS11_OBJECT_private *key, pkcs11_session_pool_release(slot, session); return rv; } +#endif /* #if !defined(OPENSSL_NO_EC) || \ + (!defined(OPENSSL_NO_ECX) && OPENSSL_VERSION_NUMBER >= 0x30000000L) */ #if OPENSSL_VERSION_NUMBER >= 0x30000000L /* @@ -790,6 +951,7 @@ static CK_RV pkcs11_decapsulate_with_mechanism( } #endif /* OPENSSL_VERSION_NUMBER >= 0x30000000L */ +#ifndef OPENSSL_NO_EC /* 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) @@ -1253,7 +1415,7 @@ int pkcs11_evp_pkey_rsa_decrypt(PKCS11_OBJECT_private *key, PKCS11_SLOT_private *slot; PKCS11_CTX_private *ctx; CK_RSA_PKCS_OAEP_PARAMS oaep_params; - int rv; + CK_RV rv; if (key == NULL || outlen == NULL || in == NULL) return -1; @@ -1443,9 +1605,191 @@ extern int pkcs11_evp_pkey_ml_kem_decapsulate(PKCS11_OBJECT_private *key, } #endif /* !defined(OPENSSL_NO_ML_KEM) && OPENSSL_VERSION_NUMBER >= 0x30500000L */ + +/******************************************************************************/ +/* Legacy ENGINE EVP_PKEY_METHOD callbacks and registration */ +/******************************************************************************/ + #if OPENSSL_VERSION_NUMBER < 0x40000000L + +/* --- RSA ENGINE method callbacks ------------------------------------------ */ + +/* 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; + const EVP_MD *md, *mgf1_md; + 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; + + /* 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; + 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; + + 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) +{ + P11_PKEY_METHOD *state; + int ret; + + state = pkey_method_by_type(EVP_PKEY_RSA); + if (state == NULL || state->kind != P11_PKEY_RSA) + return 0; + + ret = pkcs11_try_pkey_rsa_sign(evp_pkey_ctx, sig, siglen, tbs, tbslen); + if (ret < 0 && state->original_sign != NULL) + ret = state->original_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) +{ + P11_PKEY_METHOD *state; + int ret; + + state = pkey_method_by_type(EVP_PKEY_RSA); + if (state == NULL || state->kind != P11_PKEY_RSA) + return 0; + + ret = pkcs11_try_pkey_rsa_decrypt(evp_pkey_ctx, out, outlen, in, inlen); + if (ret < 0 && state->original_decrypt != NULL) + ret = state->original_decrypt(evp_pkey_ctx, out, outlen, in, inlen); + return ret; +} + #ifndef OPENSSL_NO_EC + +/* --- EC ENGINE method callbacks ------------------------------------------- */ + +/* Attempt to sign using the PKCS#11-backed EC implementation */ static int pkcs11_try_pkey_ec_sign(EVP_PKEY_CTX *evp_pkey_ctx, unsigned char *sig, size_t *siglen, const unsigned char *tbs, size_t tbslen) @@ -1453,17 +1797,16 @@ static int pkcs11_try_pkey_ec_sign(EVP_PKEY_CTX *evp_pkey_ctx, EVP_PKEY *pkey; EC_KEY *eckey; PKCS11_OBJECT_private *key; - PKCS11_SLOT_private *slot; - CK_SESSION_HANDLE session; - const EVP_MD *sig_md; + const EVP_MD *sig_md = NULL; - if (!evp_pkey_ctx) + if (!evp_pkey_ctx || !siglen) return -1; if (EVP_PKEY_CTX_get_signature_md(evp_pkey_ctx, &sig_md) <= 0) return -1; - if (tbslen < (size_t)EVP_MD_size(sig_md)) + if (sig_md != NULL && EVP_MD_size(sig_md) > 0 && + tbslen < (size_t)EVP_MD_size(sig_md)) return -1; pkey = EVP_PKEY_CTX_get0_pkey(evp_pkey_ctx); @@ -1483,254 +1826,435 @@ 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 (check_object_fork(key) < 0) - return -1; - - slot = key->slot; - if (!slot) + if (!key) return -1; - if (pkcs11_session_pool_acquire(slot, 0, &session)) + if (check_object_fork(key) < 0) return -1; - pkcs11_session_pool_release(slot, session); - return pkcs11_evp_pkey_ec_sign(key, sig, siglen, tbs, tbslen); } -#endif /* OPENSSL_NO_EC */ -#if !defined(OPENSSL_NO_ECX) && OPENSSL_VERSION_NUMBER >= 0x30000000L -/* PKCS#11 sign implementation for Ed25519 / Ed448 */ -static int pkcs11_eddsa_sign(unsigned char *sig, size_t *siglen, - const unsigned char *tbs, size_t tbslen, PKCS11_OBJECT_private *key) +static int pkcs11_pkey_ec_sign(EVP_PKEY_CTX *evp_pkey_ctx, + unsigned char *sig, size_t *siglen, + const unsigned char *tbs, size_t tbslen) { - PKCS11_SLOT_private *slot; - CK_SESSION_HANDLE session; + P11_PKEY_METHOD *state; + int ret; - slot = key->slot; - if (!slot) - return -1; + state = pkey_method_by_type(EVP_PKEY_EC); + if (state == NULL || state->kind != P11_PKEY_EC) + return 0; - if (pkcs11_session_pool_acquire(slot, 0, &session)) - return -1; + ret = pkcs11_try_pkey_ec_sign(evp_pkey_ctx, sig, siglen, tbs, tbslen); + if (ret < 0 && state->original_sign != NULL) + ret = state->original_sign(evp_pkey_ctx, sig, siglen, tbs, tbslen); + return ret; +} +#endif /* OPENSSL_NO_EC */ - pkcs11_session_pool_release(slot, session); +#ifdef LIBP11_HAVE_ECX_METHODS - return pkcs11_evp_pkey_eddsa_sign(key, sig, siglen, tbs, tbslen); -} + +/* --- ECX ENGINE method callbacks ------------------------------------------ */ /* - * 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_eddsa_sign(). + * Try Ed25519/Ed448 signing through PKCS#11. + * + * Return -1 only when the key should be handled by the original OpenSSL + * method. Once a PKCS#11 private key has been recognized, operational + * failures return 0 and must not fall back to the original method. */ -static int pkcs11_eddsa_pmeth_sign(EVP_PKEY_CTX *evp_pkey_ctx, - unsigned char *sig, size_t *siglen, - const unsigned char *tbs, size_t tbslen) +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; - int rv; + size_t required; - if (!evp_pkey_ctx) - return -1; + if (ctx == NULL) + return 0; - if (*siglen > UINT_MAX) + pkey = EVP_PKEY_CTX_get0_pkey(ctx); + if (pkey == NULL) return 0; - pkey = EVP_PKEY_CTX_get0_pkey(evp_pkey_ctx); - if (!pkey) + key = pkcs11_get_ex_data_object(pkey); + if (key == NULL) return -1; - key = pkcs11_get_ex_data_pkey(pkey); - if (!key) + if (key->object_class != CKO_PRIVATE_KEY) return -1; - if (check_object_fork(key) < 0) - return -1; + if (key->slot == NULL || key->slot->ctx == NULL) + return 0; - rv = pkcs11_eddsa_sign(sig, siglen, tbs, tbslen, key); - if (rv < 0) + if ((key->slot->ctx->flags & PKCS11_FLAG_NO_METHODS) != 0) return -1; - 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; +} + +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); } /* - * 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). + * Try X25519/X448 derive through PKCS#11. * - * If the key is managed by PKCS#11, the signing is performed via pkcs11_eddsa_sign(). - * Otherwise, the call is delegated to the original OpenSSL Ed25519/Ed448 implementation. + * The peer is validated before the length query so an incomplete derive + * context is not reported as usable. The raw peer public key passed to + * CKM_ECDH1_DERIVE is the RFC 7748 representation. */ -static int pkcs11_eddsa_pmeth_digestsign(EVP_MD_CTX *ctx, unsigned char *sig, - size_t *siglen, const unsigned char *tbs, size_t tbslen) +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; - int rv; + unsigned char peer_public[X448_KEY_LEN]; + size_t peer_public_len; + size_t required; + int type; - pkey = EVP_PKEY_CTX_get0_pkey(EVP_MD_CTX_pkey_ctx(ctx)); - if (!pkey) + 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; - key = pkcs11_get_ex_data_pkey(pkey); - if (!key) + if (key->object_class != CKO_PRIVATE_KEY) return -1; - /* 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; + 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; + + type = EVP_PKEY_get_id(pkey); + switch (type) { + case EVP_PKEY_X25519: + required = X25519_KEY_LEN; + break; + case EVP_PKEY_X448: + required = X448_KEY_LEN; + break; + default: + return 0; } - /* Step 2: actual signing */ - rv = pkcs11_eddsa_sign(sig, siglen, tbs, tbslen, key); - if (rv < 0) + peerkey = EVP_PKEY_CTX_get0_peerkey(ctx); + if (peerkey == NULL || EVP_PKEY_get_id(peerkey) != type) + return 0; + + if (secret == NULL) { + *secretlen = required; return 1; + } - return 1; + if (*secretlen < required) { + *secretlen = required; + return 0; + } + + peer_public_len = 0; + if (EVP_PKEY_get_raw_public_key(peerkey, NULL, + &peer_public_len) != 1 || peer_public_len != required) + return 0; + + if (EVP_PKEY_get_raw_public_key(peerkey, + peer_public, &peer_public_len) != 1 || + peer_public_len != required) + return 0; + + if (pkcs11_evp_pkey_xdh_derive(key, peer_public, + peer_public_len, secret, secretlen) <= 0) + return 0; + + /* RFC 7748 derives have a fixed-width output. */ + return *secretlen == required ? 1 : 0; } -static int pkcs11_pkey_ed25519_digestsign(EVP_MD_CTX *ctx, - unsigned char *sig, size_t *siglen, - const unsigned char *tbs, size_t tbslen) +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_PKEY_METHOD *state = NULL; int ret; - ret = pkcs11_eddsa_pmeth_digestsign(ctx, sig, siglen, tbs, tbslen); - if (ret < 0) - ret = (*orig_pkey_ed25519_digestsign)(ctx, sig, siglen, tbs, tbslen); + if (ctx != NULL) + pkey = EVP_PKEY_CTX_get0_pkey(ctx); + if (pkey != NULL) + state = pkey_method_by_type(EVP_PKEY_get_id(pkey)); + + if (state == NULL || state->kind != P11_PKEY_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; } -static int pkcs11_pkey_ed448_digestsign(EVP_MD_CTX *ctx, - unsigned char *sig, size_t *siglen, - const unsigned char *tbs, size_t tbslen) +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_PKEY_METHOD *state = NULL; int ret; - ret = pkcs11_eddsa_pmeth_digestsign(ctx, sig, siglen, tbs, tbslen); - if (ret < 0) - ret = (*orig_pkey_ed448_digestsign)(ctx, sig, siglen, tbs, tbslen); - return 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 = pkey_method_by_type(EVP_PKEY_get_id(pkey)); -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 */ - } + if (state == NULL || state->kind != P11_PKEY_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; } -#endif /* !defined(OPENSSL_NO_ECX) && OPENSSL_VERSION_NUMBER >= 0x30000000L */ -#ifndef OPENSSL_NO_EC -static int pkcs11_pkey_ec_sign(EVP_PKEY_CTX *evp_pkey_ctx, - unsigned char *sig, size_t *siglen, - const unsigned char *tbs, size_t tbslen) +static int pkcs11_ecx_derive(EVP_PKEY_CTX *ctx, + unsigned char *secret, size_t *secretlen) { + EVP_PKEY *pkey = NULL; + P11_PKEY_METHOD *state = NULL; int ret; - 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); + if (ctx != NULL) + pkey = EVP_PKEY_CTX_get0_pkey(ctx); + if (pkey != NULL) + state = pkey_method_by_type(EVP_PKEY_get_id(pkey)); + + if (state == NULL || state->kind != P11_PKEY_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; } -static EVP_PKEY_METHOD *pkcs11_pkey_method_ec(void) +#endif /* LIBP11_HAVE_ECX_METHODS */ + + +/* --- ENGINE method construction and registration ------------------------- */ + +/* Reset cached ENGINE method and original OpenSSL callbacks. */ +static void pkey_method_reset(P11_PKEY_METHOD *state) { - EVP_PKEY_METHOD *new_meth; + state->method = NULL; + state->original_init = NULL; + state->original_sign = NULL; + state->original_decrypt_init = NULL; + state->original_decrypt = NULL; + state->original_digestsign = NULL; + state->original_derive = NULL; +} + +/* + * Build an ENGINE-scoped EVP_PKEY_METHOD by preserving the original OpenSSL + * method, flags and callbacks and overriding only the callbacks implemented + * by PKCS#11. + */ +static EVP_PKEY_METHOD *pkcs11_pkey_method(int type) +{ + P11_PKEY_METHOD *state; +#if OPENSSL_VERSION_NUMBER < 0x10101000L || defined(LIBRESSL_VERSION_NUMBER) + EVP_PKEY_METHOD *original_meth; +#else + const EVP_PKEY_METHOD *original_meth; +#endif /* OPENSSL_VERSION_NUMBER < 0x10101000L || defined(LIBRESSL_VERSION_NUMBER) */ + int original_type; + int original_flags; + + state = pkey_method_by_type(type); + if (state == NULL) + return NULL; + + /* Already initialized */ + if (state->method != NULL) + return state->method; + + /* + * PKEY methods are wrappers around the original OpenSSL method. + * Preserve all callbacks that are not explicitly overridden below. + */ #if OPENSSL_VERSION_NUMBER < 0x10101000L || defined(LIBRESSL_VERSION_NUMBER) - EVP_PKEY_METHOD *orig_meth = (EVP_PKEY_METHOD *)EVP_PKEY_meth_find(EVP_PKEY_EC); + original_meth = (EVP_PKEY_METHOD *)EVP_PKEY_meth_find(state->type); #else - const EVP_PKEY_METHOD *orig_meth = EVP_PKEY_meth_find(EVP_PKEY_EC); + original_meth = EVP_PKEY_meth_find(state->type); #endif /* OPENSSL_VERSION_NUMBER < 0x10101000L || defined(LIBRESSL_VERSION_NUMBER) */ + if (original_meth == NULL) + return NULL; + + EVP_PKEY_meth_get0_info(&original_type, &original_flags, original_meth); + if (original_type != state->type) + return NULL; + +#ifdef LIBP11_HAVE_ECX_METHODS + /* + * Ed25519/Ed448 methods handle all signature operations and must + * not rely on generic digest-related defaults. + */ + if (state->kind == P11_PKEY_EDDSA && + !(original_flags & EVP_PKEY_FLAG_SIGCTX_CUSTOM)) + return NULL; +#endif /* LIBP11_HAVE_ECX_METHODS */ - EVP_PKEY_meth_get_sign(orig_meth, - &orig_pkey_ec_sign_init, &orig_pkey_ec_sign); + state->method = EVP_PKEY_meth_new(state->type, original_flags); + if (state->method == NULL) + return NULL; - new_meth = EVP_PKEY_meth_new(EVP_PKEY_EC, 0); + /* + * Start with the complete OpenSSL implementation so that ctrl, + * keygen, cleanup, peer-key handling, etc. remain unchanged. + */ + EVP_PKEY_meth_copy(state->method, original_meth); - EVP_PKEY_meth_copy(new_meth, orig_meth); + switch (state->kind) { + case P11_PKEY_RSA: + EVP_PKEY_meth_get_sign(original_meth, &state->original_init, + &state->original_sign); - EVP_PKEY_meth_set_sign(new_meth, - orig_pkey_ec_sign_init, pkcs11_pkey_ec_sign); + if (state->original_sign == NULL) + goto error; - return new_meth; -} + EVP_PKEY_meth_get_decrypt(original_meth, + &state->original_decrypt_init, &state->original_decrypt); + + if (state->original_decrypt == NULL) + goto error; + + EVP_PKEY_meth_set_sign(state->method, state->original_init, + pkcs11_pkey_rsa_sign); + EVP_PKEY_meth_set_decrypt(state->method, state->original_decrypt_init, + pkcs11_pkey_rsa_decrypt); + break; + +#ifndef OPENSSL_NO_EC + case P11_PKEY_EC: + EVP_PKEY_meth_get_sign(original_meth, &state->original_init, + &state->original_sign); + + if (state->original_sign == NULL) + goto error; + + EVP_PKEY_meth_set_sign(state->method, state->original_init, + pkcs11_pkey_ec_sign); + break; #endif /* OPENSSL_NO_EC */ -#if !defined(OPENSSL_NO_ECX) && OPENSSL_VERSION_NUMBER >= 0x30000000L -static EVP_PKEY_METHOD *pkcs11_pkey_method_ed25519(void) -{ - EVP_PKEY_METHOD *new_meth; -#if OPENSSL_VERSION_NUMBER < 0x10101000L || defined(LIBRESSL_VERSION_NUMBER) - EVP_PKEY_METHOD *orig_meth = (EVP_PKEY_METHOD *)EVP_PKEY_meth_find(EVP_PKEY_ED25519); -#else - const EVP_PKEY_METHOD *orig_meth = EVP_PKEY_meth_find(EVP_PKEY_ED25519); -#endif /* OPENSSL_VERSION_NUMBER < 0x10101000L || defined(LIBRESSL_VERSION_NUMBER) */ +#ifdef LIBP11_HAVE_ECX_METHODS + case P11_PKEY_EDDSA: + /* + * EVP_PKEY_sign() is useful for the ENGINE interface even though + * the native OpenSSL EdDSA implementation primarily uses the + * one-shot digestsign callback. + */ + EVP_PKEY_meth_get_sign(original_meth, &state->original_init, + &state->original_sign); - /* The digestsign() method is used to generate a signature in a one-shot mode */ - EVP_PKEY_meth_get_digestsign(orig_meth, &orig_pkey_ed25519_digestsign); - - /* Don't assume any digest related defaults */ - new_meth = EVP_PKEY_meth_new(EVP_PKEY_ED25519, EVP_PKEY_FLAG_SIGCTX_CUSTOM); + EVP_PKEY_meth_get_digestsign(original_meth, &state->original_digestsign); - /* Duplicate the original method */ - EVP_PKEY_meth_copy(new_meth, orig_meth); + if (state->original_digestsign == NULL) + goto error; - /* Override selected ED25519 method callbacks with PKCS#11 implementations */ - EVP_PKEY_meth_set_sign(new_meth, NULL, pkcs11_eddsa_pmeth_sign); - EVP_PKEY_meth_set_digestsign(new_meth, pkcs11_pkey_ed25519_digestsign); - EVP_PKEY_meth_set_ctrl(new_meth, pkcs11_eddsa_pmeth_ctrl, NULL); + EVP_PKEY_meth_set_sign(state->method, state->original_init, + pkcs11_ecx_sign); - return new_meth; -} + EVP_PKEY_meth_set_digestsign(state->method, pkcs11_ecx_digestsign); + break; -static EVP_PKEY_METHOD *pkcs11_pkey_method_ed448(void) -{ - EVP_PKEY_METHOD *new_meth; -#if OPENSSL_VERSION_NUMBER < 0x10101000L || defined(LIBRESSL_VERSION_NUMBER) - EVP_PKEY_METHOD *orig_meth = (EVP_PKEY_METHOD *)EVP_PKEY_meth_find(EVP_PKEY_ED448); -#else - const EVP_PKEY_METHOD *orig_meth = EVP_PKEY_meth_find(EVP_PKEY_ED448); -#endif /* OPENSSL_VERSION_NUMBER < 0x10101000L || defined(LIBRESSL_VERSION_NUMBER) */ + case P11_PKEY_XDH: + EVP_PKEY_meth_get_derive(original_meth, &state->original_init, + &state->original_derive); - /* The digestsign() method is used to generate a signature in a one-shot mode */ - EVP_PKEY_meth_get_digestsign(orig_meth, &orig_pkey_ed448_digestsign); + if (state->original_derive == NULL) + goto error; - /* Don't assume any digest related defaults */ - new_meth = EVP_PKEY_meth_new(EVP_PKEY_ED448, EVP_PKEY_FLAG_SIGCTX_CUSTOM); + EVP_PKEY_meth_set_derive(state->method, state->original_init, + pkcs11_ecx_derive); + break; +#endif /* LIBP11_HAVE_ECX_METHODS */ - /* Duplicate the original method */ - EVP_PKEY_meth_copy(new_meth, orig_meth); + default: + goto error; + } - /* Override selected ED448 method callbacks with PKCS#11 implementations */ - EVP_PKEY_meth_set_sign(new_meth, NULL, pkcs11_eddsa_pmeth_sign); - EVP_PKEY_meth_set_digestsign(new_meth, pkcs11_pkey_ed448_digestsign); - EVP_PKEY_meth_set_ctrl(new_meth, pkcs11_eddsa_pmeth_ctrl, NULL); + return state->method; - return new_meth; +error: + EVP_PKEY_meth_free(state->method); + pkey_method_reset(state); + return NULL; } -#endif /* !defined(OPENSSL_NO_ECX) && OPENSSL_VERSION_NUMBER >= 0x30000000L */ +/* + * Return the ENGINE-supported EVP_PKEY_METHOD for the requested key type. + */ int PKCS11_pkey_meths(ENGINE *e, EVP_PKEY_METHOD **pmeth, const int **nids, int nid) { @@ -1739,65 +2263,42 @@ int PKCS11_pkey_meths(ENGINE *e, EVP_PKEY_METHOD **pmeth, #ifndef OPENSSL_NO_EC EVP_PKEY_EC, #endif /* OPENSSL_NO_EC */ -#if !defined(OPENSSL_NO_ECX) && OPENSSL_VERSION_NUMBER >= 0x30000000L +#ifdef LIBP11_HAVE_ECX_METHODS EVP_PKEY_ED25519, EVP_PKEY_ED448, -#endif /* !defined(OPENSSL_NO_ECX) && OPENSSL_VERSION_NUMBER >= 0x30000000L */ + EVP_PKEY_X25519, + EVP_PKEY_X448, +#endif /* LIBP11_HAVE_ECX_METHODS */ 0 }; - static EVP_PKEY_METHOD *pkey_method_rsa = NULL; -#ifndef OPENSSL_NO_EC - static EVP_PKEY_METHOD *pkey_method_ec = NULL; -#endif /* OPENSSL_NO_EC */ -#if !defined(OPENSSL_NO_ECX) && OPENSSL_VERSION_NUMBER >= 0x30000000L - static EVP_PKEY_METHOD *pkey_method_ed448 = NULL; - static EVP_PKEY_METHOD *pkey_method_ed25519 = NULL; -#endif /* !defined(OPENSSL_NO_ECX) && OPENSSL_VERSION_NUMBER >= 0x30000000L */ - (void)e; /* squash the unused parameter warning */ /* all PKCS#11 engines currently share the same pkey_meths */ if (!pmeth) { /* get the list of supported nids */ + if (nids == NULL) + return 0; *nids = pkey_nids; return sizeof(pkey_nids) / sizeof(int) - 1; } + *pmeth = NULL; + /* get the EVP_PKEY_METHOD */ switch (nid) { case EVP_PKEY_RSA: - if (!pkey_method_rsa) - pkey_method_rsa = pkcs11_pkey_method_rsa(); - if (!pkey_method_rsa) - return 0; - *pmeth = pkey_method_rsa; - return 1; /* success */ #ifndef OPENSSL_NO_EC case EVP_PKEY_EC: - if (!pkey_method_ec) - pkey_method_ec = pkcs11_pkey_method_ec(); - if (!pkey_method_ec) - return 0; - *pmeth = pkey_method_ec; - return 1; /* success */ #endif /* OPENSSL_NO_EC */ -#if !defined(OPENSSL_NO_ECX) && OPENSSL_VERSION_NUMBER >= 0x30000000L - case EVP_PKEY_ED448: - if (!pkey_method_ed448) - pkey_method_ed448 = pkcs11_pkey_method_ed448(); - if (!pkey_method_ed448) - return 0; - *pmeth = pkey_method_ed448; - return 1; /* success */ +#ifdef LIBP11_HAVE_ECX_METHODS case EVP_PKEY_ED25519: - if (!pkey_method_ed25519) - pkey_method_ed25519 = pkcs11_pkey_method_ed25519(); - if (!pkey_method_ed25519) - return 0; - *pmeth = pkey_method_ed25519; - return 1; /* success */ -#endif /* !defined(OPENSSL_NO_ECX) && OPENSSL_VERSION_NUMBER >= 0x30000000L */ + case EVP_PKEY_ED448: + case EVP_PKEY_X25519: + case EVP_PKEY_X448: +#endif /* LIBP11_HAVE_ECX_METHODS */ + *pmeth = pkcs11_pkey_method(nid); + return *pmeth != NULL; } - *pmeth = NULL; + return 0; } diff --git a/src/p11_rawkey.c b/src/p11_rawkey.c new file mode 100644 index 00000000..2d162076 --- /dev/null +++ b/src/p11_rawkey.c @@ -0,0 +1,493 @@ +/* + * 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); + + 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_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/Makefile.am b/tests/Makefile.am index 855aca77..0d1b0ffc 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -22,6 +22,7 @@ check_PROGRAMS = \ rsa-oaep \ rsa-oaep-prov \ rsa-software-key \ + ed25519-software-key \ check-privkey \ check-privkey-prov \ dup-key \ @@ -60,6 +61,7 @@ dist_check_SCRIPTS = \ ec-derive.softhsm \ session-pool-test.sh \ session-pool-stress.softhsm \ + ed25519-software-key.softhsm \ ed25519-keygen.softhsm \ ed448-keygen.softhsm \ fork-change-slot.softhsm \ diff --git a/tests/common.sh b/tests/common.sh index 773e903b..23b19a73 100755 --- a/tests/common.sh +++ b/tests/common.sh @@ -234,6 +234,21 @@ generate_rsa_key_pair () { fi } +# Generate an Ed25519 key pair +generate_ed25519_key_pair () { + local obj_label="$1" + local token_label="$2" + local obj_id="${3:-${ID}}" + + echo "* Generating an Ed25519 key pair on the token ${token_label}" + pkcs11-tool --login --pin ${PIN} --module ${MODULE} --id ${obj_id} \ + --keypairgen --key-type "EC:edwards25519" --usage-sign \ + --label ${obj_label} --token-label ${token_label} + if [[ $? -ne 0 ]]; then + exit 1 + fi +} + # Generate an EC key pair with derive usage on the prime256v1 curve generate_ec_derive_key_pair () { local obj_label="$1" diff --git a/tests/ed25519-keygen.c b/tests/ed25519-keygen.c index 6b718e9d..c2d6d1c5 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) && \ + !defined(OPENSSL_NO_DEPRECATED_3_0) && \ + (OPENSSL_VERSION_NUMBER >= 0x30000000L) && \ + (OPENSSL_VERSION_NUMBER < 0x40000000L) void display_openssl_errors(void) { diff --git a/tests/ed25519-software-key.c b/tests/ed25519-software-key.c new file mode 100644 index 00000000..3d8732be --- /dev/null +++ b/tests/ed25519-software-key.c @@ -0,0 +1,260 @@ +/* + * Copyright (C) 2026 OpenSC Project + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#define OPENSSL_SUPPRESS_DEPRECATED + +#include +#include + +#include +#include + +#if OPENSSL_VERSION_NUMBER >= 0x30000000L && \ + OPENSSL_VERSION_NUMBER < 0x40000000L && \ + !defined(OPENSSL_NO_DEPRECATED_3_0) && \ + !defined(OPENSSL_NO_ENGINE) && \ + !defined(OPENSSL_NO_ECX) + +#include + +#include +#include +#include +#include + +static EVP_PKEY *generate_software_key(void) +{ + EVP_PKEY_CTX *ctx; + EVP_PKEY *key = NULL; + + ctx = EVP_PKEY_CTX_new_from_name(NULL, "ED25519", NULL); + if (!ctx) + return NULL; + + if (EVP_PKEY_keygen_init(ctx) <= 0 || EVP_PKEY_generate(ctx, &key) <= 0) { + EVP_PKEY_free(key); + key = NULL; + } + EVP_PKEY_CTX_free(ctx); + return key; +} + +static int sign_message(EVP_PKEY *key) +{ + static const unsigned char message[] = "libp11 software Ed25519 test"; + EVP_MD_CTX *ctx = NULL; + unsigned char *sig = NULL; + size_t siglen = 0; + int ret = 0; + + ctx = EVP_MD_CTX_new(); + if (!ctx) + goto cleanup; + + if (EVP_DigestSignInit(ctx, NULL, NULL, NULL, key) <= 0 || + EVP_DigestSign(ctx, NULL, &siglen, + message, sizeof(message) - 1) <= 0) + goto cleanup; + + sig = OPENSSL_malloc(siglen); + if (!sig) + goto cleanup; + + if (EVP_DigestSign(ctx, sig, &siglen, message, sizeof(message) - 1) <= 0) + goto cleanup; + + EVP_MD_CTX_free(ctx); + ctx = EVP_MD_CTX_new(); + if (!ctx) + goto cleanup; + + if (EVP_DigestVerifyInit(ctx, NULL, NULL, NULL, key) <= 0 || + EVP_DigestVerify(ctx, sig, siglen, + message, sizeof(message) - 1) <= 0) + goto cleanup; + + ret = 1; + +cleanup: + OPENSSL_free(sig); + EVP_MD_CTX_free(ctx); + return ret; +} + +/* + * Attach the test ENGINE only to an EVP_PKEY returned by this libp11 + * context. This avoids process-wide EVP_PKEY_METHOD registration. + */ +static int set_pkey_engine(PKCS11_KEY *key, EVP_PKEY *pkey, void *user_data) +{ + ENGINE *engine = user_data; + + (void)key; + + if (!pkey || !engine) + return -1; + + return EVP_PKEY_set1_engine(pkey, engine) ? 0 : -1; +} + +int main(int argc, char **argv) +{ + ENGINE *engine = NULL; + PKCS11_CTX *ctx = NULL; + PKCS11_SLOT *slots = NULL, *slot; + PKCS11_KEY *keys; + EVP_PKEY *software_key = NULL; + EVP_PKEY *new_software_key = NULL; + EVP_PKEY *token_key = NULL; + unsigned int nslots = 0, nkeys = 0; + int ret = EXIT_FAILURE; + + if (argc != 3) { + fprintf(stderr, "usage: %s [module] [PIN]\n", argv[0]); + return EXIT_FAILURE; + } + + /* + * First make sure a normal provider-backed Ed25519 key works before + * any PKCS#11 key is loaded. + */ + software_key = generate_software_key(); + if (!software_key || + !EVP_PKEY_get0_provider(software_key) || + !sign_message(software_key)) { + fprintf(stderr, "Initial software Ed25519 operation failed\n"); + goto cleanup; + } + + ctx = PKCS11_CTX_new(); + if (!ctx) { + fprintf(stderr, "Failed to initialize PKCS#11\n"); + goto cleanup; + } + + /* + * Create a small test ENGINE exposing libp11's PKEY methods. + * It is not registered globally. The callback attaches it only to + * private keys returned by this PKCS11_CTX. + */ + engine = ENGINE_new(); + if (!engine || !ENGINE_set_id(engine, "libp11-test-pkey") || + !ENGINE_set_name(engine, "libp11 test PKEY methods") || + !ENGINE_set_pkey_meths(engine, PKCS11_pkey_meths)) { + fprintf(stderr, "Failed to initialize test PKEY ENGINE\n"); + goto cleanup; + } + + if (PKCS11_CTX_set_pkey_callback(ctx, + PKCS11_PKEY_CALLBACK_GET_PRIVATE_KEY, + set_pkey_engine, engine) < 0) { + fprintf(stderr, "Failed to initialize PKEY callback\n"); + goto cleanup; + } + + if (PKCS11_CTX_load(ctx, argv[1]) < 0 || + PKCS11_enumerate_slots(ctx, &slots, &nslots) < 0) { + fprintf(stderr, "Failed to initialize PKCS#11\n"); + goto cleanup; + } + + slot = PKCS11_find_token(ctx, slots, nslots); + if (!slot || PKCS11_login(slot, 0, argv[2]) < 0 || + PKCS11_enumerate_keys(slot->token, &keys, &nkeys) < 0 || + nkeys == 0) { + fprintf(stderr, "Failed to find a PKCS#11 private key\n"); + goto cleanup; + } + + /* + * PKCS11_get_private_key() invokes the callback registered above, + * so only this EVP_PKEY receives the test ENGINE. + */ + token_key = PKCS11_get_private_key(&keys[0]); + if (!token_key) { + fprintf(stderr, "PKCS11_get_private_key failed\n"); + goto cleanup; + } + + /* + * The ENGINE-scoped Ed25519 PKEY method should route signing through + * libp11 and ultimately CKM_EDDSA. + */ + if (!sign_message(token_key)) { + fprintf(stderr, "Ed25519 signing with the PKCS#11 key failed\n"); + goto cleanup; + } + + /* + * Loading and using a PKCS#11 key must not alter a provider-backed + * Ed25519 key created earlier, nor affect subsequently created keys. + */ + new_software_key = generate_software_key(); + + if (!EVP_PKEY_get0_provider(software_key) || + !sign_message(software_key) || + !new_software_key || + !EVP_PKEY_get0_provider(new_software_key) || + !sign_message(new_software_key)) { + fprintf(stderr, "PKCS#11 key affected an unrelated software Ed25519 key\n"); + goto cleanup; + } + + ret = EXIT_SUCCESS; + +cleanup: + if (ret != EXIT_SUCCESS) + ERR_print_errors_fp(stderr); + + EVP_PKEY_free(new_software_key); + EVP_PKEY_free(token_key); + EVP_PKEY_free(software_key); + + /* + * The callback keeps a borrowed pointer to engine, so unregister it + * before freeing the ENGINE. + */ + if (ctx) + PKCS11_CTX_set_pkey_callback(ctx, + PKCS11_PKEY_CALLBACK_GET_PRIVATE_KEY, + NULL, NULL); + + ENGINE_free(engine); + + if (slots) + PKCS11_release_all_slots(ctx, slots, nslots); + + if (ctx) { + PKCS11_CTX_unload(ctx); + PKCS11_CTX_free(ctx); + } + + return ret; +} + +#else + +int main(void) +{ + fprintf(stderr, + "Skipped: test requires Ed25519 and ENGINE with OpenSSL 3.x\n"); + return 77; +} + +#endif + +/* vim: set noexpandtab: */ diff --git a/tests/ed25519-software-key.softhsm b/tests/ed25519-software-key.softhsm new file mode 100755 index 00000000..78474b7a --- /dev/null +++ b/tests/ed25519-software-key.softhsm @@ -0,0 +1,65 @@ +#!/bin/bash + +# Copyright © 2026 Mobi - Com Polska Sp. z o.o. +# Author: Małgorzata Olszówka + +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +outdir=output.$$ + +# Load common test functions +. "${srcdir}/common.sh" + +# Initialize SoftHSM DB +init_db + +if ! has_mechanism EC-EDWARDS-KEY-PAIR-GEN || ! has_mechanism EDDSA; then + echo "Skipping test: EDDSA not supported" + rm -rf "$outdir" + exit 77 +fi + +# Create token +init_card "libp11-0" + +# Generate Ed25519 key pairs on the token +if ! generate_ed25519_key_pair "ed25519-key" "libp11-0" "01020304"; then + echo "Skipping test: cannot generate Ed25519 key pairs." + exit 77 +fi + +# Load openssl settings +. ${srcdir}/openssl-settings.sh + +# Restore openssl settings +trap cleanup EXIT + +# Test direct PKCS#1 signing and isolation between software and PKCS#11 keys +# through the ENGINE interface. +${WRAPPER} ./ed25519-software-key "${MODULE}" "${PIN}" +rc=$? +if [[ $rc -eq 77 ]]; then + echo "Direct libp11 ED25519 regression test skipped." + rm -rf "$outdir" + exit 77 +elif [[ $rc -ne 0 ]]; then + echo "Direct libp11 ED25519 regression test failed." + exit 1 +fi + +rm -rf "$outdir" + +exit 0 + +# vim: set noexpandtab: diff --git a/tests/ed448-keygen.c b/tests/ed448-keygen.c index 33a87a5f..4a7bd1f5 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) && \ + !defined(OPENSSL_NO_DEPRECATED_3_0) && \ + (OPENSSL_VERSION_NUMBER >= 0x30000000L) && \ + (OPENSSL_VERSION_NUMBER < 0x40000000L) void display_openssl_errors(void) {