Skip to content

Commit 833596b

Browse files
committed
nrf_security: drivers: cracen: Implement Ed25519 in cracenpsa
Add support for Ed25519 and Ed25519ph in cracenpsa directly using silexpk/sxsymcrypt. This bypasses sicrypto, which saves on flash usage Remove sicrypto implementation of Ed25519 from being accessible from cracenpsa. Signed-off-by: Dag Erik Gjørvad <[email protected]>
1 parent 6d3f9f4 commit 833596b

File tree

5 files changed

+403
-140
lines changed

5 files changed

+403
-140
lines changed

subsys/nrf_security/src/drivers/cracen/cracenpsa/cracenpsa.cmake

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ endif()
4444
if(CONFIG_PSA_NEED_CRACEN_ASYMMETRIC_SIGNATURE_DRIVER)
4545
list(APPEND cracen_driver_sources
4646
${CMAKE_CURRENT_LIST_DIR}/src/sign.c
47+
${CMAKE_CURRENT_LIST_DIR}/src/ed25519.c
4748
)
4849
endif()
4950

@@ -90,6 +91,12 @@ if(CONFIG_PSA_NEED_CRACEN_KMU_DRIVER)
9091
)
9192
endif()
9293

94+
if(CONFIG_PSA_NEED_CRACEN_KEY_AGREEMENT_DRIVER)
95+
list(APPEND cracen_driver_sources
96+
${CMAKE_CURRENT_LIST_DIR}/src/ed25519.c
97+
)
98+
endif()
99+
93100
if(CONFIG_PSA_NEED_CRACEN_KEY_AGREEMENT_DRIVER OR CONFIG_PSA_NEED_CRACEN_KEY_DERIVATION_DRIVER)
94101
list(APPEND cracen_driver_sources
95102
${CMAKE_CURRENT_LIST_DIR}/src/key_derivation.c

subsys/nrf_security/src/drivers/cracen/cracenpsa/include/cracen_psa.h

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -361,4 +361,18 @@ psa_status_t cracen_spake2p_get_shared_key(cracen_spake2p_operation_t *operation
361361

362362
psa_status_t cracen_spake2p_abort(cracen_spake2p_operation_t *operation);
363363

364+
int cracen_ed25519_sign(const uint8_t *priv_key, char *signature, const uint8_t *message,
365+
size_t message_length);
366+
367+
int cracen_ed25519_verify(const uint8_t *pub_key, const char *message, size_t message_length,
368+
const char *signature);
369+
370+
int cracen_ed25519ph_sign(const uint8_t *priv_key, char *signature, const uint8_t *message,
371+
size_t message_length, bool is_message);
372+
373+
int cracen_ed25519ph_verify(const uint8_t *pub_key, const char *message, size_t message_length,
374+
const char *signature, bool is_message);
375+
376+
int cracen_ed25519_create_pubkey(const uint8_t *priv_key, uint8_t *pub_key);
377+
364378
#endif /* CRACEN_PSA_H */
Lines changed: 290 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,290 @@
1+
/*
2+
* Copyright (c) 2025 Nordic Semiconductor ASA
3+
*
4+
* SPDX-License-Identifier: LicenseRef-Nordic-5-Clause
5+
*
6+
* The comments in this file use the notations and conventions from RFC 8032.
7+
*
8+
* Workmem layout for an Ed25519 signature verification task:
9+
* 1. digest (size: 64 bytes).
10+
*
11+
* Workmem layout for an Ed25519 signature generation task:
12+
* The workmem is made of 5 areas of 32 bytes each (total size 160 bytes).
13+
* In the following we refer to these areas using the numbers 1 to 5. The
14+
* first hash operation computes the private key's digest, which is stored
15+
* in areas 1 and 2. The second hash operation computes r, which is stored
16+
* in areas 4 and 5. The first point multiplication computes R, which is
17+
* written directly to the output buffer. Then the secret scalar s is
18+
* computed in place in area 1. Area 2 is cleared. The second point
19+
* multiplication computes the public key A, which is stored in area 2. The
20+
* third hash operation computes k, which is stored in areas 2 and 3. The
21+
* final operation (r + k * s) mod L computes S, which is written directly
22+
* to the output buffer.
23+
*
24+
* Workmem layout for an Ed25519 public key generation task:
25+
* 1. digest (size: 64 bytes). The digest of the private key is written in
26+
* this area. Then the secret scalar s is computed in place in the first
27+
* 32 bytes of this area.
28+
*/
29+
#include <string.h>
30+
#include <sxsymcrypt/sha2.h>
31+
#include <silexpk/ed25519.h>
32+
#include <cracen/ec_helpers.h>
33+
#include <sxsymcrypt/hash.h>
34+
#include <cracen/mem_helpers.h>
35+
#include <cracen/statuscodes.h>
36+
37+
#define AREA2_MEM_OFFSET 32
38+
#define AREA4_MEM_OFFSET 96
39+
40+
/* This is the ASCII string with the
41+
* PHflag 1 and context size 0 appended as defined in:
42+
* https://datatracker.ietf.org/doc/html/rfc8032.html#section-2.
43+
* It is used for domain separation between Ed25519 and Ed25519ph.
44+
* This can not be stored as a const due to hardware limitations
45+
*/
46+
static char dom2[] = {0x53, 0x69, 0x67, 0x45, 0x64, 0x32, 0x35, 0x35, 0x31, 0x39, 0x20, 0x6e,
47+
0x6f, 0x20, 0x45, 0x64, 0x32, 0x35, 0x35, 0x31, 0x39, 0x20, 0x63, 0x6f,
48+
0x6c, 0x6c, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x01, 0x00};
49+
50+
static int hash_all_inputs(char const *inputs[], size_t inputs_lengths[], size_t input_count,
51+
const struct sxhashalg *hashalg, char *out)
52+
{
53+
struct sxhash hashopctx;
54+
int status;
55+
56+
status = sx_hash_create(&hashopctx, hashalg, sizeof(hashopctx));
57+
if (status != SX_OK) {
58+
return status;
59+
}
60+
61+
for (size_t i = 0; i < input_count; i++) {
62+
status = sx_hash_feed(&hashopctx, inputs[i], inputs_lengths[i]);
63+
if (status != SX_OK) {
64+
return status;
65+
}
66+
}
67+
status = sx_hash_digest(&hashopctx, out);
68+
if (status != SX_OK) {
69+
return status;
70+
}
71+
72+
status = sx_hash_wait(&hashopctx);
73+
74+
return status;
75+
}
76+
77+
static int hash_input(const char *input, size_t input_length, char *digest)
78+
{
79+
char const *hash_array[] = {input};
80+
size_t hash_array_lengths[] = {input_length};
81+
82+
return hash_all_inputs(hash_array, hash_array_lengths, 1, &sxhashalg_sha2_512, digest);
83+
}
84+
85+
static int ed25519_calculate_r(char *workmem, const uint8_t *message, size_t message_length,
86+
bool prehash)
87+
{
88+
char const *hash_array[] = {dom2, workmem, message};
89+
size_t hash_array_lengths[] = {sizeof(dom2), SX_ED25519_SZ, message_length};
90+
size_t offset = prehash ? 0 : 1;
91+
size_t input_count = 3 - offset;
92+
93+
return hash_all_inputs(&hash_array[offset], &hash_array_lengths[offset], input_count,
94+
&sxhashalg_sha2_512, workmem + SX_ED25519_DGST_SZ);
95+
}
96+
97+
static int ed25519_calculate_k(char *workmem, char *point_r, const char *message,
98+
size_t message_length, bool prehash)
99+
{
100+
char const *hash_array[] = {dom2, point_r, workmem, message};
101+
size_t hash_array_lengths[] = {sizeof(dom2), SX_ED25519_SZ, SX_ED25519_SZ, message_length};
102+
size_t offset = prehash ? 0 : 1;
103+
size_t input_count = 4 - offset;
104+
105+
return hash_all_inputs(&hash_array[offset], &hash_array_lengths[offset], input_count,
106+
&sxhashalg_sha2_512, workmem);
107+
}
108+
109+
static int ed25519_sign_internal(const uint8_t *priv_key, char *signature, const uint8_t *message,
110+
size_t message_length, bool prehash)
111+
{
112+
int status;
113+
char workmem[5 * SX_ED25519_SZ];
114+
uint8_t pnt_r[SX_ED25519_DGST_SZ];
115+
char *area_1 = workmem;
116+
char *area_2 = workmem + AREA2_MEM_OFFSET;
117+
char *area_4 = workmem + AREA4_MEM_OFFSET;
118+
119+
/* Hash the private key, the digest is stored in the first 64 bytes of workmem*/
120+
status = hash_input(priv_key, SX_ED25519_SZ, area_1);
121+
if (status != SX_OK) {
122+
return status;
123+
}
124+
125+
/* Obtain r by hashing (prefix || message), where prefix is the second
126+
* half of the private key digest.
127+
*/
128+
status = ed25519_calculate_r(area_2, message, message_length, prehash);
129+
if (status != SX_OK) {
130+
return status;
131+
}
132+
133+
/* Perform point multiplication R = [r]B. This is the encoded point R,
134+
* which is the first part of the signature.
135+
*/
136+
status = sx_ed25519_ptmult((const struct sx_ed25519_dgst *)area_4,
137+
(struct sx_ed25519_pt *)pnt_r);
138+
if (status != SX_OK) {
139+
return status;
140+
}
141+
142+
/* The secret scalar s is computed in place from the first half of the
143+
* private key digest.
144+
*/
145+
decode_scalar_25519(area_1);
146+
147+
/* Clear second half of private key digest: sx_ed25519_ptmult()
148+
* works on an input of SX_ED25519_DGST_SZ bytes.
149+
*/
150+
safe_memset(area_2, sizeof(workmem) - SX_ED25519_SZ, 0, SX_ED25519_SZ);
151+
/* Perform point multiplication A = [s]B,
152+
* to obtain the public key A. which is stored in workmem[32:63]
153+
*/
154+
status = sx_ed25519_ptmult((const struct sx_ed25519_dgst *)area_1,
155+
(struct sx_ed25519_pt *)area_2);
156+
157+
if (status != SX_OK) {
158+
return status;
159+
}
160+
161+
status = ed25519_calculate_k(area_2, pnt_r, message, message_length, prehash);
162+
if (status != SX_OK) {
163+
return status;
164+
}
165+
166+
/* Compute (r + k * s) mod L. This gives the second part of the
167+
* signature, which is the encoded S which is stored in pnt_r.
168+
*/
169+
status = sx_ed25519_sign((const struct sx_ed25519_dgst *)area_2,
170+
(const struct sx_ed25519_dgst *)area_4,
171+
(const struct sx_ed25519_v *)area_1,
172+
(struct sx_ed25519_v *)(pnt_r + SX_ED25519_PT_SZ));
173+
if (status != SX_OK) {
174+
return status;
175+
}
176+
177+
memcpy(signature, pnt_r, SX_ED25519_DGST_SZ);
178+
safe_memzero(workmem, sizeof(workmem));
179+
180+
return status;
181+
}
182+
183+
int cracen_ed25519_sign(const uint8_t *priv_key, char *signature, const uint8_t *message,
184+
size_t message_length)
185+
{
186+
return ed25519_sign_internal(priv_key, signature, message, message_length, false);
187+
}
188+
189+
int cracen_ed25519ph_sign(const uint8_t *priv_key, char *signature, const uint8_t *message,
190+
size_t message_length, bool is_message)
191+
{
192+
char hashedmessage[SX_ED25519_DGST_SZ];
193+
int status;
194+
195+
if (is_message) {
196+
status = hash_input(message, message_length, hashedmessage);
197+
if (status != SX_OK) {
198+
return status;
199+
}
200+
201+
return ed25519_sign_internal(priv_key, signature, hashedmessage, SX_ED25519_DGST_SZ,
202+
true);
203+
} else {
204+
return ed25519_sign_internal(priv_key, signature, message, message_length, true);
205+
}
206+
}
207+
208+
static int ed25519_verify_internal(const uint8_t *pub_key, const char *message,
209+
size_t message_length, const char *signature, bool prehash)
210+
{
211+
int status;
212+
char digest[SX_ED25519_DGST_SZ];
213+
size_t ed25519_sz = SX_ED25519_SZ;
214+
size_t offset = prehash ? 0 : 1;
215+
size_t input_count = 4 - offset;
216+
217+
char const *hash_array[] = {dom2, signature, (const char *)pub_key, message};
218+
size_t hash_array_lengths[] = {sizeof(dom2), ed25519_sz, ed25519_sz, message_length};
219+
220+
status = hash_all_inputs(&hash_array[offset], &hash_array_lengths[offset], input_count,
221+
&sxhashalg_sha2_512, digest);
222+
if (status != SX_OK) {
223+
return status;
224+
}
225+
226+
status =
227+
sx_ed25519_verify((struct sx_ed25519_dgst *)digest, (struct sx_ed25519_pt *)pub_key,
228+
(const struct sx_ed25519_v *)(signature + SX_ED25519_SZ),
229+
(const struct sx_ed25519_pt *)signature);
230+
231+
return status;
232+
}
233+
234+
int cracen_ed25519_verify(const uint8_t *pub_key, const char *message, size_t message_length,
235+
const char *signature)
236+
{
237+
return ed25519_verify_internal(pub_key, message, message_length, signature, false);
238+
}
239+
240+
int cracen_ed25519ph_verify(const uint8_t *pub_key, const char *message, size_t message_length,
241+
const char *signature, bool is_message)
242+
{
243+
int status;
244+
char message_digest[SX_ED25519_DGST_SZ];
245+
246+
if (is_message) {
247+
status = hash_input(message, message_length, message_digest);
248+
if (status != SX_OK) {
249+
return status;
250+
}
251+
252+
return ed25519_verify_internal(pub_key, message_digest, SX_ED25519_DGST_SZ,
253+
signature, true);
254+
}
255+
return ed25519_verify_internal(pub_key, message, message_length, signature, true);
256+
}
257+
258+
int cracen_ed25519_create_pubkey(const uint8_t *priv_key, uint8_t *pub_key)
259+
{
260+
int status;
261+
char digest[SX_ED25519_DGST_SZ];
262+
char *pub_key_A = digest + SX_ED25519_SZ;
263+
264+
status = hash_input(priv_key, SX_ED25519_SZ, digest);
265+
if (status != SX_OK) {
266+
return status;
267+
}
268+
/* The secret scalar s is computed in place from the first half of the
269+
* private key digest.
270+
*/
271+
decode_scalar_25519(digest);
272+
273+
/* Clear second half of private key digest: ed25519_ptmult()
274+
* works on an input of SX_ED25519_DGST_SZ bytes.
275+
*/
276+
safe_memset(pub_key_A, SX_ED25519_SZ, 0, SX_ED25519_SZ);
277+
278+
/* Perform point multiplication A = [s]B, to obtain the public key A. */
279+
status = sx_ed25519_ptmult((const struct sx_ed25519_dgst *)digest,
280+
(struct sx_ed25519_pt *)pub_key_A);
281+
282+
if (status != SX_OK) {
283+
return status;
284+
}
285+
286+
memcpy(pub_key, pub_key_A, SX_ED25519_SZ);
287+
safe_memzero(digest, SX_ED25519_DGST_SZ);
288+
289+
return status;
290+
}

subsys/nrf_security/src/drivers/cracen/cracenpsa/src/key_management.c

Lines changed: 12 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -10,12 +10,9 @@
1010
#include "cracen_psa.h"
1111
#include "platform_keys/platform_keys.h"
1212
#include <nrf_security_mutexes.h>
13-
1413
#include <sicrypto/drbghash.h>
1514
#include <sicrypto/ecc.h>
1615
#include <sicrypto/ecdsa.h>
17-
#include <sicrypto/ed25519.h>
18-
#include <sicrypto/ed25519ph.h>
1916
#include <sicrypto/ed448.h>
2017
#include <sicrypto/montgomery.h>
2118
#include <sicrypto/rsa_keygen.h>
@@ -603,10 +600,9 @@ static psa_status_t export_ecc_public_key_from_keypair(const psa_key_attributes_
603600
psa_status_t psa_status;
604601
size_t expected_pub_key_size = 0;
605602
int si_status = 0;
606-
psa_algorithm_t key_alg = psa_get_key_algorithm(attributes);
607603
const struct sx_pk_ecurve *sx_curve;
608-
struct sitask t;
609604

605+
struct sitask t;
610606
switch (psa_curve) {
611607
case PSA_ECC_FAMILY_BRAINPOOL_P_R1:
612608
case PSA_ECC_FAMILY_SECP_R1:
@@ -631,7 +627,6 @@ static psa_status_t export_ecc_public_key_from_keypair(const psa_key_attributes_
631627

632628
struct si_sig_privkey priv_key;
633629
struct si_sig_pubkey pub_key;
634-
635630
char workmem[SX_ED448_DGST_SZ] = {};
636631

637632
if (PSA_KEY_LIFETIME_GET_LOCATION(psa_get_key_lifetime(attributes)) ==
@@ -675,24 +670,20 @@ static psa_status_t export_ecc_public_key_from_keypair(const psa_key_attributes_
675670
break;
676671
case PSA_ECC_FAMILY_TWISTED_EDWARDS:
677672
if (key_bits_attr == 255) {
678-
if (key_alg == PSA_ALG_ED25519PH) {
679-
priv_key.def = si_sig_def_ed25519ph;
680-
priv_key.key.ed25519 = (struct sx_ed25519_v *)key_buffer;
681-
pub_key.key.ed25519 = (struct sx_ed25519_pt *)data;
673+
si_status = cracen_ed25519_create_pubkey(key_buffer, data);
674+
if (!si_status) {
675+
*data_length = expected_pub_key_size;
676+
}
677+
return silex_statuscodes_to_psa(si_status);
682678
} else {
683-
priv_key.def = si_sig_def_ed25519;
684-
priv_key.key.ed25519 = (struct sx_ed25519_v *)key_buffer;
685-
pub_key.key.ed25519 = (struct sx_ed25519_pt *)data;
679+
priv_key.def = si_sig_def_ed448;
680+
priv_key.key.ed448 = (struct sx_ed448_v *)key_buffer;
681+
pub_key.key.ed448 = (struct sx_ed448_pt *)data;
686682
}
687-
} else {
688-
priv_key.def = si_sig_def_ed448;
689-
priv_key.key.ed448 = (struct sx_ed448_v *)key_buffer;
690-
pub_key.key.ed448 = (struct sx_ed448_pt *)data;
683+
break;
684+
default:
685+
return PSA_ERROR_NOT_SUPPORTED;
691686
}
692-
break;
693-
default:
694-
return PSA_ERROR_NOT_SUPPORTED;
695-
}
696687
}
697688

698689
si_task_init(&t, workmem, sizeof(workmem));

0 commit comments

Comments
 (0)