Skip to content
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
36 changes: 28 additions & 8 deletions sw/device/lib/crypto/impl/ecc/curve25519.c
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,20 @@ static status_t curve25519_masked_scalar_write(const uint32_t *share0,
return OTCRYPTO_OK;
}

uint32_t curve25519_masked_scalar_checksum(
const curve25519_masked_scalar_t *scalar) {
uint32_t ctx;
crc32_init(&ctx);
// Compute the checksum only over a single share to avoid side-channel
// leakage. From a FI perspective only covering one key share is fine as
// (a) manipulating the second share with FI has only limited use to an
// adversary and (b) when manipulating the entire pointer to the key structure
// the checksum check fails.
crc32_add(&ctx, (unsigned char *)scalar->share0,
kCurve25519MaskedScalarShareBytes);
return crc32_finish(&ctx);
}

status_t curve25519_keygen_start(const curve25519_masked_scalar_s_t *s) {
// Load the Curve25519 app. Fails if OTBN is non-idle.
const otbn_app_t kOtbnAppCurve25519 = OTBN_APP_T_INIT(run_curve25519);
Expand Down Expand Up @@ -297,8 +311,7 @@ status_t curve25519_verify_finalize(hardened_bool_t *result) {
}

status_t curve25519_x25519_start(
const uint32_t s0[kCurve25519ScalarWords],
const uint32_t s1[kCurve25519ScalarWords],
const curve25519_masked_scalar_t *scalar,
const uint32_t public_key[kCurve25519PointWords]) {
// Load the Curve25519 app. Fails if OTBN is non-idle.
const otbn_app_t kOtbnAppCurve25519 = OTBN_APP_T_INIT(run_curve25519);
Expand All @@ -312,8 +325,12 @@ status_t curve25519_x25519_start(
// Write the private key arithmetic shares to DMEM.
const otbn_addr_t kOtbnVarS0 = OTBN_ADDR_T_INIT(run_curve25519, ed25519_s0);
const otbn_addr_t kOtbnVarS1 = OTBN_ADDR_T_INIT(run_curve25519, ed25519_s1);
HARDENED_TRY(otbn_dmem_write(kCurve25519ScalarWords, s0, kOtbnVarS0));
HARDENED_TRY(otbn_dmem_write(kCurve25519ScalarWords, s1, kOtbnVarS1));
HARDENED_TRY(
otbn_dmem_write(kCurve25519ScalarWords, scalar->share0, kOtbnVarS0));
HARDENED_TRY(
otbn_dmem_write(kCurve25519ScalarWords, scalar->share1, kOtbnVarS1));
HARDENED_CHECK_EQ(scalar->checksum,
launder32(curve25519_masked_scalar_checksum(scalar)));

// Write the public key to DMEM.
const otbn_addr_t kOtbnVarX25519PublicKey =
Expand Down Expand Up @@ -385,8 +402,7 @@ status_t curve25519_x25519_finalize(
}

status_t curve25519_x25519_keygen_start(
const uint32_t s0[kCurve25519ScalarWords],
const uint32_t s1[kCurve25519ScalarWords]) {
const curve25519_masked_scalar_t *scalar) {
// Load the Curve25519 app. Fails if OTBN is non-idle.
const otbn_app_t kOtbnAppCurve25519 = OTBN_APP_T_INIT(run_curve25519);
HARDENED_TRY(otbn_load_app(kOtbnAppCurve25519));
Expand All @@ -399,8 +415,12 @@ status_t curve25519_x25519_keygen_start(
// Write the private key arithmetic shares to DMEM.
const otbn_addr_t kOtbnVarS0 = OTBN_ADDR_T_INIT(run_curve25519, ed25519_s0);
const otbn_addr_t kOtbnVarS1 = OTBN_ADDR_T_INIT(run_curve25519, ed25519_s1);
HARDENED_TRY(otbn_dmem_write(kCurve25519ScalarWords, s0, kOtbnVarS0));
HARDENED_TRY(otbn_dmem_write(kCurve25519ScalarWords, s1, kOtbnVarS1));
HARDENED_TRY(
otbn_dmem_write(kCurve25519ScalarWords, scalar->share0, kOtbnVarS0));
HARDENED_TRY(
otbn_dmem_write(kCurve25519ScalarWords, scalar->share1, kOtbnVarS1));
HARDENED_CHECK_EQ(scalar->checksum,
launder32(curve25519_masked_scalar_checksum(scalar)));

// Start the OTBN routine.
return otbn_execute();
Expand Down
46 changes: 38 additions & 8 deletions sw/device/lib/crypto/impl/ecc/curve25519.h
Original file line number Diff line number Diff line change
Expand Up @@ -177,6 +177,40 @@ typedef struct curve25519_masked_scalar_r {
uint32_t share1[kCurve25519MaskedScalarRWords];
} curve25519_masked_scalar_r_t;

/**
* A type that holds a masked value from the Curve25519 scalar field.
*
* This struct is used to represent secret keys or shared secrets.
* The scalar is represented in two 256-bit shares, share0 and share1.
*/
typedef struct curve25519_masked_scalar {
/**
* First share of the secret scalar.
*/
uint32_t share0[kCurve25519MaskedScalarShareWords];

/**
* Second share of the secret scalar.
*/
uint32_t share1[kCurve25519MaskedScalarShareWords];
/**
* Checksum over share0.
*/
uint32_t checksum;
} curve25519_masked_scalar_t;

/**
* Compute the checksum of a curve25519 masked scalar.
*
* Call this routine after creating or modifying the curve25519 scalar
* structure.
*
* @param key curve25519 masked scalar.
* @returns Checksum value.
*/
uint32_t curve25519_masked_scalar_checksum(
const curve25519_masked_scalar_t *scalar);

/**
* Start an async Ed25519 keygen operation on OTBN.
*
Expand Down Expand Up @@ -282,15 +316,13 @@ status_t curve25519_verify_finalize(hardened_bool_t *result);
*
* Returns an `OTCRYPTO_ASYNC_INCOMPLETE` error if OTBN is busy.
*
* @param s0 The first arithmetic share of the masked private key.
* @param s1 The second arithmetic share of the masked private key.
* @param scalar The masked private key.
* @param public_key The public key from the other party.
* @return Result of the operation (OK or error).
*/
OT_WARN_UNUSED_RESULT
status_t curve25519_x25519_start(
const uint32_t s0[kCurve25519ScalarWords],
const uint32_t s1[kCurve25519ScalarWords],
const curve25519_masked_scalar_t *scalar,
const uint32_t public_key[kCurve25519PointWords]);

/**
Expand Down Expand Up @@ -331,14 +363,12 @@ status_t curve25519_x25519_finalize(
*
* Returns an `OTCRYPTO_ASYNC_INCOMPLETE` error if OTBN is busy.
*
* @param s0 The first arithmetic share of the masked private key.
* @param s1 The second arithmetic share of the masked private key.
* @param scalar The masked private key.
* @return Result of the operation (OK or error).
*/
OT_WARN_UNUSED_RESULT
status_t curve25519_x25519_keygen_start(
const uint32_t s0[kCurve25519ScalarWords],
const uint32_t s1[kCurve25519ScalarWords]);
const curve25519_masked_scalar_t *scalar);

/**
* Finish an async X25519 keygen operation on OTBN.
Expand Down
2 changes: 1 addition & 1 deletion sw/device/lib/crypto/impl/ecc/p256.h
Original file line number Diff line number Diff line change
Expand Up @@ -143,7 +143,7 @@ typedef struct p256_ecdh_shared_key {
*
* Call this routine after creating or modifying the p256 scalar structure.
*
* @param key p256 masked scalar.
* @param scalar p256 masked scalar.
* @returns Checksum value.
*/
uint32_t p256_masked_scalar_checksum(const p256_masked_scalar_t *scalar);
Expand Down
2 changes: 1 addition & 1 deletion sw/device/lib/crypto/impl/ecc/p384.h
Original file line number Diff line number Diff line change
Expand Up @@ -139,7 +139,7 @@ typedef struct p384_ecdh_shared_key {
*
* Call this routine after creating or modifying the p384 scalar structure.
*
* @param key p384 masked scalar.
* @param scalar p384 masked scalar.
* @returns Checksum value.
*/
uint32_t p384_masked_scalar_checksum(const p384_masked_scalar_t *scalar);
Expand Down
52 changes: 44 additions & 8 deletions sw/device/lib/crypto/impl/ecc_curve25519.c
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,38 @@ static const uint8_t kDom2Prefix[34] = {
'l', 'l', 'i', 's', 'i', 'o', 'n', 's', 1, 0 // F=1 (Ed25519ph), C_len=0
};

/**
* Extracts and verifies a Curve25519 masked scalar from a blinded key struct.
*
* This safely copies the blinded key material (two shares) from the opaque
* keyblob into the internal `curve25519_masked_scalar_t` representation.
*
* @param key The blinded key containing the raw 256-bit masked scalar shares.
* @param[out] scalar Destination struct for the extracted shares and checksum.
* @return OK on success, or an error code if a fault is detected.
*/
OT_WARN_UNUSED_RESULT
static status_t load_private_scalar(const otcrypto_blinded_key_t *key,
curve25519_masked_scalar_t *scalar) {
// From the key config we have two additional empty words per share in the
// keyblob Hence we copy from each share individually
size_t share_words = keyblob_share_num_words(key->config);

HARDENED_TRY(hardened_memcpy(scalar->share0, key->keyblob,
kCurve25519MaskedScalarShareWords));
HARDENED_TRY(hardened_memcpy(scalar->share1, key->keyblob + share_words,
kCurve25519MaskedScalarShareWords));

// We only verify share0 as one badly copied share due to FI would only leave
// randomness
HARDENED_CHECK_EQ(hardened_memeq(key->keyblob, scalar->share0,
kCurve25519MaskedScalarShareWords),
kHardenedBoolTrue);

scalar->checksum = curve25519_masked_scalar_checksum(scalar);
return OTCRYPTO_OK;
}

/**
* Check the lengths of private keys for curve 25519.
*
Expand Down Expand Up @@ -729,11 +761,13 @@ otcrypto_status_t otcrypto_x25519_keygen_async_start(

return otcrypto_eval_exit(curve25519_x25519_keygen_sideload_start());
} else if (private_key->config.hw_backed == kHardenedBoolFalse) {
uint32_t *share0 = private_key->keyblob;
uint32_t *share1 =
private_key->keyblob + keyblob_share_num_words(private_key->config);
curve25519_masked_scalar_t private_scalar;
HARDENED_TRY(load_private_scalar(private_key, &private_scalar));

HARDENED_TRY(curve25519_x25519_keygen_start(&private_scalar));

HARDENED_TRY(curve25519_x25519_keygen_start(share0, share1));
HARDENED_TRY(hardened_memshred((uint32_t *)&private_scalar,
kCurve25519MaskedScalarTotalShareWords));

return otcrypto_eval_exit(OTCRYPTO_OK);
}
Expand Down Expand Up @@ -768,11 +802,13 @@ otcrypto_status_t otcrypto_x25519_async_start(
curve25519_x25519_sideload_start(public_key->key));

} else if (private_key->config.hw_backed == kHardenedBoolFalse) {
uint32_t *share0 = private_key->keyblob;
uint32_t *share1 =
private_key->keyblob + keyblob_share_num_words(private_key->config);
curve25519_masked_scalar_t private_scalar;
HARDENED_TRY(load_private_scalar(private_key, &private_scalar));

HARDENED_TRY(curve25519_x25519_start(&private_scalar, public_key->key));

HARDENED_TRY(curve25519_x25519_start(share0, share1, public_key->key));
HARDENED_TRY(hardened_memshred((uint32_t *)&private_scalar,
kCurve25519MaskedScalarTotalShareWords));

return otcrypto_eval_exit(OTCRYPTO_OK);
}
Expand Down
Loading