From 39291e78fd781f97d24ad98194fc3788d1437f29 Mon Sep 17 00:00:00 2001 From: olszomal Date: Mon, 6 Jul 2026 15:31:44 +0200 Subject: [PATCH 1/3] Add PKCS#11 provider support for key exchange Add OSSL_OP_KEYEXCH support for EC, X25519, and X448 keys in the PKCS#11 provider path. This includes ECDH derive support, cofactor mode handling, peer public key handling, X25519/X448 key generation, and XDH derive plumbing through PKCS#11. --- NEWS | 2 + src/libp11-int.h | 22 ++- src/libp11.exports | 1 + src/libp11.h | 6 + src/p11_ec.c | 109 ++++++-------- src/p11_eddsa.c | 315 ++++++++++++++++++++++++++++++++++++++++- src/p11_front.c | 63 +++++++++ src/p11_key.c | 102 ++++++++++++- src/p11_pkey.c | 260 +++++++++++++++++++++++++++++++++- src/pkcs11.h | 3 + src/provider.c | 195 ++++++++++++++++++++++++- src/provider_helpers.c | 285 ++++++++++++++++++++++++++++++++++--- src/provider_helpers.h | 20 ++- 13 files changed, 1284 insertions(+), 99 deletions(-) diff --git a/NEWS b/NEWS index 1cb131bf..9775a686 100644 --- a/NEWS +++ b/NEWS @@ -6,6 +6,8 @@ New in 0.4.19; unreleased * Added support for ML-DSA, SLH-DSA and FALCON key generation, signing and verification (Małgorzata Olszówka) * Added PQC key generation examples and provider tests (Małgorzata Olszówka) +* Added PKCS#11 provider support for ECDH, X25519 and X448 key exchange, + including X25519 and X448 key generation (Małgorzata Olszówka) New in 0.4.18; 2026-02-16; Michał Trojnara * Support for RSA-PSS and RSA-OAEP using keys retrieved using the diff --git a/src/libp11-int.h b/src/libp11-int.h index b9b1a8c7..78e39df4 100644 --- a/src/libp11-int.h +++ b/src/libp11-int.h @@ -132,6 +132,8 @@ 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; +extern PKCS11_OBJECT_ops pkcs11_x25519_ops; +extern PKCS11_OBJECT_ops pkcs11_x448_ops; # endif /* OPENSSL_VERSION_NUMBER >= 0x30000000L */ #endif /* OPENSSL_NO_EC */ @@ -401,6 +403,10 @@ extern int pkcs11_ec_keygen(PKCS11_SLOT_private *tpriv, 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); + +extern int pkcs11_xdh_keygen(PKCS11_SLOT_private *tpriv, + int nid, const char *label, const unsigned char *id, + size_t id_len, const PKCS11_params *params); #endif /* !defined(OPENSSL_NO_ECX) && OPENSSL_VERSION_NUMBER >= 0x30000000L */ #if OPENSSL_VERSION_NUMBER >= 0x30500000L @@ -455,12 +461,12 @@ extern int pkcs11_evp_pkey_ec_sign(PKCS11_OBJECT_private *key, const unsigned char *tbs, size_t tbslen); #endif /* OPENSSL_NO_EC */ -#ifndef OPENSSL_NO_ECX +#if !defined(OPENSSL_NO_ECX) && OPENSSL_VERSION_NUMBER >= 0x30000000L /* Sign message input with EdDSA private key via PKCS#11 mechanism */ extern int pkcs11_evp_pkey_eddsa_sign(PKCS11_OBJECT_private *key, unsigned char *sig, size_t *siglen, const unsigned char *tbs, size_t tbslen); -#endif /* OPENSSL_NO_ECX */ +#endif /* !defined(OPENSSL_NO_ECX) && OPENSSL_VERSION_NUMBER >= 0x30000000L */ #if OPENSSL_VERSION_NUMBER >= 0x30500000L #ifndef OPENSSL_NO_ML_DSA @@ -497,6 +503,18 @@ extern int pkcs11_evp_pkey_rsa_decrypt(PKCS11_OBJECT_private *key, unsigned char *out, size_t *outlen, const unsigned char *in, size_t inlen); +#ifndef OPENSSL_NO_EC +extern int pkcs11_evp_pkey_ecdh_derive(PKCS11_OBJECT_private *key, + const unsigned char *peer_pub, size_t peer_pub_len, + int cofactor_mode, unsigned char *secret, size_t *secretlen); +#endif /* EVP_PKEY_EC */ + +#if !defined(OPENSSL_NO_ECX) && OPENSSL_VERSION_NUMBER >= 0x30000000L +extern int pkcs11_evp_pkey_xdh_derive(PKCS11_OBJECT_private *key, + const unsigned char *peer_pub, size_t peer_pub_len, + unsigned char *secret, size_t *secretlen); +#endif /* !defined(OPENSSL_NO_ECX) && OPENSSL_VERSION_NUMBER >= 0x30000000L */ + /* This function has never been implemented */ extern int pkcs11_verify(int type, const unsigned char *m, unsigned int m_len, diff --git a/src/libp11.exports b/src/libp11.exports index f59d61d7..d2ab387c 100644 --- a/src/libp11.exports +++ b/src/libp11.exports @@ -41,6 +41,7 @@ PKCS11_sign PKCS11_evp_pkey_sign PKCS11_evp_pkey_verify PKCS11_evp_pkey_decrypt +PKCS11_evp_pkey_derive PKCS11_private_encrypt PKCS11_private_decrypt PKCS11_verify diff --git a/src/libp11.h b/src/libp11.h index bc6d54ef..0f4b3cab 100644 --- a/src/libp11.h +++ b/src/libp11.h @@ -575,6 +575,12 @@ extern int PKCS11_evp_pkey_decrypt(EVP_PKEY *pk, int type, const char *mdname, unsigned char *oaep_label, size_t oaep_labellen, unsigned char *sig, size_t *siglen, const unsigned char *in, size_t inlen); + +/* Perform a private-key derive operation using a PKCS#11-backed EVP_PKEY */ +extern int PKCS11_evp_pkey_derive(EVP_PKEY *pk, int type, + const unsigned char *peer_pub, size_t peer_pub_len, + int cofactor_mode, unsigned char *secret, size_t *secretlen); + #endif /* OPENSSL_VERSION_NUMBER >= 0x30000000L */ /* Encrypts data using the private key */ diff --git a/src/p11_ec.c b/src/p11_ec.c index 50d18ba2..2a562a36 100644 --- a/src/p11_ec.c +++ b/src/p11_ec.c @@ -531,81 +531,66 @@ static void pkcs11_ecdh_params_free(CK_ECDH1_DERIVE_PARAMS *parms) * could also be supported, and the secret key object could be returned. */ static int pkcs11_ecdh_derive(unsigned char **out, size_t *outlen, - const int key_len, - const unsigned long ecdh_mechanism, - const void *ec_params, - void *outnewkey, - PKCS11_OBJECT_private *key) + const int key_len, const unsigned long ecdh_mechanism, + const void *ec_params, void *outnewkey, PKCS11_OBJECT_private *key) { - PKCS11_SLOT_private *slot = key->slot; - PKCS11_CTX_private *ctx = slot->ctx; - CK_SESSION_HANDLE session; - CK_MECHANISM mechanism; - int rv; + const CK_ECDH1_DERIVE_PARAMS *params; + unsigned char *secret = NULL; + size_t secretlen; + int cofactor_mode; + + if (key == NULL || out == NULL || outlen == NULL || + ec_params == NULL || key_len <= 0) + return -1; + + /* Returning the temporary derived key object is not supported by the + * shared EVP helper. pkcs11_ec_ckey() only asks for the value. */ + if (outnewkey != NULL) { + P11err(P11_F_PKCS11_ECDH_DERIVE, P11_R_NOT_SUPPORTED); + return -1; + } - CK_BBOOL _true = TRUE; - CK_BBOOL _false = FALSE; - CK_OBJECT_HANDLE newkey = CK_INVALID_HANDLE; - CK_OBJECT_CLASS newkey_class = CKO_SECRET_KEY; - CK_KEY_TYPE newkey_type = CKK_GENERIC_SECRET; - CK_ULONG newkey_len = key_len; - CK_OBJECT_HANDLE *tmpnewkey = (CK_OBJECT_HANDLE *)outnewkey; - CK_ATTRIBUTE newkey_template[] = { - {CKA_TOKEN, &_false, sizeof(_false)}, /* session only object */ - {CKA_CLASS, &newkey_class, sizeof(newkey_class)}, - {CKA_KEY_TYPE, &newkey_type, sizeof(newkey_type)}, - {CKA_VALUE_LEN, &newkey_len, sizeof(newkey_len)}, - {CKA_SENSITIVE, &_false, sizeof(_false)}, - {CKA_EXTRACTABLE, &_true, sizeof(_true)}, - {CKA_DERIVE, &_true, sizeof(_true)}, - }; - - memset(&mechanism, 0, sizeof(mechanism)); - mechanism.mechanism = ecdh_mechanism; - mechanism.pParameter = (void *)ec_params; switch (ecdh_mechanism) { - case CKM_ECDH1_DERIVE: - case CKM_ECDH1_COFACTOR_DERIVE: - mechanism.ulParameterLen = sizeof(CK_ECDH1_DERIVE_PARAMS); - break; + case CKM_ECDH1_DERIVE: + cofactor_mode = 0; + break; + case CKM_ECDH1_COFACTOR_DERIVE: + cofactor_mode = 1; + break; #if 0 - /* TODO */ - case CK_ECMQV_DERIVE_PARAMS: - mechanism.ulParameterLen = sizeof(CK_ECMQV_DERIVE_PARAMS); - break; + /* TODO + * Elliptic Curve Menezes–Qu–Vanstone key agreement + * ECMQV needs CK_ECMQV_DERIVE_PARAMS and two key pairs per party. + * Keep disabled until we have a token exposing CKM_ECMQV_DERIVE + * for interoperability testing. */ + case CKM_ECMQV_DERIVE: + break; #endif - default: - P11err(P11_F_PKCS11_ECDH_DERIVE, P11_R_NOT_SUPPORTED); - return -1; + default: + P11err(P11_F_PKCS11_ECDH_DERIVE, P11_R_NOT_SUPPORTED); + return -1; } - if (pkcs11_get_session(slot, 0, &session)) + params = (const CK_ECDH1_DERIVE_PARAMS *)ec_params; + if (params->pPublicData == NULL || params->ulPublicDataLen == 0) return -1; - rv = CRYPTOKI_call(ctx, C_DeriveKey(session, &mechanism, key->object, - newkey_template, sizeof(newkey_template)/sizeof(*newkey_template), &newkey)); - if (rv != CKR_OK) - goto error; + secretlen = (size_t)key_len; + secret = OPENSSL_malloc(secretlen); + if (secret == NULL) + return -1; - /* Return the value of the secret key and/or the object handle of the secret key */ - if (out && outlen) { /* pkcs11_ec_ckey only asks for the value */ - if (pkcs11_getattr_alloc(ctx, session, newkey, CKA_VALUE, out, outlen)) { - CRYPTOKI_call(ctx, C_DestroyObject(session, newkey)); - goto error; - } + /* Return the value of the secret key. */ + if (pkcs11_evp_pkey_ecdh_derive(key, + params->pPublicData, params->ulPublicDataLen, + cofactor_mode, secret, &secretlen) <= 0) { + OPENSSL_clear_free(secret, (size_t)key_len); + return -1; } - if (tmpnewkey) /* For future use (not used by pkcs11_ec_ckey) */ - *tmpnewkey = newkey; - else /* Destroy the temporary key */ - CRYPTOKI_call(ctx, C_DestroyObject(session, newkey)); - - pkcs11_put_session(slot, session); + *out = secret; + *outlen = secretlen; return 0; -error: - pkcs11_put_session(slot, session); - CKRerr(CKR_F_PKCS11_ECDH_DERIVE, rv); - return -1; } static int pkcs11_ecdh_compute_key(unsigned char **buf, size_t *buflen, diff --git a/src/p11_eddsa.c b/src/p11_eddsa.c index ab16201c..85a86326 100644 --- a/src/p11_eddsa.c +++ b/src/p11_eddsa.c @@ -3,8 +3,9 @@ * 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. + * This file implements the handling of EdDSA and XDH keys stored on a PKCS11 token. + * Inside EVP_PKEY, Ed25519/Ed448 and X25519/X448 keys are stored in an ECX_KEY + * structure. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -40,6 +41,11 @@ static EVP_PKEY_METHOD *pkcs11_ed25519_method = NULL; static EVP_PKEY_METHOD *pkcs11_ed448_method = NULL; static const EVP_PKEY_METHOD *orig_ed25519_method = NULL; static const EVP_PKEY_METHOD *orig_ed448_method = NULL; + +static EVP_PKEY_METHOD *pkcs11_x25519_method = NULL; +static EVP_PKEY_METHOD *pkcs11_x448_method = NULL; +static const EVP_PKEY_METHOD *orig_x25519_method = NULL; +static const EVP_PKEY_METHOD *orig_x448_method = NULL; #endif /* OPENSSL_VERSION_NUMBER < 0x40000000L */ int (*orig_ed25519_digestsign)(EVP_MD_CTX *ctx, unsigned char *sig, size_t *siglen, @@ -48,8 +54,16 @@ int (*orig_ed25519_digestsign)(EVP_MD_CTX *ctx, unsigned char *sig, size_t *sigl int (*orig_ed448_digestsign)(EVP_MD_CTX *ctx, unsigned char *sig, size_t *siglen, const unsigned char *tbs, size_t tbslen); +int (*orig_x25519_derive)(EVP_PKEY_CTX *ctx, unsigned char *secret, size_t *secretlen); + +int (*orig_x448_derive)(EVP_PKEY_CTX *ctx, unsigned char *secret, size_t *secretlen); + #if OPENSSL_VERSION_NUMBER < 0x40000000L +/******************************************************************************/ +/* EdDSA methods */ +/******************************************************************************/ + /* * EVP_PKEY method sign wrapper for EdDSA. * This function is invoked internally by EVP_PKEY_sign(). @@ -147,6 +161,7 @@ static int pkcs11_eddsa_pmeth_digestsign(EVP_MD_CTX *ctx, unsigned char *sig, 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) @@ -304,8 +319,220 @@ void pkcs11_ed_key_method_free(void) pkcs11_ed448_method_free(); } + +/******************************************************************************/ +/* XDH methods */ +/******************************************************************************/ + +static int pkcs11_xdh_pmeth_derive(EVP_PKEY_CTX *ctx, unsigned char *secret, + size_t *secretlen) +{ + EVP_PKEY *pkey, *peerkey; + PKCS11_OBJECT_private *key; + PKCS11_SLOT_private *slot; + CK_SESSION_HANDLE session; + unsigned char peer_pub[56]; + size_t peer_pub_len = sizeof(peer_pub); + size_t expected_len; + int type; + + if (ctx == NULL || secretlen == NULL) + return 0; + + pkey = EVP_PKEY_CTX_get0_pkey(ctx); + if (!pkey) + return 0; + + key = pkcs11_get_ex_data_pkey(pkey); + if (!key) + return 0; + + slot = key->slot; + if (!slot) + return 0; + + if (pkcs11_get_session(slot, 0, &session)) + return 0; + + pkcs11_put_session(slot, session); + + type = EVP_PKEY_id(pkey); + switch (type) { + case EVP_PKEY_X25519: + expected_len = 32; + break; + case EVP_PKEY_X448: + expected_len = 56; + break; + default: + return -1; + } + + if (secret == NULL) { + *secretlen = expected_len; + return 1; + } + + if (*secretlen < expected_len) { + *secretlen = expected_len; + return 0; + } + + peerkey = EVP_PKEY_CTX_get0_peerkey(ctx); + if (peerkey == NULL) + return 0; + + if (EVP_PKEY_get_raw_public_key(peerkey, NULL, &peer_pub_len) <= 0) + return 0; + + if (peer_pub_len != expected_len || peer_pub_len > sizeof(peer_pub)) + return 0; + + if (EVP_PKEY_get_raw_public_key(peerkey, peer_pub, &peer_pub_len) <= 0) + return 0; + + if (!pkcs11_evp_pkey_xdh_derive(key, peer_pub, peer_pub_len, + secret, secretlen)) + return 0; + + return 1; +} + +static int pkcs11_pkey_x25519_derive(EVP_PKEY_CTX *ctx, unsigned char *secret, + size_t *secretlen) +{ + int ret; + + ret = pkcs11_xdh_pmeth_derive(ctx, secret, secretlen); + if (ret < 0) + /* assume a foreign key */ + ret = (*orig_x25519_derive)(ctx, secret, secretlen); + return ret; +} + +static int pkcs11_pkey_x448_derive(EVP_PKEY_CTX *ctx, unsigned char *secret, + size_t *secretlen) +{ + int ret; + + ret = pkcs11_xdh_pmeth_derive(ctx, secret, secretlen); + if (ret < 0) + ret = (*orig_x448_derive)(ctx, secret, secretlen); + return ret; +} + +/* Global initialize X25519 EVP_PKEY_METHOD */ +static int pkcs11_x25519_method_new(void) +{ + int orig_id, orig_flags; + + if (pkcs11_x25519_method) + return 1; /* EVP_PKEY_X25519 method already initialized */ + + orig_x25519_method = EVP_PKEY_meth_find(EVP_PKEY_X25519); + if (!orig_x25519_method) + return 0; + + EVP_PKEY_meth_get0_info(&orig_id, &orig_flags, orig_x25519_method); + if (orig_id != EVP_PKEY_X25519 || !(orig_flags & EVP_PKEY_FLAG_SIGCTX_CUSTOM)) + return 0; + + EVP_PKEY_meth_get_derive(orig_x25519_method, NULL, &orig_x25519_derive); + if (!orig_x25519_derive) + return 0; + + pkcs11_x25519_method = EVP_PKEY_meth_new(EVP_PKEY_X25519, EVP_PKEY_FLAG_AUTOARGLEN); + if (!pkcs11_x25519_method) + return 0; + + /* Duplicate the original method */ + EVP_PKEY_meth_copy(pkcs11_x25519_method, orig_x25519_method); + + /* Override selected X25519 method callbacks with PKCS#11 implementations */ + EVP_PKEY_meth_set_derive(pkcs11_x25519_method, NULL, pkcs11_pkey_x25519_derive); + + /* Register the method globally */ + if (!EVP_PKEY_meth_add0(pkcs11_x25519_method)) { + EVP_PKEY_meth_free(pkcs11_x25519_method); + pkcs11_x25519_method = NULL; + return 0; + } + return 1; +} + +/* Global initialize X448 EVP_PKEY_METHOD */ +static int pkcs11_x448_method_new(void) +{ + int orig_id, orig_flags; + + if (pkcs11_x448_method) + return 1; /* EVP_PKEY_X448 method already initialized */ + + orig_x448_method = EVP_PKEY_meth_find(EVP_PKEY_X448); + if (!orig_x448_method) + return 0; + + EVP_PKEY_meth_get0_info(&orig_id, &orig_flags, orig_x448_method); + if (orig_id != EVP_PKEY_X448 || !(orig_flags & EVP_PKEY_FLAG_SIGCTX_CUSTOM)) + return 0; + + EVP_PKEY_meth_get_derive(orig_x448_method, NULL, &orig_x448_derive); + if (!orig_x448_derive) + return 0; + + pkcs11_x448_method = EVP_PKEY_meth_new(EVP_PKEY_X448, EVP_PKEY_FLAG_AUTOARGLEN); + if (!pkcs11_x448_method) + return 0; + + /* Duplicate the original method */ + EVP_PKEY_meth_copy(pkcs11_x448_method, orig_x448_method); + + /* Override selected X448 method callbacks with PKCS#11 implementations */ + EVP_PKEY_meth_set_derive(pkcs11_x448_method, NULL, pkcs11_pkey_x448_derive); + + /* Register the method globally */ + if (!EVP_PKEY_meth_add0(pkcs11_x448_method)) { + EVP_PKEY_meth_free(pkcs11_x448_method); + pkcs11_x448_method = NULL; + return 0; + } + return 1; +} + +void pkcs11_x25519_method_free(void) +{ + if (pkcs11_x25519_method) { + free_pkey_ex_index(); + /* Remove an EVP_PKEY_METHOD object added by EVP_PKEY_meth_add0() */ + EVP_PKEY_meth_remove(pkcs11_x25519_method); + EVP_PKEY_meth_free(pkcs11_x25519_method); + pkcs11_x25519_method = NULL; + } +} + +void pkcs11_x448_method_free(void) +{ + if (pkcs11_x448_method) { + free_pkey_ex_index(); + EVP_PKEY_meth_remove(pkcs11_x448_method); + EVP_PKEY_meth_free(pkcs11_x448_method); + pkcs11_x448_method = NULL; + } +} + +void pkcs11_xdh_key_method_free(void) +{ + pkcs11_x25519_method_free(); + pkcs11_x448_method_free(); +} + #endif /* OPENSSL_VERSION_NUMBER < 0x40000000L */ + +/******************************************************************************/ +/* Get EVP_PKEY from raw public key bytes */ +/******************************************************************************/ + /* * Decode a DER OCTET STRING and return its contents. * If decoding fails, duplicate the input buffer unchanged. @@ -631,6 +858,78 @@ static EVP_PKEY *pkcs11_get_evp_key_ed448(PKCS11_OBJECT_private *key) return pkey; } +static EVP_PKEY *pkcs11_get_evp_key_x25519(PKCS11_OBJECT_private *key) +{ + EVP_PKEY *pkey = NULL; + unsigned char *raw = NULL; + size_t rawlen = 0; + + /* Retrieve the public key in raw format from PKCS#11 */ + if (pkcs11_get_raw_public_key(key, &raw, &rawlen) < 0) + return NULL; + + /* Build a EVP_PKEY from the raw public key, used only as software public key */ + pkey = EVP_PKEY_new_raw_public_key_ex(NULL, "X25519", NULL, raw, rawlen); + OPENSSL_free(raw); + + if (!pkey) + return NULL; + +#if OPENSSL_VERSION_NUMBER < 0x40000000L + if (key->object_class == CKO_PRIVATE_KEY) { + if ((key->slot->ctx->flags & PKCS11_FLAG_NO_METHODS) == 0) { + /* global initialize X25519 EVP_PKEY_METHOD */ + if (!pkcs11_x25519_method_new()) { + EVP_PKEY_free(pkey); + return NULL; + } + /* creates a new EVP_PKEY object which requires its own key object reference */ + key = pkcs11_object_ref(key); + alloc_pkey_ex_index(); + pkcs11_set_ex_data_pkey(pkey, key); + atexit(pkcs11_x25519_method_free); + } + } +#endif /* OPENSSL_VERSION_NUMBER < 0x40000000L */ + return pkey; +} + +static EVP_PKEY *pkcs11_get_evp_key_x448(PKCS11_OBJECT_private *key) +{ + EVP_PKEY *pkey = NULL; + unsigned char *raw = NULL; + size_t rawlen = 0; + + /* Retrieve the public key in raw format from PKCS#11 */ + if (pkcs11_get_raw_public_key(key, &raw, &rawlen) < 0) + return NULL; + + /* Build a EVP_PKEY from the raw public key, used only as software public key */ + pkey = EVP_PKEY_new_raw_public_key_ex(NULL, "X448", NULL, raw, rawlen); + OPENSSL_free(raw); + + if (!pkey) + return NULL; + +#if OPENSSL_VERSION_NUMBER < 0x40000000L + if (key->object_class == CKO_PRIVATE_KEY) { + if ((key->slot->ctx->flags & PKCS11_FLAG_NO_METHODS) == 0) { + /* global initialize X448 EVP_PKEY_METHOD */ + if (!pkcs11_x448_method_new()) { + EVP_PKEY_free(pkey); + return NULL; + } + /* create a new EVP_PKEY object which requires its own key object reference */ + key = pkcs11_object_ref(key); + alloc_pkey_ex_index(); + pkcs11_set_ex_data_pkey(pkey, key); + atexit(pkcs11_x25519_method_free); + } + } +#endif /* OPENSSL_VERSION_NUMBER < 0x40000000L */ + return pkey; +} + PKCS11_OBJECT_ops pkcs11_ed25519_ops = { EVP_PKEY_ED25519, @@ -642,10 +941,20 @@ PKCS11_OBJECT_ops pkcs11_ed448_ops = { pkcs11_get_evp_key_ed448, }; +PKCS11_OBJECT_ops pkcs11_x25519_ops = { + EVP_PKEY_X25519, + pkcs11_get_evp_key_x25519, +}; + +PKCS11_OBJECT_ops pkcs11_x448_ops = { + EVP_PKEY_X448, + pkcs11_get_evp_key_x448, +}; + #else /* !defined(OPENSSL_NO_ECX) && OPENSSL_VERSION_NUMBER >= 0x30000000L */ /* - * EdDSA (Ed25519/Ed448) support is not available: + * EdDSA (Ed25519/Ed448) and XDH (X5519/X448) support are not available: * - either OpenSSL was built without ECX (no-ecx), or * - OpenSSL version is older than 3.0. * diff --git a/src/p11_front.c b/src/p11_front.c index a0f6c67c..4c31bfff 100644 --- a/src/p11_front.c +++ b/src/p11_front.c @@ -436,6 +436,10 @@ int PKCS11_keygen(PKCS11_TOKEN *token, PKCS11_KGEN_ATTRS *kg) case EVP_PKEY_ED448: return pkcs11_eddsa_keygen(slot, kg->kgen.nid->nid, kg->key_label, kg->key_id, kg->id_len, kg->key_params); + case EVP_PKEY_X25519: + case EVP_PKEY_X448: + return pkcs11_xdh_keygen(slot, kg->kgen.eddsa->nid, + kg->key_label, kg->key_id, kg->id_len, kg->key_params); #endif /* !defined(OPENSSL_NO_ECX) && OPENSSL_VERSION_NUMBER >= 0x30000000L */ #if OPENSSL_VERSION_NUMBER >= 0x30500000L @@ -532,6 +536,32 @@ int PKCS11_generate_key(PKCS11_TOKEN *token, int algorithm, .key_params = &key_params }; break; + + case EVP_PKEY_X25519: + nid_kgen.nid = NID_X25519; + kgen_attrs = (PKCS11_KGEN_ATTRS){ + .type = EVP_PKEY_X25519, + .kgen.nid = &nid_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_X448: + nid_kgen.nid = NID_X448; + kgen_attrs = (PKCS11_KGEN_ATTRS){ + .type = EVP_PKEY_X448, + .kgen.nid = &nid_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; #endif /* !defined(OPENSSL_NO_ECX) && OPENSSL_VERSION_NUMBER >= 0x30000000L */ #if OPENSSL_VERSION_NUMBER >= 0x30500000L @@ -923,6 +953,39 @@ int PKCS11_evp_pkey_decrypt(EVP_PKEY *pk, int type, const char *mdname, } } +#if !defined(OPENSSL_NO_EC) || !defined(OPENSSL_NO_ECX) +int PKCS11_evp_pkey_derive(EVP_PKEY *pk, int type, + const unsigned char *peer_pub, size_t peer_pub_len, + int cofactor_mode, unsigned char *secret, size_t *secretlen) +{ + PKCS11_OBJECT_private *key; + PKCS11_KEY *pkey = pkcs11_get_pkcs11_key(pk); + + if (pkey == NULL) + return -1; + + key = pkey->_private; + if (check_object_fork(key) < 0) + return -1; + + switch (type) { +#ifndef OPENSSL_NO_EC + case EVP_PKEY_EC: + return pkcs11_evp_pkey_ecdh_derive(key, peer_pub, peer_pub_len, + cofactor_mode, secret, secretlen); +#endif /* EVP_PKEY_EC */ +#ifndef OPENSSL_NO_ECX + case EVP_PKEY_X25519: + case EVP_PKEY_X448: + return pkcs11_evp_pkey_xdh_derive(key, peer_pub, peer_pub_len, + secret, secretlen); +#endif /* EVP_PKEY_ECX */ + default: + return -2; /* type not supported */ + } +} +#endif /* !defined(OPENSSL_NO_EC) || !defined(OPENSSL_NO_ECX) */ + #endif /* OPENSSL_VERSION_NUMBER >= 0x30000000L */ int PKCS11_private_encrypt(int flen, const unsigned char *from, unsigned char *to, diff --git a/src/p11_key.c b/src/p11_key.c index 00a521f3..b7323f22 100644 --- a/src/p11_key.c +++ b/src/p11_key.c @@ -55,16 +55,29 @@ static int (*orig_pkey_rsa_decrypt) (EVP_PKEY_CTX *ctx, /* DER OIDs */ static const unsigned char OID_ED25519[] = { 0x06, 0x03, 0x2B, 0x65, 0x70 }; static const unsigned char OID_ED448[] = { 0x06, 0x03, 0x2B, 0x65, 0x71 }; +static const unsigned char OID_X25519[] = { 0x06, 0x03, 0x2b, 0x65, 0x6e }; +static const unsigned char OID_X448[] = { 0x06, 0x03, 0x2b, 0x65, 0x6f }; /* 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' + 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' + 0x13, 0x0A, /* tag + length */ + 'e','d','w','a','r','d','s','4','4','8' }; + +static const unsigned char STR_X25519[] = { + 0x13, 0x0B, /* tag + length */ + 'c','u','r','v','e','2','5','5','1','9' +}; + +static const unsigned char STR_X448[] = { + 0x13, 0x09, /* tag + length */ + 'c','u','r','v','e','4','4','8' +}; + #endif /* !defined(OPENSSL_NO_ECX) && OPENSSL_VERSION_NUMBER >= 0x30000000L */ #if OPENSSL_VERSION_NUMBER < 0x100020d0L || defined(LIBRESSL_VERSION_NUMBER) @@ -232,6 +245,30 @@ PKCS11_OBJECT_private *pkcs11_object_from_handle(PKCS11_SLOT_private *slot, } OPENSSL_free(data); break; + case CKK_EC_MONTGOMERY: + /* Read the CKA_EC_PARAMS to distinguish X25519 vs X448 */ + 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_X25519) && + !memcmp(data, OID_X25519, sizeof(OID_X25519))) || + (size == sizeof(STR_X25519) && + !memcmp(data, STR_X25519, sizeof(STR_X25519)))) { + ops = &pkcs11_x25519_ops; + } else if ((size == sizeof(OID_X448) && + !memcmp(data, OID_X448, sizeof(OID_X448))) || + (size == sizeof(STR_X448) && + !memcmp(data, STR_X448, sizeof(STR_X448)))) { + ops = &pkcs11_x448_ops; + } else { + pkcs11_log(ctx, LOG_DEBUG, "Unsupported XDH OID\n"); + OPENSSL_free(data); + return NULL; + } + OPENSSL_free(data); + break; #endif /* !defined(OPENSSL_NO_ECX) && OPENSSL_VERSION_NUMBER >= 0x30000000L */ #if OPENSSL_VERSION_NUMBER >= 0x30500000L @@ -726,6 +763,63 @@ int pkcs11_eddsa_keygen(PKCS11_SLOT_private *slot, CRYPTOKI_checkerr(CKR_F_PKCS11_GENERATE_KEY, rv); return 0; } + +/** + * Generate XDH (X25519 / X448) key pair directly on token + */ +int pkcs11_xdh_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_MONTGOMERY_KEY_PAIR_GEN, NULL_PTR, 0 + }; + CK_OBJECT_HANDLE pub_key_obj, priv_key_obj; + int rv; + unsigned char *xdh_params = NULL; + size_t xdh_params_len = 0; + + if (pkcs11_init_keygen(slot, &session)) + return -1; + + if (nid == NID_X25519) { + xdh_params = (unsigned char *)OID_X25519; + xdh_params_len = sizeof(OID_X25519); + } else if (nid == NID_X448) { + xdh_params = (unsigned char *)OID_X448; + xdh_params_len = sizeof(OID_X448); + } else { + pkcs11_put_session(slot, session); + return -1; /* unsupported */ + } + + /* public key attributes */ + pkcs11_common_pubkey_attr(&pubtmpl, label, id, id_len); + pkcs11_addattr(&pubtmpl, CKA_EC_PARAMS, xdh_params, xdh_params_len); + pkcs11_addattr_bool(&pubtmpl, CKA_DERIVE, TRUE); + + /* private key attributes */ + pkcs11_common_privkey_attr(&privtmpl, label, id, id_len, params); + pkcs11_addattr_bool(&privtmpl, CKA_DERIVE, 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)); + + /* cleanup */ + pkcs11_put_session(slot, session); + pkcs11_zap_attrs(&privtmpl); + pkcs11_zap_attrs(&pubtmpl); + + CRYPTOKI_checkerr(CKR_F_PKCS11_GENERATE_KEY, rv); + return 0; +} #endif /* !defined(OPENSSL_NO_ECX) && OPENSSL_VERSION_NUMBER >= 0x30000000L */ #if OPENSSL_VERSION_NUMBER >= 0x30500000L diff --git a/src/p11_pkey.c b/src/p11_pkey.c index 1c947d06..6e8b7515 100644 --- a/src/p11_pkey.c +++ b/src/p11_pkey.c @@ -338,6 +338,10 @@ const char *pkcs11_mechanism_name(CK_MECHANISM *mechanism) return "CKM_FALCON"; case CKM_PQC_FALCON: return "CKM_PQC_FALCON"; + case CKM_ECDH1_DERIVE: + return "CKM_ECDH1_DERIVE"; + case CKM_ECDH1_COFACTOR_DERIVE: + return "CKM_ECDH1_COFACTOR_DERIVE"; default: return "UNKNOWN_MECHANISM"; } @@ -559,6 +563,142 @@ static int pkcs11_decrypt_with_mechanism(PKCS11_OBJECT_private *key, return rv; } +#ifndef OPENSSL_NO_EC +/* + * Execute a PKCS#11 derive operation using the specified mechanism. + * Returns: CKR_OK on success or PKCS#11/vendor defined error code on failure. + */ +static CK_RV pkcs11_derive_with_mechanism(PKCS11_OBJECT_private *key, + CK_MECHANISM *mechanism, unsigned char *secret, size_t *secretlen) +{ + CK_RV rv = CKR_GENERAL_ERROR; + PKCS11_SLOT_private *slot; + PKCS11_CTX_private *ctx; + CK_SESSION_HANDLE session; + CK_OBJECT_HANDLE newkey = CK_INVALID_HANDLE; + CK_OBJECT_CLASS newkey_class = CKO_SECRET_KEY; + CK_KEY_TYPE newkey_type = CKK_GENERIC_SECRET; + CK_BBOOL ck_false = CK_FALSE; + CK_BBOOL ck_true = CK_TRUE; + CK_ULONG newkey_len = 0; + unsigned char *value = NULL; + size_t len, value_len_alloc = 0; + CK_ATTRIBUTE newkey_template[] = { + {CKA_TOKEN, &ck_false, sizeof(ck_false)}, /* session only object */ + {CKA_CLASS, &newkey_class, sizeof(newkey_class)}, + {CKA_KEY_TYPE, &newkey_type, sizeof(newkey_type)}, + {CKA_VALUE_LEN, &newkey_len, sizeof(newkey_len)}, + {CKA_SENSITIVE, &ck_false, sizeof(ck_false)}, + {CKA_EXTRACTABLE, &ck_true, sizeof(ck_true)}, + {CKA_DERIVE, &ck_true, sizeof(ck_true)}, + }; + + if (key == NULL || mechanism == NULL || secret == NULL || + secretlen == NULL || *secretlen == 0) + return CKR_ARGUMENTS_BAD; + + if (*secretlen > (size_t)(CK_ULONG)-1) + return CKR_ARGUMENTS_BAD; + + slot = key->slot; + if (slot == NULL) + return CKR_GENERAL_ERROR; + + ctx = slot->ctx; + if (ctx == NULL) + return CKR_GENERAL_ERROR; + +#ifdef DEBUG + pkcs11_log(ctx, LOG_DEBUG, "%s:%d pkcs11_derive_with_mechanism() " + "%s secret=%p *secretlen=%lu\n", + __FILE__, __LINE__, + pkcs11_mechanism_name(mechanism), secret, (unsigned long)*secretlen); +#endif + + if (pkcs11_get_session(slot, 0, &session)) + return CKR_GENERAL_ERROR; + + len = *secretlen; + newkey_len = (CK_ULONG)len; + + rv = CRYPTOKI_call(ctx, C_DeriveKey(session, mechanism, key->object, + newkey_template, sizeof(newkey_template)/sizeof(*newkey_template), + &newkey)); + if (rv != CKR_OK) { + pkcs11_log(ctx, LOG_DEBUG, "%s:%d C_DeriveKey rv=0x%08lX (%lu)\n", + __FILE__, __LINE__, (unsigned long)rv, (unsigned long)rv); + goto end; + } + + if (pkcs11_getattr_alloc(ctx, session, newkey, CKA_VALUE, + &value, &value_len_alloc)) { + rv = CKR_GENERAL_ERROR; + goto end; + } + + if (value_len_alloc > len) { + *secretlen = value_len_alloc; + rv = CKR_BUFFER_TOO_SMALL; + goto end; + } + + memcpy(secret, value, value_len_alloc); + *secretlen = value_len_alloc; + rv = CKR_OK; + +end: + if (newkey != CK_INVALID_HANDLE) + CRYPTOKI_call(ctx, C_DestroyObject(session, newkey)); + + OPENSSL_clear_free(value, value_len_alloc); + pkcs11_put_session(slot, session); + return rv; +} + +/* DER-encode data as an ASN.1 OCTET STRING. */ +static unsigned char *der_encode_octet_string(const unsigned char *data, + size_t data_len, size_t *der_len) +{ + ASN1_OCTET_STRING *os = NULL; + unsigned char *der = NULL, *p; + int len; + + if (data == NULL || data_len == 0 || der_len == NULL) + return NULL; + + if (data_len > INT_MAX) + return NULL; + + os = ASN1_OCTET_STRING_new(); + if (os == NULL) + return NULL; + + if (!ASN1_OCTET_STRING_set(os, data, (int)data_len)) + goto err; + + len = i2d_ASN1_OCTET_STRING(os, NULL); + if (len <= 0) + goto err; + + der = OPENSSL_malloc((size_t)len); + if (der == NULL) + goto err; + + p = der; + if (i2d_ASN1_OCTET_STRING(os, &p) != len) + goto err; + + *der_len = (size_t)len; + ASN1_OCTET_STRING_free(os); + return der; + +err: + OPENSSL_free(der); + ASN1_OCTET_STRING_free(os); + return NULL; +} +#endif /* OPENSSL_NO_EC */ + /* Build ASN.1 DigestInfo for PKCS#1 v1.5 signing. */ static int pkcs11_build_digestinfo(const char *mdname, const unsigned char *dgst, size_t dgstlen, @@ -828,7 +968,7 @@ int pkcs11_evp_pkey_ec_sign(PKCS11_OBJECT_private *key, } #endif /* OPENSSL_NO_EC */ -#ifndef OPENSSL_NO_ECX +#if !defined(OPENSSL_NO_ECX) && OPENSSL_VERSION_NUMBER >= 0x30000000L /* * Sign message input with EdDSA private key via PKCS#11 mechanism. * Returns 1 on success or -1 on failure. @@ -851,7 +991,7 @@ int pkcs11_evp_pkey_eddsa_sign(PKCS11_OBJECT_private *key, return 1; } -#endif /* OPENSSL_NO_ECX */ +#endif /* !defined(OPENSSL_NO_ECX) && OPENSSL_VERSION_NUMBER >= 0x30000000L */ #if OPENSSL_VERSION_NUMBER >= 0x30500000L #ifndef OPENSSL_NO_ML_DSA @@ -1001,6 +1141,122 @@ int pkcs11_evp_pkey_rsa_decrypt(PKCS11_OBJECT_private *key, return (int)*outlen; } +#ifndef OPENSSL_NO_EC +/* + * Derive an ECDH shared secret. Raw uncompressed peer EC points are tried + * first, with a fallback to the DER OCTET STRING form used by CKA_EC_POINT. + */ +extern int pkcs11_evp_pkey_ecdh_derive(PKCS11_OBJECT_private *key, + const unsigned char *peer_pub, size_t peer_pub_len, + int cofactor_mode, unsigned char *secret, size_t *secretlen) +{ + CK_MECHANISM mechanism; + CK_ECDH1_DERIVE_PARAMS derive_params; + unsigned char *der_pub = NULL; + size_t der_pub_len = 0; + size_t saved_secretlen = 0; + CK_RV rv; + + if (key == NULL || peer_pub == NULL || peer_pub_len == 0 || secretlen == NULL) + return -1; + + if (peer_pub_len > (size_t)(CK_ULONG)-1) + return -1; + + memset(&derive_params, 0, sizeof(derive_params)); + derive_params.kdf = CKD_NULL; + derive_params.pSharedData = NULL_PTR; + derive_params.ulSharedDataLen = 0; + derive_params.pPublicData = (CK_BYTE_PTR)peer_pub; + derive_params.ulPublicDataLen = (CK_ULONG)peer_pub_len; + + memset(&mechanism, 0, sizeof(mechanism)); + mechanism.mechanism = cofactor_mode == 1 + ? CKM_ECDH1_COFACTOR_DERIVE : CKM_ECDH1_DERIVE; + + /* Both ECDH variants use CK_ECDH1_DERIVE_PARAMS. + * The mechanism type selects plain vs cofactor ECDH; the token + * obtains the cofactor from the EC domain parameters. */ + mechanism.pParameter = &derive_params; + mechanism.ulParameterLen = sizeof(derive_params); + + saved_secretlen = *secretlen; + rv = pkcs11_derive_with_mechanism(key, &mechanism, secret, secretlen); + if (rv == CKR_OK) + return 1; + + /* + * SoftHSM accepts raw uncompressed EC points in pPublicData, while + * some tokens such as Luna expect the DER OCTET STRING form used by + * CKA_EC_POINT. Try raw first and fall back to DER-wrapped form on + * CKR_ECC_POINT_INVALID. + */ + if (rv != CKR_ECC_POINT_INVALID || peer_pub[0] != POINT_CONVERSION_UNCOMPRESSED) + return -1; + + der_pub = der_encode_octet_string(peer_pub, peer_pub_len, &der_pub_len); + if (der_pub == NULL || der_pub_len > (size_t)(CK_ULONG)-1) { + OPENSSL_free(der_pub); + return -1; + } + + *secretlen = saved_secretlen; + derive_params.pPublicData = der_pub; + derive_params.ulPublicDataLen = (CK_ULONG)der_pub_len; + + rv = pkcs11_derive_with_mechanism(key, &mechanism, secret, secretlen); + + OPENSSL_clear_free(der_pub, der_pub_len); + + if (rv != CKR_OK) + return -1; + + return 1; +} +#endif /* OPENSSL_NO_EC */ + +#if !defined(OPENSSL_NO_ECX) && OPENSSL_VERSION_NUMBER >= 0x30000000L +/* + * Derive an X25519/X448 shared secret using CKM_ECDH1_DERIVE and a raw + * RFC 7748 peer public key. + * TODO: Test this path once a token/module advertising + * EC_MONTGOMERY derive support is available. + */ +extern int pkcs11_evp_pkey_xdh_derive(PKCS11_OBJECT_private *key, + const unsigned char *peer_pub, size_t peer_pub_len, + unsigned char *secret, size_t *secretlen) +{ + CK_MECHANISM mechanism; + CK_ECDH1_DERIVE_PARAMS derive_params; + CK_RV rv; + + if (key == NULL || peer_pub == NULL || peer_pub_len == 0 || secretlen == NULL) + return -1; + + if (peer_pub_len > (size_t)(CK_ULONG)-1) + return -1; + + memset(&derive_params, 0, sizeof(derive_params)); + derive_params.kdf = CKD_NULL; + derive_params.pSharedData = NULL_PTR; + derive_params.ulSharedDataLen = 0; + derive_params.pPublicData = (CK_BYTE_PTR)peer_pub; + derive_params.ulPublicDataLen = (CK_ULONG)peer_pub_len; + + memset(&mechanism, 0, sizeof(mechanism)); + mechanism.mechanism = CKM_ECDH1_DERIVE; + mechanism.pParameter = &derive_params; + mechanism.ulParameterLen = sizeof(derive_params); + + /* X25519/X448 use CKM_ECDH1_DERIVE with raw RFC 7748 public keys + * on tokens that support EC_MONTGOMERY derive. */ + rv = pkcs11_derive_with_mechanism(key, &mechanism, secret, secretlen); + if (rv != CKR_OK) + return -1; + + return 1; +} +#endif /* !defined(OPENSSL_NO_ECX) && OPENSSL_VERSION_NUMBER >= 0x30000000L */ #if OPENSSL_VERSION_NUMBER < 0x40000000L #ifndef OPENSSL_NO_EC diff --git a/src/pkcs11.h b/src/pkcs11.h index 615f4ca5..93960433 100644 --- a/src/pkcs11.h +++ b/src/pkcs11.h @@ -360,6 +360,7 @@ typedef unsigned long ck_key_type_t; #define CKK_GOSTR3411 (0x31UL) #define CKK_GOST28147 (0x32UL) #define CKK_EC_EDWARDS (0x40UL) +#define CKK_EC_MONTGOMERY (0x41UL) #define CKK_ML_DSA (0x4AUL) #define CKK_SLH_DSA (0x4BUL) /* @@ -731,6 +732,7 @@ typedef unsigned long ck_mechanism_type_t; #define CKM_ECDH1_COFACTOR_DERIVE (0x1051UL) #define CKM_ECMQV_DERIVE (0x1052UL) #define CKM_EC_EDWARDS_KEY_PAIR_GEN (0x1055UL) +#define CKM_EC_MONTGOMERY_KEY_PAIR_GEN (0x1056UL) #define CKM_EDDSA (0x1057UL) #define CKM_JUNIPER_KEY_GEN (0x1060UL) #define CKM_JUNIPER_ECB128 (0x1061UL) @@ -1404,6 +1406,7 @@ struct ck_c_initialize_args #define CKR_MUTEX_BAD (0x1a0UL) #define CKR_MUTEX_NOT_LOCKED (0x1a1UL) #define CKR_FUNCTION_REJECTED (0x200UL) +#define CKR_ECC_POINT_INVALID (CKR_VENDOR_DEFINED + 0x2F) #define CKR_VENDOR_DEFINED (1UL << 31) diff --git a/src/provider.c b/src/provider.c index 84ef33b1..3e2d3006 100644 --- a/src/provider.c +++ b/src/provider.c @@ -108,6 +108,17 @@ PROVIDER_FN(asym_cipher_gettable_ctx_params); PROVIDER_FN(asym_cipher_set_ctx_params); PROVIDER_FN(asym_cipher_settable_ctx_params); +PROVIDER_FN(keyexch_newctx); +PROVIDER_FN(keyexch_freectx); +PROVIDER_FN(keyexch_dupctx); +PROVIDER_FN(keyexch_init); +PROVIDER_FN(keyexch_set_peer); +PROVIDER_FN(keyexch_derive); +PROVIDER_FN(keyexch_set_ctx_params); +PROVIDER_FN(keyexch_settable_ctx_params); +PROVIDER_FN(keyexch_get_ctx_params); +PROVIDER_FN(keyexch_gettable_ctx_params); + PROVIDER_FN(store_open); PROVIDER_FN(store_set_ctx_params); PROVIDER_FN(store_settable_ctx_params); @@ -186,6 +197,20 @@ static const OSSL_DISPATCH asym_cipher_functions[] = { OSSL_DISPATCH_END }; +static const OSSL_DISPATCH keyexch_functions[] = { + {OSSL_FUNC_KEYEXCH_NEWCTX, (void (*)(void))keyexch_newctx}, + {OSSL_FUNC_KEYEXCH_FREECTX, (void (*)(void))keyexch_freectx}, + {OSSL_FUNC_KEYEXCH_DUPCTX, (void (*)(void))keyexch_dupctx}, + {OSSL_FUNC_KEYEXCH_INIT, (void (*)(void))keyexch_init}, + {OSSL_FUNC_KEYEXCH_SET_PEER, (void (*)(void))keyexch_set_peer}, + {OSSL_FUNC_KEYEXCH_DERIVE, (void (*)(void))keyexch_derive}, + {OSSL_FUNC_KEYEXCH_GET_CTX_PARAMS, (void (*)(void))keyexch_get_ctx_params}, + {OSSL_FUNC_KEYEXCH_GETTABLE_CTX_PARAMS, (void (*)(void))keyexch_gettable_ctx_params}, + {OSSL_FUNC_KEYEXCH_SET_CTX_PARAMS, (void (*)(void))keyexch_set_ctx_params}, + {OSSL_FUNC_KEYEXCH_SETTABLE_CTX_PARAMS, (void (*)(void))keyexch_settable_ctx_params}, + OSSL_DISPATCH_END +}; + static const OSSL_DISPATCH store_functions[] = { {OSSL_FUNC_STORE_OPEN, (void (*)(void))store_open}, {OSSL_FUNC_STORE_SET_CTX_PARAMS, (void (*)(void))store_set_ctx_params}, @@ -199,9 +224,16 @@ static const OSSL_DISPATCH store_functions[] = { /* Keymgmt algorithms: must be real key types (e.g. RSA, EC), not provider names */ static const OSSL_ALGORITHM p11_keymgmts[] = { {"RSA:rsaEncryption", FIPS_PROPQ, keymgmt_functions, "PKCS#11 RSA keymgm functions"}, +#ifndef OPENSSL_NO_EC {"EC:id-ecPublicKey", FIPS_PROPQ, keymgmt_functions, "PKCS#11 EC keymgm functions"}, + {"ECDH", FIPS_PROPQ, keymgmt_functions, "PKCS#11 key exchange functions"}, +#endif /* OPENSSL_NO_EC */ +#ifndef OPENSSL_NO_ECX {"ED25519", FIPS_PROPQ, keymgmt_functions, "PKCS#11 Ed25519 keymgm functions"}, {"ED448", FIPS_PROPQ, keymgmt_functions, "PKCS#11 Ed448 keymgm functions"}, + {"X25519", FIPS_PROPQ, keymgmt_functions, "PKCS#11 X25519 keymgm functions"}, + {"X448", FIPS_PROPQ, keymgmt_functions, "PKCS#11 X448 keymgm functions"}, +#endif /* OPENSSL_NO_ECX */ #if OPENSSL_VERSION_NUMBER >= 0x30500000L #ifndef OPENSSL_NO_ML_DSA {"ML-DSA-44", FIPS_PROPQ, keymgmt_functions, "PKCS#11 ML-DSA-44 keymgmt functions"}, @@ -252,6 +284,11 @@ static const OSSL_ALGORITHM p11_asym_cipher[] = { {NULL, NULL, NULL, NULL} }; +static const OSSL_ALGORITHM p11_keyexch[] = { + {"PKCS11", FIPS_PROPQ, keyexch_functions, "PKCS#11 key exchange functions"}, + {NULL, NULL, NULL, NULL} +}; + static const OSSL_ALGORITHM p11_storemgmt[] = { {"PKCS11", FIPS_PROPQ, store_functions, "PKCS#11 storage functions"}, {NULL, NULL, NULL, NULL} @@ -406,6 +443,8 @@ static const OSSL_ALGORITHM *provider_query_operation(void *ctx, return p11_signatures; case OSSL_OP_ASYM_CIPHER: return p11_asym_cipher; + case OSSL_OP_KEYEXCH: + return p11_keyexch; case OSSL_OP_STORE: return p11_storemgmt; } @@ -525,6 +564,7 @@ static const char *keymgmt_query_operation_name(int id) switch (id) { case OSSL_OP_SIGNATURE: case OSSL_OP_ASYM_CIPHER: + case OSSL_OP_KEYEXCH: return "PKCS11"; } return NULL; @@ -587,7 +627,7 @@ static const OSSL_PARAM *keymgmt_export_types(int selection) /* RSA */ OSSL_PARAM_BN(OSSL_PKEY_PARAM_RSA_N, NULL, 0), OSSL_PARAM_BN(OSSL_PKEY_PARAM_RSA_E, NULL, 0), - /* EC, ED25519, ED449 */ + /* EC, ED25519, ED449, X25519, X448 */ OSSL_PARAM_utf8_string(OSSL_PKEY_PARAM_GROUP_NAME, NULL, 0), OSSL_PARAM_octet_string(OSSL_PKEY_PARAM_PUB_KEY, NULL, 0), OSSL_PARAM_END @@ -635,7 +675,7 @@ static int keymgmt_get_params(void *provkey, OSSL_PARAM params[]) /* EVP_PKEY_get_size() */ p = OSSL_PARAM_locate(params, OSSL_PKEY_PARAM_MAX_SIZE); - if (p != NULL && !OSSL_PARAM_set_int(p, (int)p11_keydata_get_sigsize(keydata))) + if (p != NULL && !OSSL_PARAM_set_int(p, (int)p11_keydata_get_maxsize(keydata))) return 0; #if OPENSSL_VERSION_NUMBER >= 0x30600000L @@ -1866,6 +1906,155 @@ static const OSSL_PARAM *asym_cipher_settable_ctx_params(void *ctx, void *provct return asym_cipher_gettable_ctx_params(ctx, provctx); } + +/******************************************************************************/ +/* Key exchange functions */ +/******************************************************************************/ + +/* Create and initialize key exchange context. */ +void *keyexch_newctx(void *provctx) +{ + return p11_keyexch_ctx_new(provctx); +} + +/* Free key exchange context. */ +void keyexch_freectx(void *ctx) +{ + p11_keyexch_ctx_free(ctx); +} + +/* Duplicate key exchange context. */ +void *keyexch_dupctx(void *ctx) +{ + return p11_keyexch_dupctx(ctx); +} + +/* Initialize a key exchange operation with the local private key. */ +int keyexch_init(void *ctx, void *provkey, const OSSL_PARAM params[]) +{ + return p11_keyexch_ctx_init(ctx, provkey, params); +} + +/* Set the peer public key for shared-secret derivation. */ +int keyexch_set_peer(void *ctx, void *provkey) +{ + return p11_keyexch_set_peer(ctx, provkey); +} + +/* Derive the shared secret, or return the required output size. */ +static int keyexch_derive(void *ctx, + unsigned char *secret, size_t *secretlen, size_t outlen) +{ + P11_KEYEXCH_CTX *exch_ctx = (P11_KEYEXCH_CTX *)ctx; + unsigned char *peer_pub = NULL; + size_t peer_pub_len = 0; + size_t need; + + if (exch_ctx == NULL || secretlen == NULL) + return 0; + + if (!p11_keyexch_ctx_get_peer_pub(exch_ctx, &peer_pub, &peer_pub_len)) + return 0; + + need = p11_keyexch_ctx_get_outsize(exch_ctx); + if (need == 0) + return 0; + + if (secret == NULL) { + /* Return the maximum shared-secret output size. */ + *secretlen = need; + return 1; + } + + if (outlen < need) { + *secretlen = need; + return 0; /* buffer too small */ + } + + /* Pass the caller-provided buffer size to the PKCS#11 derive helper. */ + *secretlen = outlen; + + return PKCS11_evp_pkey_derive( + p11_keyexch_ctx_get_evp_pkey(exch_ctx), + p11_keyexch_ctx_get_type(exch_ctx), + peer_pub, peer_pub_len, + p11_keyexch_ctx_get_cofactor_mode(exch_ctx), + secret, secretlen); +} + +/* Return current key exchange context parameters. */ +static int keyexch_get_ctx_params(void *ctx, OSSL_PARAM params[]) +{ + P11_KEYEXCH_CTX *exch_ctx = (P11_KEYEXCH_CTX *)ctx; + OSSL_PARAM *p; + + if (exch_ctx == NULL) + return 0; + + if (params == NULL) + return 1; + + p = OSSL_PARAM_locate(params, OSSL_EXCHANGE_PARAM_EC_ECDH_COFACTOR_MODE); + if (p != NULL) { + int mode = p11_keyexch_ctx_get_cofactor_mode(exch_ctx); + + if (!OSSL_PARAM_set_int(p, mode)) + return 0; + } + + return 1; +} + +/* Return the list of gettable key exchange context parameters. */ +const OSSL_PARAM *keyexch_gettable_ctx_params(void *ctx, void *provctx) +{ + static const OSSL_PARAM gettable_ctx_params[] = { + OSSL_PARAM_int(OSSL_EXCHANGE_PARAM_EC_ECDH_COFACTOR_MODE, NULL), + OSSL_PARAM_END + }; + + (void)ctx; + (void)provctx; + return gettable_ctx_params; +} + +/* Set key exchange context parameters. */ +static int keyexch_set_ctx_params(void *ctx, const OSSL_PARAM params[]) +{ + P11_KEYEXCH_CTX *exch_ctx = (P11_KEYEXCH_CTX *)ctx; + const OSSL_PARAM *p; + + if (exch_ctx == NULL) + return 0; + + if (params == NULL) + return 1; + + /* Cofactor mode is meaningful only for ECDH. It is accepted here + * because the same key exchange implementation is shared with X25519/X448. */ + p = OSSL_PARAM_locate_const(params, OSSL_EXCHANGE_PARAM_EC_ECDH_COFACTOR_MODE); + if (p != NULL) { + int mode; + + if (!OSSL_PARAM_get_int(p, &mode)) + return 0; + + if (mode < -1 || mode > 1) + return 0; + + return p11_keyexch_ctx_set_cofactor_mode(exch_ctx, mode); + } + + return 1; +} + +/* Return the list of settable key exchange context parameters. */ +static const OSSL_PARAM *keyexch_settable_ctx_params(void *ctx, void *provctx) +{ + return keyexch_gettable_ctx_params(ctx, provctx); +} + + /******************************************************************************/ /* Store functions */ /******************************************************************************/ @@ -1952,7 +2141,7 @@ static const OSSL_PARAM *store_settable_ctx_params(void *ctx) OSSL_PARAM_END }; - (void)(ctx); + (void)ctx; return settable_ctx_params; } diff --git a/src/provider_helpers.c b/src/provider_helpers.c index 05fed82f..1b5bb093 100644 --- a/src/provider_helpers.c +++ b/src/provider_helpers.c @@ -45,6 +45,8 @@ #define ED448_KEYLEN 57 #define ED25519_SIGSIZE 64 #define ED448_SIGSIZE 114 +#define X25519_KEYLEN 32 +#define X448_KEYLEN 56 #endif /* OPENSSL_NO_ECX */ #if OPENSSL_VERSION_NUMBER >= 0x30500000L @@ -157,9 +159,9 @@ struct p11_keydata_st { * - EC: group order size * - EdDSA / ML-DSA / SLH-DSA: raw public key size */ size_t keysize; - /* Signature size in bytes if fixed-size, - * otherwise 0 for variable-size signatures */ - size_t sigsize; + /* Maximum output size in bytes. + * Zero means variable-size or not applicable. */ + size_t maxsize; /* owned by this struct; free with OSSL_PARAM_free() */ OSSL_PARAM *params; /* optional provider-side public key cache */ @@ -196,6 +198,13 @@ struct p11_asym_cipher_ctx { size_t oaep_labellen; }; +struct p11_keyexch_ctx { + PROVIDER_CTX *prov_ctx; + P11_KEYDATA *keydata; /* local private key */ + P11_KEYDATA *peerkeydata; /* peer public key */ + int cofactor_mode; /* ECDH only: -1 default, 0 disable, 1 enable */ +}; + /* Internal helper functions */ static void PROVIDER_CTX_get_environment_parameters(PROVIDER_CTX *prov_ctx); static int PROVIDER_CTX_get_specific_parameters(PROVIDER_CTX *prov_ctx); @@ -210,7 +219,7 @@ static int p11_keydata_init_rsa_from_params(P11_KEYDATA *keydata); static int p11_keydata_init_ec_from_params(P11_KEYDATA *keydata); #endif /* OPENSSL_NO_EC */ #ifndef OPENSSL_NO_ECX -static int p11_keydata_init_eddsa_from_params(P11_KEYDATA *keydata, int type); +static int p11_keydata_init_ecx_from_params(P11_KEYDATA *keydata, int type); #endif /* OPENSSL_NO_ECX */ #if OPENSSL_VERSION_NUMBER >= 0x30500000L #ifndef OPENSSL_NO_ML_DSA @@ -735,12 +744,12 @@ int p11_keydata_get_bits(const P11_KEYDATA *keydata) } /* Return maximum signature or operation size in bytes. */ -size_t p11_keydata_get_sigsize(const P11_KEYDATA *keydata) +size_t p11_keydata_get_maxsize(const P11_KEYDATA *keydata) { if (keydata == NULL) return 0; - return keydata->sigsize; + return keydata->maxsize; } /* @@ -811,9 +820,11 @@ int p11_keydata_set_params(P11_KEYDATA *key, const OSSL_PARAM *params) int p11_public_equal(const P11_KEYDATA *k1, const P11_KEYDATA *k2) { const OSSL_PARAM *n1, *e1, *n2, *e2; - const OSSL_PARAM *g1, *g2; const OSSL_PARAM *pub1, *pub2; +#ifndef OPENSSL_NO_EC + const OSSL_PARAM *g1, *g2; const char *group1 = NULL, *group2 = NULL; +#endif /* OPENSSL_NO_EC */ if (k1 == NULL || k2 == NULL || k1->params == NULL || k2->params == NULL) @@ -1224,7 +1235,7 @@ size_t p11_signature_ctx_get_sigsize(const P11_SIGNATURE_CTX *sig_ctx) if (sig_ctx == NULL) return 0; - return p11_keydata_get_sigsize(sig_ctx->keydata); + return p11_keydata_get_maxsize(sig_ctx->keydata); } /* Return key type used in signature context. */ @@ -1652,7 +1663,7 @@ size_t p11_asym_cipher_ctx_get_outsize(const P11_ASYM_CIPHER_CTX *asym_ctx) * For RSA decrypt the plaintext is at most modulus size. * This is a safe upper bound for output buffer sizing. */ - return p11_keydata_get_sigsize(asym_ctx->keydata); + return p11_keydata_get_maxsize(asym_ctx->keydata); } /* Return key type used in asymmetric cipher context. */ @@ -1781,6 +1792,201 @@ size_t p11_asym_cipher_ctx_get_oaep_labellen(const P11_ASYM_CIPHER_CTX *asym_ctx return asym_ctx->oaep_labellen; } +/******************************************************************************/ +/* KEY EXCHANGE helper functions */ +/******************************************************************************/ + +/* Allocate a key exchange context. */ +P11_KEYEXCH_CTX *p11_keyexch_ctx_new(PROVIDER_CTX *ctx) +{ + P11_KEYEXCH_CTX *keyexch_ctx; + + keyexch_ctx = OPENSSL_zalloc(sizeof(P11_KEYEXCH_CTX)); + if (!keyexch_ctx) + return NULL; + + keyexch_ctx->prov_ctx = ctx; + keyexch_ctx->cofactor_mode = -1; /* default */ + return keyexch_ctx; +} + +/* Free the key exchange context and release referenced keys. */ +void p11_keyexch_ctx_free(P11_KEYEXCH_CTX *keyexch_ctx) +{ + if (keyexch_ctx == NULL) + return; + + p11_keydata_free(keyexch_ctx->keydata); + p11_keydata_free(keyexch_ctx->peerkeydata); + OPENSSL_free(keyexch_ctx); +} + +/* + * Duplicate a key exchange context. + * Key objects are shared by reference; mutable context state is copied. + */ +P11_KEYEXCH_CTX *p11_keyexch_dupctx(P11_KEYEXCH_CTX *keyexch_ctx) +{ + P11_KEYEXCH_CTX *dst = NULL; + + if (keyexch_ctx == NULL) + return NULL; + + dst = p11_keyexch_ctx_new(keyexch_ctx->prov_ctx); + if (dst == NULL) + return NULL; + + /* copy simple scalar state */ + dst->cofactor_mode = keyexch_ctx->cofactor_mode; + + /* share local keydata by reference */ + if (keyexch_ctx->keydata != NULL) { + if (!p11_keydata_up_ref(keyexch_ctx->keydata)) + goto err; + dst->keydata = keyexch_ctx->keydata; + } + + /* share peer keydata by reference */ + if (keyexch_ctx->peerkeydata != NULL) { + if (!p11_keydata_up_ref(keyexch_ctx->peerkeydata)) + goto err; + dst->peerkeydata = keyexch_ctx->peerkeydata; + } + + return dst; + +err: + p11_keyexch_ctx_free(dst); + return NULL; +} + +/* Set the local private key and reset per-operation state. */ +int p11_keyexch_ctx_init(P11_KEYEXCH_CTX *keyexch_ctx, P11_KEYDATA *keydata, + const OSSL_PARAM params[]) +{ + (void)params; + + if (keyexch_ctx == NULL || keydata == NULL) + return 0; + + /* Take a reference first, so replacing with the same object is safe. */ + if (!p11_keydata_up_ref(keydata)) + return 0; + + p11_keydata_free(keyexch_ctx->keydata); + keyexch_ctx->keydata = keydata; + + /* A new derive operation must not reuse the previous peer key. */ + p11_keydata_free(keyexch_ctx->peerkeydata); + keyexch_ctx->peerkeydata = NULL; + + keyexch_ctx->cofactor_mode = -1; /* default */ + + return 1; +} + +/* Set the peer public key for shared-secret derivation. */ +int p11_keyexch_set_peer(P11_KEYEXCH_CTX *keyexch_ctx, P11_KEYDATA *keydata) +{ + if (keyexch_ctx == NULL || keydata == NULL) + return 0; + + /* Take a reference first, so replacing with the same object is safe. */ + if (!p11_keydata_up_ref(keydata)) + return 0; + + p11_keydata_free(keyexch_ctx->peerkeydata); + keyexch_ctx->peerkeydata = keydata; + + return 1; +} + +/* Set ECDH cofactor mode: -1 = default, 0 = disabled, 1 = enabled. */ +int p11_keyexch_ctx_set_cofactor_mode(P11_KEYEXCH_CTX *keyexch_ctx, int cofactor_mode) +{ + if (keyexch_ctx == NULL) + return 0; + + keyexch_ctx->cofactor_mode = cofactor_mode; + return 1; +} + +/* Return ECDH cofactor mode. */ +int p11_keyexch_ctx_get_cofactor_mode(P11_KEYEXCH_CTX *keyexch_ctx) +{ + if (keyexch_ctx == NULL) + return 0; + + return keyexch_ctx->cofactor_mode; +} + +/* Return the local private EVP_PKEY. The returned pointer is borrowed. */ +EVP_PKEY *p11_keyexch_ctx_get_evp_pkey(const P11_KEYEXCH_CTX *keyexch_ctx) +{ + if (keyexch_ctx == NULL) + return NULL; + + return p11_keydata_get_evp_pkey(keyexch_ctx->keydata); +} + +/* Return the peer public key bytes. The returned buffer is borrowed. */ +int p11_keyexch_ctx_get_peer_pub(const P11_KEYEXCH_CTX *keyexch_ctx, + unsigned char **buf, size_t *len) +{ + if (keyexch_ctx == NULL) + return 0; + + return p11_keydata_get_pub(keyexch_ctx->peerkeydata, buf, len); +} + +/* Return the local key type. */ +int p11_keyexch_ctx_get_type(const P11_KEYEXCH_CTX *keyexch_ctx) +{ + if (keyexch_ctx == NULL) + return 0; + + return p11_keydata_get_type(keyexch_ctx->keydata); +} + +/* Return the maximum shared-secret size in bytes. */ +size_t p11_keyexch_ctx_get_outsize(const P11_KEYEXCH_CTX *keyexch_ctx) +{ +#ifndef OPENSSL_NO_EC + EVP_PKEY *pkey; + int bits; +#endif /* OPENSSL_NO_EC */ + size_t outsize = 0; + + if (keyexch_ctx == NULL) + return 0; + + switch (p11_keyexch_ctx_get_type(keyexch_ctx)) { +#ifndef OPENSSL_NO_EC + case EVP_PKEY_EC: + /* ECDH output is at most the EC field size. */ + pkey = p11_keyexch_ctx_get_evp_pkey(keyexch_ctx); + if (pkey == NULL) + return 0; + + bits = EVP_PKEY_bits(pkey); + if (bits <= 0) + return 0; + + outsize = ((size_t)bits + 7) / 8; + break; +#endif /* OPENSSL_NO_EC */ +#ifndef OPENSSL_NO_ECX + case EVP_PKEY_X25519: + return 32; /* X25519 shared secret has fixed length. */ + + case EVP_PKEY_X448: + return 56; /* X448 shared secret has fixed length. */ +#endif /* OPENSSL_NO_ECX */ + default: + return 0; + } + return outsize; +} /******************************************************************************/ /* Internal helper functions */ @@ -2028,9 +2234,28 @@ static OSSL_PARAM *public_params_from_evp_pkey(EVP_PKEY *pkey) #ifndef OPENSSL_NO_ECX case EVP_PKEY_ED25519: case EVP_PKEY_ED448: + case EVP_PKEY_X25519: + case EVP_PKEY_X448: { size_t publen = 0; - const char *name = (nid == EVP_PKEY_ED25519) ? "ED25519" : "ED448"; + const char *name; + + switch (nid) { + case EVP_PKEY_ED25519: + name = "ED25519"; + break; + case EVP_PKEY_ED448: + name = "ED448"; + break; + case EVP_PKEY_X25519: + name = "X25519"; + break; + case EVP_PKEY_X448: + name = "X448"; + break; + default: + goto err; + } if (!OSSL_PARAM_BLD_push_utf8_string(bld, OSSL_PKEY_PARAM_GROUP_NAME, name, 0)) @@ -2119,7 +2344,7 @@ static int p11_keydata_init_from_params(EVP_PKEY *pkey, P11_KEYDATA *keydata) keydata->type = 0; keydata->name = NULL; keydata->keysize = 0; - keydata->sigsize = 0; + keydata->maxsize = 0; type = evp_pkey_get_type_id(pkey); @@ -2136,7 +2361,9 @@ static int p11_keydata_init_from_params(EVP_PKEY *pkey, P11_KEYDATA *keydata) #ifndef OPENSSL_NO_ECX case EVP_PKEY_ED25519: case EVP_PKEY_ED448: - return p11_keydata_init_eddsa_from_params(keydata, type); + case EVP_PKEY_X25519: + case EVP_PKEY_X448: + return p11_keydata_init_ecx_from_params(keydata, type); #endif /* OPENSSL_NO_ECX */ #if OPENSSL_VERSION_NUMBER >= 0x30500000L @@ -2198,7 +2425,7 @@ static int p11_keydata_init_rsa_from_params(P11_KEYDATA *keydata) keydata->type = EVP_PKEY_RSA; keydata->name = "RSA"; keydata->keysize = p->data_size; - keydata->sigsize = p->data_size; /* RSA signature == modulus size */ + keydata->maxsize = p->data_size; /* RSA signature == modulus size */ return 1; } @@ -2261,7 +2488,7 @@ static int p11_keydata_init_ec_from_params(P11_KEYDATA *keydata) keydata->keysize = order_bytes; /* safe upper bound for DER-encoded ECDSA signatures */ - keydata->sigsize = (2 * (order_bytes + 3)) + + keydata->maxsize = (2 * (order_bytes + 3)) + ((2 * (order_bytes + 3) < 128) ? 2 : 3); BN_free(order); @@ -2278,23 +2505,33 @@ static int p11_keydata_init_ec_from_params(P11_KEYDATA *keydata) #endif /* OPENSSL_NO_EC */ #ifndef OPENSSL_NO_ECX -static int p11_keydata_init_eddsa_from_params(P11_KEYDATA *keydata, int type) +static int p11_keydata_init_ecx_from_params(P11_KEYDATA *keydata, int type) { const OSSL_PARAM *p; size_t keysize = 0; - size_t sigsize = 0; + size_t maxsize = 0; const char *name = NULL; switch (type) { case EVP_PKEY_ED25519: name = "ED25519"; keysize = ED25519_KEYLEN; - sigsize = ED25519_SIGSIZE; + maxsize = ED25519_SIGSIZE; break; case EVP_PKEY_ED448: name = "ED448"; keysize = ED448_KEYLEN; - sigsize = ED448_SIGSIZE; + maxsize = ED448_SIGSIZE; + break; + case EVP_PKEY_X25519: + name = "X25519"; + keysize = X25519_KEYLEN; + maxsize = X25519_KEYLEN; /* max derive output size */ + break; + case EVP_PKEY_X448: + name = "X448"; + keysize = X448_KEYLEN; + maxsize = X448_KEYLEN; /* max derive output size */ break; default: return 0; @@ -2311,7 +2548,7 @@ static int p11_keydata_init_eddsa_from_params(P11_KEYDATA *keydata, int type) keydata->type = type; keydata->name = name; keydata->keysize = keysize; - keydata->sigsize = sigsize; + keydata->maxsize = maxsize; return 1; } #endif /* OPENSSL_NO_ECX */ @@ -2356,7 +2593,7 @@ static int p11_keydata_init_mldsa_from_params(P11_KEYDATA *keydata, int type) keydata->type = type; keydata->name = name; keydata->keysize = keysize; - keydata->sigsize = sigsize; + keydata->maxsize = sigsize; return 1; } #endif /* OPENSSL_NO_ML_DSA */ @@ -2457,7 +2694,7 @@ static int p11_keydata_init_slhdsa_from_params(P11_KEYDATA *keydata, int type) keydata->type = type; keydata->name = name; keydata->keysize = keysize; - keydata->sigsize = sigsize; + keydata->maxsize = sigsize; return 1; } #endif /* OPENSSL_NO_SLH_DSA */ @@ -2498,7 +2735,7 @@ static int p11_keydata_init_falcon_from_params(P11_KEYDATA *keydata, int type) keydata->type = type; keydata->name = name; keydata->keysize = keysize; - keydata->sigsize = sigsize; + keydata->maxsize = sigsize; return 1; } @@ -2765,6 +3002,10 @@ static int evp_pkey_get_type_id(const EVP_PKEY *pkey) return EVP_PKEY_ED25519; if (EVP_PKEY_is_a(pkey, "ED448")) return EVP_PKEY_ED448; + if (EVP_PKEY_is_a(pkey, "X25519")) + return EVP_PKEY_X25519; + if (EVP_PKEY_is_a(pkey, "X448")) + return EVP_PKEY_X448; #endif /* OPENSSL_NO_ECX */ #if OPENSSL_VERSION_NUMBER >= 0x30500000L diff --git a/src/provider_helpers.h b/src/provider_helpers.h index 77116a47..7a46599b 100644 --- a/src/provider_helpers.h +++ b/src/provider_helpers.h @@ -51,6 +51,7 @@ typedef struct provider_ctx PROVIDER_CTX; typedef struct p11_keydata_st P11_KEYDATA; typedef struct p11_signature_ctx P11_SIGNATURE_CTX; typedef struct p11_asym_cipher_ctx P11_ASYM_CIPHER_CTX; +typedef struct p11_keyexch_ctx P11_KEYEXCH_CTX; /******************************************************************************/ /* PROVIDER interface helpers */ @@ -88,7 +89,7 @@ int p11_keydata_get_security_category(const P11_KEYDATA *keydata); #endif /* OPENSSL_VERSION_NUMBER >= 0x30600000L */ int p11_keydata_get_security_bits(const P11_KEYDATA *keydata); int p11_keydata_get_bits(const P11_KEYDATA *keydata); -size_t p11_keydata_get_sigsize(const P11_KEYDATA *keydata); +size_t p11_keydata_get_maxsize(const P11_KEYDATA *keydata); int p11_keydata_get_type(const P11_KEYDATA *keydata); OSSL_PARAM *p11_keydata_get_params(const P11_KEYDATA *key); int p11_keydata_set_params(P11_KEYDATA *keydata, const OSSL_PARAM *params); @@ -167,6 +168,23 @@ int p11_asym_cipher_ctx_set_oaep_label(P11_ASYM_CIPHER_CTX *asym_ctx, unsigned char *p11_asym_cipher_ctx_get_oaep_label(const P11_ASYM_CIPHER_CTX *asym_ctx); size_t p11_asym_cipher_ctx_get_oaep_labellen(const P11_ASYM_CIPHER_CTX *asym_ctx); +/******************************************************************************/ +/* KEY EXCHANGE helper functions */ +/******************************************************************************/ +P11_KEYEXCH_CTX *p11_keyexch_ctx_new(PROVIDER_CTX *ctx); +void p11_keyexch_ctx_free(P11_KEYEXCH_CTX *keyexch_ctx); +P11_KEYEXCH_CTX *p11_keyexch_dupctx(P11_KEYEXCH_CTX *keyexch_ctx); +int p11_keyexch_ctx_init(P11_KEYEXCH_CTX *keyexch_ctx, P11_KEYDATA *keydata, + const OSSL_PARAM params[]); +int p11_keyexch_set_peer(P11_KEYEXCH_CTX *keyexch_ctx, P11_KEYDATA *provkey); +int p11_keyexch_ctx_set_cofactor_mode(P11_KEYEXCH_CTX *keyexch_ctx, int cofactor_mode); +int p11_keyexch_ctx_get_cofactor_mode(P11_KEYEXCH_CTX *keyexch_ctx); +EVP_PKEY *p11_keyexch_ctx_get_evp_pkey(const P11_KEYEXCH_CTX *keyexch_ctx); +int p11_keyexch_ctx_get_peer_pub(const P11_KEYEXCH_CTX *keyexch_ctx, + unsigned char **buf, size_t *len); +int p11_keyexch_ctx_get_type(const P11_KEYEXCH_CTX *keyexch_ctx); +size_t p11_keyexch_ctx_get_outsize(const P11_KEYEXCH_CTX *keyexch_ctx); + #endif /* _PROVIDER_HELPERS_H */ /* vim: set noexpandtab: */ From 4b65609880f0281e697f6b1dbe8afd71c77e14a2 Mon Sep 17 00:00:00 2001 From: olszomal Date: Mon, 6 Jul 2026 15:34:50 +0200 Subject: [PATCH 2/3] Add X25519 and X448 key generation examples --- examples/Makefile.am | 1 + examples/x25519keygen.c | 191 ++++++++++++++++++++++++++++++++++++++++ examples/x448keygen.c | 191 ++++++++++++++++++++++++++++++++++++++++ 3 files changed, 383 insertions(+) create mode 100644 examples/x25519keygen.c create mode 100644 examples/x448keygen.c diff --git a/examples/Makefile.am b/examples/Makefile.am index 35d227bd..f20d0fa9 100644 --- a/examples/Makefile.am +++ b/examples/Makefile.am @@ -6,6 +6,7 @@ EXTRA_DIST = README noinst_PROGRAMS = auth decrypt getrandom listkeys listkeys_ext \ ed25519keygen ed448keygen eckeygen rsakeygen \ + x25519keygen x448keygen \ mldsa87keygen slhdsa256skeygen falcon512keygen \ storecert diff --git a/examples/x25519keygen.c b/examples/x25519keygen.c new file mode 100644 index 00000000..5b4fd687 --- /dev/null +++ b/examples/x25519keygen.c @@ -0,0 +1,191 @@ +/* + * Copyright © 2026 Mobi - Com Polska Sp. z o.o. + * Author: Małgorzata Olszówka + * All rights reserved. + * + * Elliptic Montgomery Curve X25519 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 + +#if !defined(OPENSSL_NO_ECX) && (OPENSSL_VERSION_NUMBER >= 0x30000000L) + +#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 xdh = {.nid = NID_X25519}; + 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_X25519; + eckg.kgen.eddsa = &xdh; + 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("\nX25519 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; +} + +#else /* !OPENSSL_NO_ECX && OpenSSL 3.x */ + +#include + +int main(void) +{ + fprintf(stderr, "Skipped: requires OpenSSL 3.x built with ECX support\n"); + return 77; +} + +#endif /* !OPENSSL_NO_ECX && OpenSSL 3.x */ + +/* vim: set noexpandtab: */ diff --git a/examples/x448keygen.c b/examples/x448keygen.c new file mode 100644 index 00000000..318b9035 --- /dev/null +++ b/examples/x448keygen.c @@ -0,0 +1,191 @@ +/* + * Copyright © 2026 Mobi - Com Polska Sp. z o.o. + * Author: Małgorzata Olszówka + * All rights reserved. + * + * Elliptic Montgomery Curve X448 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 + +#if !defined(OPENSSL_NO_ECX) && (OPENSSL_VERSION_NUMBER >= 0x30000000L) + +#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 xdh = {.nid = NID_X448}; + 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_X448; + eckg.kgen.eddsa = &xdh; + 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("\nX448 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; +} + +#else /* !OPENSSL_NO_ECX && OpenSSL 3.x */ + +#include + +int main(void) +{ + fprintf(stderr, "Skipped: requires OpenSSL 3.x built with ECX support\n"); + return 77; +} + +#endif /* !OPENSSL_NO_ECX && OpenSSL 3.x */ + +/* vim: set noexpandtab: */ From f47f5bc25324748f03d74436017c77061481d2f5 Mon Sep 17 00:00:00 2001 From: olszomal Date: Mon, 6 Jul 2026 15:41:57 +0200 Subject: [PATCH 3/3] Add ECDH derive tests for engine and provider --- .gitignore | 4 + tests/Makefile.am | 5 + tests/common.sh | 14 ++ tests/ec-derive-prov.c | 304 ++++++++++++++++++++++++ tests/ec-derive.c | 385 +++++++++++++++++++++++++++++++ tests/ec-derive.softhsm | 64 +++++ tests/provider-ec-derive.softhsm | 72 ++++++ 7 files changed, 848 insertions(+) create mode 100644 tests/ec-derive-prov.c create mode 100644 tests/ec-derive.c create mode 100755 tests/ec-derive.softhsm create mode 100755 tests/provider-ec-derive.softhsm diff --git a/.gitignore b/.gitignore index 4ff7122e..18b037df 100644 --- a/.gitignore +++ b/.gitignore @@ -66,6 +66,8 @@ examples/eckeygen examples/rsakeygen examples/ed25519keygen examples/ed448keygen +examples/x25519keygen +examples/x448keygen examples/falcon512keygen examples/mldsa87keygen examples/slhdsa256skeygen @@ -89,6 +91,8 @@ tests/rsa-oaep-prov tests/rsa-pss-sign-prov tests/rsa-keygen tests/ec-keygen +tests/ec-derive +tests/ec-derive-prov tests/ed25519-keygen tests/ed448-keygen tests/ed25519-keygen-prov diff --git a/tests/Makefile.am b/tests/Makefile.am index 7ec87474..c6a6e514 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -28,6 +28,8 @@ check_PROGRAMS = \ check-all-prov \ rsa-keygen \ ec-keygen \ + ec-derive \ + ec-derive-prov \ ed25519-keygen \ ed448-keygen \ ed25519-keygen-prov \ @@ -50,6 +52,7 @@ dist_check_SCRIPTS = \ ec-cert-store.softhsm \ ec-copy.softhsm \ ec-keygen.softhsm \ + ec-derive.softhsm \ ed25519-keygen.softhsm \ ed448-keygen.softhsm \ fork-change-slot.softhsm \ @@ -66,6 +69,7 @@ dist_check_SCRIPTS = \ provider-ec-check-privkey.softhsm \ provider-ec-check-all.softhsm \ provider-ec-copy.softhsm \ + provider-ec-derive.softhsm \ provider-ed25519-check-privkey.softhsm \ provider-ed448-check-privkey.softhsm \ provider-ed25519-keygen.softhsm \ @@ -97,6 +101,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 +ec_derive_prov_SOURCES = ec-derive-prov.c helpers_prov.c TESTS = $(dist_check_SCRIPTS) diff --git a/tests/common.sh b/tests/common.sh index 43cf31d0..481c92c0 100755 --- a/tests/common.sh +++ b/tests/common.sh @@ -226,6 +226,20 @@ generate_rsa_key_pair () { fi } +# Generate an EC key pair with derive usage on the prime256v1 curve +generate_ec_derive_key_pair () { + local obj_label="$1" + local token_label="$2" + + echo "* Generating an EC derive key pair on the token ${token_label}" + pkcs11-tool --login --pin ${PIN} --module ${MODULE} --id ${ID} \ + --keypairgen --key-type "EC:prime256v1" --usage-derive \ + --label ${obj_label} --token-label ${token_label} + if [[ $? -ne 0 ]]; then + exit 1 + fi +} + # Do the token initialization init_token () { local key_type="$1" diff --git a/tests/ec-derive-prov.c b/tests/ec-derive-prov.c new file mode 100644 index 00000000..3fc0f9f7 --- /dev/null +++ b/tests/ec-derive-prov.c @@ -0,0 +1,304 @@ +/* + * Copyright © 2026 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 . + * + * + * ECDH derive test for pkcs11 provider. + * + * The test loads an EC private key and public key from the PKCS#11 provider, + * generates a temporary software EC key on the same group, and verifies that: + * ECDH(token_private, software_public) == ECDH(software_private, token_public) + */ + +#include "helpers_prov.h" + +#include +#include +#include + +#if OPENSSL_VERSION_NUMBER >= 0x30000000L && !defined(OPENSSL_NO_EC) + +#include +#include +#include +#include +#include +#include + +static EVP_PKEY *public_key_copy(EVP_PKEY *key) +{ + EVP_PKEY *copy = NULL; + unsigned char *der = NULL, *p = NULL; + const unsigned char *q = NULL; + int der_len; + + der_len = i2d_PUBKEY(key, NULL); + if (der_len <= 0) { + display_openssl_errors(); + goto end; + } + der = OPENSSL_malloc((size_t)der_len); + if (der == NULL) { + display_openssl_errors(); + goto end; + } + p = der; + if (i2d_PUBKEY(key, &p) != der_len) { + display_openssl_errors(); + goto end; + } + q = der; + copy = d2i_PUBKEY(NULL, &q, der_len); + if (copy == NULL) + display_openssl_errors(); + +end: + OPENSSL_free(der); + return copy; +} + +static EVP_PKEY *generate_software_ec_key(EVP_PKEY *template_key) +{ + EVP_PKEY_CTX *ctx = NULL; + EVP_PKEY *pkey = NULL; + char group_name[128]; + size_t group_name_len = 0; + + if (EVP_PKEY_get_utf8_string_param(template_key, + OSSL_PKEY_PARAM_GROUP_NAME, + group_name, sizeof(group_name), + &group_name_len) != 1) { + fprintf(stderr, "Cannot get EC group name from token public key\n"); + display_openssl_errors(); + goto end; + } + + group_name[sizeof(group_name) - 1] = '\0'; + printf("Using EC group: %s\n", group_name); + + ctx = EVP_PKEY_CTX_new_from_name(NULL, "EC", "provider=default"); + if (ctx == NULL) { + display_openssl_errors(); + goto end; + } + if (EVP_PKEY_keygen_init(ctx) <= 0) { + display_openssl_errors(); + goto end; + } + if (EVP_PKEY_CTX_set_group_name(ctx, group_name) <= 0) { + display_openssl_errors(); + goto end; + } + if (EVP_PKEY_keygen(ctx, &pkey) <= 0) { + display_openssl_errors(); + goto end; + } + +end: + EVP_PKEY_CTX_free(ctx); + return pkey; +} + +/* SoftHSM does not support ECDH cofactor derivation: + * EVP_PKEY_CTX_set_ecdh_cofactor_mode(ctx, 1) */ +static int derive_secret(EVP_PKEY *priv, EVP_PKEY *peer, + unsigned char **secret, size_t *secret_len) +{ + EVP_PKEY_CTX *ctx = NULL; + int ret = 0; + + *secret = NULL; + *secret_len = 0; + + ctx = EVP_PKEY_CTX_new_from_pkey(NULL, priv, NULL); + if (ctx == NULL) { + display_openssl_errors(); + goto end; + } + if (EVP_PKEY_derive_init(ctx) <= 0) { + display_openssl_errors(); + goto end; + } + /* Set up peer's public key */ + if (EVP_PKEY_derive_set_peer(ctx, peer) <= 0) { + display_openssl_errors(); + goto end; + } + if (EVP_PKEY_derive(ctx, NULL, secret_len) <= 0) { + display_openssl_errors(); + goto end; + } + if (*secret_len == 0) { + fprintf(stderr, "Derived secret has zero length\n"); + goto end; + } + *secret = OPENSSL_malloc(*secret_len); + if (*secret == NULL){ + display_openssl_errors(); + goto end; + } + if (EVP_PKEY_derive(ctx, *secret, secret_len) <= 0) { + display_openssl_errors(); + goto end; + } + ret = 1; + +end: + EVP_PKEY_CTX_free(ctx); + if (!ret) { + OPENSSL_free(*secret); + *secret = NULL; + *secret_len = 0; + } + return ret; +} + +static void dump_digest(const char *label, const unsigned char *buf, size_t len) +{ + EVP_MD_CTX *ctx = NULL; + unsigned char md[EVP_MAX_MD_SIZE]; + unsigned int md_len = 0; + unsigned int i; + + ctx = EVP_MD_CTX_new(); + if (ctx == NULL) + return; + + if (EVP_DigestInit_ex(ctx, EVP_sha256(), NULL) != 1 || + EVP_DigestUpdate(ctx, buf, len) != 1 || + EVP_DigestFinal_ex(ctx, md, &md_len) != 1) { + EVP_MD_CTX_free(ctx); + return; + } + + fprintf(stderr, "%s SHA256: ", label); + for (i = 0; i < md_len; i++) + fprintf(stderr, "%02x", md[i]); + fprintf(stderr, "\n"); + + EVP_MD_CTX_free(ctx); +} + +int main(int argc, char **argv) +{ + const char *private_key_name, *public_key_name; + EVP_PKEY *token_priv = NULL; + EVP_PKEY *token_pub = NULL; + EVP_PKEY *token_pub_sw = NULL; + EVP_PKEY *peer_priv = NULL; + EVP_PKEY *peer_pub = NULL; + unsigned char *secret_token = NULL; + unsigned char *secret_ref = NULL; + size_t secret_token_len = 0; + size_t secret_ref_len = 0; + int ret = EXIT_FAILURE; + + if (argc != 3) { + fprintf(stderr, "usage: %s [private key URL] [public key URL]\n", + argv[0]); + return EXIT_FAILURE; + } + + private_key_name = argv[1]; + public_key_name = argv[2]; + + if (!providers_load()) { + display_openssl_errors(); + goto cleanup; + } + + token_priv = load_pkey(private_key_name, "provider=pkcs11prov", NULL); + if (token_priv == NULL) { + fprintf(stderr, "cannot load private key: %s\n", private_key_name); + display_openssl_errors(); + goto cleanup; + } + + token_pub = load_pubkey(public_key_name, "provider=pkcs11prov"); + if (token_pub == NULL) { + fprintf(stderr, "cannot load public key: %s\n", public_key_name); + display_openssl_errors(); + goto cleanup; + } + + token_pub_sw = public_key_copy(token_pub); + if (token_pub_sw == NULL) { + fprintf(stderr, "cannot create software copy of token public key\n"); + goto cleanup; + } + + peer_priv = generate_software_ec_key(token_pub_sw); + if (peer_priv == NULL) { + fprintf(stderr, "cannot generate software EC peer key\n"); + goto cleanup; + } + + peer_pub = public_key_copy(peer_priv); + if (peer_pub == NULL) { + fprintf(stderr, "cannot create software EC peer public key\n"); + goto cleanup; + } + + printf("Deriving with token private key and software public key\n"); + if (!derive_secret(token_priv, peer_pub, + &secret_token, &secret_token_len)) { + fprintf(stderr, "token ECDH derive failed\n"); + goto cleanup; + } + + printf("Deriving with software private key and token public key\n"); + if (!derive_secret(peer_priv, token_pub_sw, + &secret_ref, &secret_ref_len)) { + fprintf(stderr, "software reference ECDH derive failed\n"); + goto cleanup; + } + + if (secret_token_len != secret_ref_len || + memcmp(secret_token, secret_ref, secret_token_len) != 0) { + fprintf(stderr, "ECDH secrets differ\n"); + dump_digest("token", secret_token, secret_token_len); + dump_digest("ref ", secret_ref, secret_ref_len); + goto cleanup; + } + + printf("ECDH derive success\n"); + ret = EXIT_SUCCESS; + +cleanup: + OPENSSL_free(secret_token); + OPENSSL_free(secret_ref); + EVP_PKEY_free(token_priv); + EVP_PKEY_free(token_pub); + EVP_PKEY_free(token_pub_sw); + EVP_PKEY_free(peer_priv); + EVP_PKEY_free(peer_pub); + providers_cleanup(); + + return ret; +} + +#else + +int main(void) +{ + fprintf(stderr, "Skipped: requires OpenSSL >= 3.0 with EC support\n"); + return 77; +} + +#endif + +/* vim: set noexpandtab: */ diff --git a/tests/ec-derive.c b/tests/ec-derive.c new file mode 100644 index 00000000..f5a3915f --- /dev/null +++ b/tests/ec-derive.c @@ -0,0 +1,385 @@ +/* + * Copyright © 2026 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 . + * + * ECDH derive test for engine pkcs11. + * + * The test loads an EC private key and public key from the PKCS#11 engine, + * generates a temporary software EC key on the same group, and verifies that: + * ECDH(token_private, software_public) == ECDH(software_private, token_public) + */ + +#include +#include +#include + +/* this code intentionally uses deprecated ENGINE/EC APIs */ +#define OPENSSL_SUPPRESS_DEPRECATED +#include + +#if !defined(OPENSSL_NO_ENGINE) && !defined(OPENSSL_NO_EC) + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static void display_openssl_errors(int l) +{ + const char *file; + char buf[120]; + unsigned long e; + int line; + + if (ERR_peek_error() == 0) + return; + + fprintf(stderr, "At ec-derive.c:%d:\n", l); + while ((e = ERR_get_error_line(&file, &line)) != 0) { + ERR_error_string_n(e, buf, sizeof(buf)); + fprintf(stderr, "- SSL %s: %s:%d\n", buf, file, line); + } +} + +static EVP_PKEY *public_key_copy(EVP_PKEY *key) +{ + EVP_PKEY *copy = NULL; + unsigned char *der = NULL, *p = NULL; + const unsigned char *q = NULL; + int der_len; + + der_len = i2d_PUBKEY(key, NULL); + if (der_len <= 0) { + display_openssl_errors(__LINE__); + goto end; + } + der = OPENSSL_malloc((size_t)der_len); + if (der == NULL) { + display_openssl_errors(__LINE__); + goto end; + } + p = der; + if (i2d_PUBKEY(key, &p) != der_len) { + display_openssl_errors(__LINE__); + goto end; + } + q = der; + copy = d2i_PUBKEY(NULL, &q, der_len); + if (copy == NULL) + display_openssl_errors(__LINE__); + +end: + OPENSSL_free(der); + return copy; +} + +static EVP_PKEY *generate_software_ec_key(EVP_PKEY *template_key) +{ + EVP_PKEY *pkey = NULL; + EC_KEY *template_ec = NULL; + EC_KEY *ec = NULL; + const EC_GROUP *group; + int nid; + + template_ec = EVP_PKEY_get1_EC_KEY(template_key); + if (template_ec == NULL) { + display_openssl_errors(__LINE__); + goto end; + } + group = EC_KEY_get0_group(template_ec); + if (group == NULL) { + fprintf(stderr, "Token public key has no EC group\n"); + goto end; + } + nid = EC_GROUP_get_curve_name(group); + if (nid != NID_undef) + printf("Using EC group: %s\n", OBJ_nid2sn(nid)); + else + printf("Using EC group with explicit parameters\n"); + + ec = EC_KEY_new(); + if (ec == NULL) { + display_openssl_errors(__LINE__); + goto end; + } + if (EC_KEY_set_group(ec, group) != 1) { + display_openssl_errors(__LINE__); + goto end; + } + if (EC_KEY_generate_key(ec) != 1) { + display_openssl_errors(__LINE__); + goto end; + } + pkey = EVP_PKEY_new(); + if (pkey == NULL) { + display_openssl_errors(__LINE__); + goto end; + } + if (EVP_PKEY_assign_EC_KEY(pkey, ec) != 1) { + display_openssl_errors(__LINE__); + EVP_PKEY_free(pkey); + pkey = NULL; + goto end; + } + /* pkey owns ec now */ + ec = NULL; + +end: + EC_KEY_free(template_ec); + EC_KEY_free(ec); + return pkey; +} + +static int derive_secret(EVP_PKEY *priv, EVP_PKEY *peer, ENGINE *engine, + unsigned char **secret, size_t *secret_len) +{ + EVP_PKEY_CTX *ctx = NULL; + int ret = 0; + + *secret = NULL; + *secret_len = 0; + + ctx = EVP_PKEY_CTX_new(priv, engine); + if (ctx == NULL) { + display_openssl_errors(__LINE__); + goto end; + } + if (EVP_PKEY_derive_init(ctx) <= 0) { + display_openssl_errors(__LINE__); + goto end; + } + /* Set up peer's public key */ + if (EVP_PKEY_derive_set_peer(ctx, peer) <= 0) { + display_openssl_errors(__LINE__); + goto end; + } + if (EVP_PKEY_derive(ctx, NULL, secret_len) <= 0) { + display_openssl_errors(__LINE__); + goto end; + } + if (*secret_len == 0) { + fprintf(stderr, "Derived secret has zero length\n"); + goto end; + } + *secret = OPENSSL_malloc(*secret_len); + if (*secret == NULL){ + display_openssl_errors(__LINE__); + goto end; + } + if (EVP_PKEY_derive(ctx, *secret, secret_len) <= 0) { + display_openssl_errors(__LINE__); + goto end; + } + ret = 1; + +end: + EVP_PKEY_CTX_free(ctx); + if (!ret) { + OPENSSL_free(*secret); + *secret = NULL; + *secret_len = 0; + } + return ret; +} + +static void dump_digest(const char *label, const unsigned char *buf, size_t len) +{ + EVP_MD_CTX *ctx = NULL; + unsigned char md[EVP_MAX_MD_SIZE]; + unsigned int md_len = 0; + unsigned int i; + + ctx = EVP_MD_CTX_create(); + if (ctx == NULL) + return; + + if (EVP_DigestInit_ex(ctx, EVP_sha256(), NULL) != 1 || + EVP_DigestUpdate(ctx, buf, len) != 1 || + EVP_DigestFinal_ex(ctx, md, &md_len) != 1) { + EVP_MD_CTX_destroy(ctx); + return; + } + + fprintf(stderr, "%s SHA256: ", label); + for (i = 0; i < md_len; i++) + fprintf(stderr, "%02x", md[i]); + fprintf(stderr, "\n"); + + EVP_MD_CTX_destroy(ctx); +} + +int main(int argc, char **argv) +{ + const char *efile, *private_key_name, *public_key_name, *module_path; + ENGINE *engine = NULL; + EVP_PKEY *token_priv = NULL; + EVP_PKEY *token_pub = NULL; + EVP_PKEY *token_pub_sw = NULL; + EVP_PKEY *peer_priv = NULL; + EVP_PKEY *peer_pub = NULL; + unsigned char *secret_token = NULL; + unsigned char *secret_ref = NULL; + size_t secret_token_len = 0; + size_t secret_ref_len = 0; + int ret = EXIT_FAILURE; + int engine_initialized = 0; + int engine_structural_freed = 0; + int res; + + if (argc != 5) { + fprintf(stderr, "usage: %s [CONF] [private key URL] [public key URL] [module]\n", argv[0]); + return EXIT_FAILURE; + } + + efile = argv[1]; + private_key_name = argv[2]; + public_key_name = argv[3]; + module_path = argv[4]; + + res = CONF_modules_load_file(efile, "engines", 0); + if (res <= 0) { + fprintf(stderr, "cannot load %s\n", efile); + display_openssl_errors(__LINE__); + goto cleanup; + } + + ENGINE_add_conf_module(); + +#if OPENSSL_VERSION_NUMBER >= 0x10100000L + 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) { + fprintf(stderr, "Could not get pkcs11 engine\n"); + display_openssl_errors(__LINE__); + goto cleanup; + } + if (!ENGINE_ctrl_cmd_string(engine, "DEBUG_LEVEL", "7", 0)) { + display_openssl_errors(__LINE__); + goto cleanup; + } + if (module_path != NULL && module_path[0] != '\0') { + if (!ENGINE_ctrl_cmd_string(engine, "MODULE_PATH", module_path, 0)) { + display_openssl_errors(__LINE__); + goto cleanup; + } + } + if (!ENGINE_init(engine)) { + fprintf(stderr, "Could not initialize pkcs11 engine\n"); + display_openssl_errors(__LINE__); + goto cleanup; + } + /* ENGINE_init() returned a functional reference; drop structural ref. */ + engine_initialized = 1; + ENGINE_free(engine); + engine_structural_freed = 1; + + token_priv = ENGINE_load_private_key(engine, private_key_name, NULL, NULL); + if (token_priv == NULL) { + fprintf(stderr, "cannot load private key: %s\n", private_key_name); + display_openssl_errors(__LINE__); + goto cleanup; + } + token_pub = ENGINE_load_public_key(engine, public_key_name, NULL, NULL); + if (token_pub == NULL) { + fprintf(stderr, "cannot load public key: %s\n", public_key_name); + display_openssl_errors(__LINE__); + goto cleanup; + } + peer_priv = generate_software_ec_key(token_pub); + if (peer_priv == NULL) { + fprintf(stderr, "cannot generate software EC peer key\n"); + goto cleanup; + } + peer_pub = public_key_copy(peer_priv); + if (peer_pub == NULL) { + fprintf(stderr, "cannot create software EC peer public key\n"); + goto cleanup; + } + token_pub_sw = public_key_copy(token_pub); + if (token_pub_sw == NULL) { + fprintf(stderr, "cannot create software copy of token public key\n"); + goto cleanup; + } + printf("Deriving with token private key and software public key\n"); + if (!derive_secret(token_priv, peer_pub, engine, + &secret_token, &secret_token_len)) { + fprintf(stderr, "token ECDH derive failed\n"); + goto cleanup; + } + printf("Deriving with software private key and token public key\n"); + if (!derive_secret(peer_priv, token_pub_sw, NULL, + &secret_ref, &secret_ref_len)) { + fprintf(stderr, "software reference ECDH derive failed\n"); + goto cleanup; + } + if (secret_token_len != secret_ref_len || + memcmp(secret_token, secret_ref, secret_token_len) != 0) { + fprintf(stderr, "ECDH secrets differ\n"); + dump_digest("token", secret_token, secret_token_len); + dump_digest("ref ", secret_ref, secret_ref_len); + goto cleanup; + } + + printf("ECDH derive success\n"); + ret = EXIT_SUCCESS; + +cleanup: + OPENSSL_free(secret_token); + OPENSSL_free(secret_ref); + EVP_PKEY_free(token_priv); + EVP_PKEY_free(token_pub); + EVP_PKEY_free(token_pub_sw); + EVP_PKEY_free(peer_priv); + EVP_PKEY_free(peer_pub); + + if (engine != NULL) { + if (engine_initialized) + ENGINE_finish(engine); + if (!engine_structural_freed) + ENGINE_free(engine); + } + CONF_modules_unload(1); + return ret; +} + +#else /* !OPENSSL_NO_ENGINE && !OPENSSL_NO_EC */ + +int main(void) +{ + fprintf(stderr, "Skipped: requires OpenSSL with ENGINE and EC support\n"); + return 77; +} + +#endif /* !OPENSSL_NO_ENGINE && !OPENSSL_NO_EC */ + +/* vim: set noexpandtab: */ diff --git a/tests/ec-derive.softhsm b/tests/ec-derive.softhsm new file mode 100755 index 00000000..2967dd3c --- /dev/null +++ b/tests/ec-derive.softhsm @@ -0,0 +1,64 @@ +#!/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.$$" + +PRIVATE_KEY="pkcs11:token=libp11-0;id=%01%02%03%04;object=ecdh-key;type=private;pin-value=1234" +PUBLIC_KEY="pkcs11:token=libp11-0;id=%01%02%03%04;object=ecdh-key;type=public;pin-value=1234" + +# Load common test functions +. ${srcdir}/common.sh + +# Initialize SoftHSM DB +init_db + +if ! has_mechanism ECDH1-DERIVE; then + echo "Skipping test: ECDH1-DERIVE not supported." + rm -rf "$outdir" + exit 77 +fi + +# Create token +init_card "libp11-0" + +# Generate an EC derive key pair on the token +generate_ec_derive_key_pair "ecdh-key" "libp11-0" + +if [[ $? -ne 0 ]]; then + echo "Skipping test: cannot generate EC derive keypair." + rm -rf "$outdir" + exit 77 +fi + +# Load openssl settings +. ${srcdir}/openssl-settings.sh + +# Restore openssl settings +trap cleanup EXIT + +${WRAPPER} ./ec-derive "${outdir}/engines.cnf" "${PRIVATE_KEY}" "${PUBLIC_KEY}" "${MODULE}" +rc=$? +if [[ $rc -eq 77 ]]; then + echo "ECDH derive test skipped." + rm -rf "$outdir" + exit 77 +elif [[ $rc -ne 0 ]]; then + echo "ECDH derive test failed." + exit 1 +fi + +rm -rf "$outdir" + +exit 0 diff --git a/tests/provider-ec-derive.softhsm b/tests/provider-ec-derive.softhsm new file mode 100755 index 00000000..b5ec1503 --- /dev/null +++ b/tests/provider-ec-derive.softhsm @@ -0,0 +1,72 @@ +#!/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.$$" + +PRIVATE_KEY="pkcs11:token=libp11-0;id=%01%02%03%04;object=ecdh-key;type=private;pin-value=1234" +PUBLIC_KEY="pkcs11:token=libp11-0;id=%01%02%03%04;object=ecdh-key;type=public" + +# Load common test functions +. ${srcdir}/common.sh + +# Initialize SoftHSM DB +init_db + +if ! has_mechanism ECDH1-DERIVE; then + echo "Skipping test: ECDH1-DERIVE not supported." + rm -rf "$outdir" + exit 77 +fi + +# Create token +init_card "libp11-0" + +# Generate an EC derive key pair on the token +generate_ec_derive_key_pair "ecdh-key" "libp11-0" + +if [[ $? -ne 0 ]]; then + echo "Skipping test: cannot generate EC derive keypair." + rm -rf "$outdir" + exit 77 +fi + +# Ensure the use of the locally built provider +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 +. ${srcdir}/openssl-settings.sh + +# Restore openssl settings +trap cleanup EXIT + +${WRAPPER} ./ec-derive-prov "${PRIVATE_KEY}" "${PUBLIC_KEY}" +rc=$? +if [[ $rc -eq 77 ]]; then + echo "ECDH provider derive test skipped." + rm -rf "$outdir" + exit 77 +elif [[ $rc -ne 0 ]]; then + echo "ECDH provider derive test failed." + exit 1 +fi + +rm -rf "$outdir" + +exit 0