Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

nrf_security: drivers: cracen: adding support for ed25519 without sicrypto #19812

Merged
merged 1 commit into from
Feb 20, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ endif()
if(CONFIG_PSA_NEED_CRACEN_ASYMMETRIC_SIGNATURE_DRIVER)
list(APPEND cracen_driver_sources
${CMAKE_CURRENT_LIST_DIR}/src/sign.c
${CMAKE_CURRENT_LIST_DIR}/src/ed25519.c
)
endif()

Expand Down Expand Up @@ -90,6 +91,12 @@ if(CONFIG_PSA_NEED_CRACEN_KMU_DRIVER)
)
endif()

if(CONFIG_PSA_NEED_CRACEN_KEY_AGREEMENT_DRIVER)
list(APPEND cracen_driver_sources
${CMAKE_CURRENT_LIST_DIR}/src/ed25519.c
)
endif()

if(CONFIG_PSA_NEED_CRACEN_KEY_AGREEMENT_DRIVER OR CONFIG_PSA_NEED_CRACEN_KEY_DERIVATION_DRIVER)
list(APPEND cracen_driver_sources
${CMAKE_CURRENT_LIST_DIR}/src/key_derivation.c
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -361,4 +361,18 @@ psa_status_t cracen_spake2p_get_shared_key(cracen_spake2p_operation_t *operation

psa_status_t cracen_spake2p_abort(cracen_spake2p_operation_t *operation);

int cracen_ed25519_sign(const uint8_t *priv_key, char *signature, const uint8_t *message,
size_t message_length);

int cracen_ed25519_verify(const uint8_t *pub_key, const char *message, size_t message_length,
const char *signature);

int cracen_ed25519ph_sign(const uint8_t *priv_key, char *signature, const uint8_t *message,
size_t message_length, bool is_message);

int cracen_ed25519ph_verify(const uint8_t *pub_key, const char *message, size_t message_length,
const char *signature, bool is_message);

int cracen_ed25519_create_pubkey(const uint8_t *priv_key, uint8_t *pub_key);

#endif /* CRACEN_PSA_H */
290 changes: 290 additions & 0 deletions subsys/nrf_security/src/drivers/cracen/cracenpsa/src/ed25519.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,290 @@
/*
* Copyright (c) 2025 Nordic Semiconductor ASA
*
* SPDX-License-Identifier: LicenseRef-Nordic-5-Clause
*
* The comments in this file use the notations and conventions from RFC 8032.
*
* Workmem layout for an Ed25519 signature verification task:
* 1. digest (size: 64 bytes).
*
* Workmem layout for an Ed25519 signature generation task:
* The workmem is made of 5 areas of 32 bytes each (total size 160 bytes).
* In the following we refer to these areas using the numbers 1 to 5. The
* first hash operation computes the private key's digest, which is stored
* in areas 1 and 2. The second hash operation computes r, which is stored
* in areas 4 and 5. The first point multiplication computes R, which is
* written directly to the output buffer. Then the secret scalar s is
* computed in place in area 1. Area 2 is cleared. The second point
* multiplication computes the public key A, which is stored in area 2. The
* third hash operation computes k, which is stored in areas 2 and 3. The
* final operation (r + k * s) mod L computes S, which is written directly
* to the output buffer.
*
* Workmem layout for an Ed25519 public key generation task:
* 1. digest (size: 64 bytes). The digest of the private key is written in
* this area. Then the secret scalar s is computed in place in the first
* 32 bytes of this area.
*/
#include <string.h>
#include <sxsymcrypt/sha2.h>
#include <silexpk/ed25519.h>
#include <cracen/ec_helpers.h>
#include <sxsymcrypt/hash.h>
#include <cracen/mem_helpers.h>
#include <cracen/statuscodes.h>

#define AREA2_MEM_OFFSET 32
#define AREA4_MEM_OFFSET 96

/* This is the ASCII string with the
* PHflag 1 and context size 0 appended as defined in:
* https://datatracker.ietf.org/doc/html/rfc8032.html#section-2.
* It is used for domain separation between Ed25519 and Ed25519ph.
* This can not be stored as a const due to hardware limitations
*/
static char dom2[] = {0x53, 0x69, 0x67, 0x45, 0x64, 0x32, 0x35, 0x35, 0x31, 0x39, 0x20, 0x6e,
0x6f, 0x20, 0x45, 0x64, 0x32, 0x35, 0x35, 0x31, 0x39, 0x20, 0x63, 0x6f,
0x6c, 0x6c, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x01, 0x00};

static int hash_all_inputs(char const *inputs[], size_t inputs_lengths[], size_t input_count,
const struct sxhashalg *hashalg, char *out)
{
struct sxhash hashopctx;
int status;

status = sx_hash_create(&hashopctx, hashalg, sizeof(hashopctx));
if (status != SX_OK) {
return status;
}

for (size_t i = 0; i < input_count; i++) {
status = sx_hash_feed(&hashopctx, inputs[i], inputs_lengths[i]);
if (status != SX_OK) {
return status;
}
}
status = sx_hash_digest(&hashopctx, out);
if (status != SX_OK) {
return status;
}

status = sx_hash_wait(&hashopctx);

return status;
}

static int hash_input(const char *input, size_t input_length, char *digest)
{
char const *hash_array[] = {input};
size_t hash_array_lengths[] = {input_length};

return hash_all_inputs(hash_array, hash_array_lengths, 1, &sxhashalg_sha2_512, digest);
}

static int ed25519_calculate_r(char *workmem, const uint8_t *message, size_t message_length,
bool prehash)
{
char const *hash_array[] = {dom2, workmem, message};
size_t hash_array_lengths[] = {sizeof(dom2), SX_ED25519_SZ, message_length};
size_t offset = prehash ? 0 : 1;
size_t input_count = 3 - offset;

return hash_all_inputs(&hash_array[offset], &hash_array_lengths[offset], input_count,
&sxhashalg_sha2_512, workmem + SX_ED25519_DGST_SZ);
}

static int ed25519_calculate_k(char *workmem, char *point_r, const char *message,
size_t message_length, bool prehash)
{
char const *hash_array[] = {dom2, point_r, workmem, message};
size_t hash_array_lengths[] = {sizeof(dom2), SX_ED25519_SZ, SX_ED25519_SZ, message_length};
size_t offset = prehash ? 0 : 1;
size_t input_count = 4 - offset;

return hash_all_inputs(&hash_array[offset], &hash_array_lengths[offset], input_count,
&sxhashalg_sha2_512, workmem);
}

static int ed25519_sign_internal(const uint8_t *priv_key, char *signature, const uint8_t *message,
size_t message_length, bool prehash)
{
int status;
char workmem[5 * SX_ED25519_SZ];
uint8_t pnt_r[SX_ED25519_DGST_SZ];
char *area_1 = workmem;
char *area_2 = workmem + AREA2_MEM_OFFSET;
char *area_4 = workmem + AREA4_MEM_OFFSET;

/* Hash the private key, the digest is stored in the first 64 bytes of workmem*/
status = hash_input(priv_key, SX_ED25519_SZ, area_1);
if (status != SX_OK) {
return status;
}

/* Obtain r by hashing (prefix || message), where prefix is the second
* half of the private key digest.
*/
status = ed25519_calculate_r(area_2, message, message_length, prehash);
if (status != SX_OK) {
return status;
}

/* Perform point multiplication R = [r]B. This is the encoded point R,
* which is the first part of the signature.
*/
status = sx_ed25519_ptmult((const struct sx_ed25519_dgst *)area_4,
(struct sx_ed25519_pt *)pnt_r);
if (status != SX_OK) {
return status;
}

/* The secret scalar s is computed in place from the first half of the
* private key digest.
*/
decode_scalar_25519(area_1);

/* Clear second half of private key digest: sx_ed25519_ptmult()
* works on an input of SX_ED25519_DGST_SZ bytes.
*/
safe_memset(area_2, sizeof(workmem) - SX_ED25519_SZ, 0, SX_ED25519_SZ);
/* Perform point multiplication A = [s]B,
* to obtain the public key A. which is stored in workmem[32:63]
*/
status = sx_ed25519_ptmult((const struct sx_ed25519_dgst *)area_1,
(struct sx_ed25519_pt *)area_2);

if (status != SX_OK) {
return status;
}

status = ed25519_calculate_k(area_2, pnt_r, message, message_length, prehash);
if (status != SX_OK) {
return status;
}

/* Compute (r + k * s) mod L. This gives the second part of the
* signature, which is the encoded S which is stored in pnt_r.
*/
status = sx_ed25519_sign((const struct sx_ed25519_dgst *)area_2,
(const struct sx_ed25519_dgst *)area_4,
(const struct sx_ed25519_v *)area_1,
(struct sx_ed25519_v *)(pnt_r + SX_ED25519_PT_SZ));
if (status != SX_OK) {
return status;
}

memcpy(signature, pnt_r, SX_ED25519_DGST_SZ);
safe_memzero(workmem, sizeof(workmem));

return status;
}

int cracen_ed25519_sign(const uint8_t *priv_key, char *signature, const uint8_t *message,
size_t message_length)
{
return ed25519_sign_internal(priv_key, signature, message, message_length, false);
}

int cracen_ed25519ph_sign(const uint8_t *priv_key, char *signature, const uint8_t *message,
size_t message_length, bool is_message)
{
char hashedmessage[SX_ED25519_DGST_SZ];
int status;

if (is_message) {
status = hash_input(message, message_length, hashedmessage);
if (status != SX_OK) {
return status;
}

return ed25519_sign_internal(priv_key, signature, hashedmessage, SX_ED25519_DGST_SZ,
true);
} else {
return ed25519_sign_internal(priv_key, signature, message, message_length, true);
}
}

static int ed25519_verify_internal(const uint8_t *pub_key, const char *message,
size_t message_length, const char *signature, bool prehash)
{
int status;
char digest[SX_ED25519_DGST_SZ];
size_t ed25519_sz = SX_ED25519_SZ;
size_t offset = prehash ? 0 : 1;
size_t input_count = 4 - offset;

char const *hash_array[] = {dom2, signature, (const char *)pub_key, message};
size_t hash_array_lengths[] = {sizeof(dom2), ed25519_sz, ed25519_sz, message_length};

status = hash_all_inputs(&hash_array[offset], &hash_array_lengths[offset], input_count,
&sxhashalg_sha2_512, digest);
if (status != SX_OK) {
return status;
}

status =
sx_ed25519_verify((struct sx_ed25519_dgst *)digest, (struct sx_ed25519_pt *)pub_key,
(const struct sx_ed25519_v *)(signature + SX_ED25519_SZ),
(const struct sx_ed25519_pt *)signature);

return status;
}

int cracen_ed25519_verify(const uint8_t *pub_key, const char *message, size_t message_length,
const char *signature)
{
return ed25519_verify_internal(pub_key, message, message_length, signature, false);
}

int cracen_ed25519ph_verify(const uint8_t *pub_key, const char *message, size_t message_length,
const char *signature, bool is_message)
{
int status;
char message_digest[SX_ED25519_DGST_SZ];

if (is_message) {
status = hash_input(message, message_length, message_digest);
if (status != SX_OK) {
return status;
}

return ed25519_verify_internal(pub_key, message_digest, SX_ED25519_DGST_SZ,
signature, true);
}
return ed25519_verify_internal(pub_key, message, message_length, signature, true);
}

int cracen_ed25519_create_pubkey(const uint8_t *priv_key, uint8_t *pub_key)
{
int status;
char digest[SX_ED25519_DGST_SZ];
char *pub_key_A = digest + SX_ED25519_SZ;

status = hash_input(priv_key, SX_ED25519_SZ, digest);
if (status != SX_OK) {
return status;
}
/* The secret scalar s is computed in place from the first half of the
* private key digest.
*/
decode_scalar_25519(digest);

/* Clear second half of private key digest: ed25519_ptmult()
* works on an input of SX_ED25519_DGST_SZ bytes.
*/
safe_memset(pub_key_A, SX_ED25519_SZ, 0, SX_ED25519_SZ);

/* Perform point multiplication A = [s]B, to obtain the public key A. */
status = sx_ed25519_ptmult((const struct sx_ed25519_dgst *)digest,
(struct sx_ed25519_pt *)pub_key_A);

if (status != SX_OK) {
return status;
}

memcpy(pub_key, pub_key_A, SX_ED25519_SZ);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does it make sense to clear the scalar stored in the first part of the digest buffer here?

Maybe the workbuf in the other functions is also worth considering to clear before exiting the function.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That does make sense

safe_memzero(digest, SX_ED25519_DGST_SZ);

return status;
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,9 @@
#include "cracen_psa.h"
#include "platform_keys/platform_keys.h"
#include <nrf_security_mutexes.h>

#include <sicrypto/drbghash.h>
#include <sicrypto/ecc.h>
#include <sicrypto/ecdsa.h>
#include <sicrypto/ed25519.h>
#include <sicrypto/ed25519ph.h>
#include <sicrypto/ed448.h>
#include <sicrypto/montgomery.h>
#include <sicrypto/rsa_keygen.h>
Expand Down Expand Up @@ -603,10 +600,9 @@ static psa_status_t export_ecc_public_key_from_keypair(const psa_key_attributes_
psa_status_t psa_status;
size_t expected_pub_key_size = 0;
int si_status = 0;
psa_algorithm_t key_alg = psa_get_key_algorithm(attributes);
const struct sx_pk_ecurve *sx_curve;
struct sitask t;

struct sitask t;
switch (psa_curve) {
case PSA_ECC_FAMILY_BRAINPOOL_P_R1:
case PSA_ECC_FAMILY_SECP_R1:
Expand All @@ -631,7 +627,6 @@ static psa_status_t export_ecc_public_key_from_keypair(const psa_key_attributes_

struct si_sig_privkey priv_key;
struct si_sig_pubkey pub_key;

char workmem[SX_ED448_DGST_SZ] = {};

if (PSA_KEY_LIFETIME_GET_LOCATION(psa_get_key_lifetime(attributes)) ==
Expand Down Expand Up @@ -675,15 +670,11 @@ static psa_status_t export_ecc_public_key_from_keypair(const psa_key_attributes_
break;
case PSA_ECC_FAMILY_TWISTED_EDWARDS:
if (key_bits_attr == 255) {
if (key_alg == PSA_ALG_ED25519PH) {
priv_key.def = si_sig_def_ed25519ph;
priv_key.key.ed25519 = (struct sx_ed25519_v *)key_buffer;
pub_key.key.ed25519 = (struct sx_ed25519_pt *)data;
} else {
priv_key.def = si_sig_def_ed25519;
priv_key.key.ed25519 = (struct sx_ed25519_v *)key_buffer;
pub_key.key.ed25519 = (struct sx_ed25519_pt *)data;
si_status = cracen_ed25519_create_pubkey(key_buffer, data);
if (si_status == SX_OK) {
*data_length = expected_pub_key_size;
}
return silex_statuscodes_to_psa(si_status);
} else {
priv_key.def = si_sig_def_ed448;
priv_key.key.ed448 = (struct sx_ed448_v *)key_buffer;
Expand Down
Loading