From 96365ccc3834d78ffb414b65c8e204e083e7d040 Mon Sep 17 00:00:00 2001 From: Antoine Lochet <30625353+antoinelochet@users.noreply.github.com> Date: Sun, 5 Jul 2026 17:55:27 +0200 Subject: [PATCH] Fixed EDDSA context/pre-hash support --- src/lib/SoftHSM.cpp | 84 +++++++++++ src/lib/crypto/BotanEDDSA.cpp | 83 +++++++++-- src/lib/crypto/CMakeLists.txt | 1 + src/lib/crypto/EDDSAMechanismParam.cpp | 44 ++++++ src/lib/crypto/EDDSAMechanismParam.h | 44 ++++++ src/lib/crypto/Makefile.am | 1 + src/lib/crypto/OSSLEDDSA.cpp | 177 +++++++++++++++++++++-- src/lib/test/SignVerifyTests.cpp | 191 +++++++++++++++++++++++++ src/lib/test/SignVerifyTests.h | 7 + 9 files changed, 613 insertions(+), 19 deletions(-) create mode 100644 src/lib/crypto/EDDSAMechanismParam.cpp create mode 100644 src/lib/crypto/EDDSAMechanismParam.h diff --git a/src/lib/SoftHSM.cpp b/src/lib/SoftHSM.cpp index ae4211ec7..b1789f6e1 100644 --- a/src/lib/SoftHSM.cpp +++ b/src/lib/SoftHSM.cpp @@ -58,6 +58,9 @@ #include "ECParameters.h" #include "EDPublicKey.h" #include "EDPrivateKey.h" +#ifdef WITH_EDDSA +#include "EDDSAMechanismParam.h" +#endif #include "DHParameters.h" #include "DHPublicKey.h" #include "DHPrivateKey.h" @@ -4320,6 +4323,7 @@ CK_RV SoftHSM::AsymSignInit(CK_SESSION_HANDLE hSession, CK_MECHANISM_PTR pMechan #endif #ifdef WITH_EDDSA bool isEDDSA = false; + EDDSAMechanismParam eddsaParam; #endif #ifdef WITH_ML_DSA bool isMLDSA = false; @@ -4585,6 +4589,46 @@ CK_RV SoftHSM::AsymSignInit(CK_SESSION_HANDLE hSession, CK_MECHANISM_PTR pMechan mechanism = AsymMech::EDDSA; bAllowMultiPartOp = false; isEDDSA = true; + INFO_MSG("SignInit EDDSA mechanism, parameterLen=%lu, pParameter=%p", (unsigned long)pMechanism->ulParameterLen, pMechanism->pParameter); + if (pMechanism->pParameter == NULL_PTR) + { + INFO_MSG("EDDSA mechanism parameters not specified, using default values"); + if (pMechanism->ulParameterLen != 0) + { + ERROR_MSG("Invalid parameters"); + return CKR_ARGUMENTS_BAD; + } + // Default EdDSA parameters + eddsaParam.flag = false; + } + else + { + INFO_MSG("EDDSA mechanism parameters specified, parameterLen=%lu", (unsigned long)pMechanism->ulParameterLen); + if (pMechanism->ulParameterLen != sizeof(CK_EDDSA_PARAMS)) + { + ERROR_MSG("Invalid parameters"); + return CKR_ARGUMENTS_BAD; + } + CK_EDDSA_PARAMS* ckEddsaParams = (CK_EDDSA_PARAMS*) pMechanism->pParameter; + if (ckEddsaParams->ulContextDataLen > 255) + { + ERROR_MSG("Invalid parameters"); + return CKR_ARGUMENTS_BAD; + } + INFO_MSG("SignInit EDDSA flag=%s, contextDataLen=%lu", ckEddsaParams->phFlag != 0x00 ? "true" : "false", (unsigned long)ckEddsaParams->ulContextDataLen); + eddsaParam.flag = (ckEddsaParams->phFlag != 0x00); + if (ckEddsaParams->ulContextDataLen > 0) + { + if (ckEddsaParams->pContextData == NULL_PTR) + { + ERROR_MSG("Invalid parameters"); + return CKR_ARGUMENTS_BAD; + } + eddsaParam.contextData = ByteString(ckEddsaParams->pContextData, ckEddsaParams->ulContextDataLen); + DEBUG_MSG("Sign EdDSA flag=%s, contextDataLen=%lu", eddsaParam.flag ? "true" : "false", (unsigned long)eddsaParam.contextData.size()); + } + mechanismParam = &eddsaParam; + } break; #endif #ifdef WITH_ML_DSA @@ -5410,6 +5454,7 @@ CK_RV SoftHSM::AsymVerifyInit(CK_SESSION_HANDLE hSession, CK_MECHANISM_PTR pMech #endif #ifdef WITH_EDDSA bool isEDDSA = false; + EDDSAMechanismParam eddsaParam; #endif #ifdef WITH_ML_DSA bool isMLDSA = false; @@ -5674,6 +5719,45 @@ CK_RV SoftHSM::AsymVerifyInit(CK_SESSION_HANDLE hSession, CK_MECHANISM_PTR pMech mechanism = AsymMech::EDDSA; bAllowMultiPartOp = false; isEDDSA = true; + INFO_MSG("Found EDDSA mechanism, checking parameters"); + if (pMechanism->pParameter == NULL_PTR) + { + if (pMechanism->ulParameterLen != 0) + { + ERROR_MSG("Invalid parameters"); + return CKR_ARGUMENTS_BAD; + } + // Default EdDSA parameters + eddsaParam.flag = false; + INFO_MSG("EDDSA parameters not specified, using default values"); + } + else + { + INFO_MSG("EDDSA parameters specified, checking values"); + if (pMechanism->ulParameterLen != sizeof(CK_EDDSA_PARAMS)) + { + ERROR_MSG("Invalid parameters"); + return CKR_ARGUMENTS_BAD; + } + CK_EDDSA_PARAMS* ckEddsaParams = (CK_EDDSA_PARAMS*) pMechanism->pParameter; + if (ckEddsaParams->ulContextDataLen > 255) + { + ERROR_MSG("Invalid parameters"); + return CKR_ARGUMENTS_BAD; + } + eddsaParam.flag = (ckEddsaParams->phFlag != 0x00); + if (ckEddsaParams->ulContextDataLen > 0) + { + if (ckEddsaParams->pContextData == NULL_PTR) + { + ERROR_MSG("Invalid parameters"); + return CKR_ARGUMENTS_BAD; + } + eddsaParam.contextData = ByteString(ckEddsaParams->pContextData, ckEddsaParams->ulContextDataLen); + INFO_MSG("Verify EdDSA flag=%s, contextDataLen=%lu", eddsaParam.flag ? "true" : "false", (unsigned long)eddsaParam.contextData.size()); + } + mechanismParam = &eddsaParam; + } break; #endif #ifdef WITH_ML_DSA diff --git a/src/lib/crypto/BotanEDDSA.cpp b/src/lib/crypto/BotanEDDSA.cpp index f10a07877..a46052fc2 100644 --- a/src/lib/crypto/BotanEDDSA.cpp +++ b/src/lib/crypto/BotanEDDSA.cpp @@ -38,6 +38,7 @@ #include "CryptoFactory.h" #include "BotanCryptoFactory.h" #include "ECParameters.h" +#include "EDDSAMechanismParam.h" #include "BotanEDKeyPair.h" #include "BotanUtil.h" #include @@ -65,7 +66,7 @@ BotanEDDSA::~BotanEDDSA() // Signing functions bool BotanEDDSA::sign(PrivateKey* privateKey, const ByteString& dataToSign, ByteString& signature, const AsymMech::Type mechanism, - const MechanismParam* /* mechanismParam */) + const MechanismParam* mechanismParam) { std::string emsa; @@ -73,11 +74,11 @@ bool BotanEDDSA::sign(PrivateKey* privateKey, const ByteString& dataToSign, { emsa = "Pure"; } - else - { + else + { ERROR_MSG("Invalid mechanism supplied (%i)", mechanism); return false; - } + } // Check if the private key is the right type if (!privateKey->isOfType(BotanEDPrivateKey::type)) @@ -87,16 +88,48 @@ bool BotanEDDSA::sign(PrivateKey* privateKey, const ByteString& dataToSign, return false; } - BotanEDPrivateKey* pk = (BotanEDPrivateKey*) privateKey; - Botan::Ed25519_PrivateKey* botanKey = dynamic_cast(pk->getBotanKey()); + BotanEDPrivateKey* pk = (BotanEDPrivateKey*) privateKey; + Botan::Ed25519_PrivateKey* botanKey = dynamic_cast(pk->getBotanKey()); - if (botanKey == NULL) - { + if (botanKey == NULL) + { ERROR_MSG("Could not get the Botan private key"); return false; } + // Extract context parameters if provided + bool preHash = false; + if (mechanismParam != NULL && mechanismParam->isOfType(EDDSAMechanismParam::type)) + { + EDDSAMechanismParam* eddsaParam = (EDDSAMechanismParam*) mechanismParam; + preHash = eddsaParam->flag; + if (!preHash && eddsaParam->contextData.size() > 0) + { + ERROR_MSG("Context data is not supported in Botan EDDSA implementation"); + return false; + } + } + + if (preHash) + { + unsigned long len = pk->getOrderLength(); + if (len == 0) + { + ERROR_MSG("EDDSA: Could not get the order length"); + return false; + } + + if (pk->getOrderLength() == 32) + { + emsa = "Ed25519ph"; + } + else + { + emsa = "Ed448ph"; + } + } + try { BotanRNG* rng = (BotanRNG*)BotanCryptoFactory::i()->getRNG(); @@ -162,7 +195,7 @@ bool BotanEDDSA::signFinal(ByteString& /*signature*/) // Verification functions bool BotanEDDSA::verify(PublicKey* publicKey, const ByteString& originalData, const ByteString& signature, const AsymMech::Type mechanism, - const MechanismParam* /* mechanismParam */) + const MechanismParam* mechanismParam) { std::string emsa; @@ -195,6 +228,38 @@ bool BotanEDDSA::verify(PublicKey* publicKey, const ByteString& originalData, return false; } + // Extract context parameters if provided + bool preHash = false; + if (mechanismParam != NULL && mechanismParam->isOfType(EDDSAMechanismParam::type)) + { + EDDSAMechanismParam* eddsaParam = (EDDSAMechanismParam*) mechanismParam; + preHash = eddsaParam->flag; + if (!preHash && eddsaParam->contextData.size() > 0) + { + ERROR_MSG("Context data is not supported in Botan EDDSA implementation"); + return false; + } + } + + if (preHash) + { + unsigned long len = pk->getOrderLength(); + if (len == 0) + { + ERROR_MSG("EDDSA: Could not get the order length"); + return false; + } + + if (pk->getOrderLength() == 32) + { + emsa = "Ed25519ph"; + } + else + { + emsa = "Ed448ph"; + } + } + try { verifier = new Botan::PK_Verifier(*botanKey, emsa); diff --git a/src/lib/crypto/CMakeLists.txt b/src/lib/crypto/CMakeLists.txt index fe0d47f7b..8e76fc577 100644 --- a/src/lib/crypto/CMakeLists.txt +++ b/src/lib/crypto/CMakeLists.txt @@ -26,6 +26,7 @@ set(SOURCES AESKey.cpp ECPublicKey.cpp EDPrivateKey.cpp EDPublicKey.cpp + EDDSAMechanismParam.cpp GOSTPrivateKey.cpp GOSTPublicKey.cpp HashAlgorithm.cpp diff --git a/src/lib/crypto/EDDSAMechanismParam.cpp b/src/lib/crypto/EDDSAMechanismParam.cpp new file mode 100644 index 000000000..0cdb2563d --- /dev/null +++ b/src/lib/crypto/EDDSAMechanismParam.cpp @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2026 SoftHSMv2 contributors + * + * SPDX-License-Identifier: BSD-2-Clause + */ +/***************************************************************************** + EDDSAMechanismParam.cpp + + EdDSA mechanism parameters used for signing/verifying operations + *****************************************************************************/ + +#include "config.h" +#ifdef WITH_EDDSA +#include "ByteString.h" +#include "MechanismParam.h" +#include "EDDSAMechanismParam.h" + +EDDSAMechanismParam::EDDSAMechanismParam() { + this->flag = false; +} + +EDDSAMechanismParam::EDDSAMechanismParam(bool flag) { + this->flag = flag; +} + +EDDSAMechanismParam::EDDSAMechanismParam(bool flag, ByteString contextData) { + this->flag = flag; + this->contextData = contextData; +} + +// Set the type +/*static*/ const char* EDDSAMechanismParam::type = "EdDSA Signature param"; + +EDDSAMechanismParam* EDDSAMechanismParam::clone() const +{ + return new EDDSAMechanismParam(static_cast(*this)); // call the copy ctor. +} + +// Check if the parameter is of the given type +bool EDDSAMechanismParam::isOfType(const char* inType) const +{ + return !strcmp(type, inType); +} +#endif diff --git a/src/lib/crypto/EDDSAMechanismParam.h b/src/lib/crypto/EDDSAMechanismParam.h new file mode 100644 index 000000000..ae046cdbd --- /dev/null +++ b/src/lib/crypto/EDDSAMechanismParam.h @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2026 SoftHSMv2 contributors + * + * SPDX-License-Identifier: BSD-2-Clause + */ +/***************************************************************************** + EDDSAMechanismParam.h + + EdDSA mechanism parameters used for signing/verifying operations + *****************************************************************************/ + +#ifndef _SOFTHSM_V2_EDDSAMECHANISMPARAM_H +#define _SOFTHSM_V2_EDDSAMECHANISMPARAM_H + +#include "config.h" +#ifdef WITH_EDDSA +#include "ByteString.h" +#include "MechanismParam.h" + +class EDDSAMechanismParam : public MechanismParam +{ +public: + + // EdDSA parameters from ck_eddsa_params + bool flag; // false = no pre-hash (pure), true = with pre-hash (ph) + ByteString contextData; + + // The type + static const char* type; + + EDDSAMechanismParam(); + + EDDSAMechanismParam(bool flag); + + EDDSAMechanismParam(bool flag, ByteString contextData); + + EDDSAMechanismParam* clone() const; + + // Check if the mechanism param is of the given type + virtual bool isOfType(const char* inType) const; +}; + +#endif // WITH_EDDSA +#endif // !_SOFTHSM_V2_EDDSAMECHANISMPARAM_H diff --git a/src/lib/crypto/Makefile.am b/src/lib/crypto/Makefile.am index 781375be0..d70281fd1 100644 --- a/src/lib/crypto/Makefile.am +++ b/src/lib/crypto/Makefile.am @@ -26,6 +26,7 @@ libsofthsm_crypto_la_SOURCES = AESKey.cpp \ ECPrivateKey.cpp \ EDPublicKey.cpp \ EDPrivateKey.cpp \ + EDDSAMechanismParam.cpp \ GOSTPublicKey.cpp \ GOSTPrivateKey.cpp \ HashAlgorithm.cpp \ diff --git a/src/lib/crypto/OSSLEDDSA.cpp b/src/lib/crypto/OSSLEDDSA.cpp index 58cf7aebd..e85e36c8d 100644 --- a/src/lib/crypto/OSSLEDDSA.cpp +++ b/src/lib/crypto/OSSLEDDSA.cpp @@ -36,6 +36,7 @@ #include "OSSLEDDSA.h" #include "CryptoFactory.h" #include "ECParameters.h" +#include "EDDSAMechanismParam.h" #include "OSSLEDKeyPair.h" #include "OSSLComp.h" #include "OSSLUtil.h" @@ -43,23 +44,27 @@ #include #include #include +#if OPENSSL_VERSION_NUMBER >= 0x30200000L +#include +#include +#endif #include // Signing functions bool OSSLEDDSA::sign(PrivateKey* privateKey, const ByteString& dataToSign, ByteString& signature, const AsymMech::Type mechanism, - const MechanismParam* /* mechanismParam */) + const MechanismParam* mechanismParam) { if (mechanism != AsymMech::EDDSA) { - ERROR_MSG("Invalid mechanism supplied (%i)", mechanism); + ERROR_MSG("EDDSA: Invalid mechanism supplied (%i)", mechanism); return false; } // Check if the private key is the right type if (!privateKey->isOfType(OSSLEDPrivateKey::type)) { - ERROR_MSG("Invalid key type supplied"); + ERROR_MSG("EDDSA: Invalid key type supplied"); return false; } @@ -69,31 +74,107 @@ bool OSSLEDDSA::sign(PrivateKey* privateKey, const ByteString& dataToSign, if (pkey == NULL) { - ERROR_MSG("Could not get the OpenSSL private key"); + ERROR_MSG("EDDSA: Could not get the OpenSSL private key"); return false; } + #if OPENSSL_VERSION_NUMBER >= 0x30200000L + // Extract context parameters if provided + bool preHash = false; + ByteString contextData; + #endif + if (mechanismParam != NULL && mechanismParam->isOfType(EDDSAMechanismParam::type)) + { + #if OPENSSL_VERSION_NUMBER >= 0x30200000L + EDDSAMechanismParam* eddsaParam = (EDDSAMechanismParam*) mechanismParam; + preHash = eddsaParam->flag; + contextData = eddsaParam->contextData; + #else + ERROR_MSG("EDDSA: Context data is not supported in OpenSSL < 3.2"); + return false; + #endif + } + // Perform the signature operation size_t len = pk->getOrderLength(); if (len == 0) { - ERROR_MSG("Could not get the order length"); + ERROR_MSG("EDDSA: Could not get the order length"); return false; } len *= 2; signature.resize(len); memset(&signature[0], 0, len); EVP_MD_CTX* ctx = EVP_MD_CTX_new(); + + // Select digest algorithm based on key type and pre-hash flag + // Ed25519: orderLength = 32, Ed448: orderLength = 57 + #if OPENSSL_VERSION_PREREQ(3,2) + const char* instance = NULL; + INFO_MSG("EDDSA: Signing with key of order length %zu, contextData.size(): %zu", pk->getOrderLength(), contextData.size()); + if (pk->getOrderLength() == 32) + { + if (preHash) + { + instance = "Ed25519ph"; + } + else if (contextData.size() > 0) + { + instance = "Ed25519ctx"; + } + } + else if (pk->getOrderLength() == 57) + { + if (preHash) + { + instance = "Ed448ph"; + } + else + { + instance = "Ed448"; + } + } + else + { + ERROR_MSG("EDDSA: Unknown EdDSA key size: %zu", pk->getOrderLength()); + EVP_MD_CTX_free(ctx); + return false; + } + #endif + + if (!EVP_DigestSignInit(ctx, NULL, NULL, NULL, pkey)) { - ERROR_MSG("EDDSA sign init failed (0x%08X)", ERR_get_error()); + ERROR_MSG("EDDSA sign init failed (0x%08X): %s", ERR_get_error(), ERR_error_string(ERR_get_error(), NULL)); EVP_MD_CTX_free(ctx); return false; } + + #if OPENSSL_VERSION_PREREQ(3,2) + // Set context if present (OpenSSL 3.2+) + if (instance != NULL) + { + INFO_MSG("EDDSA: Setting context for signature with instance: %s", instance); + EVP_PKEY_CTX* pctx = EVP_MD_CTX_get_pkey_ctx(ctx); + if (pctx != NULL) + { + OSSL_PARAM params[] = { + OSSL_PARAM_utf8_string(OSSL_SIGNATURE_PARAM_INSTANCE, &instance, sizeof(instance)), + OSSL_PARAM_octet_string(OSSL_SIGNATURE_PARAM_CONTEXT_STRING, (unsigned char*)contextData.const_byte_str(), contextData.size()), + OSSL_PARAM_END + }; + if (!EVP_PKEY_CTX_set_params(pctx, params)) + { + INFO_MSG("EDDSA: Could not set EdDSA context (OpenSSL version may not support context) (0x%08X): %s", ERR_get_error(), ERR_error_string(ERR_get_error(), NULL)); + } + } + } + #endif + if (!EVP_DigestSign(ctx, &signature[0], &len, dataToSign.const_byte_str(), dataToSign.size())) { - ERROR_MSG("EDDSA sign failed (0x%08X)", ERR_get_error()); + ERROR_MSG("EDDSA sign failed (0x%08X): %s", ERR_get_error(), ERR_error_string(ERR_get_error(), NULL)); EVP_MD_CTX_free(ctx); return false; } @@ -126,7 +207,7 @@ bool OSSLEDDSA::signFinal(ByteString& /*signature*/) // Verification functions bool OSSLEDDSA::verify(PublicKey* publicKey, const ByteString& originalData, const ByteString& signature, const AsymMech::Type mechanism, - const MechanismParam* /* mechanismParam */) + const MechanismParam* mechanismParam) { if (mechanism != AsymMech::EDDSA) { @@ -152,6 +233,23 @@ bool OSSLEDDSA::verify(PublicKey* publicKey, const ByteString& originalData, return false; } + #if OPENSSL_VERSION_PREREQ(3,2) + // Extract context parameters if provided + bool preHash = false; + ByteString contextData; + #endif + if (mechanismParam != NULL && mechanismParam->isOfType(EDDSAMechanismParam::type)) + { + #if OPENSSL_VERSION_PREREQ(3,2) + EDDSAMechanismParam* eddsaParam = (EDDSAMechanismParam*) mechanismParam; + preHash = eddsaParam->flag; + contextData = eddsaParam->contextData; + #else + ERROR_MSG("EDDSA: Context data is not supported in OpenSSL < 3.2"); + return false; + #endif + } + // Perform the verify operation size_t len = pk->getOrderLength(); if (len == 0) @@ -166,17 +264,76 @@ bool OSSLEDDSA::verify(PublicKey* publicKey, const ByteString& originalData, return false; } EVP_MD_CTX* ctx = EVP_MD_CTX_new(); + + // Select digest algorithm based on key type and pre-hash flag + // Ed25519: orderLength = 32, Ed448: orderLength = 57 + #if OPENSSL_VERSION_PREREQ(3,2) + const char* instance = NULL; + if (pk->getOrderLength() == 32) + { + if (contextData.size() > 0) + { + instance = "Ed25519ctx"; + } + else if (preHash) + { + instance = "Ed25519ph"; + } + } + else if (pk->getOrderLength() == 57) + { + if (preHash) + { + instance = "Ed448ph"; + } + else + { + instance = "Ed448"; + } + } + else + { + ERROR_MSG("EDDSA: Unknown EdDSA key size: %zu", pk->getOrderLength()); + EVP_MD_CTX_free(ctx); + return false; + } + #endif + if (!EVP_DigestVerifyInit(ctx, NULL, NULL, NULL, pkey)) { - ERROR_MSG("EDDSA verify init failed (0x%08X)", ERR_get_error()); + ERROR_MSG("EDDSA verify init failed (0x%08X): %s", ERR_get_error(), ERR_error_string(ERR_get_error(), NULL)); EVP_MD_CTX_free(ctx); return false; } + + #if OPENSSL_VERSION_PREREQ(3,2) + // Set context if present (OpenSSL 3.2+) + if (instance != NULL) + { + INFO_MSG("EDDSA: Setting context for signature with instance: %s", instance); + EVP_PKEY_CTX* pctx = EVP_MD_CTX_get_pkey_ctx(ctx); + if (pctx != NULL) + { + OSSL_PARAM params[] = { + OSSL_PARAM_utf8_string(OSSL_SIGNATURE_PARAM_INSTANCE, &instance, sizeof(instance)), + OSSL_PARAM_octet_string(OSSL_SIGNATURE_PARAM_CONTEXT_STRING, (unsigned char*)contextData.const_byte_str(), contextData.size()), + OSSL_PARAM_END + }; + if (!EVP_PKEY_CTX_set_params(pctx, params)) + { + DEBUG_MSG("EDDSA: Could not set EdDSA context (OpenSSL version may not support context)"); + } + } + } + #endif + int ret = EVP_DigestVerify(ctx, signature.const_byte_str(), len, originalData.const_byte_str(), originalData.size()); if (ret != 1) { if (ret < 0) - ERROR_MSG("EDDSA verify failed (0x%08X)", ERR_get_error()); + { + ERROR_MSG("EDDSA verify failed (0x%08X): %s", ERR_get_error(), ERR_error_string(ERR_get_error(), NULL)); + } EVP_MD_CTX_free(ctx); return false; } diff --git a/src/lib/test/SignVerifyTests.cpp b/src/lib/test/SignVerifyTests.cpp index a85b91d52..48053ecfb 100644 --- a/src/lib/test/SignVerifyTests.cpp +++ b/src/lib/test/SignVerifyTests.cpp @@ -844,6 +844,197 @@ void SignVerifyTests::testEdSignVerify(const char* curve) CPPUNIT_ASSERT(rv == CKR_OK); signVerifySingle(CKM_EDDSA, hSessionRO, hPuk,hPrk); } + +void SignVerifyTests::testEdSignVerifyWithContext(const char* curve) +{ +#ifdef WITH_BOTAN + // Botan 2.X does not support EdDSA with context, so we skip this test for now. + return; +#endif +#ifdef WITH_OPENSSL + #if !OPENSSL_VERSION_PREREQ(3,2) + // OpenSSL 3.2.0 or later is required for EdDSA with pre-hash support + return; + #endif +#endif + + CK_RV rv; + CK_SESSION_HANDLE hSessionRO; + CK_SESSION_HANDLE hSessionRW; + + // Just make sure that we finalize any previous tests + CRYPTOKI_F_PTR( C_Finalize(NULL_PTR) ); + + // Open read-only session on when the token is not initialized should fail + rv = CRYPTOKI_F_PTR( C_OpenSession(m_initializedTokenSlotID, CKF_SERIAL_SESSION, NULL_PTR, NULL_PTR, &hSessionRO) ); + CPPUNIT_ASSERT(rv == CKR_CRYPTOKI_NOT_INITIALIZED); + + // Initialize the library and start the test. + rv = CRYPTOKI_F_PTR( C_Initialize(NULL_PTR) ); + CPPUNIT_ASSERT(rv == CKR_OK); + + // Open read-only session + rv = CRYPTOKI_F_PTR( C_OpenSession(m_initializedTokenSlotID, CKF_SERIAL_SESSION, NULL_PTR, NULL_PTR, &hSessionRO) ); + CPPUNIT_ASSERT(rv == CKR_OK); + + // Open read-write session + rv = CRYPTOKI_F_PTR( C_OpenSession(m_initializedTokenSlotID, CKF_SERIAL_SESSION | CKF_RW_SESSION, NULL_PTR, NULL_PTR, &hSessionRW) ); + CPPUNIT_ASSERT(rv == CKR_OK); + + // Login USER into the sessions so we can create a private objects + rv = CRYPTOKI_F_PTR( C_Login(hSessionRO,CKU_USER,m_userPin1,m_userPin1Length) ); + CPPUNIT_ASSERT(rv==CKR_OK); + + CK_OBJECT_HANDLE hPuk = CK_INVALID_HANDLE; + CK_OBJECT_HANDLE hPrk = CK_INVALID_HANDLE; + + // Test EdDSA signature with context data + // Create EdDSA parameters with context + CK_BYTE contextData[] = "context-data"; + CK_ULONG dataSize = (CK_ULONG)(sizeof(contextData) - 1); // exclude trailing NULL + CK_EDDSA_PARAMS params[] = { + { + 0x00, // phFlag = 0 (no pre-hash) + dataSize, // context_data_len + contextData // context_data + } + }; + + // Public Session keys with context + rv = generateED(curve, hSessionRW,IN_SESSION,IS_PUBLIC,IN_SESSION,IS_PUBLIC,hPuk,hPrk); + CPPUNIT_ASSERT(rv == CKR_OK); + signVerifySingle(CKM_EDDSA, hSessionRO, hPuk, hPrk, ¶ms[0], sizeof(params[0])); + + // Private Session Keys with context + rv = generateED(curve, hSessionRW,IN_SESSION,IS_PRIVATE,IN_SESSION,IS_PRIVATE,hPuk,hPrk); + CPPUNIT_ASSERT(rv == CKR_OK); + signVerifySingle(CKM_EDDSA, hSessionRO, hPuk, hPrk, ¶ms[0], sizeof(params[0])); + + // Test with different context data + CK_BYTE anotherContext[] = "Bob"; + dataSize = (CK_ULONG)(sizeof(anotherContext) - 1); // exclude trailing NULL + CK_EDDSA_PARAMS eddsaParams2 = { + 0x00, // phFlag = 0 + dataSize, // context_data_len + anotherContext // context_data + }; + + // Public Token Keys with different context + rv = generateED(curve, hSessionRW,ON_TOKEN,IS_PUBLIC,ON_TOKEN,IS_PUBLIC,hPuk,hPrk); + CPPUNIT_ASSERT(rv == CKR_OK); + signVerifySingle(CKM_EDDSA, hSessionRO, hPuk, hPrk, &eddsaParams2, sizeof(eddsaParams2)); + + // Private Token Keys with different context + rv = generateED(curve, hSessionRW,ON_TOKEN,IS_PRIVATE,ON_TOKEN,IS_PRIVATE,hPuk,hPrk); + CPPUNIT_ASSERT(rv == CKR_OK); + signVerifySingle(CKM_EDDSA, hSessionRO, hPuk, hPrk, &eddsaParams2, sizeof(eddsaParams2)); + + // Test with empty context + CK_EDDSA_PARAMS eddsaParamsNoCtx = { + 0x00, // phFlag = 0 + 0, // context_data_len = 0 + NULL // context_data = NULL + }; + + // Generate new keys for empty context test + rv = generateED(curve, hSessionRW,IN_SESSION,IS_PUBLIC,IN_SESSION,IS_PUBLIC,hPuk,hPrk); + CPPUNIT_ASSERT(rv == CKR_OK); + signVerifySingle(CKM_EDDSA, hSessionRO, hPuk, hPrk, &eddsaParamsNoCtx, sizeof(CK_EDDSA_PARAMS)); +} + +void SignVerifyTests::testEdSignVerifyWithContextPreHashed(const char* curve) +{ +#ifdef WITH_BOTAN + // Botan 2.X does not support Ed448 curve, so we skip this test for now. + if (strcmp(curve, "Ed448") == 0) + return; +#endif +#ifdef WITH_OPENSSL + #if !OPENSSL_VERSION_PREREQ(3,2) + // OpenSSL 3.2.0 or later is required for EdDSA with pre-hash support + return; + #endif +#endif + + CK_RV rv; + CK_SESSION_HANDLE hSessionRO; + CK_SESSION_HANDLE hSessionRW; + + // Just make sure that we finalize any previous tests + CRYPTOKI_F_PTR( C_Finalize(NULL_PTR) ); + + // Open read-only session on when the token is not initialized should fail + rv = CRYPTOKI_F_PTR( C_OpenSession(m_initializedTokenSlotID, CKF_SERIAL_SESSION, NULL_PTR, NULL_PTR, &hSessionRO) ); + CPPUNIT_ASSERT(rv == CKR_CRYPTOKI_NOT_INITIALIZED); + + // Initialize the library and start the test. + rv = CRYPTOKI_F_PTR( C_Initialize(NULL_PTR) ); + CPPUNIT_ASSERT(rv == CKR_OK); + + // Open read-only session + rv = CRYPTOKI_F_PTR( C_OpenSession(m_initializedTokenSlotID, CKF_SERIAL_SESSION, NULL_PTR, NULL_PTR, &hSessionRO) ); + CPPUNIT_ASSERT(rv == CKR_OK); + + // Open read-write session + rv = CRYPTOKI_F_PTR( C_OpenSession(m_initializedTokenSlotID, CKF_SERIAL_SESSION | CKF_RW_SESSION, NULL_PTR, NULL_PTR, &hSessionRW) ); + CPPUNIT_ASSERT(rv == CKR_OK); + + // Login USER into the sessions so we can create a private objects + rv = CRYPTOKI_F_PTR( C_Login(hSessionRO,CKU_USER,m_userPin1,m_userPin1Length) ); + CPPUNIT_ASSERT(rv==CKR_OK); + + CK_OBJECT_HANDLE hPuk = CK_INVALID_HANDLE; + CK_OBJECT_HANDLE hPrk = CK_INVALID_HANDLE; + + // Test EdDSA signature with context data + // Create EdDSA parameters with context + CK_BYTE contextData[] = { 0x4a, 0x65, 0x73, 0x73, 0x69, 0x63, 0x61 }; // "Jessica" + CK_EDDSA_PARAMS eddsaParams = { + 0x01, // phFlag = 1 (pre-hash) + sizeof(contextData), // context_data_len + contextData // context_data + }; + + // Public Session keys with context + rv = generateED(curve, hSessionRW,IN_SESSION,IS_PUBLIC,IN_SESSION,IS_PUBLIC,hPuk,hPrk); + CPPUNIT_ASSERT(rv == CKR_OK); + signVerifySingle(CKM_EDDSA, hSessionRO, hPuk, hPrk, &eddsaParams, sizeof(CK_EDDSA_PARAMS)); + + // Private Session Keys with context + rv = generateED(curve, hSessionRW,IN_SESSION,IS_PRIVATE,IN_SESSION,IS_PRIVATE,hPuk,hPrk); + CPPUNIT_ASSERT(rv == CKR_OK); + signVerifySingle(CKM_EDDSA, hSessionRO, hPuk, hPrk, &eddsaParams, sizeof(CK_EDDSA_PARAMS)); + + // Test with different context data + CK_BYTE anotherContext[] = { 0x42, 0x6f, 0x62 }; // "Bob" + CK_EDDSA_PARAMS eddsaParams2 = { + 0x01, // phFlag = 1 (pre-hash) + sizeof(anotherContext), // context_data_len + anotherContext // context_data + }; + + // Public Token Keys with different context + rv = generateED(curve, hSessionRW,ON_TOKEN,IS_PUBLIC,ON_TOKEN,IS_PUBLIC,hPuk,hPrk); + CPPUNIT_ASSERT(rv == CKR_OK); + signVerifySingle(CKM_EDDSA, hSessionRO, hPuk, hPrk, &eddsaParams2, sizeof(CK_EDDSA_PARAMS)); + + // Private Token Keys with different context + rv = generateED(curve, hSessionRW,ON_TOKEN,IS_PRIVATE,ON_TOKEN,IS_PRIVATE,hPuk,hPrk); + CPPUNIT_ASSERT(rv == CKR_OK); + signVerifySingle(CKM_EDDSA, hSessionRO, hPuk, hPrk, &eddsaParams2, sizeof(CK_EDDSA_PARAMS)); + + // Test with empty context + CK_EDDSA_PARAMS eddsaParamsNoCtx = { + 0x01, // phFlag = 1 (pre-hash) + 0, // context_data_len = 0 + NULL // context_data = NULL + }; + + // Generate new keys for empty context test + rv = generateED(curve, hSessionRW,IN_SESSION,IS_PUBLIC,IN_SESSION,IS_PUBLIC,hPuk,hPrk); + CPPUNIT_ASSERT(rv == CKR_OK); + signVerifySingle(CKM_EDDSA, hSessionRO, hPuk, hPrk, &eddsaParamsNoCtx, sizeof(CK_EDDSA_PARAMS)); +} #endif #ifdef WITH_ML_DSA diff --git a/src/lib/test/SignVerifyTests.h b/src/lib/test/SignVerifyTests.h index a7c22e083..aa3d8c05e 100644 --- a/src/lib/test/SignVerifyTests.h +++ b/src/lib/test/SignVerifyTests.h @@ -37,6 +37,9 @@ #include "config.h" #include "TestsBase.h" #include +#ifdef WITH_OPENSSL + #include +#endif class SignVerifyTests : public TestsBase { @@ -47,6 +50,8 @@ class SignVerifyTests : public TestsBase #endif #ifdef WITH_EDDSA CPPUNIT_TEST_PARAMETERIZED(testEdSignVerify, {"Ed25519", "Ed448"}); + CPPUNIT_TEST_PARAMETERIZED(testEdSignVerifyWithContext, {"Ed25519", "Ed448"}); + CPPUNIT_TEST_PARAMETERIZED(testEdSignVerifyWithContextPreHashed, {"Ed25519", "Ed448"}); #endif CPPUNIT_TEST(testMacSignVerify); #ifdef WITH_ML_DSA @@ -63,6 +68,8 @@ class SignVerifyTests : public TestsBase #endif #ifdef WITH_EDDSA void testEdSignVerify(const char* curve); + void testEdSignVerifyWithContext(const char* curve); + void testEdSignVerifyWithContextPreHashed(const char* curve); #endif void testMacSignVerify(); #ifdef WITH_ML_DSA