Skip to content

Commit 8849a9d

Browse files
committed
Fixed EDDSA context/pre-hash support
1 parent f12916e commit 8849a9d

16 files changed

Lines changed: 979 additions & 38 deletions

‎src/bin/util/softhsm2-util-botan.cpp‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -837,7 +837,7 @@ eddsa_key_material_t* crypto_malloc_eddsa
837837
return NULL;
838838
}
839839

840-
eddsa_key_material_t* keyMat = (eddsa_key_material_t*)malloc(sizeof(eddsa_key_material_t));
840+
eddsa_key_material_t* keyMat = (eddsa_key_material_t*)calloc(1, sizeof(eddsa_key_material_t));
841841
if (keyMat == NULL)
842842
{
843843
return NULL;
@@ -848,13 +848,13 @@ eddsa_key_material_t* crypto_malloc_eddsa
848848
if (ed25519) oid = Botan::OIDS::lookup("Ed25519");
849849
if (oid.empty())
850850
{
851+
crypto_free_eddsa(keyMat);
851852
return NULL;
852853
}
853854

854855
Botan::secure_vector<Botan::byte> derOID;
855856
derOID = Botan::DER_Encoder().encode(oid).get_contents();
856857

857-
memset(keyMat, 0, sizeof(*keyMat));
858858
keyMat->sizeOID = derOID.size();
859859
keyMat->derOID = (CK_VOID_PTR)malloc(keyMat->sizeOID);
860860

‎src/lib/SoftHSM.cpp‎

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,10 @@
5858
#include "ECParameters.h"
5959
#include "EDPublicKey.h"
6060
#include "EDPrivateKey.h"
61+
#ifdef WITH_EDDSA
62+
#include "EDDSAMechanismParam.h"
63+
#include "EDDSAUtil.h"
64+
#endif
6165
#include "DHParameters.h"
6266
#include "DHPublicKey.h"
6367
#include "DHPrivateKey.h"
@@ -4320,6 +4324,7 @@ CK_RV SoftHSM::AsymSignInit(CK_SESSION_HANDLE hSession, CK_MECHANISM_PTR pMechan
43204324
#endif
43214325
#ifdef WITH_EDDSA
43224326
bool isEDDSA = false;
4327+
EDDSAMechanismParam eddsaParam;
43234328
#endif
43244329
#ifdef WITH_ML_DSA
43254330
bool isMLDSA = false;
@@ -4585,6 +4590,10 @@ CK_RV SoftHSM::AsymSignInit(CK_SESSION_HANDLE hSession, CK_MECHANISM_PTR pMechan
45854590
mechanism = AsymMech::EDDSA;
45864591
bAllowMultiPartOp = false;
45874592
isEDDSA = true;
4593+
{
4594+
CK_RV eddsaRv = EDDSAUtil::getEddsaParam(pMechanism, eddsaParam, &mechanismParam);
4595+
if (eddsaRv != CKR_OK) return eddsaRv;
4596+
}
45884597
break;
45894598
#endif
45904599
#ifdef WITH_ML_DSA
@@ -5410,6 +5419,7 @@ CK_RV SoftHSM::AsymVerifyInit(CK_SESSION_HANDLE hSession, CK_MECHANISM_PTR pMech
54105419
#endif
54115420
#ifdef WITH_EDDSA
54125421
bool isEDDSA = false;
5422+
EDDSAMechanismParam eddsaParam;
54135423
#endif
54145424
#ifdef WITH_ML_DSA
54155425
bool isMLDSA = false;
@@ -5674,6 +5684,10 @@ CK_RV SoftHSM::AsymVerifyInit(CK_SESSION_HANDLE hSession, CK_MECHANISM_PTR pMech
56745684
mechanism = AsymMech::EDDSA;
56755685
bAllowMultiPartOp = false;
56765686
isEDDSA = true;
5687+
{
5688+
CK_RV eddsaRv = EDDSAUtil::getEddsaParam(pMechanism, eddsaParam, &mechanismParam);
5689+
if (eddsaRv != CKR_OK) return eddsaRv;
5690+
}
56775691
break;
56785692
#endif
56795693
#ifdef WITH_ML_DSA

‎src/lib/crypto/BotanEDDSA.cpp‎

Lines changed: 62 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@
3838
#include "CryptoFactory.h"
3939
#include "BotanCryptoFactory.h"
4040
#include "ECParameters.h"
41+
#include "EDDSAMechanismParam.h"
4142
#include "BotanEDKeyPair.h"
4243
#include "BotanUtil.h"
4344
#include <algorithm>
@@ -62,22 +63,56 @@ BotanEDDSA::~BotanEDDSA()
6263
delete verifier;
6364
}
6465

66+
// Select the Botan EMSA for the requested EdDSA instance
67+
bool BotanEDDSA::selectEmsa(std::string& emsa, size_t orderLength, const MechanismParam* mechanismParam)
68+
{
69+
emsa = "Pure";
70+
71+
if (mechanismParam == NULL || !mechanismParam->isOfType(EDDSAMechanismParam::type))
72+
{
73+
return true;
74+
}
75+
76+
const EDDSAMechanismParam* eddsaParam = (const EDDSAMechanismParam*) mechanismParam;
77+
78+
if (eddsaParam->contextData.size() > 0)
79+
{
80+
ERROR_MSG("EDDSA: Context data is not supported in the Botan EDDSA implementation");
81+
return false;
82+
}
83+
84+
if (!eddsaParam->flag)
85+
{
86+
return true;
87+
}
88+
89+
// Ed25519: orderLength = 32, Ed448: orderLength = 57
90+
if (orderLength == 32)
91+
{
92+
emsa = "Ed25519ph";
93+
return true;
94+
}
95+
96+
if (orderLength == 57)
97+
{
98+
ERROR_MSG("EDDSA: Ed448 is not supported in the Botan EDDSA implementation");
99+
return false;
100+
}
101+
102+
ERROR_MSG("EDDSA: Unknown EdDSA key size: %lu", (unsigned long) orderLength);
103+
return false;
104+
}
105+
65106
// Signing functions
66107
bool BotanEDDSA::sign(PrivateKey* privateKey, const ByteString& dataToSign,
67108
ByteString& signature, const AsymMech::Type mechanism,
68-
const MechanismParam* /* mechanismParam */)
109+
const MechanismParam* mechanismParam)
69110
{
70-
std::string emsa;
71-
72-
if (mechanism == AsymMech::EDDSA)
111+
if (mechanism != AsymMech::EDDSA)
73112
{
74-
emsa = "Pure";
75-
}
76-
else
77-
{
78113
ERROR_MSG("Invalid mechanism supplied (%i)", mechanism);
79114
return false;
80-
}
115+
}
81116

82117
// Check if the private key is the right type
83118
if (!privateKey->isOfType(BotanEDPrivateKey::type))
@@ -87,16 +122,22 @@ bool BotanEDDSA::sign(PrivateKey* privateKey, const ByteString& dataToSign,
87122
return false;
88123
}
89124

90-
BotanEDPrivateKey* pk = (BotanEDPrivateKey*) privateKey;
91-
Botan::Ed25519_PrivateKey* botanKey = dynamic_cast<Botan::Ed25519_PrivateKey*>(pk->getBotanKey());
125+
BotanEDPrivateKey* pk = (BotanEDPrivateKey*) privateKey;
126+
Botan::Ed25519_PrivateKey* botanKey = dynamic_cast<Botan::Ed25519_PrivateKey*>(pk->getBotanKey());
92127

93-
if (botanKey == NULL)
94-
{
128+
if (botanKey == NULL)
129+
{
95130
ERROR_MSG("Could not get the Botan private key");
96131

97132
return false;
98133
}
99134

135+
std::string emsa;
136+
if (!selectEmsa(emsa, pk->getOrderLength(), mechanismParam))
137+
{
138+
return false;
139+
}
140+
100141
try
101142
{
102143
BotanRNG* rng = (BotanRNG*)BotanCryptoFactory::i()->getRNG();
@@ -162,16 +203,10 @@ bool BotanEDDSA::signFinal(ByteString& /*signature*/)
162203
// Verification functions
163204
bool BotanEDDSA::verify(PublicKey* publicKey, const ByteString& originalData,
164205
const ByteString& signature, const AsymMech::Type mechanism,
165-
const MechanismParam* /* mechanismParam */)
206+
const MechanismParam* mechanismParam)
166207
{
167-
std::string emsa;
168-
169-
if (mechanism == AsymMech::EDDSA)
208+
if (mechanism != AsymMech::EDDSA)
170209
{
171-
emsa = "Pure";
172-
}
173-
else
174-
{
175210
ERROR_MSG("Invalid mechanism supplied (%i)", mechanism);
176211

177212
return false;
@@ -195,6 +230,12 @@ bool BotanEDDSA::verify(PublicKey* publicKey, const ByteString& originalData,
195230
return false;
196231
}
197232

233+
std::string emsa;
234+
if (!selectEmsa(emsa, pk->getOrderLength(), mechanismParam))
235+
{
236+
return false;
237+
}
238+
198239
try
199240
{
200241
verifier = new Botan::PK_Verifier(*botanKey, emsa);

‎src/lib/crypto/BotanEDDSA.h‎

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,9 @@ class BotanEDDSA : public AsymmetricAlgorithm
8080
virtual AsymmetricParameters* newParameters();
8181

8282
private:
83+
// Derive the Botan EMSA from the key size and the pre-hash flag; context data is not supported
84+
static bool selectEmsa(std::string& emsa, size_t orderLength, const MechanismParam* mechanismParam);
85+
8386
Botan::PK_Signer* signer;
8487
Botan::PK_Verifier* verifier;
8588
};

‎src/lib/crypto/CMakeLists.txt‎

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,8 @@ set(SOURCES AESKey.cpp
2626
ECPublicKey.cpp
2727
EDPrivateKey.cpp
2828
EDPublicKey.cpp
29+
EDDSAMechanismParam.cpp
30+
EDDSAUtil.cpp
2931
GOSTPrivateKey.cpp
3032
GOSTPublicKey.cpp
3133
HashAlgorithm.cpp
@@ -156,7 +158,7 @@ if(CMAKE_CXX_COMPILER_ID STREQUAL "MSVC")
156158
list(APPEND INCLUDE_DIRS ${PROJECT_SOURCE_DIR}/../win32)
157159
ENDIF()
158160

159-
if(WITH_BOTAN)
161+
if(WITH_BOTAN AND CMAKE_CXX_COMPILER_ID STREQUAL "MSVC")
160162
# mute botan specific warnings
161163
# https://github.com/randombit/botan/issues/486
162164
list(APPEND COMPILE_OPTIONS "/wd4250;/wd4251;/wd4275;/wd4127;/wd4273")
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
/*
2+
* Copyright (c) 2026 SoftHSMv2 contributors
3+
*
4+
* SPDX-License-Identifier: BSD-2-Clause
5+
*/
6+
/*****************************************************************************
7+
EDDSAMechanismParam.cpp
8+
9+
EdDSA mechanism parameters used for signing/verifying operations
10+
*****************************************************************************/
11+
12+
#include "config.h"
13+
#ifdef WITH_EDDSA
14+
#include "ByteString.h"
15+
#include "MechanismParam.h"
16+
#include "EDDSAMechanismParam.h"
17+
#include <string.h>
18+
19+
EDDSAMechanismParam::EDDSAMechanismParam()
20+
{
21+
flag = false;
22+
}
23+
24+
// Set the type
25+
/*static*/ const char* EDDSAMechanismParam::type = "EdDSA Signature param";
26+
27+
EDDSAMechanismParam* EDDSAMechanismParam::clone() const
28+
{
29+
return new EDDSAMechanismParam(*this);
30+
}
31+
32+
// Check if the parameter is of the given type
33+
bool EDDSAMechanismParam::isOfType(const char* inType) const
34+
{
35+
return !strcmp(type, inType);
36+
}
37+
#endif
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
/*
2+
* Copyright (c) 2026 SoftHSMv2 contributors
3+
*
4+
* SPDX-License-Identifier: BSD-2-Clause
5+
*/
6+
/*****************************************************************************
7+
EDDSAMechanismParam.h
8+
9+
EdDSA mechanism parameters used for signing/verifying operations
10+
*****************************************************************************/
11+
12+
#ifndef _SOFTHSM_V2_EDDSAMECHANISMPARAM_H
13+
#define _SOFTHSM_V2_EDDSAMECHANISMPARAM_H
14+
15+
#include "config.h"
16+
#ifdef WITH_EDDSA
17+
#include "ByteString.h"
18+
#include "MechanismParam.h"
19+
20+
class EDDSAMechanismParam : public MechanismParam
21+
{
22+
public:
23+
24+
// EdDSA parameters from ck_eddsa_params
25+
bool flag; // false = no pre-hash (pure), true = with pre-hash (ph)
26+
ByteString contextData;
27+
28+
// The type
29+
static const char* type;
30+
31+
EDDSAMechanismParam();
32+
33+
virtual EDDSAMechanismParam* clone() const;
34+
35+
// Check if the mechanism param is of the given type
36+
virtual bool isOfType(const char* inType) const;
37+
};
38+
39+
#endif // WITH_EDDSA
40+
#endif // !_SOFTHSM_V2_EDDSAMECHANISMPARAM_H

‎src/lib/crypto/EDDSAUtil.cpp‎

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
/*
2+
* Copyright (c) 2026 SoftHSMv2 contributors
3+
*
4+
* SPDX-License-Identifier: BSD-2-Clause
5+
*/
6+
/*****************************************************************************
7+
EDDSAUtil.cpp
8+
9+
EdDSA convenience functions
10+
*****************************************************************************/
11+
12+
#include "config.h"
13+
#ifdef WITH_EDDSA
14+
#include "EDDSAUtil.h"
15+
#include "log.h"
16+
17+
// Maximum context data length allowed by CK_EDDSA_PARAMS
18+
static const CK_ULONG maxEddsaContextDataLen = 255;
19+
20+
/*static*/ CK_RV EDDSAUtil::getEddsaParam(CK_MECHANISM_PTR pMechanism, EDDSAMechanismParam& eddsaParam, MechanismParam** mechanismParam)
21+
{
22+
if (pMechanism->pParameter == NULL_PTR)
23+
{
24+
if (pMechanism->ulParameterLen != 0)
25+
{
26+
ERROR_MSG("EDDSA: No parameters supplied but ulParameterLen is %lu", (unsigned long) pMechanism->ulParameterLen);
27+
return CKR_ARGUMENTS_BAD;
28+
}
29+
30+
// No parameters means the pure variant without context data
31+
return CKR_OK;
32+
}
33+
34+
if (pMechanism->ulParameterLen != sizeof(CK_EDDSA_PARAMS))
35+
{
36+
ERROR_MSG("EDDSA: ulParameterLen is %lu, expected %lu", (unsigned long) pMechanism->ulParameterLen, (unsigned long) sizeof(CK_EDDSA_PARAMS));
37+
return CKR_ARGUMENTS_BAD;
38+
}
39+
40+
CK_EDDSA_PARAMS* ckEddsaParams = (CK_EDDSA_PARAMS*) pMechanism->pParameter;
41+
42+
if (ckEddsaParams->ulContextDataLen > maxEddsaContextDataLen)
43+
{
44+
ERROR_MSG("EDDSA: ulContextDataLen is %lu, maximum is %lu", (unsigned long) ckEddsaParams->ulContextDataLen, (unsigned long) maxEddsaContextDataLen);
45+
return CKR_ARGUMENTS_BAD;
46+
}
47+
48+
if (ckEddsaParams->ulContextDataLen > 0 && ckEddsaParams->pContextData == NULL_PTR)
49+
{
50+
ERROR_MSG("EDDSA: ulContextDataLen is %lu but pContextData is NULL", (unsigned long) ckEddsaParams->ulContextDataLen);
51+
return CKR_ARGUMENTS_BAD;
52+
}
53+
54+
eddsaParam.flag = (ckEddsaParams->phFlag != CK_FALSE);
55+
if (ckEddsaParams->ulContextDataLen > 0)
56+
{
57+
eddsaParam.contextData = ByteString(ckEddsaParams->pContextData, ckEddsaParams->ulContextDataLen);
58+
}
59+
60+
DEBUG_MSG("EDDSA: preHash=%s, contextDataLen=%lu", eddsaParam.flag ? "true" : "false", (unsigned long) eddsaParam.contextData.size());
61+
62+
*mechanismParam = &eddsaParam;
63+
64+
return CKR_OK;
65+
}
66+
#endif

0 commit comments

Comments
 (0)