From be4cda1d6dc7acf2c8303d3d9ff51cc732666ad0 Mon Sep 17 00:00:00 2001 From: olszomal Date: Mon, 20 Oct 2025 10:57:21 +0200 Subject: [PATCH 1/5] Add support for Ed25519 and Ed448 curves --- examples/Makefile.am | 2 +- examples/ed25519keygen.c | 177 +++++++++++++ src/Makefile.am | 2 +- src/libp11-int.h | 20 ++ src/libp11.h | 9 +- src/p11_ckr.c | 1 + src/p11_eddsa.c | 496 +++++++++++++++++++++++++++++++++++ src/p11_front.c | 38 ++- src/p11_key.c | 106 +++++++- src/p11_load.c | 2 + src/p11_pkey.c | 174 ++++++++++++ src/pkcs11.h | 12 + tests/Makefile.am | 5 +- tests/ed25519-keygen.c | 227 ++++++++++++++++ tests/ed25519-keygen.softhsm | 60 +++++ 15 files changed, 1320 insertions(+), 11 deletions(-) create mode 100644 examples/ed25519keygen.c create mode 100644 src/p11_eddsa.c create mode 100644 tests/ed25519-keygen.c create mode 100755 tests/ed25519-keygen.softhsm diff --git a/examples/Makefile.am b/examples/Makefile.am index 76eb017e..9962d289 100644 --- a/examples/Makefile.am +++ b/examples/Makefile.am @@ -4,7 +4,7 @@ AM_CPPFLAGS = -I$(srcdir) -I$(top_srcdir)/src \ EXTRA_DIST = README -noinst_PROGRAMS = auth decrypt getrandom listkeys listkeys_ext eckeygen rsakeygen storecert +noinst_PROGRAMS = auth decrypt getrandom listkeys listkeys_ext ed25519keygen eckeygen rsakeygen storecert LDADD = ../src/libp11.la $(OPENSSL_LIBS) diff --git a/examples/ed25519keygen.c b/examples/ed25519keygen.c new file mode 100644 index 00000000..8a5d7f98 --- /dev/null +++ b/examples/ed25519keygen.c @@ -0,0 +1,177 @@ +/* + * Copyright © 2025 Mobi - Com Polska Sp. z o.o. + * Author: Małgorzata Olszówka + * All rights reserved. + * + * Elliptic Curve key generation + * + * 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 +#include + +#define CHECK_ERR(cond, txt, code) \ + do { \ + if (cond) { \ + fprintf(stderr, "%s\n", (txt)); \ + rc=(code); \ + goto end; \ + } \ + } while (0) + +static void error_queue(const char *name) +{ + if (ERR_peek_last_error()) { + fprintf(stderr, "%s generated errors:\n", name); + ERR_print_errors_fp(stderr); + } +} + +static int hex_to_bytes(const char *hex, unsigned char *out, size_t out_len) +{ + size_t i; + + for (i = 0; i < out_len; i++) { + if (sscanf(hex + (i * 2), "%2hhx", &out[i]) != 1) { + return -1; + } + } + return 0; +} + +static void list_keys(const char *title, const PKCS11_KEY *keys, + const unsigned int nkeys) { + unsigned int i; + + printf("\n%s:\n", title); + for (i = 0; i < nkeys; i++) { + printf(" #%d id=", i); + for (size_t j = 0; j < keys[i].id_len; j++) { + printf("%02x", keys[i].id[j]); + } + printf(";object=%s\n", keys[i].label); + } +} + +int main(int argc, char *argv[]) +{ + PKCS11_CTX *ctx = NULL; + PKCS11_SLOT *slots = NULL, *slot; + PKCS11_KEY *keys; + unsigned int nslots, nkeys; + unsigned char *key_id = NULL; + size_t len, key_id_len; + const char *key_id_str; + int rc = 0; + PKCS11_params params = {.sensitive = 1, .extractable = 0}; + PKCS11_EDDSA_KGEN eddsa = {.nid = NID_ED25519}; + PKCS11_KGEN_ATTRS eckg = {0}; + + if (argc < 6) { + fprintf(stderr, "usage: %s [module] [TOKEN] [KEY-LABEL] [KEY-ID] [PIN]\n", argv[0]); + return 1; + } + key_id_str = argv[4]; + len = strlen(key_id_str); + CHECK_ERR(len % 2 != 0, "Invalid key ID format: odd length", 1); + + /* key_id_str is a null-terminated string, but key_id is not */ + key_id_len = len / 2; + key_id = OPENSSL_malloc(key_id_len); + CHECK_ERR(!key_id, "Memory allocation failed for key ID", 2); + + rc = hex_to_bytes(key_id_str, key_id, key_id_len); + CHECK_ERR(rc != 0, "Invalid hex digit in key ID", 3); + + ctx = PKCS11_CTX_new(); + error_queue("PKCS11_CTX_new"); + + /* load PKCS#11 module */ + rc = PKCS11_CTX_load(ctx, argv[1]); + error_queue("PKCS11_CTX_load"); + CHECK_ERR(rc < 0, "loading PKCS#11 module failed", 4); + + /* get information on all slots */ + rc = PKCS11_enumerate_slots(ctx, &slots, &nslots); + error_queue("PKCS11_enumerate_slots"); + CHECK_ERR(rc < 0, "no slots available", 5); + + slot = PKCS11_find_token(ctx, slots, nslots); + error_queue("PKCS11_find_token"); + while (slot) { + if (slot->token && slot->token->initialized && slot->token->label + && strcmp(argv[2], slot->token->label) == 0) + break; + slot = PKCS11_find_next_token(ctx, slots, nslots, slot); + }; + CHECK_ERR(!slot || !slot->token, "no token available", 6); + + printf("Found token:\n"); + printf("Slot manufacturer......: %s\n", slot->manufacturer); + printf("Slot description.......: %s\n", slot->description); + printf("Slot token label.......: %s\n", slot->token->label); + printf("Slot token serialnr....: %s\n", slot->token->serialnr); + + rc = PKCS11_login(slot, 0, argv[5]); + error_queue("PKCS11_login"); + CHECK_ERR(rc < 0, "PKCS11_login failed", 7); + + eckg.type = EVP_PKEY_ED25519; + eckg.kgen.eddsa = &eddsa; + eckg.token_label = argv[2]; + eckg.key_label = argv[3]; + /* key_id is a raw binary buffer of length key_id_len */ + eckg.key_id = (const unsigned char *)key_id; + eckg.id_len = key_id_len; + eckg.key_params = ¶ms; + + rc = PKCS11_keygen(slot->token, &eckg); + error_queue("PKCS11_keygen"); + CHECK_ERR(rc < 0, "Failed to generate a key pair on the token", 8); + + printf("\nEd25519 keys generated\n"); + + /* get private keys */ + rc = PKCS11_enumerate_keys(slot->token, &keys, &nkeys); + error_queue("PKCS11_enumerate_keys"); + CHECK_ERR(rc < 0, "PKCS11_enumerate_keys failed", 9); + CHECK_ERR(nkeys == 0, "No private keys found", 10); + list_keys("Private keys", keys, nkeys); + +end: + if (slots) + PKCS11_release_all_slots(ctx, slots, nslots); + if (ctx) { + PKCS11_CTX_unload(ctx); + PKCS11_CTX_free(ctx); + } + OPENSSL_free(key_id); + + if (rc) + printf("Failed (error code %d).\n", rc); + else + printf("Success.\n"); + return rc; +} + +/* vim: set noexpandtab: */ diff --git a/src/Makefile.am b/src/Makefile.am index a48a0f15..63a3f9b1 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -22,7 +22,7 @@ endif SHARED_EXT=@SHARED_EXT@ libp11_la_SOURCES = libpkcs11.c p11_attr.c p11_cert.c p11_err.c p11_ckr.c \ - p11_key.c p11_load.c p11_misc.c p11_rsa.c p11_ec.c p11_pkey.c \ + p11_key.c p11_load.c p11_misc.c p11_rsa.c p11_ec.c p11_eddsa.c p11_pkey.c \ p11_slot.c p11_front.c p11_atfork.c libp11.exports libp11_la_CFLAGS = $(AM_CFLAGS) $(OPENSSL_CFLAGS) libp11_la_LIBADD = $(OPENSSL_LIBS) diff --git a/src/libp11-int.h b/src/libp11-int.h index 07a4bc1c..55bf8f5d 100644 --- a/src/libp11-int.h +++ b/src/libp11-int.h @@ -1,6 +1,7 @@ /* libp11, a simple layer on top of PKCS#11 API * Copyright (C) 2005 Olaf Kirch * Copyright (C) 2015-2025 Michał Trojnara + * Copyright © 2025 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 @@ -121,6 +122,8 @@ struct pkcs11_object_ops { extern PKCS11_OBJECT_ops pkcs11_rsa_ops; extern PKCS11_OBJECT_ops pkcs11_ec_ops; +extern PKCS11_OBJECT_ops pkcs11_ed25519_ops; +extern PKCS11_OBJECT_ops pkcs11_ed448_ops; extern int pkcs11_global_data_refs; @@ -342,6 +345,10 @@ extern int pkcs11_rsa_keygen(PKCS11_SLOT_private *tpriv, extern int pkcs11_ec_keygen(PKCS11_SLOT_private *tpriv, const char *curve , const char *label, const unsigned char *id, size_t id_len, const PKCS11_params *params); + +extern int pkcs11_eddsa_keygen(PKCS11_SLOT_private *tpriv, + int nid, const char *label, const unsigned char *id, + size_t id_len, const PKCS11_params *params); #endif /* OPENSSL_NO_EC */ /* Get the RSA key modulus size (in bytes) */ @@ -379,12 +386,20 @@ extern PKCS11_OBJECT_private *pkcs11_get_ex_data_rsa(const RSA *rsa); /* Set PKCS11_KEY for an RSA key */ void pkcs11_set_ex_data_rsa(RSA *rsa, PKCS11_OBJECT_private *key); +#ifndef OPENSSL_NO_EC /* Retrieve PKCS11_KEY from an EC_KEY */ extern PKCS11_OBJECT_private *pkcs11_get_ex_data_ec(const EC_KEY *ec); /* Set PKCS11_KEY for an EC_KEY */ extern void pkcs11_set_ex_data_ec(EC_KEY *ec, PKCS11_OBJECT_private *key); +/* Retrieve PKCS11_KEY from an EVP_PKEY */ +extern PKCS11_OBJECT_private *pkcs11_get_ex_data_pkey(const EVP_PKEY *pkey); + +/* Set PKCS11_KEY for an EVP_PKEY */ +extern void pkcs11_set_ex_data_pkey(EVP_PKEY *pkey, PKCS11_OBJECT_private *key); +#endif /* OPENSSL_NO_EC */ + /* Free the global RSA_METHOD */ extern void pkcs11_rsa_method_free(void); @@ -397,6 +412,11 @@ extern void pkcs11_ecdsa_method_free(void); /* Free the global ECDH_METHOD */ extern void pkcs11_ecdh_method_free(void); +# if OPENSSL_VERSION_NUMBER >= 0x30000000L +/* Free the global ED25519/ED448 EVP_PKEY_METHOD */ +extern void pkcs11_ed_key_method_free(void); +# endif /* OPENSSL_VERSION_NUMBER >= 0x30000000L */ + #endif /* vim: set noexpandtab: */ diff --git a/src/libp11.h b/src/libp11.h index 02eb46bc..9b344932 100644 --- a/src/libp11.h +++ b/src/libp11.h @@ -1,5 +1,6 @@ /* libp11, a simple layer on top of PKCS#11 API * Copyright (C) 2005 Olaf Kirch + * Copyright © 2025 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 @@ -110,6 +111,10 @@ typedef struct PKCS11_ec_kgen_st { const char *curve; } PKCS11_EC_KGEN; +typedef struct PKCS11_eddsa_kgen_st { + int nid; /* NID_ED25519 or NID_ED448 */ +} PKCS11_EDDSA_KGEN; + typedef struct PKCS11_rsa_kgen_st { unsigned int bits; } PKCS11_RSA_KGEN; @@ -121,11 +126,12 @@ typedef struct PKCS11_params { typedef struct PKCS11_kgen_attrs_st { /* Key generation type from OpenSSL. Given the union below this should - * be either EVP_PKEY_EC or EVP_PKEY_RSA + * be either EVP_PKEY_EC or EVP_PKEY_RSA or EVP_PKEY_ED25519 or EVP_PKEY_ED448 */ int type; union { PKCS11_EC_KGEN *ec; + PKCS11_EDDSA_KGEN *eddsa; PKCS11_RSA_KGEN *rsa; } kgen; const char *token_label; @@ -566,6 +572,7 @@ extern void PKCS11_set_vlog_a_method(PKCS11_CTX *pctx, PKCS11_VLOG_A_CB cb); # define CKR_F_PKCS11_GENERATE_KEY 130 # define CKR_F_PKCS11_RELOAD_CERTIFICATE 131 # define CKR_F_PKCS11_GET_SESSION 132 +# define CKR_F_PKCS11_EDDSA_SIGN 133 /* Backward compatibility of error function codes */ #define PKCS11_F_PKCS11_CHANGE_PIN CKR_F_PKCS11_CHANGE_PIN diff --git a/src/p11_ckr.c b/src/p11_ckr.c index 3f2f98e3..6a15971f 100644 --- a/src/p11_ckr.c +++ b/src/p11_ckr.c @@ -57,6 +57,7 @@ static ERR_STRING_DATA CKR_str_functs[] = { {ERR_FUNC(CKR_F_PKCS11_STORE_KEY), "pkcs11_store_key"}, {ERR_FUNC(CKR_F_PKCS11_RELOAD_CERTIFICATE), "pkcs11_reload_certificate"}, {ERR_FUNC(CKR_F_PKCS11_GET_SESSION), "pkcs11_get_session"}, + {ERR_FUNC(CKR_F_PKCS11_EDDSA_SIGN), "pkcs11_eddsa_sign"}, {0, NULL} }; diff --git a/src/p11_eddsa.c b/src/p11_eddsa.c new file mode 100644 index 00000000..cf35bc84 --- /dev/null +++ b/src/p11_eddsa.c @@ -0,0 +1,496 @@ +/* + * Copyright © 2025 Mobi - Com Polska Sp. z o.o. + * Author: Małgorzata Olszówka + * All rights reserved. + * + * This file implements the handling of EdDSA keys stored on a PKCS11 token. + * Inside EVP_PKEY, Ed25519/Ed448 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 + +#ifndef OPENSSL_NO_EC +#include +#include + +static int pkey_ex_idx = 0; +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; + +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); + +static void alloc_pkey_ex_index(void) +{ + if (pkey_ex_idx == 0) { + while (pkey_ex_idx == 0) /* Workaround for OpenSSL RT3710 */ + pkey_ex_idx = EVP_PKEY_get_ex_new_index(0, "libp11 eddsa", + NULL, NULL, NULL); + if (pkey_ex_idx < 0) + pkey_ex_idx = 0; /* Fallback to app_data */ + } +} + +static void free_pkey_ex_index(void) +{ + if (pkey_ex_idx > 0) { + CRYPTO_free_ex_index(CRYPTO_EX_INDEX_EVP_PKEY, pkey_ex_idx); + pkey_ex_idx = 0; + } +} + +/* PKCS#11 sign implementation for Ed25519 / Ed448 */ +static int pkcs11_eddsa_sign(unsigned char *sigret, unsigned int *siglen, + const unsigned char *tbs, unsigned int tbslen, PKCS11_OBJECT_private *key) +{ + int rv; + PKCS11_SLOT_private *slot = key->slot; + PKCS11_CTX_private *ctx = slot->ctx; + CK_SESSION_HANDLE session; + CK_EDDSA_PARAMS eddsa_params; + CK_MECHANISM mechanism; + CK_ULONG ck_siglen = (CK_ULONG)(*siglen); + CK_ULONG ck_tbslen = (CK_ULONG)tbslen; + + /* eddsa_params.phFlag = 0 => PureEdDSA, no prehash */ + memset(&eddsa_params, 0, sizeof(eddsa_params)); + memset(&mechanism, 0, sizeof(mechanism)); + mechanism.mechanism = CKM_EDDSA; + mechanism.pParameter = &eddsa_params; + mechanism.ulParameterLen = sizeof(eddsa_params); + + if (pkcs11_get_session(slot, 0, &session)) + return -1; + + rv = CRYPTOKI_call(ctx, + C_SignInit(session, &mechanism, key->object)); + if (!rv && key->always_authenticate == CK_TRUE) + rv = pkcs11_authenticate(key, session); + if (!rv) + rv = CRYPTOKI_call(ctx, + C_Sign(session, (CK_BYTE_PTR)tbs, ck_tbslen, sigret, &ck_siglen)); + pkcs11_put_session(slot, session); + + if (rv) { + CKRerr(CKR_F_PKCS11_EDDSA_SIGN, rv); + return -1; + } + *siglen = (unsigned int)ck_siglen; + return (int)ck_siglen; +} + + +/* + * 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(). + */ +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; + unsigned int tmp_len; + + if (*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; + + tmp_len = (unsigned int)*siglen; + rv = pkcs11_eddsa_sign(sig, &tmp_len, tbs, (unsigned int)tbslen, key); + if (rv < 0) + return 0; + + *siglen = tmp_len; + 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_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; + unsigned int tmp_len; + int rv; + + pkey = EVP_PKEY_CTX_get0_pkey(EVP_MD_CTX_pkey_ctx(ctx)); + if (!pkey) + return 0; + + key = pkcs11_get_ex_data_pkey(pkey); + if (!key) { + /* assume a foreign key */ + if (EVP_PKEY_id(pkey) == EVP_PKEY_ED25519) + return orig_ed25519_digestsign(ctx, sig, siglen, tbs, tbslen); + else if (EVP_PKEY_id(pkey) == EVP_PKEY_ED448) + return orig_ed448_digestsign(ctx, sig, siglen, tbs, tbslen); + else + return 0; + + } + /* 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 0; + /* success: report the expected signature length only, + * no signing is performed in this call */ + return 1; + } + + /* Step 2: actual signing */ + tmp_len = (unsigned int)*siglen; + rv = pkcs11_eddsa_sign(sig, &tmp_len, tbs, (unsigned int)tbslen, key); + if (rv < 0) + return 0; + + *siglen = tmp_len; + return 1; +} + +/* + * 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() +{ + 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; + + 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_eddsa_pmeth_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() +{ + 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; + + 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_eddsa_pmeth_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) +{ + if (pkcs11_global_data_refs == 0) { + pkcs11_ed25519_method_free(); + pkcs11_ed448_method_free(); + } +} + +void pkcs11_set_ex_data_pkey(EVP_PKEY *pkey, PKCS11_OBJECT_private *key) +{ + EVP_PKEY_set_ex_data(pkey, pkey_ex_idx, key); +} + +PKCS11_OBJECT_private *pkcs11_get_ex_data_pkey(const EVP_PKEY *pkey) +{ + return EVP_PKEY_get_ex_data(pkey, pkey_ex_idx); +} + +/* + * Retrieve the raw public key (EdDSA) from a PKCS#11 object. + * The buffer `*raw` is allocated and must be freed by the caller + * using OPENSSL_free(). + */ +static int pkcs11_get_raw_public_key(PKCS11_OBJECT_private *key, + unsigned char **raw, size_t *rawlen) +{ + CK_ATTRIBUTE attr; + CK_RV rv; + CK_SESSION_HANDLE session; + CK_MECHANISM mechanism; + PKCS11_SLOT_private *slot = key->slot; + PKCS11_CTX_private *ctx = slot->ctx; + PKCS11_OBJECT_private *pubkey; + + *raw = NULL; + *rawlen = 0; + + memset(&mechanism, 0, sizeof(mechanism)); + mechanism.mechanism = CKM_EDDSA; + + memset(&attr, 0, sizeof(attr)); + + /* CKA_EC_POINT: DER-encoding of the b-bit public key value + * in little endian order as defined in RFC 8032 */ + attr.type = CKA_EC_POINT; + + if (pkcs11_get_session(slot, 0, &session)) + return -1; + + if (key->object_class == CKO_PRIVATE_KEY) + pubkey = pkcs11_object_from_object(key, session, CKO_PUBLIC_KEY); + else + pubkey = key; + + rv = CRYPTOKI_call(ctx, C_GetAttributeValue(session, pubkey->object, &attr, 1)); + if (rv != CKR_OK) + return -1; + + if (attr.ulValueLen <= 0 || attr.ulValueLen == CK_UNAVAILABLE_INFORMATION) + return -1; + + *raw = OPENSSL_malloc(attr.ulValueLen); + if (!*raw) + return -1; + + attr.pValue = *raw; + + rv = CRYPTOKI_call(ctx, C_GetAttributeValue(session, pubkey->object, &attr, 1)); + + if (key->object_class == CKO_PRIVATE_KEY) + pkcs11_object_free(pubkey); + + if (rv != CKR_OK) { + OPENSSL_free(*raw); + *raw = NULL; + return -1; + } + *rawlen = attr.ulValueLen; + + /* For EdDSA (RFC8032) the CKA_EC_POINT attribute may be encoded + * as a DER OCTET STRING. In such a case, the ASN.1 header needs + * to be stripped, leaving only the raw key bytes. */ + if (*rawlen > 2 && (*raw)[0] == 0x04) { + /* simple OCTET STRING parser */ + size_t len = (*raw)[1]; + if (len + 2 == *rawlen) { + memmove(*raw, *raw + 2, len); + *rawlen = len; + } + } + return 0; +} + +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; + + pkey = EVP_PKEY_new_raw_public_key(EVP_PKEY_ED25519, NULL, raw, rawlen); + OPENSSL_free(raw); + + if (!pkey) + return NULL; + + if (key->object_class == CKO_PRIVATE_KEY) { + /* global initialize ED25519 EVP_PKEY_METHOD */ + if (!pkcs11_ed25519_method_new()) { + EVP_PKEY_free(pkey); + return NULL; + } + /* creates a new EVP_PKEY object which requires its own key object reference */ + alloc_pkey_ex_index(); + key = pkcs11_object_ref(key); + pkcs11_set_ex_data_pkey(pkey, key); + atexit(pkcs11_ed25519_method_free); + } + 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; + + pkey = EVP_PKEY_new_raw_public_key(EVP_PKEY_ED448, NULL, raw, rawlen); + OPENSSL_free(raw); + + if (!pkey) + return NULL; + + if (key->object_class == CKO_PRIVATE_KEY) { + /* global initialize ED448 EVP_PKEY_METHOD */ + if (!pkcs11_ed448_method_new()) { + EVP_PKEY_free(pkey); + return NULL; + } + /* create a new EVP_PKEY object which requires its own key object reference */ + alloc_pkey_ex_index(); + key = pkcs11_object_ref(key); + pkcs11_set_ex_data_pkey(pkey, key); + atexit(pkcs11_ed448_method_free); + } + 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, +}; + +#else /* OPENSSL_NO_EC */ + +/* if not built with EC or OpenSSL does not support EdDSA + * add these routines so engine_pkcs11 can be built now and not + * require further changes */ +#warning "EdDSA support not built with libp11" + +#endif /* OPENSSL_NO_EC */ diff --git a/src/p11_front.c b/src/p11_front.c index 0a07775e..93ed0ed7 100644 --- a/src/p11_front.c +++ b/src/p11_front.c @@ -1,5 +1,6 @@ /* libp11, a simple layer on top of PKCS#11 API * Copyright (C) 2016-2025 Michał Trojnara + * Copyright © 2025 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 @@ -424,24 +425,29 @@ int PKCS11_keygen(PKCS11_TOKEN *token, PKCS11_KGEN_ATTRS *kg) case EVP_PKEY_EC: return pkcs11_ec_keygen(slot, kg->kgen.ec->curve, kg->key_label, kg->key_id, kg->id_len, kg->key_params); + case EVP_PKEY_ED25519: + case EVP_PKEY_ED448: + return pkcs11_eddsa_keygen(slot, kg->kgen.eddsa->nid, + kg->key_label, kg->key_id, kg->id_len, kg->key_params); #endif /* OPENSSL_NO_EC */ default: return -1; } } -int PKCS11_generate_key(PKCS11_TOKEN *token, - int algorithm, unsigned int bits_or_nid, +int PKCS11_generate_key(PKCS11_TOKEN *token, int algorithm, + unsigned int param, /* bits for RSA, nid for EC, unused for EdDSA */ char *label, unsigned char *id, size_t id_len) { PKCS11_params key_params = { .extractable = 0, .sensitive = 1 }; PKCS11_EC_KGEN ec_kgen; + PKCS11_EDDSA_KGEN eddsa_kgen; PKCS11_RSA_KGEN rsa_kgen; PKCS11_KGEN_ATTRS kgen_attrs = { 0 }; switch (algorithm) { case EVP_PKEY_EC: - ec_kgen.curve = OBJ_nid2sn(bits_or_nid); + ec_kgen.curve = OBJ_nid2sn(param); kgen_attrs = (PKCS11_KGEN_ATTRS){ .type = EVP_PKEY_EC, .kgen.ec = &ec_kgen, @@ -452,9 +458,33 @@ int PKCS11_generate_key(PKCS11_TOKEN *token, .key_params = &key_params }; break; + case EVP_PKEY_ED25519: + eddsa_kgen.nid = NID_ED25519; + kgen_attrs = (PKCS11_KGEN_ATTRS){ + .type = EVP_PKEY_ED25519, + .kgen.eddsa = &eddsa_kgen, + .token_label = (const char *)token->label, + .key_label = label, + .key_id = (const unsigned char *)id, + .id_len = id_len, + .key_params = &key_params + }; + break; + case EVP_PKEY_ED448: + eddsa_kgen.nid = NID_ED448; + kgen_attrs = (PKCS11_KGEN_ATTRS){ + .type = EVP_PKEY_ED448, + .kgen.eddsa = &eddsa_kgen, + .token_label = (const char *)token->label, + .key_label = label, + .key_id = (const unsigned char *)id, + .id_len = id_len, + .key_params = &key_params + }; + break; default: - rsa_kgen.bits = bits_or_nid; + rsa_kgen.bits = param; kgen_attrs = (PKCS11_KGEN_ATTRS){ .type = EVP_PKEY_RSA, .kgen.rsa = &rsa_kgen, diff --git a/src/p11_key.c b/src/p11_key.c index 9db7ff38..f2f94fb7 100644 --- a/src/p11_key.c +++ b/src/p11_key.c @@ -1,6 +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. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -25,6 +26,22 @@ /* The maximum length of PIN */ #define MAX_PIN_LENGTH 256 +#ifndef OPENSSL_NO_EC +/* DER OIDs */ +static const unsigned char OID_ED25519[] = { 0x06, 0x03, 0x2B, 0x65, 0x70 }; +static const unsigned char OID_ED448[] = { 0x06, 0x03, 0x2B, 0x65, 0x71 }; + +/* PrintableString forms used by some tokens (e.g. SoftHSM) */ +static const unsigned char STR_ED25519[] = { + 0x13, 0x0C, /* tag + length */ + 'e','d','w','a','r','d','s','2','5','5','1','9' +}; +static const unsigned char STR_ED448[] = { + 0x13, 0x0A, /* tag + length */ + 'e','d','w','a','r','d','s','4','4','8' +}; +#endif /* OPENSSL_NO_EC */ + 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, @@ -98,7 +115,31 @@ PKCS11_OBJECT_private *pkcs11_object_from_handle(PKCS11_SLOT_private *slot, case CKK_EC: ops = &pkcs11_ec_ops; break; -#endif + case CKK_EC_EDWARDS: + /* Read the CKA_EC_PARAMS to distinguish Ed25519 vs Ed448 */ + if (pkcs11_getattr_alloc(ctx, session, object, + CKA_EC_PARAMS, &data, &size)) { + pkcs11_log(ctx, LOG_DEBUG, "Missing CKA_EC_PARAMS attribute\n"); + return NULL; + } + if ((size == sizeof(OID_ED25519) && + !memcmp(data, OID_ED25519, sizeof(OID_ED25519))) || + (size == sizeof(STR_ED25519) && + !memcmp(data, STR_ED25519, sizeof(STR_ED25519)))) { + ops = &pkcs11_ed25519_ops; + } else if ((size == sizeof(OID_ED448) && + !memcmp(data, OID_ED448, sizeof(OID_ED448))) || + (size == sizeof(STR_ED448) && + !memcmp(data, STR_ED448, sizeof(STR_ED448)))) { + ops = &pkcs11_ed448_ops; + } else { + pkcs11_log(ctx, LOG_DEBUG, "Unsupported EdDSA OID\n"); + OPENSSL_free(data); + return NULL; + } + OPENSSL_free(data); + break; +#endif /* OPENSSL_NO_EC */ default: /* Ignore any keys we don't understand */ pkcs11_log(ctx, LOG_DEBUG, @@ -402,6 +443,62 @@ int pkcs11_ec_keygen(PKCS11_SLOT_private *slot, const char *curve, return 0; } +/** + * Generate EdDSA (Ed25519 / Ed448) key pair directly on token + */ +int pkcs11_eddsa_keygen(PKCS11_SLOT_private *slot, + int nid, const char *label, const unsigned char *id, + size_t id_len, const PKCS11_params *params) +{ + PKCS11_CTX_private *ctx = slot->ctx; + CK_SESSION_HANDLE session; + PKCS11_TEMPLATE pubtmpl = {0}, privtmpl = {0}; + CK_MECHANISM mechanism = { + CKM_EC_EDWARDS_KEY_PAIR_GEN, NULL_PTR, 0 + }; + CK_OBJECT_HANDLE pub_key_obj, priv_key_obj; + int rv; + unsigned char *eddsa_params = NULL; + size_t eddsa_params_len = 0; + + if (pkcs11_init_keygen(slot, &session)) + return -1; + + if (nid == NID_ED25519) { + eddsa_params = (unsigned char *)OID_ED25519; + eddsa_params_len = sizeof(OID_ED25519); + } else if (nid == NID_ED448) { + eddsa_params = (unsigned char *)OID_ED448; + eddsa_params_len = sizeof(OID_ED448); + } else { + return -1; /* unsupported */ + } + + /* public key attributes */ + pkcs11_common_pubkey_attr(&pubtmpl, label, id, id_len); + pkcs11_addattr(&pubtmpl, CKA_EC_PARAMS, eddsa_params, eddsa_params_len); + pkcs11_addattr_bool(&pubtmpl, CKA_VERIFY, TRUE); + + /* private key attributes */ + pkcs11_common_privkey_attr(&privtmpl, label, id, id_len, params); + pkcs11_addattr_bool(&privtmpl, CKA_SIGN, TRUE); + + /* generate key pair */ + rv = CRYPTOKI_call(ctx, C_GenerateKeyPair( + session, &mechanism, + pubtmpl.attrs, pubtmpl.nattr, + privtmpl.attrs, privtmpl.nattr, + &pub_key_obj, &priv_key_obj)); + pkcs11_put_session(slot, session); + + /* cleanup */ + pkcs11_zap_attrs(&privtmpl); + pkcs11_zap_attrs(&pubtmpl); + + CRYPTOKI_checkerr(CKR_F_PKCS11_GENERATE_KEY, rv); + return 0; +} + #endif /* OPENSSL_NO_EC */ /* @@ -595,6 +692,11 @@ EVP_PKEY *pkcs11_get_key(PKCS11_OBJECT_private *key0, CK_OBJECT_CLASS object_cla ret = EVP_PKEY_dup(key->evp_key); #endif break; + case EVP_PKEY_ED25519: + case EVP_PKEY_ED448: + ret = key->evp_key; + EVP_PKEY_up_ref(key->evp_key); + break; default: pkcs11_log(key0->slot->ctx, LOG_DEBUG, "Unsupported key type\n"); } @@ -678,7 +780,6 @@ int pkcs11_enumerate_keys(PKCS11_SLOT_private *slot, unsigned int type, const PK if (key_template->label) pkcs11_addattr_s(&tmpl, CKA_LABEL, key_template->label); } - if (pkcs11_get_session(slot, 0, &session)) return -1; @@ -688,7 +789,6 @@ int pkcs11_enumerate_keys(PKCS11_SLOT_private *slot, unsigned int type, const PK pkcs11_destroy_keys(slot, type); return -1; } - if (keyp) *keyp = keys->keys; if (countp) diff --git a/src/p11_load.c b/src/p11_load.c index 3239d18c..e59f79b5 100644 --- a/src/p11_load.c +++ b/src/p11_load.c @@ -1,5 +1,6 @@ /* libp11, a simple layer on top of PKCS#11 API * Copyright (C) 2005 Olaf Kirch + * Copyright © 2025 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 @@ -183,6 +184,7 @@ void pkcs11_CTX_free(PKCS11_CTX *ctx) #if OPENSSL_VERSION_NUMBER >= 0x10100002L #ifndef OPENSSL_NO_EC pkcs11_ec_key_method_free(); + pkcs11_ed_key_method_free(); #endif /* OPENSSL_NO_EC */ #else /* OPENSSL_VERSION_NUMBER */ #ifndef OPENSSL_NO_ECDSA diff --git a/src/p11_pkey.c b/src/p11_pkey.c index fb1a395f..24d33abb 100644 --- a/src/p11_pkey.c +++ b/src/p11_pkey.c @@ -1,6 +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. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -34,6 +35,16 @@ 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); + +static int (*orig_pkey_ed448_sign_init) (EVP_PKEY_CTX *ctx); +static int (*orig_pkey_ed448_sign) (EVP_PKEY_CTX *ctx, + unsigned char *sig, size_t *siglen, + const unsigned char *tbs, size_t tbslen); + +static int (*orig_pkey_ed25519_sign_init) (EVP_PKEY_CTX *ctx); +static int (*orig_pkey_ed25519_sign) (EVP_PKEY_CTX *ctx, + unsigned char *sig, size_t *siglen, + const unsigned char *tbs, size_t tbslen); #endif /* OPENSSL_NO_EC */ #if OPENSSL_VERSION_NUMBER < 0x10002000L || defined(LIBRESSL_VERSION_NUMBER) @@ -607,6 +618,89 @@ static int pkcs11_try_pkey_ec_sign(EVP_PKEY_CTX *evp_pkey_ctx, return 1; } +static int pkcs11_try_pkey_eddsa_sign(EVP_PKEY_CTX *evp_pkey_ctx, + unsigned char *sig, size_t *siglen, + const unsigned char *tbs, size_t tbslen) +{ + EVP_PKEY *pkey; + size_t expected_size; + int type; + int rv = CKR_GENERAL_ERROR; + CK_ULONG size = (CK_ULONG)*siglen; + PKCS11_OBJECT_private *key; + PKCS11_SLOT_private *slot; + PKCS11_CTX_private *ctx; + CK_SESSION_HANDLE session; + CK_MECHANISM mechanism; + + if (!evp_pkey_ctx) + return -1; + + pkey = EVP_PKEY_CTX_get0_pkey(evp_pkey_ctx); + if (!pkey) + return -1; + + key = pkcs11_get_ex_data_pkey(pkey); + if (!key) + return -1; + + if (check_object_fork(key) < 0) + return -1; + + slot = key->slot; + ctx = slot->ctx; + if (!ctx) + return -1; + + type = EVP_PKEY_id(pkey); + if (type == EVP_PKEY_ED25519) + expected_size = 64; + else if (type == EVP_PKEY_ED448) + expected_size = 114; + else + return -1; + + if (!sig) { + *siglen = expected_size; + return 1; + } + if (*siglen < expected_size) + return -1; + + pkcs11_log(ctx, LOG_DEBUG, "%s:%d pkcs11_try_pkey_eddsa_sign() " + "sig=%p *siglen=%lu tbs=%p tbslen=%lu\n", + __FILE__, __LINE__, sig, *siglen, tbs, tbslen); + + memset(&mechanism, 0, sizeof mechanism); + mechanism.mechanism = CKM_EDDSA; + + if (pkcs11_get_session(slot, 0, &session)) + return -1; + + rv = CRYPTOKI_call(ctx, C_SignInit(session, &mechanism, key->object)); + if (rv != CKR_OK) { + pkcs11_log(ctx, LOG_DEBUG, "%s:%d C_SignInit rv=%d\n", + __FILE__, __LINE__, rv); + } else if (key->always_authenticate == CK_TRUE) + rv = pkcs11_authenticate(key, session); + + if (rv == CKR_OK) { + rv = CRYPTOKI_call(ctx, + C_Sign(session, (CK_BYTE_PTR)tbs, (CK_ULONG)tbslen, sig, &size)); + if (rv != CKR_OK) { + pkcs11_log(ctx, LOG_DEBUG, "%s:%d C_Sign rv=%d\n", + __FILE__, __LINE__, rv); + } + } + pkcs11_put_session(slot, session); + + if (rv != CKR_OK) + return -1; + + *siglen = size; + return 1; +} + 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) @@ -619,6 +713,30 @@ static int pkcs11_pkey_ec_sign(EVP_PKEY_CTX *evp_pkey_ctx, return ret; } +static int pkcs11_pkey_ed448_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_eddsa_sign(evp_pkey_ctx, sig, siglen, tbs, tbslen); + if (ret < 0) + ret = (*orig_pkey_ed448_sign)(evp_pkey_ctx, sig, siglen, tbs, tbslen); + return ret; +} + +static int pkcs11_pkey_ed25519_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_eddsa_sign(evp_pkey_ctx, sig, siglen, tbs, tbslen); + if (ret < 0) + ret = (*orig_pkey_ed25519_sign)(evp_pkey_ctx, sig, siglen, tbs, tbslen); + return ret; +} + static EVP_PKEY_METHOD *pkcs11_pkey_method_ec(void) { EVP_PKEY_METHOD *orig_meth, *new_meth; @@ -637,6 +755,44 @@ static EVP_PKEY_METHOD *pkcs11_pkey_method_ec(void) return new_meth; } +static EVP_PKEY_METHOD *pkcs11_pkey_method_ed448(void) +{ + EVP_PKEY_METHOD *orig_meth, *new_meth; + + orig_meth = (EVP_PKEY_METHOD *)EVP_PKEY_meth_find(EVP_PKEY_ED448); + EVP_PKEY_meth_get_sign(orig_meth, + &orig_pkey_ed448_sign_init, &orig_pkey_ed448_sign); + + /* don't assume any digest related defaults */ + new_meth = EVP_PKEY_meth_new(EVP_PKEY_ED448, EVP_PKEY_FLAG_SIGCTX_CUSTOM); + + EVP_PKEY_meth_copy(new_meth, orig_meth); + + EVP_PKEY_meth_set_sign(new_meth, + orig_pkey_ed448_sign_init, pkcs11_pkey_ed448_sign); + + return new_meth; +} + +static EVP_PKEY_METHOD *pkcs11_pkey_method_ed25519(void) +{ + EVP_PKEY_METHOD *orig_meth, *new_meth; + + orig_meth = (EVP_PKEY_METHOD *)EVP_PKEY_meth_find(EVP_PKEY_ED25519); + EVP_PKEY_meth_get_sign(orig_meth, + &orig_pkey_ed25519_sign_init, &orig_pkey_ed25519_sign); + + /* don't assume any digest related defaults */ + new_meth = EVP_PKEY_meth_new(EVP_PKEY_ED25519, EVP_PKEY_FLAG_SIGCTX_CUSTOM); + + EVP_PKEY_meth_copy(new_meth, orig_meth); + + EVP_PKEY_meth_set_sign(new_meth, + orig_pkey_ed25519_sign_init, pkcs11_pkey_ed25519_sign); + + return new_meth; +} + #endif /* OPENSSL_NO_EC */ int PKCS11_pkey_meths(ENGINE *e, EVP_PKEY_METHOD **pmeth, @@ -646,12 +802,16 @@ int PKCS11_pkey_meths(ENGINE *e, EVP_PKEY_METHOD **pmeth, EVP_PKEY_RSA, #ifndef OPENSSL_NO_EC EVP_PKEY_EC, + EVP_PKEY_ED25519, + EVP_PKEY_ED448, #endif /* OPENSSL_NO_EC */ 0 }; static EVP_PKEY_METHOD *pkey_method_rsa = NULL; #ifndef OPENSSL_NO_EC static EVP_PKEY_METHOD *pkey_method_ec = NULL; + static EVP_PKEY_METHOD *pkey_method_ed448 = NULL; + static EVP_PKEY_METHOD *pkey_method_ed25519 = NULL; #endif /* OPENSSL_NO_EC */ (void)e; /* squash the unused parameter warning */ @@ -679,6 +839,20 @@ int PKCS11_pkey_meths(ENGINE *e, EVP_PKEY_METHOD **pmeth, return 0; *pmeth = pkey_method_ec; return 1; /* success */ + 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 */ + 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 /* OPENSSL_NO_EC */ } *pmeth = NULL; diff --git a/src/pkcs11.h b/src/pkcs11.h index 1d677ef8..c42257bb 100644 --- a/src/pkcs11.h +++ b/src/pkcs11.h @@ -359,6 +359,7 @@ typedef unsigned long ck_key_type_t; #define CKK_GOSTR3410 (0x30UL) #define CKK_GOSTR3411 (0x31UL) #define CKK_GOST28147 (0x32UL) +#define CKK_EC_EDWARDS (0x40UL) #define CKK_VENDOR_DEFINED (1UL << 31) /* @@ -715,6 +716,8 @@ typedef unsigned long ck_mechanism_type_t; #define CKM_ECDH1_DERIVE (0x1050UL) #define CKM_ECDH1_COFACTOR_DERIVE (0x1051UL) #define CKM_ECMQV_DERIVE (0x1052UL) +#define CKM_EC_EDWARDS_KEY_PAIR_GEN (0x1055UL) +#define CKM_EDDSA (0x1057UL) #define CKM_JUNIPER_KEY_GEN (0x1060UL) #define CKM_JUNIPER_ECB128 (0x1061UL) #define CKM_JUNIPER_CBC128 (0x1062UL) @@ -858,6 +861,15 @@ typedef struct CK_RSA_PKCS_PSS_PARAMS { unsigned long sLen; } CK_RSA_PKCS_PSS_PARAMS; +/* EDDSA */ +typedef struct CK_EDDSA_PARAMS { + unsigned char phFlag; + unsigned long ulContextDataLen; + unsigned char *pContextData; +} CK_EDDSA_PARAMS; + +typedef CK_EDDSA_PARAMS *CK_EDDSA_PARAMS_PTR; + #define CKG_MGF1_SHA1 (0x00000001UL) #define CKG_MGF1_SHA224 (0x00000005UL) #define CKG_MGF1_SHA256 (0x00000002UL) diff --git a/tests/Makefile.am b/tests/Makefile.am index b2597fa1..ca493229 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -27,7 +27,8 @@ check_PROGRAMS = \ dup-key-prov \ check-all-prov \ rsa-keygen \ - ec-keygen + ec-keygen \ + ed25519-keygen dist_check_SCRIPTS = \ rsa-testpkcs11.softhsm \ rsa-testfork.softhsm \ @@ -45,6 +46,7 @@ dist_check_SCRIPTS = \ ec-cert-store.softhsm \ ec-copy.softhsm \ ec-keygen.softhsm \ + ed25519-keygen.softhsm \ fork-change-slot.softhsm \ case-insensitive.softhsm \ pkcs11-uri-pin-source.softhsm \ @@ -74,6 +76,7 @@ check_privkey_prov_SOURCES = check-privkey-prov.c helpers_prov.c rsa_pss_sign_prov_SOURCES = rsa-pss-sign-prov.c helpers_prov.c rsa_oaep_prov_SOURCES = rsa-oaep-prov.c helpers_prov.c check_all_prov_SOURCES = check-all-prov.c helpers_prov.c +ed25519_keygen_SOURCES = ed25519-keygen.c helpers_prov.c TESTS = $(dist_check_SCRIPTS) diff --git a/tests/ed25519-keygen.c b/tests/ed25519-keygen.c new file mode 100644 index 00000000..1fcd4686 --- /dev/null +++ b/tests/ed25519-keygen.c @@ -0,0 +1,227 @@ +/* + * Copyright © 2025 Mobi - Com Polska Sp. z o.o. + * Author: Małgorzata Olszówka + * All rights reserved. + * + * 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 . + */ + +#include +#include +#include "helpers_prov.h" + +#if OPENSSL_VERSION_NUMBER >= 0x30000000L + +#define CHECK_ERR(cond, txt, code) \ + do { \ + if (cond) { \ + fprintf(stderr, "%s\n", (txt)); \ + rc=(code); \ + goto cleanup; \ + } \ + } while (0) + +static void error_queue(const char *name) +{ + if (ERR_peek_last_error()) { + fprintf(stderr, "%s generated errors:\n", name); + ERR_print_errors_fp(stderr); + } +} + +static int sign_verify_test(EVP_PKEY *priv, EVP_PKEY *pub) { + EVP_MD_CTX *mdctx = NULL; + int retval = 0; + const char *msg = "libp11"; + size_t siglen, msglen = strlen(msg); + unsigned char *sig = NULL; + + if (!priv || !pub) { + printf("Where are the keys?\n"); + return -1; + } + + siglen = (size_t)EVP_PKEY_get_size(priv); + sig = OPENSSL_malloc(siglen); + if (!sig) { + ERR_print_errors_fp(stderr); + return -2; + } + + EVP_PKEY_CTX *ctx = EVP_PKEY_CTX_new_from_pkey(NULL, priv, NULL); + if (!ctx) { + ERR_print_errors_fp(stderr); + retval = -3; + goto err; + } + + if (EVP_PKEY_sign_init(ctx) <= 0) { + ERR_print_errors_fp(stderr); + retval = -4; + goto err; + } + + if (EVP_PKEY_sign(ctx, sig, &siglen, msg, msglen) <= 0) { + ERR_print_errors_fp(stderr); + retval = -5; + goto err; + } + printf("Sign success\n"); + + /* --- Verify --- */ + EVP_MD_CTX_reset(mdctx); + + if (EVP_DigestVerifyInit(mdctx, NULL, NULL, NULL, pub) != 1) { + ERR_print_errors_fp(stderr); + retval = -6; + goto err; + } + + if (EVP_DigestVerify(mdctx, sig, siglen, (const unsigned char *)msg, msglen) == 1) { + printf("Verify success\n"); + retval = 0; + } else { + ERR_print_errors_fp(stderr); + printf("Verify fail\n"); + retval = -7; + } + +err: + if (sig) + OPENSSL_free(sig); + if (mdctx) + EVP_MD_CTX_free(mdctx); + return retval; +} + +int main(int argc, char *argv[]) +{ + int ret = EXIT_FAILURE; + int rc = 0; + PKCS11_CTX *ctx = NULL; + PKCS11_SLOT *slots = NULL, *slot; + unsigned int nslots; + EVP_PKEY *private_key = NULL, *public_key = NULL; + PKCS11_EDDSA_KGEN eddsa = { + .nid = NID_ED25519 + }; + PKCS11_params params = { + .sensitive = 1, + .extractable = 0, + }; + PKCS11_KGEN_ATTRS eckg = { + .type = EVP_PKEY_ED25519, + .kgen.eddsa = &eddsa, + .token_label = NULL, + .key_label = NULL, + .key_id = (const unsigned char *)"\x22\x33", + .id_len = 2, + .key_params = ¶ms, + }; + + if (argc < 4) { + printf("Too few arguments\n"); + printf("%s /usr/lib/opensc-pkcs11.so [TOKEN1] [KEY-LABEL] [PIN]\n", argv[0]); + goto cleanup; + } + eckg.token_label = argv[2]; + eckg.key_label = argv[3]; + + ctx = PKCS11_CTX_new(); + error_queue("PKCS11_CTX_new"); + + /* load PKCS#11 module */ + rc = PKCS11_CTX_load(ctx, argv[1]); + error_queue("PKCS11_CTX_load"); + CHECK_ERR(rc < 0, "loading PKCS#11 module failed", 4); + + /* get information on all slots */ + rc = PKCS11_enumerate_slots(ctx, &slots, &nslots); + error_queue("PKCS11_enumerate_slots"); + CHECK_ERR(rc < 0, "no slots available", 5); + + slot = PKCS11_find_token(ctx, slots, nslots); + error_queue("PKCS11_find_token"); + while (slot) { + if (slot->token && slot->token->initialized && slot->token->label + && strcmp(argv[2], slot->token->label) == 0) + break; + slot = PKCS11_find_next_token(ctx, slots, nslots, slot); + }; + CHECK_ERR(!slot || !slot->token, "no token available", 6); + + printf("Found token:\n"); + printf("Slot manufacturer......: %s\n", slot->manufacturer); + printf("Slot description.......: %s\n", slot->description); + printf("Slot token label.......: %s\n", slot->token->label); + printf("Slot token serialnr....: %s\n", slot->token->serialnr); + + rc = PKCS11_login(slot, 0, argv[4]); + error_queue("PKCS11_login"); + CHECK_ERR(rc < 0, "PKCS11_login failed", 7); + /* + * EC key generation test + */ + rc = PKCS11_keygen(slot->token, &eckg); + error_queue("PKCS11_keygen"); + CHECK_ERR(rc < 0, "Failed to generate a key pair on the token", 8); + printf("EC keys generated\n"); + + /* Load pkcs11prov and default providers */ + if (!providers_load()) { + display_openssl_errors(); + goto cleanup; + } + + /* Load keys */ + private_key = load_pkey("pkcs11:token=token1;object=libp11-keylabel;type=private", NULL); + if (!private_key) { + printf("Cannot load private key: %s\n", argv[3]); + display_openssl_errors(); + goto cleanup; + } + printf("Private key found.\n"); + + public_key = load_pubkey("pkcs11:token=token1;object=libp11-keylabel;type=public"); + if (!public_key) { + printf("Cannot load public key: %s\n", argv[3]); + display_openssl_errors(); + goto cleanup; + } + printf("Public key found.\n"); + + if ((ret = sign_verify_test(private_key, public_key)) < 0) { + printf("EC Sign-verify failed with err code: %d\n", ret); + goto cleanup; + } + printf("EC Sign-verify success\n"); + + ret = 0; +cleanup: + EVP_PKEY_free(private_key); + EVP_PKEY_free(public_key); + providers_cleanup(); + printf("\n"); + return ret; +} + +#else + +int main() { + return 0; +} + +#endif /* OPENSSL_VERSION_NUMBER >= 0x30000000L */ + +/* vim: set noexpandtab: */ diff --git a/tests/ed25519-keygen.softhsm b/tests/ed25519-keygen.softhsm new file mode 100755 index 00000000..841ffb48 --- /dev/null +++ b/tests/ed25519-keygen.softhsm @@ -0,0 +1,60 @@ +#!/bin/bash + +# 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 + +if [[ "${OPENSSL_VERSION}" =~ ^[012].* ]]; then + echo "Skipping test with OpenSSL ${OPENSSL_VERSION}" + exit 77 +fi + +# Initialize SoftHSM DB +init_db + +# Create token +init_card "token1" + +unset OPENSSL_ENGINES +export OPENSSL_MODULES="../src/.libs/" +export PKCS11_MODULE_PATH=${MODULE} +echo "OPENSSL_MODULES=${OPENSSL_MODULES}" +echo "PKCS11_MODULE_PATH=${PKCS11_MODULE_PATH}" + +# Load openssl settings +TEMP_LD_LIBRARY_PATH=${LD_LIBRARY_PATH} +. ${srcdir}/openssl-settings.sh + +${WRAPPER} ./ed25519-keygen ${MODULE} token1 libp11-keylabel ${PIN} +if test $? != 0; then + echo "Key generation failed" + exit 1 +fi + +# Restore settings +export LD_LIBRARY_PATH=${TEMP_LD_LIBRARY_PATH} + +echo "Checking pkcs11-tool result..." +list_objects | grep -q libp11-keylabel +if test $? != 0; then + echo "The key was not properly generated" + exit 1 +fi + +rm -rf "$outdir" + +exit 0 From 7febf5b7450a1f6038fa79aed41c377e6cbfd482 Mon Sep 17 00:00:00 2001 From: olszomal Date: Thu, 23 Oct 2025 11:25:37 +0200 Subject: [PATCH 2/5] Add libp11 tests and examples: generating Ed25519 and Ed448 keys --- .gitignore | 6 + examples/Makefile.am | 2 +- examples/ed25519keygen.c | 4 +- examples/ed448keygen.c | 177 +++++++++++++++++++++ tests/Makefile.am | 15 +- tests/ed25519-keygen-prov.c | 164 ++++++++++++++++++++ tests/ed25519-keygen.c | 211 ++++++++++---------------- tests/ed25519-keygen.softhsm | 11 +- tests/ed448-keygen-prov.c | 164 ++++++++++++++++++++ tests/ed448-keygen.c | 176 +++++++++++++++++++++ tests/ed448-keygen.softhsm | 57 +++++++ tests/eddsa_common.c | 183 ++++++++++++++++++++++ tests/eddsa_common.h | 53 +++++++ tests/provider-ed25519-keygen.softhsm | 63 ++++++++ tests/provider-ed448-keygen.softhsm | 63 ++++++++ 15 files changed, 1205 insertions(+), 144 deletions(-) create mode 100644 examples/ed448keygen.c create mode 100644 tests/ed25519-keygen-prov.c create mode 100644 tests/ed448-keygen-prov.c create mode 100644 tests/ed448-keygen.c create mode 100755 tests/ed448-keygen.softhsm create mode 100644 tests/eddsa_common.c create mode 100644 tests/eddsa_common.h create mode 100755 tests/provider-ed25519-keygen.softhsm create mode 100755 tests/provider-ed448-keygen.softhsm diff --git a/.gitignore b/.gitignore index 53113fa1..a9eb838e 100644 --- a/.gitignore +++ b/.gitignore @@ -64,6 +64,8 @@ examples/listkeys examples/listkeys_ext examples/eckeygen examples/rsakeygen +examples/ed25519keygen +examples/ed448keygen examples/storecert test-driver @@ -84,6 +86,10 @@ tests/rsa-oaep-prov tests/rsa-pss-sign-prov tests/rsa-keygen tests/ec-keygen +tests/ed25519-keygen +tests/ed448-keygen +tests/ed25519-keygen-prov +tests/ed448-keygen-prov tests/check-all-prov tests/*.log diff --git a/examples/Makefile.am b/examples/Makefile.am index 9962d289..a3a15365 100644 --- a/examples/Makefile.am +++ b/examples/Makefile.am @@ -4,7 +4,7 @@ AM_CPPFLAGS = -I$(srcdir) -I$(top_srcdir)/src \ EXTRA_DIST = README -noinst_PROGRAMS = auth decrypt getrandom listkeys listkeys_ext ed25519keygen eckeygen rsakeygen storecert +noinst_PROGRAMS = auth decrypt getrandom listkeys listkeys_ext ed25519keygen ed448keygen eckeygen rsakeygen storecert LDADD = ../src/libp11.la $(OPENSSL_LIBS) diff --git a/examples/ed25519keygen.c b/examples/ed25519keygen.c index 8a5d7f98..15b09f3f 100644 --- a/examples/ed25519keygen.c +++ b/examples/ed25519keygen.c @@ -3,7 +3,7 @@ * Author: Małgorzata Olszówka * All rights reserved. * - * Elliptic Curve key generation + * Elliptic Curve Ed25519 key generation * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -87,7 +87,7 @@ int main(int argc, char *argv[]) PKCS11_EDDSA_KGEN eddsa = {.nid = NID_ED25519}; PKCS11_KGEN_ATTRS eckg = {0}; - if (argc < 6) { + if (argc < 5) { fprintf(stderr, "usage: %s [module] [TOKEN] [KEY-LABEL] [KEY-ID] [PIN]\n", argv[0]); return 1; } diff --git a/examples/ed448keygen.c b/examples/ed448keygen.c new file mode 100644 index 00000000..69babe11 --- /dev/null +++ b/examples/ed448keygen.c @@ -0,0 +1,177 @@ +/* + * Copyright © 2025 Mobi - Com Polska Sp. z o.o. + * Author: Małgorzata Olszówka + * All rights reserved. + * + * Elliptic Curve Ed448 key generation + * + * 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 +#include + +#define CHECK_ERR(cond, txt, code) \ + do { \ + if (cond) { \ + fprintf(stderr, "%s\n", (txt)); \ + rc=(code); \ + goto end; \ + } \ + } while (0) + +static void error_queue(const char *name) +{ + if (ERR_peek_last_error()) { + fprintf(stderr, "%s generated errors:\n", name); + ERR_print_errors_fp(stderr); + } +} + +static int hex_to_bytes(const char *hex, unsigned char *out, size_t out_len) +{ + size_t i; + + for (i = 0; i < out_len; i++) { + if (sscanf(hex + (i * 2), "%2hhx", &out[i]) != 1) { + return -1; + } + } + return 0; +} + +static void list_keys(const char *title, const PKCS11_KEY *keys, + const unsigned int nkeys) { + unsigned int i; + + printf("\n%s:\n", title); + for (i = 0; i < nkeys; i++) { + printf(" #%d id=", i); + for (size_t j = 0; j < keys[i].id_len; j++) { + printf("%02x", keys[i].id[j]); + } + printf(";object=%s\n", keys[i].label); + } +} + +int main(int argc, char *argv[]) +{ + PKCS11_CTX *ctx = NULL; + PKCS11_SLOT *slots = NULL, *slot; + PKCS11_KEY *keys; + unsigned int nslots, nkeys; + unsigned char *key_id = NULL; + size_t len, key_id_len; + const char *key_id_str; + int rc = 0; + PKCS11_params params = {.sensitive = 1, .extractable = 0}; + PKCS11_EDDSA_KGEN eddsa = {.nid = NID_ED448}; + PKCS11_KGEN_ATTRS eckg = {0}; + + if (argc < 5) { + fprintf(stderr, "usage: %s [module] [TOKEN] [KEY-LABEL] [KEY-ID] [PIN]\n", argv[0]); + return 1; + } + key_id_str = argv[4]; + len = strlen(key_id_str); + CHECK_ERR(len % 2 != 0, "Invalid key ID format: odd length", 1); + + /* key_id_str is a null-terminated string, but key_id is not */ + key_id_len = len / 2; + key_id = OPENSSL_malloc(key_id_len); + CHECK_ERR(!key_id, "Memory allocation failed for key ID", 2); + + rc = hex_to_bytes(key_id_str, key_id, key_id_len); + CHECK_ERR(rc != 0, "Invalid hex digit in key ID", 3); + + ctx = PKCS11_CTX_new(); + error_queue("PKCS11_CTX_new"); + + /* load PKCS#11 module */ + rc = PKCS11_CTX_load(ctx, argv[1]); + error_queue("PKCS11_CTX_load"); + CHECK_ERR(rc < 0, "loading PKCS#11 module failed", 4); + + /* get information on all slots */ + rc = PKCS11_enumerate_slots(ctx, &slots, &nslots); + error_queue("PKCS11_enumerate_slots"); + CHECK_ERR(rc < 0, "no slots available", 5); + + slot = PKCS11_find_token(ctx, slots, nslots); + error_queue("PKCS11_find_token"); + while (slot) { + if (slot->token && slot->token->initialized && slot->token->label + && strcmp(argv[2], slot->token->label) == 0) + break; + slot = PKCS11_find_next_token(ctx, slots, nslots, slot); + }; + CHECK_ERR(!slot || !slot->token, "no token available", 6); + + printf("Found token:\n"); + printf("Slot manufacturer......: %s\n", slot->manufacturer); + printf("Slot description.......: %s\n", slot->description); + printf("Slot token label.......: %s\n", slot->token->label); + printf("Slot token serialnr....: %s\n", slot->token->serialnr); + + rc = PKCS11_login(slot, 0, argv[5]); + error_queue("PKCS11_login"); + CHECK_ERR(rc < 0, "PKCS11_login failed", 7); + + eckg.type = EVP_PKEY_ED448; + eckg.kgen.eddsa = &eddsa; + eckg.token_label = argv[2]; + eckg.key_label = argv[3]; + /* key_id is a raw binary buffer of length key_id_len */ + eckg.key_id = (const unsigned char *)key_id; + eckg.id_len = key_id_len; + eckg.key_params = ¶ms; + + rc = PKCS11_keygen(slot->token, &eckg); + error_queue("PKCS11_keygen"); + CHECK_ERR(rc < 0, "Failed to generate a key pair on the token", 8); + + printf("\nEd448 keys generated\n"); + + /* get private keys */ + rc = PKCS11_enumerate_keys(slot->token, &keys, &nkeys); + error_queue("PKCS11_enumerate_keys"); + CHECK_ERR(rc < 0, "PKCS11_enumerate_keys failed", 9); + CHECK_ERR(nkeys == 0, "No private keys found", 10); + list_keys("Private keys", keys, nkeys); + +end: + if (slots) + PKCS11_release_all_slots(ctx, slots, nslots); + if (ctx) { + PKCS11_CTX_unload(ctx); + PKCS11_CTX_free(ctx); + } + OPENSSL_free(key_id); + + if (rc) + printf("Failed (error code %d).\n", rc); + else + printf("Success.\n"); + return rc; +} + +/* vim: set noexpandtab: */ diff --git a/tests/Makefile.am b/tests/Makefile.am index ca493229..b18ddaec 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -1,4 +1,4 @@ -EXTRA_DIST = engines.cnf.in common.sh openssl-settings.sh helpers_prov.h +EXTRA_DIST = engines.cnf.in common.sh openssl-settings.sh helpers_prov.h eddsa_common.h AM_CFLAGS = $(OPENSSL_CFLAGS) AM_CPPFLAGS = \ @@ -28,7 +28,10 @@ check_PROGRAMS = \ check-all-prov \ rsa-keygen \ ec-keygen \ - ed25519-keygen + ed25519-keygen \ + ed448-keygen \ + ed25519-keygen-prov \ + ed448-keygen-prov dist_check_SCRIPTS = \ rsa-testpkcs11.softhsm \ rsa-testfork.softhsm \ @@ -47,6 +50,7 @@ dist_check_SCRIPTS = \ ec-copy.softhsm \ ec-keygen.softhsm \ ed25519-keygen.softhsm \ + ed448-keygen.softhsm \ fork-change-slot.softhsm \ case-insensitive.softhsm \ pkcs11-uri-pin-source.softhsm \ @@ -61,6 +65,8 @@ dist_check_SCRIPTS = \ provider-ec-check-privkey.softhsm \ provider-ec-check-all.softhsm \ provider-ec-copy.softhsm \ + provider-ed25519-keygen.softhsm \ + provider-ed448-keygen.softhsm \ provider-fork-change-slot.softhsm \ provider-case-insensitive.softhsm \ provider-pkcs11-uri-without-token.softhsm \ @@ -69,6 +75,10 @@ dist_check_DATA = \ rsa-cert.der rsa-privkey.der rsa-pubkey.der \ ec-cert.der ec-privkey.der ec-pubkey.der +ed25519_keygen_SOURCES = ed25519-keygen.c eddsa_common.c +ed448_keygen_SOURCES = ed448-keygen.c eddsa_common.c +ed25519_keygen_prov_SOURCES = ed25519-keygen-prov.c helpers_prov.c eddsa_common.c +ed448_keygen_prov_SOURCES = ed448-keygen-prov.c helpers_prov.c eddsa_common.c evp_sign_prov_SOURCES = evp-sign-prov.c helpers_prov.c fork_change_slot_prov_SOURCES = fork-change-slot-prov.c helpers_prov.c dup_key_prov_SOURCES = dup-key-prov.c helpers_prov.c @@ -76,7 +86,6 @@ check_privkey_prov_SOURCES = check-privkey-prov.c helpers_prov.c rsa_pss_sign_prov_SOURCES = rsa-pss-sign-prov.c helpers_prov.c rsa_oaep_prov_SOURCES = rsa-oaep-prov.c helpers_prov.c check_all_prov_SOURCES = check-all-prov.c helpers_prov.c -ed25519_keygen_SOURCES = ed25519-keygen.c helpers_prov.c TESTS = $(dist_check_SCRIPTS) diff --git a/tests/ed25519-keygen-prov.c b/tests/ed25519-keygen-prov.c new file mode 100644 index 00000000..ac377888 --- /dev/null +++ b/tests/ed25519-keygen-prov.c @@ -0,0 +1,164 @@ +/* + * Copyright © 2025 Mobi - Com Polska Sp. z o.o. + * Author: Małgorzata Olszówka + * All rights reserved. + * + * 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 . + */ + +#include "helpers_prov.h" +#include "eddsa_common.h" + +#if OPENSSL_VERSION_NUMBER >= 0x30000000L + +static void error_queue(const char *name) +{ + if (ERR_peek_last_error()) { + fprintf(stderr, "%s generated errors:\n", name); + ERR_print_errors_fp(stderr); + } +} + +int main(int argc, char *argv[]) +{ + int ret = EXIT_FAILURE; + int rc = 0; + PKCS11_CTX *ctx = NULL; + PKCS11_SLOT *slots = NULL, *slot; + unsigned int nslots; + EVP_PKEY *private_key = NULL, *public_key = NULL; + PKCS11_EDDSA_KGEN eddsa = { + .nid = NID_ED25519 + }; + PKCS11_params params = { + .sensitive = 1, + .extractable = 0, + }; + PKCS11_KGEN_ATTRS eckg = { + .type = EVP_PKEY_ED25519, + .kgen.eddsa = &eddsa, + .token_label = NULL, + .key_label = NULL, + .key_id = (const unsigned char *)"\x22\x33", + .id_len = 2, + .key_params = ¶ms, + }; + + if (argc < 4) { + printf("Too few arguments\n"); + printf("%s /usr/lib/opensc-pkcs11.so [MODULE] [TOKEN1] [KEY-LABEL] [PIN]\n", argv[0]); + goto cleanup; + } + eckg.token_label = argv[2]; + eckg.key_label = argv[3]; + + ctx = PKCS11_CTX_new(); + error_queue("PKCS11_CTX_new"); + + /* load PKCS#11 module */ + rc = PKCS11_CTX_load(ctx, argv[1]); + error_queue("PKCS11_CTX_load"); + CHECK_ERR(rc < 0, "loading PKCS#11 module failed", 4); + + /* get information on all slots */ + rc = PKCS11_enumerate_slots(ctx, &slots, &nslots); + error_queue("PKCS11_enumerate_slots"); + CHECK_ERR(rc < 0, "no slots available", 5); + + slot = PKCS11_find_token(ctx, slots, nslots); + error_queue("PKCS11_find_token"); + while (slot) { + if (slot->token && slot->token->initialized && slot->token->label + && strcmp(argv[2], slot->token->label) == 0) + break; + slot = PKCS11_find_next_token(ctx, slots, nslots, slot); + }; + CHECK_ERR(!slot || !slot->token, "no token available", 6); + + printf("Found token:\n"); + printf("Slot manufacturer......: %s\n", slot->manufacturer); + printf("Slot description.......: %s\n", slot->description); + printf("Slot token label.......: %s\n", slot->token->label); + printf("Slot token serialnr....: %s\n", slot->token->serialnr); + + rc = PKCS11_login(slot, 0, argv[4]); + error_queue("PKCS11_login"); + CHECK_ERR(rc < 0, "PKCS11_login failed", 7); + /* + * Ed25519 key generation test + */ + rc = PKCS11_keygen(slot->token, &eckg); + error_queue("PKCS11_keygen"); + CHECK_ERR(rc < 0, "Failed to generate a key pair on the token", 8); + printf("Ed25519 keys generated\n"); + + /* Load pkcs11prov and default providers */ + if (!providers_load()) { + display_openssl_errors(); + goto cleanup; + } + + /* Load keys */ + private_key = load_pkey("pkcs11:token=token1;object=libp11-keylabel;type=private", NULL); + if (!private_key) { + printf("Cannot load private key: %s\n", argv[3]); + display_openssl_errors(); + goto cleanup; + } + printf("Private key found.\n"); + + public_key = load_pubkey("pkcs11:token=token1;object=libp11-keylabel;type=public"); + if (!public_key) { + printf("Cannot load public key: %s\n", argv[3]); + display_openssl_errors(); + goto cleanup; + } + printf("Public key found.\n"); + + if ((ret = EVP_Digest_sign_verify_test(private_key, public_key)) < 0) { + printf("EVP_Digest_sign_verify_test() failed with err code: %d\n", ret); + display_openssl_errors(); + goto cleanup; + } + if ((ret = EVP_PKEY_sign_verify_test(private_key, public_key)) < 0) { + printf("EVP_PKEY_sign_verify_test() failed with err code: %d\n", ret); + display_openssl_errors(); + goto cleanup; + } + printf("Ed25519 Sign-verify success\n"); + + ret = 0; +cleanup: + EVP_PKEY_free(private_key); + EVP_PKEY_free(public_key); + if (slots) + PKCS11_release_all_slots(ctx, slots, nslots); + if (ctx) { + PKCS11_CTX_unload(ctx); + PKCS11_CTX_free(ctx); + } + providers_cleanup(); + printf("\n"); + return ret; +} + +#else + +int main() { + return 0; +} + +#endif /* OPENSSL_VERSION_NUMBER >= 0x30000000L */ + +/* vim: set noexpandtab: */ diff --git a/tests/ed25519-keygen.c b/tests/ed25519-keygen.c index 1fcd4686..54c28af8 100644 --- a/tests/ed25519-keygen.c +++ b/tests/ed25519-keygen.c @@ -17,101 +17,32 @@ * along with this program. If not, see . */ -#include -#include -#include "helpers_prov.h" +#define OPENSSL_SUPPRESS_DEPRECATED -#if OPENSSL_VERSION_NUMBER >= 0x30000000L +#include +#include "eddsa_common.h" -#define CHECK_ERR(cond, txt, code) \ - do { \ - if (cond) { \ - fprintf(stderr, "%s\n", (txt)); \ - rc=(code); \ - goto cleanup; \ - } \ - } while (0) +#if OPENSSL_VERSION_NUMBER >= 0x30000000L -static void error_queue(const char *name) +void display_openssl_errors(void) { - if (ERR_peek_last_error()) { - fprintf(stderr, "%s generated errors:\n", name); - ERR_print_errors_fp(stderr); + unsigned long e; + const char *file = NULL, *func = NULL, *reason = NULL; + int line = 0, flags = 0; + char err_buf[256]; + + while ((e = ERR_get_error_all(&file, &line, &func, &reason, &flags))) { + ERR_error_string_n(e, err_buf, sizeof(err_buf)); + fprintf(stderr, "%s:%d: %s: %s: %s\n", file ? file : "unknown file", + line, func ? func : "unknown function", + err_buf, reason ? reason : "unknown reason"); } } -static int sign_verify_test(EVP_PKEY *priv, EVP_PKEY *pub) { - EVP_MD_CTX *mdctx = NULL; - int retval = 0; - const char *msg = "libp11"; - size_t siglen, msglen = strlen(msg); - unsigned char *sig = NULL; - - if (!priv || !pub) { - printf("Where are the keys?\n"); - return -1; - } - - siglen = (size_t)EVP_PKEY_get_size(priv); - sig = OPENSSL_malloc(siglen); - if (!sig) { - ERR_print_errors_fp(stderr); - return -2; - } - - EVP_PKEY_CTX *ctx = EVP_PKEY_CTX_new_from_pkey(NULL, priv, NULL); - if (!ctx) { - ERR_print_errors_fp(stderr); - retval = -3; - goto err; - } - - if (EVP_PKEY_sign_init(ctx) <= 0) { - ERR_print_errors_fp(stderr); - retval = -4; - goto err; - } - - if (EVP_PKEY_sign(ctx, sig, &siglen, msg, msglen) <= 0) { - ERR_print_errors_fp(stderr); - retval = -5; - goto err; - } - printf("Sign success\n"); - - /* --- Verify --- */ - EVP_MD_CTX_reset(mdctx); - - if (EVP_DigestVerifyInit(mdctx, NULL, NULL, NULL, pub) != 1) { - ERR_print_errors_fp(stderr); - retval = -6; - goto err; - } - - if (EVP_DigestVerify(mdctx, sig, siglen, (const unsigned char *)msg, msglen) == 1) { - printf("Verify success\n"); - retval = 0; - } else { - ERR_print_errors_fp(stderr); - printf("Verify fail\n"); - retval = -7; - } - -err: - if (sig) - OPENSSL_free(sig); - if (mdctx) - EVP_MD_CTX_free(mdctx); - return retval; -} - int main(int argc, char *argv[]) { + ENGINE *engine = NULL; int ret = EXIT_FAILURE; - int rc = 0; - PKCS11_CTX *ctx = NULL; - PKCS11_SLOT *slots = NULL, *slot; - unsigned int nslots; EVP_PKEY *private_key = NULL, *public_key = NULL; PKCS11_EDDSA_KGEN eddsa = { .nid = NID_ED25519 @@ -130,62 +61,74 @@ int main(int argc, char *argv[]) .key_params = ¶ms, }; - if (argc < 4) { + if (argc < 5) { printf("Too few arguments\n"); - printf("%s /usr/lib/opensc-pkcs11.so [TOKEN1] [KEY-LABEL] [PIN]\n", argv[0]); + printf("%s /usr/lib/opensc-pkcs11.so [MODULE] [TOKEN1] [KEY-LABEL] [PIN] [CONF]\n", argv[0]); goto cleanup; } eckg.token_label = argv[2]; eckg.key_label = argv[3]; - ctx = PKCS11_CTX_new(); - error_queue("PKCS11_CTX_new"); - - /* load PKCS#11 module */ - rc = PKCS11_CTX_load(ctx, argv[1]); - error_queue("PKCS11_CTX_load"); - CHECK_ERR(rc < 0, "loading PKCS#11 module failed", 4); - - /* get information on all slots */ - rc = PKCS11_enumerate_slots(ctx, &slots, &nslots); - error_queue("PKCS11_enumerate_slots"); - CHECK_ERR(rc < 0, "no slots available", 5); - - slot = PKCS11_find_token(ctx, slots, nslots); - error_queue("PKCS11_find_token"); - while (slot) { - if (slot->token && slot->token->initialized && slot->token->label - && strcmp(argv[2], slot->token->label) == 0) - break; - slot = PKCS11_find_next_token(ctx, slots, nslots, slot); - }; - CHECK_ERR(!slot || !slot->token, "no token available", 6); + if (CONF_modules_load_file(argv[5], "engines", 0) <= 0) { + printf("cannot load %s\n", argv[5]); + display_openssl_errors(); + goto cleanup; + } - printf("Found token:\n"); - printf("Slot manufacturer......: %s\n", slot->manufacturer); - printf("Slot description.......: %s\n", slot->description); - printf("Slot token label.......: %s\n", slot->token->label); - printf("Slot token serialnr....: %s\n", slot->token->serialnr); + ENGINE_add_conf_module(); +# if OPENSSL_VERSION_NUMBER>=0x10100000 + OPENSSL_init_crypto(OPENSSL_INIT_ADD_ALL_CIPHERS \ + | OPENSSL_INIT_ADD_ALL_DIGESTS \ + | OPENSSL_INIT_LOAD_CONFIG, NULL); +# else + OpenSSL_add_all_algorithms(); + OpenSSL_add_all_digests(); + ERR_load_crypto_strings(); +# endif + ERR_clear_error(); + + ENGINE_load_builtin_engines(); + engine = ENGINE_by_id("pkcs11"); + if (engine == NULL) { + printf("Could not get engine\n"); + display_openssl_errors(); + goto cleanup; + } - rc = PKCS11_login(slot, 0, argv[4]); - error_queue("PKCS11_login"); - CHECK_ERR(rc < 0, "PKCS11_login failed", 7); + if (!ENGINE_ctrl_cmd_string(engine, "PIN", argv[4], 0)) { + display_openssl_errors(); + goto cleanup; + } + if (!ENGINE_ctrl_cmd_string(engine, "DEBUG_LEVEL", "7", 0)) { + display_openssl_errors(); + goto cleanup; + } + if (!ENGINE_ctrl_cmd_string(engine, "MODULE_PATH", argv[1], 0)) { + display_openssl_errors(); + goto cleanup; + } + if (!ENGINE_init(engine)) { + printf("Could not initialize engine\n"); + display_openssl_errors(); + goto cleanup; + } /* - * EC key generation test + * ENGINE_init() returned a functional reference, so free the structural + * reference from ENGINE_by_id(). */ - rc = PKCS11_keygen(slot->token, &eckg); - error_queue("PKCS11_keygen"); - CHECK_ERR(rc < 0, "Failed to generate a key pair on the token", 8); - printf("EC keys generated\n"); + ENGINE_free(engine); - /* Load pkcs11prov and default providers */ - if (!providers_load()) { - display_openssl_errors(); + /* + * Ed25519 key generation test + */ + if (!ENGINE_ctrl_cmd(engine, "KEYGEN", 0, &eckg, NULL, 1)) { + printf("Could not generate keys\n"); goto cleanup; } + printf("Ed25519 keys generated\n"); /* Load keys */ - private_key = load_pkey("pkcs11:token=token1;object=libp11-keylabel;type=private", NULL); + private_key = ENGINE_load_private_key(engine, "2233", NULL, NULL); if (!private_key) { printf("Cannot load private key: %s\n", argv[3]); display_openssl_errors(); @@ -193,7 +136,7 @@ int main(int argc, char *argv[]) } printf("Private key found.\n"); - public_key = load_pubkey("pkcs11:token=token1;object=libp11-keylabel;type=public"); + public_key = ENGINE_load_public_key(engine, "2233", NULL, NULL); if (!public_key) { printf("Cannot load public key: %s\n", argv[3]); display_openssl_errors(); @@ -201,17 +144,23 @@ int main(int argc, char *argv[]) } printf("Public key found.\n"); - if ((ret = sign_verify_test(private_key, public_key)) < 0) { - printf("EC Sign-verify failed with err code: %d\n", ret); + if ((ret = EVP_Digest_sign_verify_test(private_key, public_key)) < 0) { + printf("EVP_Digest_sign_verify_test() failed with err code: %d\n", ret); + display_openssl_errors(); + goto cleanup; + } + if ((ret = EVP_PKEY_sign_verify_test(private_key, public_key)) < 0) { + printf("EVP_PKEY_sign_verify_test() failed with err code: %d\n", ret); + display_openssl_errors(); goto cleanup; } - printf("EC Sign-verify success\n"); + printf("Ed25519 Sign-verify success\n"); ret = 0; cleanup: + ENGINE_finish(engine); EVP_PKEY_free(private_key); EVP_PKEY_free(public_key); - providers_cleanup(); printf("\n"); return ret; } diff --git a/tests/ed25519-keygen.softhsm b/tests/ed25519-keygen.softhsm index 841ffb48..9f0d1119 100755 --- a/tests/ed25519-keygen.softhsm +++ b/tests/ed25519-keygen.softhsm @@ -1,5 +1,8 @@ #!/bin/bash +# Copyright © 2024 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 @@ -29,17 +32,11 @@ init_db # Create token init_card "token1" -unset OPENSSL_ENGINES -export OPENSSL_MODULES="../src/.libs/" -export PKCS11_MODULE_PATH=${MODULE} -echo "OPENSSL_MODULES=${OPENSSL_MODULES}" -echo "PKCS11_MODULE_PATH=${PKCS11_MODULE_PATH}" - # Load openssl settings TEMP_LD_LIBRARY_PATH=${LD_LIBRARY_PATH} . ${srcdir}/openssl-settings.sh -${WRAPPER} ./ed25519-keygen ${MODULE} token1 libp11-keylabel ${PIN} +${WRAPPER} ./ed25519-keygen ${MODULE} token1 libp11-keylabel ${PIN} "${outdir}/engines.cnf" if test $? != 0; then echo "Key generation failed" exit 1 diff --git a/tests/ed448-keygen-prov.c b/tests/ed448-keygen-prov.c new file mode 100644 index 00000000..df4fa98b --- /dev/null +++ b/tests/ed448-keygen-prov.c @@ -0,0 +1,164 @@ +/* + * Copyright © 2025 Mobi - Com Polska Sp. z o.o. + * Author: Małgorzata Olszówka + * All rights reserved. + * + * 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 . + */ + +#include "helpers_prov.h" +#include "eddsa_common.h" + +#if OPENSSL_VERSION_NUMBER >= 0x30000000L + +static void error_queue(const char *name) +{ + if (ERR_peek_last_error()) { + fprintf(stderr, "%s generated errors:\n", name); + ERR_print_errors_fp(stderr); + } +} + +int main(int argc, char *argv[]) +{ + int ret = EXIT_FAILURE; + int rc = 0; + PKCS11_CTX *ctx = NULL; + PKCS11_SLOT *slots = NULL, *slot; + unsigned int nslots; + EVP_PKEY *private_key = NULL, *public_key = NULL; + PKCS11_EDDSA_KGEN eddsa = { + .nid = NID_ED448 + }; + PKCS11_params params = { + .sensitive = 1, + .extractable = 0, + }; + PKCS11_KGEN_ATTRS eckg = { + .type = EVP_PKEY_ED448, + .kgen.eddsa = &eddsa, + .token_label = NULL, + .key_label = NULL, + .key_id = (const unsigned char *)"\x22\x33", + .id_len = 2, + .key_params = ¶ms, + }; + + if (argc < 4) { + printf("Too few arguments\n"); + printf("%s /usr/lib/opensc-pkcs11.so [MODULE] [TOKEN1] [KEY-LABEL] [PIN]\n", argv[0]); + goto cleanup; + } + eckg.token_label = argv[2]; + eckg.key_label = argv[3]; + + ctx = PKCS11_CTX_new(); + error_queue("PKCS11_CTX_new"); + + /* load PKCS#11 module */ + rc = PKCS11_CTX_load(ctx, argv[1]); + error_queue("PKCS11_CTX_load"); + CHECK_ERR(rc < 0, "loading PKCS#11 module failed", 4); + + /* get information on all slots */ + rc = PKCS11_enumerate_slots(ctx, &slots, &nslots); + error_queue("PKCS11_enumerate_slots"); + CHECK_ERR(rc < 0, "no slots available", 5); + + slot = PKCS11_find_token(ctx, slots, nslots); + error_queue("PKCS11_find_token"); + while (slot) { + if (slot->token && slot->token->initialized && slot->token->label + && strcmp(argv[2], slot->token->label) == 0) + break; + slot = PKCS11_find_next_token(ctx, slots, nslots, slot); + }; + CHECK_ERR(!slot || !slot->token, "no token available", 6); + + printf("Found token:\n"); + printf("Slot manufacturer......: %s\n", slot->manufacturer); + printf("Slot description.......: %s\n", slot->description); + printf("Slot token label.......: %s\n", slot->token->label); + printf("Slot token serialnr....: %s\n", slot->token->serialnr); + + rc = PKCS11_login(slot, 0, argv[4]); + error_queue("PKCS11_login"); + CHECK_ERR(rc < 0, "PKCS11_login failed", 7); + /* + * Ed448 key generation test + */ + rc = PKCS11_keygen(slot->token, &eckg); + error_queue("PKCS11_keygen"); + CHECK_ERR(rc < 0, "Failed to generate a key pair on the token", 8); + printf("Ed448 keys generated\n"); + + /* Load pkcs11prov and default providers */ + if (!providers_load()) { + display_openssl_errors(); + goto cleanup; + } + + /* Load keys */ + private_key = load_pkey("pkcs11:token=token1;object=libp11-keylabel;type=private", NULL); + if (!private_key) { + printf("Cannot load private key: %s\n", argv[3]); + display_openssl_errors(); + goto cleanup; + } + printf("Private key found.\n"); + + public_key = load_pubkey("pkcs11:token=token1;object=libp11-keylabel;type=public"); + if (!public_key) { + printf("Cannot load public key: %s\n", argv[3]); + display_openssl_errors(); + goto cleanup; + } + printf("Public key found.\n"); + + if ((ret = EVP_Digest_sign_verify_test(private_key, public_key)) < 0) { + printf("EVP_Digest_sign_verify_test() failed with err code: %d\n", ret); + display_openssl_errors(); + goto cleanup; + } + if ((ret = EVP_PKEY_sign_verify_test(private_key, public_key)) < 0) { + printf("EVP_PKEY_sign_verify_test() failed with err code: %d\n", ret); + display_openssl_errors(); + goto cleanup; + } + printf("Ed448 Sign-verify success\n"); + + ret = 0; +cleanup: + EVP_PKEY_free(private_key); + EVP_PKEY_free(public_key); + if (slots) + PKCS11_release_all_slots(ctx, slots, nslots); + if (ctx) { + PKCS11_CTX_unload(ctx); + PKCS11_CTX_free(ctx); + } + providers_cleanup(); + printf("\n"); + return ret; +} + +#else + +int main() { + return 0; +} + +#endif /* OPENSSL_VERSION_NUMBER >= 0x30000000L */ + +/* vim: set noexpandtab: */ diff --git a/tests/ed448-keygen.c b/tests/ed448-keygen.c new file mode 100644 index 00000000..a5549f42 --- /dev/null +++ b/tests/ed448-keygen.c @@ -0,0 +1,176 @@ +/* + * Copyright © 2025 Mobi - Com Polska Sp. z o.o. + * Author: Małgorzata Olszówka + * All rights reserved. + * + * 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 "eddsa_common.h" + +#if OPENSSL_VERSION_NUMBER >= 0x30000000L + +void display_openssl_errors(void) +{ + unsigned long e; + const char *file = NULL, *func = NULL, *reason = NULL; + int line = 0, flags = 0; + char err_buf[256]; + + while ((e = ERR_get_error_all(&file, &line, &func, &reason, &flags))) { + ERR_error_string_n(e, err_buf, sizeof(err_buf)); + fprintf(stderr, "%s:%d: %s: %s: %s\n", file ? file : "unknown file", + line, func ? func : "unknown function", + err_buf, reason ? reason : "unknown reason"); + } +} + +int main(int argc, char *argv[]) +{ + ENGINE *engine = NULL; + int ret = EXIT_FAILURE; + EVP_PKEY *private_key = NULL, *public_key = NULL; + PKCS11_EDDSA_KGEN eddsa = { + .nid = NID_ED448 + }; + PKCS11_params params = { + .sensitive = 1, + .extractable = 0, + }; + PKCS11_KGEN_ATTRS eckg = { + .type = EVP_PKEY_ED448, + .kgen.eddsa = &eddsa, + .token_label = NULL, + .key_label = NULL, + .key_id = (const unsigned char *)"\x22\x33", + .id_len = 2, + .key_params = ¶ms, + }; + + if (argc < 5) { + printf("Too few arguments\n"); + printf("%s /usr/lib/opensc-pkcs11.so [MODULE] [TOKEN1] [KEY-LABEL] [PIN] [CONF]\n", argv[0]); + goto cleanup; + } + eckg.token_label = argv[2]; + eckg.key_label = argv[3]; + + if (CONF_modules_load_file(argv[5], "engines", 0) <= 0) { + printf("cannot load %s\n", argv[5]); + display_openssl_errors(); + goto cleanup; + } + + ENGINE_add_conf_module(); +# if OPENSSL_VERSION_NUMBER>=0x10100000 + OPENSSL_init_crypto(OPENSSL_INIT_ADD_ALL_CIPHERS \ + | OPENSSL_INIT_ADD_ALL_DIGESTS \ + | OPENSSL_INIT_LOAD_CONFIG, NULL); +# else + OpenSSL_add_all_algorithms(); + OpenSSL_add_all_digests(); + ERR_load_crypto_strings(); +# endif + ERR_clear_error(); + + ENGINE_load_builtin_engines(); + engine = ENGINE_by_id("pkcs11"); + if (engine == NULL) { + printf("Could not get engine\n"); + display_openssl_errors(); + goto cleanup; + } + + if (!ENGINE_ctrl_cmd_string(engine, "PIN", argv[4], 0)) { + display_openssl_errors(); + goto cleanup; + } + if (!ENGINE_ctrl_cmd_string(engine, "DEBUG_LEVEL", "7", 0)) { + display_openssl_errors(); + goto cleanup; + } + if (!ENGINE_ctrl_cmd_string(engine, "MODULE_PATH", argv[1], 0)) { + display_openssl_errors(); + goto cleanup; + } + if (!ENGINE_init(engine)) { + printf("Could not initialize engine\n"); + display_openssl_errors(); + goto cleanup; + } + /* + * ENGINE_init() returned a functional reference, so free the structural + * reference from ENGINE_by_id(). + */ + ENGINE_free(engine); + + /* + * Ed448 key generation test + */ + if (!ENGINE_ctrl_cmd(engine, "KEYGEN", 0, &eckg, NULL, 1)) { + printf("Could not generate keys\n"); + goto cleanup; + } + printf("Ed448 keys generated\n"); + + /* Load keys */ + private_key = ENGINE_load_private_key(engine, "2233", NULL, NULL); + if (!private_key) { + printf("Cannot load private key: %s\n", argv[3]); + display_openssl_errors(); + goto cleanup; + } + printf("Private key found.\n"); + + public_key = ENGINE_load_public_key(engine, "2233", NULL, NULL); + if (!public_key) { + printf("Cannot load public key: %s\n", argv[3]); + display_openssl_errors(); + goto cleanup; + } + printf("Public key found.\n"); + + if ((ret = EVP_Digest_sign_verify_test(private_key, public_key)) < 0) { + printf("EVP_Digest_sign_verify_test() failed with err code: %d\n", ret); + display_openssl_errors(); + goto cleanup; + } + if ((ret = EVP_PKEY_sign_verify_test(private_key, public_key)) < 0) { + printf("EVP_PKEY_sign_verify_test() failed with err code: %d\n", ret); + display_openssl_errors(); + goto cleanup; + } + printf("Ed448 Sign-verify success\n"); + + ret = 0; +cleanup: + ENGINE_finish(engine); + EVP_PKEY_free(private_key); + EVP_PKEY_free(public_key); + printf("\n"); + return ret; +} + +#else + +int main() { + return 0; +} + +#endif /* OPENSSL_VERSION_NUMBER >= 0x30000000L */ + +/* vim: set noexpandtab: */ diff --git a/tests/ed448-keygen.softhsm b/tests/ed448-keygen.softhsm new file mode 100755 index 00000000..62c04204 --- /dev/null +++ b/tests/ed448-keygen.softhsm @@ -0,0 +1,57 @@ +#!/bin/bash + +# Copyright © 2024 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 + +if [[ "${OPENSSL_VERSION}" =~ ^[012].* ]]; then + echo "Skipping test with OpenSSL ${OPENSSL_VERSION}" + exit 77 +fi + +# Initialize SoftHSM DB +init_db + +# Create token +init_card "token1" + +# Load openssl settings +TEMP_LD_LIBRARY_PATH=${LD_LIBRARY_PATH} +. ${srcdir}/openssl-settings.sh + +${WRAPPER} ./ed448-keygen ${MODULE} token1 libp11-keylabel ${PIN} "${outdir}/engines.cnf" +if test $? != 0; then + echo "Key generation failed" + exit 1 +fi + +# Restore settings +export LD_LIBRARY_PATH=${TEMP_LD_LIBRARY_PATH} + +echo "Checking pkcs11-tool result..." +list_objects | grep -q libp11-keylabel +if test $? != 0; then + echo "The key was not properly generated" + exit 1 +fi + +rm -rf "$outdir" + +exit 0 diff --git a/tests/eddsa_common.c b/tests/eddsa_common.c new file mode 100644 index 00000000..fa7979b6 --- /dev/null +++ b/tests/eddsa_common.c @@ -0,0 +1,183 @@ +/* + * Copyright © 2025 Mobi - Com Polska Sp. z o.o. + * Author: Małgorzata Olszówka + * All rights reserved. + * + * PKCS#11 provider tests support library + * + * 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. + */ + +/* Ed25519/ED448 common functions */ + +#include "eddsa_common.h" + +#if OPENSSL_VERSION_NUMBER >= 0x30000000L + +int EVP_Digest_sign_verify_test(EVP_PKEY *priv, EVP_PKEY *pub) +{ + EVP_MD_CTX *mdctx = NULL; + int retval = 0; + const unsigned char msg[] = "libp11"; + size_t siglen, msglen = sizeof(msg) - 1; + unsigned char *sig = NULL; + + if (!priv || !pub) { + printf("Where are the keys?\n"); + return -1; + } + + /* --- Sign --- */ + mdctx = EVP_MD_CTX_new(); + if (!mdctx) { + retval = -2; + goto err; + } + /* initialize the sign context using an Ed25519/Ed448 private key, + * notice that the digest name must NOT be used */ + if (EVP_DigestSignInit(mdctx, NULL, NULL, NULL, priv) != 1) { + retval = -3; + goto err; + } + /* calculate the required size for the signature by passing a NULL buffer */ + if (EVP_DigestSign(mdctx, NULL, &siglen, msg, msglen) != 1) { + retval = -4; + goto err; + } + sig = OPENSSL_malloc(siglen); + if (!sig) { + retval = -5; + goto err; + } + /* generate the signature */ + if (EVP_DigestSign(mdctx, sig, &siglen, msg, msglen) != 1) { + retval = -6; + goto err; + } + EVP_MD_CTX_destroy(mdctx); + + /* --- Verify --- */ + mdctx = EVP_MD_CTX_new(); + if (!mdctx) { + retval = -7; + goto err; + } + /* initialize the verify context with a Ed25519/Ed448 public key */ + if (EVP_DigestVerifyInit(mdctx, NULL, NULL, NULL, pub) != 1) { + retval = -8; + goto err; + } + /* Ed25519/Ed448 only supports the one shot interface using EVP_DigestVerify(), + * the streaming EVP_DigestVerifyUpdate() API is not supported */ + if (EVP_DigestVerify(mdctx, sig, siglen, msg, msglen) == 1) { + retval = 0; + goto err; + } else { + retval = -9; + goto err; + } + +err: + if (sig) + OPENSSL_free(sig); + if (mdctx) + EVP_MD_CTX_destroy(mdctx); + return retval; +} + +int EVP_PKEY_sign_verify_test(EVP_PKEY *priv, EVP_PKEY *pub) +{ + EVP_PKEY_CTX *ctx = NULL; + EVP_MD_CTX *mdctx = NULL; + int retval = 0; + const unsigned char msg[] = "libp11"; + size_t siglen, msglen = sizeof(msg) - 1; + unsigned char *sig = NULL; + + if (!priv || !pub) { + printf("Where are the keys?\n"); + return -1; + } + + /* --- Sign --- */ + siglen = (size_t)EVP_PKEY_get_size(priv); + sig = OPENSSL_malloc(siglen); + if (!sig) { + return -2; + } + ctx = EVP_PKEY_CTX_new_from_pkey(NULL, priv, NULL); + if (!ctx) { + retval = -3; + goto err; + } + if (EVP_PKEY_sign_init(ctx) <= 0) { + retval = -4; + goto err; + } + if (EVP_PKEY_sign(ctx, sig, &siglen, msg, msglen) <= 0) { + retval = -5; + goto err; + } + + /* --- Verify --- + * Ed25519 and Ed448 do not implement verify_init/verify in EVP_PKEY_METHOD. + * These algorithms support only one-shot signing and verification operations. + * See also: EVP_SIGNATURE-ED25519 and EVP_SIGNATURE-ED448. + */ + mdctx = EVP_MD_CTX_new(); + if (!mdctx) { + retval = -6; + goto err; + } + /* initialize the verify context with a Ed25519/Ed448 public key */ + if (EVP_DigestVerifyInit(mdctx, NULL, NULL, NULL, pub) != 1) { + retval = -7; + goto err; + } + /* Ed25519/Ed448 only supports the one shot interface using EVP_DigestVerify(), + * the streaming EVP_DigestVerifyUpdate() API is not supported */ + if (EVP_DigestVerify(mdctx, sig, siglen, msg, msglen) == 1) { + retval = 0; + goto err; + } else { + retval = -8; + goto err; + } + +err: + if (sig) + OPENSSL_free(sig); + if (ctx) + EVP_PKEY_CTX_free(ctx); + if (mdctx) + EVP_MD_CTX_destroy(mdctx); + return retval; +} + +#else + +/* Disable ISO C forbids an empty translation unit [-Wpedantic] warning */ +extern int make_iso_compilers_happy; + +#endif /* OPENSSL_VERSION_NUMBER >= 0x30000000L */ + +/* vim: set noexpandtab: */ diff --git a/tests/eddsa_common.h b/tests/eddsa_common.h new file mode 100644 index 00000000..c61e445c --- /dev/null +++ b/tests/eddsa_common.h @@ -0,0 +1,53 @@ +/* + * Copyright © 2025 Mobi - Com Polska Sp. z o.o. + * Author: Małgorzata Olszówka + * All rights reserved. + * + * PKCS#11 provider tests support library + * + * 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. + */ + +/* Ed25519/ED448 common functions */ + +#include + +#if OPENSSL_VERSION_NUMBER >= 0x30000000L + +#include +#include + +#define CHECK_ERR(cond, txt, code) \ + do { \ + if (cond) { \ + fprintf(stderr, "%s\n", (txt)); \ + rc=(code); \ + goto cleanup; \ + } \ + } while (0) + +int EVP_Digest_sign_verify_test(EVP_PKEY *, EVP_PKEY *); +int EVP_PKEY_sign_verify_test(EVP_PKEY *, EVP_PKEY *); + +#endif /* OPENSSL_VERSION_NUMBER >= 0x30000000L */ + +/* vim: set noexpandtab: */ diff --git a/tests/provider-ed25519-keygen.softhsm b/tests/provider-ed25519-keygen.softhsm new file mode 100755 index 00000000..4c01fa98 --- /dev/null +++ b/tests/provider-ed25519-keygen.softhsm @@ -0,0 +1,63 @@ +#!/bin/bash + +# Copyright © 2024 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 + +if [[ "${OPENSSL_VERSION}" =~ ^[012].* ]]; then + echo "Skipping test with OpenSSL ${OPENSSL_VERSION}" + exit 77 +fi + +# Initialize SoftHSM DB +init_db + +# Create token +init_card "token1" + +unset OPENSSL_ENGINES +export OPENSSL_MODULES="../src/.libs/" +export PKCS11_MODULE_PATH=${MODULE} +echo "OPENSSL_MODULES=${OPENSSL_MODULES}" +echo "PKCS11_MODULE_PATH=${PKCS11_MODULE_PATH}" + +# Load openssl settings +TEMP_LD_LIBRARY_PATH=${LD_LIBRARY_PATH} +. ${srcdir}/openssl-settings.sh + +${WRAPPER} ./ed25519-keygen-prov ${MODULE} token1 libp11-keylabel ${PIN} +if test $? != 0; then + echo "Key generation failed" + exit 1 +fi + +# Restore settings +export LD_LIBRARY_PATH=${TEMP_LD_LIBRARY_PATH} + +echo "Checking pkcs11-tool result..." +list_objects | grep -q libp11-keylabel +if test $? != 0; then + echo "The key was not properly generated" + exit 1 +fi + +rm -rf "$outdir" + +exit 0 diff --git a/tests/provider-ed448-keygen.softhsm b/tests/provider-ed448-keygen.softhsm new file mode 100755 index 00000000..182afc47 --- /dev/null +++ b/tests/provider-ed448-keygen.softhsm @@ -0,0 +1,63 @@ +#!/bin/bash + +# Copyright © 2024 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 + +if [[ "${OPENSSL_VERSION}" =~ ^[012].* ]]; then + echo "Skipping test with OpenSSL ${OPENSSL_VERSION}" + exit 77 +fi + +# Initialize SoftHSM DB +init_db + +# Create token +init_card "token1" + +unset OPENSSL_ENGINES +export OPENSSL_MODULES="../src/.libs/" +export PKCS11_MODULE_PATH=${MODULE} +echo "OPENSSL_MODULES=${OPENSSL_MODULES}" +echo "PKCS11_MODULE_PATH=${PKCS11_MODULE_PATH}" + +# Load openssl settings +TEMP_LD_LIBRARY_PATH=${LD_LIBRARY_PATH} +. ${srcdir}/openssl-settings.sh + +${WRAPPER} ./ed448-keygen-prov ${MODULE} token1 libp11-keylabel ${PIN} +if test $? != 0; then + echo "Key generation failed" + exit 1 +fi + +# Restore settings +export LD_LIBRARY_PATH=${TEMP_LD_LIBRARY_PATH} + +echo "Checking pkcs11-tool result..." +list_objects | grep -q libp11-keylabel +if test $? != 0; then + echo "The key was not properly generated" + exit 1 +fi + +rm -rf "$outdir" + +exit 0 From 42d5fdc260ca4b1ccf308da152199b62f67985ea Mon Sep 17 00:00:00 2001 From: olszomal Date: Thu, 23 Oct 2025 12:21:54 +0200 Subject: [PATCH 3/5] Fix Windows build by including p11_eddsa.c in Makefile.mak --- src/Makefile.mak | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Makefile.mak b/src/Makefile.mak index e113250f..9de361d4 100644 --- a/src/Makefile.mak +++ b/src/Makefile.mak @@ -5,7 +5,7 @@ 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_atfork.obj p11_eddsa.obj LIBP11_LIB = libp11.lib LIBP11_TARGET = libp11.dll From e93c99093a27df4448fa6ccf1ee486723016c932 Mon Sep 17 00:00:00 2001 From: olszomal Date: Thu, 23 Oct 2025 14:12:05 +0200 Subject: [PATCH 4/5] Enable EdDSA/EC code only for OpenSSL 3.0+ with EC support --- examples/ed25519keygen.c | 11 +++++++++++ examples/ed448keygen.c | 11 +++++++++++ src/libp11-int.h | 7 +++++++ src/p11_eddsa.c | 6 +++--- src/p11_front.c | 10 ++++++++++ src/p11_key.c | 9 ++++++++- src/p11_load.c | 2 ++ src/p11_pkey.c | 14 ++++++++++++++ 8 files changed, 66 insertions(+), 4 deletions(-) diff --git a/examples/ed25519keygen.c b/examples/ed25519keygen.c index 15b09f3f..a954ac75 100644 --- a/examples/ed25519keygen.c +++ b/examples/ed25519keygen.c @@ -27,6 +27,8 @@ * SUCH DAMAGE. */ +#if !defined(OPENSSL_NO_EC) && OPENSSL_VERSION_NUMBER >= 0x30000000L + #include #include @@ -174,4 +176,13 @@ int main(int argc, char *argv[]) return rc; } +#else /* !defined(OPENSSL_NO_EC) && OPENSSL_VERSION_NUMBER >= 0x30000000L */ + +int main(void) +{ + return 0; +} + +#endif /* !defined(OPENSSL_NO_EC) && OPENSSL_VERSION_NUMBER >= 0x30000000L */ + /* vim: set noexpandtab: */ diff --git a/examples/ed448keygen.c b/examples/ed448keygen.c index 69babe11..8b8ddf23 100644 --- a/examples/ed448keygen.c +++ b/examples/ed448keygen.c @@ -27,6 +27,8 @@ * SUCH DAMAGE. */ +#if !defined(OPENSSL_NO_EC) && OPENSSL_VERSION_NUMBER >= 0x30000000L + #include #include @@ -174,4 +176,13 @@ int main(int argc, char *argv[]) return rc; } +#else /* !defined(OPENSSL_NO_EC) && OPENSSL_VERSION_NUMBER >= 0x30000000L */ + +int main(void) +{ + return 0; +} + +#endif /* !defined(OPENSSL_NO_EC) && OPENSSL_VERSION_NUMBER >= 0x30000000L */ + /* vim: set noexpandtab: */ diff --git a/src/libp11-int.h b/src/libp11-int.h index 55bf8f5d..bfa8d5f5 100644 --- a/src/libp11-int.h +++ b/src/libp11-int.h @@ -121,9 +121,14 @@ struct pkcs11_object_ops { }; extern PKCS11_OBJECT_ops pkcs11_rsa_ops; + +#ifndef OPENSSL_NO_EC extern PKCS11_OBJECT_ops pkcs11_ec_ops; +# if OPENSSL_VERSION_NUMBER >= 0x30000000L extern PKCS11_OBJECT_ops pkcs11_ed25519_ops; extern PKCS11_OBJECT_ops pkcs11_ed448_ops; +# endif /* OPENSSL_VERSION_NUMBER >= 0x30000000L */ +#endif /* OPENSSL_NO_EC */ extern int pkcs11_global_data_refs; @@ -393,8 +398,10 @@ extern PKCS11_OBJECT_private *pkcs11_get_ex_data_ec(const EC_KEY *ec); /* Set PKCS11_KEY for an EC_KEY */ extern void pkcs11_set_ex_data_ec(EC_KEY *ec, PKCS11_OBJECT_private *key); +# if OPENSSL_VERSION_NUMBER >= 0x30000000L /* Retrieve PKCS11_KEY from an EVP_PKEY */ extern PKCS11_OBJECT_private *pkcs11_get_ex_data_pkey(const EVP_PKEY *pkey); +# endif /* OPENSSL_VERSION_NUMBER >= 0x30000000L */ /* Set PKCS11_KEY for an EVP_PKEY */ extern void pkcs11_set_ex_data_pkey(EVP_PKEY *pkey, PKCS11_OBJECT_private *key); diff --git a/src/p11_eddsa.c b/src/p11_eddsa.c index cf35bc84..92dbe674 100644 --- a/src/p11_eddsa.c +++ b/src/p11_eddsa.c @@ -31,7 +31,7 @@ #include "libp11-int.h" #include -#ifndef OPENSSL_NO_EC +#if !defined(OPENSSL_NO_EC) && OPENSSL_VERSION_NUMBER >= 0x30000000L #include #include @@ -486,11 +486,11 @@ PKCS11_OBJECT_ops pkcs11_ed448_ops = { pkcs11_get_evp_key_ed448, }; -#else /* OPENSSL_NO_EC */ +#else /* !defined(OPENSSL_NO_EC) && OPENSSL_VERSION_NUMBER >= 0x30000000L */ /* if not built with EC or OpenSSL does not support EdDSA * add these routines so engine_pkcs11 can be built now and not * require further changes */ #warning "EdDSA support not built with libp11" -#endif /* OPENSSL_NO_EC */ +#endif /* !defined(OPENSSL_NO_EC) && OPENSSL_VERSION_NUMBER >= 0x30000000L */ diff --git a/src/p11_front.c b/src/p11_front.c index 93ed0ed7..ef81f540 100644 --- a/src/p11_front.c +++ b/src/p11_front.c @@ -425,10 +425,12 @@ int PKCS11_keygen(PKCS11_TOKEN *token, PKCS11_KGEN_ATTRS *kg) case EVP_PKEY_EC: return pkcs11_ec_keygen(slot, kg->kgen.ec->curve, kg->key_label, kg->key_id, kg->id_len, kg->key_params); +# if OPENSSL_VERSION_NUMBER >= 0x30000000L case EVP_PKEY_ED25519: case EVP_PKEY_ED448: return pkcs11_eddsa_keygen(slot, kg->kgen.eddsa->nid, kg->key_label, kg->key_id, kg->id_len, kg->key_params); +# endif /* OPENSSL_VERSION_NUMBER >= 0x30000000L */ #endif /* OPENSSL_NO_EC */ default: return -1; @@ -440,12 +442,17 @@ int PKCS11_generate_key(PKCS11_TOKEN *token, int algorithm, char *label, unsigned char *id, size_t id_len) { PKCS11_params key_params = { .extractable = 0, .sensitive = 1 }; +#ifndef OPENSSL_NO_EC PKCS11_EC_KGEN ec_kgen; +# if OPENSSL_VERSION_NUMBER >= 0x30000000L PKCS11_EDDSA_KGEN eddsa_kgen; +# endif /* OPENSSL_VERSION_NUMBER >= 0x30000000L */ +#endif /* OPENSSL_NO_EC */ PKCS11_RSA_KGEN rsa_kgen; PKCS11_KGEN_ATTRS kgen_attrs = { 0 }; switch (algorithm) { +#ifndef OPENSSL_NO_EC case EVP_PKEY_EC: ec_kgen.curve = OBJ_nid2sn(param); kgen_attrs = (PKCS11_KGEN_ATTRS){ @@ -458,6 +465,7 @@ int PKCS11_generate_key(PKCS11_TOKEN *token, int algorithm, .key_params = &key_params }; break; +# if OPENSSL_VERSION_NUMBER >= 0x30000000L case EVP_PKEY_ED25519: eddsa_kgen.nid = NID_ED25519; kgen_attrs = (PKCS11_KGEN_ATTRS){ @@ -483,6 +491,8 @@ int PKCS11_generate_key(PKCS11_TOKEN *token, int algorithm, .key_params = &key_params }; break; +# endif /* OPENSSL_VERSION_NUMBER >= 0x30000000L */ +#endif /* OPENSSL_NO_EC */ default: rsa_kgen.bits = param; kgen_attrs = (PKCS11_KGEN_ATTRS){ diff --git a/src/p11_key.c b/src/p11_key.c index f2f94fb7..f96155db 100644 --- a/src/p11_key.c +++ b/src/p11_key.c @@ -27,6 +27,7 @@ #define MAX_PIN_LENGTH 256 #ifndef OPENSSL_NO_EC +# if OPENSSL_VERSION_NUMBER >= 0x30000000L /* DER OIDs */ static const unsigned char OID_ED25519[] = { 0x06, 0x03, 0x2B, 0x65, 0x70 }; static const unsigned char OID_ED448[] = { 0x06, 0x03, 0x2B, 0x65, 0x71 }; @@ -40,6 +41,7 @@ static const unsigned char STR_ED448[] = { 0x13, 0x0A, /* tag + length */ 'e','d','w','a','r','d','s','4','4','8' }; +# endif /* OPENSSL_VERSION_NUMBER >= 0x30000000L */ #endif /* OPENSSL_NO_EC */ static int pkcs11_find_keys(PKCS11_SLOT_private *, CK_SESSION_HANDLE, unsigned int, @@ -115,6 +117,7 @@ PKCS11_OBJECT_private *pkcs11_object_from_handle(PKCS11_SLOT_private *slot, case CKK_EC: ops = &pkcs11_ec_ops; break; +# if OPENSSL_VERSION_NUMBER >= 0x30000000L case CKK_EC_EDWARDS: /* Read the CKA_EC_PARAMS to distinguish Ed25519 vs Ed448 */ if (pkcs11_getattr_alloc(ctx, session, object, @@ -139,6 +142,7 @@ PKCS11_OBJECT_private *pkcs11_object_from_handle(PKCS11_SLOT_private *slot, } OPENSSL_free(data); break; +# endif /* OPENSSL_VERSION_NUMBER >= 0x30000000L */ #endif /* OPENSSL_NO_EC */ default: /* Ignore any keys we don't understand */ @@ -443,6 +447,7 @@ int pkcs11_ec_keygen(PKCS11_SLOT_private *slot, const char *curve, return 0; } +# if OPENSSL_VERSION_NUMBER >= 0x30000000L /** * Generate EdDSA (Ed25519 / Ed448) key pair directly on token */ @@ -498,7 +503,7 @@ int pkcs11_eddsa_keygen(PKCS11_SLOT_private *slot, CRYPTOKI_checkerr(CKR_F_PKCS11_GENERATE_KEY, rv); return 0; } - +# endif /* OPENSSL_VERSION_NUMBER >= 0x30000000L */ #endif /* OPENSSL_NO_EC */ /* @@ -692,11 +697,13 @@ EVP_PKEY *pkcs11_get_key(PKCS11_OBJECT_private *key0, CK_OBJECT_CLASS object_cla ret = EVP_PKEY_dup(key->evp_key); #endif break; +#if OPENSSL_VERSION_NUMBER >= 0x30000000L case EVP_PKEY_ED25519: case EVP_PKEY_ED448: ret = key->evp_key; EVP_PKEY_up_ref(key->evp_key); break; +#endif /* OPENSSL_VERSION_NUMBER >= 0x30000000L */ default: pkcs11_log(key0->slot->ctx, LOG_DEBUG, "Unsupported key type\n"); } diff --git a/src/p11_load.c b/src/p11_load.c index e59f79b5..e5b7840b 100644 --- a/src/p11_load.c +++ b/src/p11_load.c @@ -184,7 +184,9 @@ void pkcs11_CTX_free(PKCS11_CTX *ctx) #if OPENSSL_VERSION_NUMBER >= 0x10100002L #ifndef OPENSSL_NO_EC pkcs11_ec_key_method_free(); +# if OPENSSL_VERSION_NUMBER >= 0x30000000L pkcs11_ed_key_method_free(); +# endif /* OPENSSL_VERSION_NUMBER >= 0x30000000L */ #endif /* OPENSSL_NO_EC */ #else /* OPENSSL_VERSION_NUMBER */ #ifndef OPENSSL_NO_ECDSA diff --git a/src/p11_pkey.c b/src/p11_pkey.c index 24d33abb..07a3c82c 100644 --- a/src/p11_pkey.c +++ b/src/p11_pkey.c @@ -36,6 +36,7 @@ static int (*orig_pkey_ec_sign) (EVP_PKEY_CTX *ctx, unsigned char *sig, size_t *siglen, const unsigned char *tbs, size_t tbslen); +# if OPENSSL_VERSION_NUMBER >= 0x30000000L static int (*orig_pkey_ed448_sign_init) (EVP_PKEY_CTX *ctx); static int (*orig_pkey_ed448_sign) (EVP_PKEY_CTX *ctx, unsigned char *sig, size_t *siglen, @@ -45,6 +46,7 @@ static int (*orig_pkey_ed25519_sign_init) (EVP_PKEY_CTX *ctx); static int (*orig_pkey_ed25519_sign) (EVP_PKEY_CTX *ctx, unsigned char *sig, size_t *siglen, const unsigned char *tbs, size_t tbslen); +# endif /* OPENSSL_VERSION_NUMBER >= 0x30000000L */ #endif /* OPENSSL_NO_EC */ #if OPENSSL_VERSION_NUMBER < 0x10002000L || defined(LIBRESSL_VERSION_NUMBER) @@ -618,6 +620,7 @@ static int pkcs11_try_pkey_ec_sign(EVP_PKEY_CTX *evp_pkey_ctx, return 1; } +# if OPENSSL_VERSION_NUMBER >= 0x30000000L static int pkcs11_try_pkey_eddsa_sign(EVP_PKEY_CTX *evp_pkey_ctx, unsigned char *sig, size_t *siglen, const unsigned char *tbs, size_t tbslen) @@ -700,6 +703,7 @@ static int pkcs11_try_pkey_eddsa_sign(EVP_PKEY_CTX *evp_pkey_ctx, *siglen = size; return 1; } +# endif /* OPENSSL_VERSION_NUMBER >= 0x30000000L */ static int pkcs11_pkey_ec_sign(EVP_PKEY_CTX *evp_pkey_ctx, unsigned char *sig, size_t *siglen, @@ -713,6 +717,7 @@ static int pkcs11_pkey_ec_sign(EVP_PKEY_CTX *evp_pkey_ctx, return ret; } +# if OPENSSL_VERSION_NUMBER >= 0x30000000L static int pkcs11_pkey_ed448_sign(EVP_PKEY_CTX *evp_pkey_ctx, unsigned char *sig, size_t *siglen, const unsigned char *tbs, size_t tbslen) @@ -736,6 +741,7 @@ static int pkcs11_pkey_ed25519_sign(EVP_PKEY_CTX *evp_pkey_ctx, ret = (*orig_pkey_ed25519_sign)(evp_pkey_ctx, sig, siglen, tbs, tbslen); return ret; } +# endif /* OPENSSL_VERSION_NUMBER >= 0x30000000L */ static EVP_PKEY_METHOD *pkcs11_pkey_method_ec(void) { @@ -755,6 +761,7 @@ static EVP_PKEY_METHOD *pkcs11_pkey_method_ec(void) return new_meth; } +# if OPENSSL_VERSION_NUMBER >= 0x30000000L static EVP_PKEY_METHOD *pkcs11_pkey_method_ed448(void) { EVP_PKEY_METHOD *orig_meth, *new_meth; @@ -792,6 +799,7 @@ static EVP_PKEY_METHOD *pkcs11_pkey_method_ed25519(void) return new_meth; } +# endif /* OPENSSL_VERSION_NUMBER >= 0x30000000L */ #endif /* OPENSSL_NO_EC */ @@ -802,16 +810,20 @@ int PKCS11_pkey_meths(ENGINE *e, EVP_PKEY_METHOD **pmeth, EVP_PKEY_RSA, #ifndef OPENSSL_NO_EC EVP_PKEY_EC, +# if OPENSSL_VERSION_NUMBER >= 0x30000000L EVP_PKEY_ED25519, EVP_PKEY_ED448, +# endif /* OPENSSL_VERSION_NUMBER >= 0x30000000L */ #endif /* OPENSSL_NO_EC */ 0 }; static EVP_PKEY_METHOD *pkey_method_rsa = NULL; #ifndef OPENSSL_NO_EC static EVP_PKEY_METHOD *pkey_method_ec = NULL; +# if OPENSSL_VERSION_NUMBER >= 0x30000000L static EVP_PKEY_METHOD *pkey_method_ed448 = NULL; static EVP_PKEY_METHOD *pkey_method_ed25519 = NULL; +# endif /* OPENSSL_VERSION_NUMBER >= 0x30000000L */ #endif /* OPENSSL_NO_EC */ (void)e; /* squash the unused parameter warning */ @@ -839,6 +851,7 @@ int PKCS11_pkey_meths(ENGINE *e, EVP_PKEY_METHOD **pmeth, return 0; *pmeth = pkey_method_ec; return 1; /* success */ +# if OPENSSL_VERSION_NUMBER >= 0x30000000L case EVP_PKEY_ED448: if (!pkey_method_ed448) pkey_method_ed448 = pkcs11_pkey_method_ed448(); @@ -853,6 +866,7 @@ int PKCS11_pkey_meths(ENGINE *e, EVP_PKEY_METHOD **pmeth, return 0; *pmeth = pkey_method_ed25519; return 1; /* success */ +# endif /* OPENSSL_VERSION_NUMBER >= 0x30000000L */ #endif /* OPENSSL_NO_EC */ } *pmeth = NULL; From 74860302f106b05d7aa7b62241923c5361633cd0 Mon Sep 17 00:00:00 2001 From: olszomal Date: Thu, 30 Oct 2025 15:41:59 +0100 Subject: [PATCH 5/5] Align Ed25519 and Ed448 engine methods with global EVP_PKEY methods --- src/p11_eddsa.c | 61 ++++++++----- src/p11_pkey.c | 232 +++++++++++++++++++++++++++++++----------------- 2 files changed, 187 insertions(+), 106 deletions(-) diff --git a/src/p11_eddsa.c b/src/p11_eddsa.c index 92dbe674..43c81ffc 100644 --- a/src/p11_eddsa.c +++ b/src/p11_eddsa.c @@ -74,17 +74,13 @@ static int pkcs11_eddsa_sign(unsigned char *sigret, unsigned int *siglen, PKCS11_SLOT_private *slot = key->slot; PKCS11_CTX_private *ctx = slot->ctx; CK_SESSION_HANDLE session; - CK_EDDSA_PARAMS eddsa_params; CK_MECHANISM mechanism; CK_ULONG ck_siglen = (CK_ULONG)(*siglen); CK_ULONG ck_tbslen = (CK_ULONG)tbslen; - /* eddsa_params.phFlag = 0 => PureEdDSA, no prehash */ - memset(&eddsa_params, 0, sizeof(eddsa_params)); + /* PureEdDSA, no prehash */ memset(&mechanism, 0, sizeof(mechanism)); mechanism.mechanism = CKM_EDDSA; - mechanism.pParameter = &eddsa_params; - mechanism.ulParameterLen = sizeof(eddsa_params); if (pkcs11_get_session(slot, 0, &session)) return -1; @@ -106,7 +102,6 @@ static int pkcs11_eddsa_sign(unsigned char *sigret, unsigned int *siglen, return (int)ck_siglen; } - /* * EVP_PKEY method sign wrapper for EdDSA. * This function is invoked internally by EVP_PKEY_sign(). @@ -160,19 +155,12 @@ static int pkcs11_eddsa_pmeth_digestsign(EVP_MD_CTX *ctx, unsigned char *sig, pkey = EVP_PKEY_CTX_get0_pkey(EVP_MD_CTX_pkey_ctx(ctx)); if (!pkey) - return 0; + return -1; key = pkcs11_get_ex_data_pkey(pkey); - if (!key) { - /* assume a foreign key */ - if (EVP_PKEY_id(pkey) == EVP_PKEY_ED25519) - return orig_ed25519_digestsign(ctx, sig, siglen, tbs, tbslen); - else if (EVP_PKEY_id(pkey) == EVP_PKEY_ED448) - return orig_ed448_digestsign(ctx, sig, siglen, tbs, tbslen); - else - return 0; - - } + if (!key) + return -1; + /* Step 1: caller asks for signature length only */ if (sig == NULL) { if (EVP_PKEY_id(pkey) == EVP_PKEY_ED25519) @@ -180,7 +168,7 @@ static int pkcs11_eddsa_pmeth_digestsign(EVP_MD_CTX *ctx, unsigned char *sig, else if (EVP_PKEY_id(pkey) == EVP_PKEY_ED448) *siglen = 114; /* fixed size for Ed448 */ else - return 0; + return -1; /* success: report the expected signature length only, * no signing is performed in this call */ return 1; @@ -190,12 +178,37 @@ static int pkcs11_eddsa_pmeth_digestsign(EVP_MD_CTX *ctx, unsigned char *sig, tmp_len = (unsigned int)*siglen; rv = pkcs11_eddsa_sign(sig, &tmp_len, tbs, (unsigned int)tbslen, key); if (rv < 0) - return 0; + return -1; *siglen = tmp_len; 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). @@ -231,11 +244,12 @@ static int pkcs11_ed25519_method_new() 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 */ + /* 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; @@ -245,7 +259,7 @@ static int pkcs11_ed25519_method_new() /* 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_eddsa_pmeth_digestsign); + 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 */ @@ -273,11 +287,12 @@ static int pkcs11_ed448_method_new() 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 */ + /* 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; @@ -287,7 +302,7 @@ static int pkcs11_ed448_method_new() /* 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_eddsa_pmeth_digestsign); + 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 */ diff --git a/src/p11_pkey.c b/src/p11_pkey.c index 07a3c82c..760ca56e 100644 --- a/src/p11_pkey.c +++ b/src/p11_pkey.c @@ -37,13 +37,10 @@ static int (*orig_pkey_ec_sign) (EVP_PKEY_CTX *ctx, const unsigned char *tbs, size_t tbslen); # if OPENSSL_VERSION_NUMBER >= 0x30000000L -static int (*orig_pkey_ed448_sign_init) (EVP_PKEY_CTX *ctx); -static int (*orig_pkey_ed448_sign) (EVP_PKEY_CTX *ctx, +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_ed25519_sign_init) (EVP_PKEY_CTX *ctx); -static int (*orig_pkey_ed25519_sign) (EVP_PKEY_CTX *ctx, +static int (*orig_pkey_ed448_digestsign)(EVP_MD_CTX *ctx, unsigned char *sig, size_t *siglen, const unsigned char *tbs, size_t tbslen); # endif /* OPENSSL_VERSION_NUMBER >= 0x30000000L */ @@ -621,24 +618,69 @@ static int pkcs11_try_pkey_ec_sign(EVP_PKEY_CTX *evp_pkey_ctx, } # if OPENSSL_VERSION_NUMBER >= 0x30000000L -static int pkcs11_try_pkey_eddsa_sign(EVP_PKEY_CTX *evp_pkey_ctx, +/* PKCS#11 sign implementation for Ed25519 / Ed448 */ +static int pkcs11_eddsa_sign(unsigned char *sigret, unsigned int *siglen, + const unsigned char *tbs, unsigned int tbslen, PKCS11_OBJECT_private *key) +{ + int rv; + PKCS11_SLOT_private *slot = key->slot; + PKCS11_CTX_private *ctx = slot->ctx; + CK_SESSION_HANDLE session; + CK_MECHANISM mechanism; + CK_ULONG ck_siglen = (CK_ULONG)(*siglen); + CK_ULONG ck_tbslen = (CK_ULONG)tbslen; + + if (!ctx) + return -1; + + /* PureEdDSA, no prehash */ + memset(&mechanism, 0, sizeof(mechanism)); + mechanism.mechanism = CKM_EDDSA; + + pkcs11_log(ctx, LOG_DEBUG, "%s:%d pkcs11_eddsa_sign() " + "sigret=%p *siglen=%lu tbs=%p tbslen=%lu\n", + __FILE__, __LINE__, sigret, *siglen, tbs, tbslen); + + if (pkcs11_get_session(slot, 0, &session)) + return -1; + + rv = CRYPTOKI_call(ctx, + C_SignInit(session, &mechanism, key->object)); + if (!rv && key->always_authenticate == CK_TRUE) + rv = pkcs11_authenticate(key, session); + if (!rv) + rv = CRYPTOKI_call(ctx, + C_Sign(session, (CK_BYTE_PTR)tbs, ck_tbslen, sigret, &ck_siglen)); + pkcs11_put_session(slot, session); + + if (rv) { + CKRerr(CKR_F_PKCS11_EDDSA_SIGN, rv); + return -1; + } + *siglen = (unsigned int)ck_siglen; + return (int)ck_siglen; +} + +/* + * 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(). + */ +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) { EVP_PKEY *pkey; - size_t expected_size; - int type; - int rv = CKR_GENERAL_ERROR; - CK_ULONG size = (CK_ULONG)*siglen; PKCS11_OBJECT_private *key; - PKCS11_SLOT_private *slot; - PKCS11_CTX_private *ctx; - CK_SESSION_HANDLE session; - CK_MECHANISM mechanism; + unsigned int tmp_len; + int rv; if (!evp_pkey_ctx) return -1; + if (*siglen > UINT_MAX) + return 0; + pkey = EVP_PKEY_CTX_get0_pkey(evp_pkey_ctx); if (!pkey) return -1; @@ -650,98 +692,114 @@ static int pkcs11_try_pkey_eddsa_sign(EVP_PKEY_CTX *evp_pkey_ctx, if (check_object_fork(key) < 0) return -1; - slot = key->slot; - ctx = slot->ctx; - if (!ctx) - return -1; - - type = EVP_PKEY_id(pkey); - if (type == EVP_PKEY_ED25519) - expected_size = 64; - else if (type == EVP_PKEY_ED448) - expected_size = 114; - else + tmp_len = (unsigned int)*siglen; + rv = pkcs11_eddsa_sign(sig, &tmp_len, tbs, (unsigned int)tbslen, key); + if (rv < 0) return -1; - if (!sig) { - *siglen = expected_size; - return 1; - } - if (*siglen < expected_size) - return -1; - - pkcs11_log(ctx, LOG_DEBUG, "%s:%d pkcs11_try_pkey_eddsa_sign() " - "sig=%p *siglen=%lu tbs=%p tbslen=%lu\n", - __FILE__, __LINE__, sig, *siglen, tbs, tbslen); + *siglen = tmp_len; + return 1; +} - memset(&mechanism, 0, sizeof mechanism); - mechanism.mechanism = CKM_EDDSA; +/* + * 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_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; + unsigned int tmp_len; + int rv; - if (pkcs11_get_session(slot, 0, &session)) + pkey = EVP_PKEY_CTX_get0_pkey(EVP_MD_CTX_pkey_ctx(ctx)); + if (!pkey) return -1; - rv = CRYPTOKI_call(ctx, C_SignInit(session, &mechanism, key->object)); - if (rv != CKR_OK) { - pkcs11_log(ctx, LOG_DEBUG, "%s:%d C_SignInit rv=%d\n", - __FILE__, __LINE__, rv); - } else if (key->always_authenticate == CK_TRUE) - rv = pkcs11_authenticate(key, session); + key = pkcs11_get_ex_data_pkey(pkey); + if (!key) + return -1; - if (rv == CKR_OK) { - rv = CRYPTOKI_call(ctx, - C_Sign(session, (CK_BYTE_PTR)tbs, (CK_ULONG)tbslen, sig, &size)); - if (rv != CKR_OK) { - pkcs11_log(ctx, LOG_DEBUG, "%s:%d C_Sign rv=%d\n", - __FILE__, __LINE__, rv); - } + /* 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; } - pkcs11_put_session(slot, session); - if (rv != CKR_OK) - return -1; + /* Step 2: actual signing */ + tmp_len = (unsigned int)*siglen; + rv = pkcs11_eddsa_sign(sig, &tmp_len, tbs, (unsigned int)tbslen, key); + if (rv < 0) + return 1; - *siglen = size; + *siglen = tmp_len; return 1; } -# endif /* OPENSSL_VERSION_NUMBER >= 0x30000000L */ -static int pkcs11_pkey_ec_sign(EVP_PKEY_CTX *evp_pkey_ctx, +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_try_pkey_ec_sign(evp_pkey_ctx, sig, siglen, tbs, tbslen); + ret = pkcs11_eddsa_pmeth_digestsign(ctx, sig, siglen, tbs, tbslen); if (ret < 0) - ret = (*orig_pkey_ec_sign)(evp_pkey_ctx, sig, siglen, tbs, tbslen); + ret = (*orig_pkey_ed25519_digestsign)(ctx, sig, siglen, tbs, tbslen); return ret; } -# if OPENSSL_VERSION_NUMBER >= 0x30000000L -static int pkcs11_pkey_ed448_sign(EVP_PKEY_CTX *evp_pkey_ctx, +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_try_pkey_eddsa_sign(evp_pkey_ctx, sig, siglen, tbs, tbslen); + ret = pkcs11_eddsa_pmeth_digestsign(ctx, sig, siglen, tbs, tbslen); if (ret < 0) - ret = (*orig_pkey_ed448_sign)(evp_pkey_ctx, sig, siglen, tbs, tbslen); + ret = (*orig_pkey_ed448_digestsign)(ctx, sig, siglen, tbs, tbslen); return ret; } -static int pkcs11_pkey_ed25519_sign(EVP_PKEY_CTX *evp_pkey_ctx, +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 */ + } +} +# endif /* OPENSSL_VERSION_NUMBER >= 0x30000000L */ + +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) { int ret; - ret = pkcs11_try_pkey_eddsa_sign(evp_pkey_ctx, sig, siglen, tbs, tbslen); + ret = pkcs11_try_pkey_ec_sign(evp_pkey_ctx, sig, siglen, tbs, tbslen); if (ret < 0) - ret = (*orig_pkey_ed25519_sign)(evp_pkey_ctx, sig, siglen, tbs, tbslen); + ret = (*orig_pkey_ec_sign)(evp_pkey_ctx, sig, siglen, tbs, tbslen); return ret; } -# endif /* OPENSSL_VERSION_NUMBER >= 0x30000000L */ static EVP_PKEY_METHOD *pkcs11_pkey_method_ec(void) { @@ -762,40 +820,48 @@ static EVP_PKEY_METHOD *pkcs11_pkey_method_ec(void) } # if OPENSSL_VERSION_NUMBER >= 0x30000000L -static EVP_PKEY_METHOD *pkcs11_pkey_method_ed448(void) +static EVP_PKEY_METHOD *pkcs11_pkey_method_ed25519(void) { EVP_PKEY_METHOD *orig_meth, *new_meth; - orig_meth = (EVP_PKEY_METHOD *)EVP_PKEY_meth_find(EVP_PKEY_ED448); - EVP_PKEY_meth_get_sign(orig_meth, - &orig_pkey_ed448_sign_init, &orig_pkey_ed448_sign); + orig_meth = (EVP_PKEY_METHOD *)EVP_PKEY_meth_find(EVP_PKEY_ED25519); - /* don't assume any digest related defaults */ - new_meth = EVP_PKEY_meth_new(EVP_PKEY_ED448, EVP_PKEY_FLAG_SIGCTX_CUSTOM); + /* 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); + /* Duplicate the original method */ EVP_PKEY_meth_copy(new_meth, orig_meth); - EVP_PKEY_meth_set_sign(new_meth, - orig_pkey_ed448_sign_init, pkcs11_pkey_ed448_sign); + /* 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); return new_meth; } -static EVP_PKEY_METHOD *pkcs11_pkey_method_ed25519(void) +static EVP_PKEY_METHOD *pkcs11_pkey_method_ed448(void) { EVP_PKEY_METHOD *orig_meth, *new_meth; - orig_meth = (EVP_PKEY_METHOD *)EVP_PKEY_meth_find(EVP_PKEY_ED25519); - EVP_PKEY_meth_get_sign(orig_meth, - &orig_pkey_ed25519_sign_init, &orig_pkey_ed25519_sign); + orig_meth = (EVP_PKEY_METHOD *)EVP_PKEY_meth_find(EVP_PKEY_ED448); + + /* 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); - /* don't assume any digest related defaults */ - new_meth = EVP_PKEY_meth_new(EVP_PKEY_ED25519, EVP_PKEY_FLAG_SIGCTX_CUSTOM); + /* Don't assume any digest related defaults */ + new_meth = EVP_PKEY_meth_new(EVP_PKEY_ED448, EVP_PKEY_FLAG_SIGCTX_CUSTOM); + /* Duplicate the original method */ EVP_PKEY_meth_copy(new_meth, orig_meth); - EVP_PKEY_meth_set_sign(new_meth, - orig_pkey_ed25519_sign_init, pkcs11_pkey_ed25519_sign); + /* 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 new_meth; }