Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
51 commits
Select commit Hold shift + click to select a range
680e92e
[crypto] Randomize memory before generating symmetric keys.
jadephilipoom May 23, 2025
b28b784
[crypto] Randomize buffer before constructing a keyblob.
jadephilipoom May 23, 2025
e663829
[crypto] Use the `unmask` function in HKDF.
jadephilipoom May 23, 2025
639a58f
[crypto] Randomize buffers in key transport functions.
jadephilipoom May 23, 2025
6d052a3
[crypto] Randomize destination buffers for ECC keys and shared secrets.
jadephilipoom May 23, 2025
fd42774
[crypto] Randomize destination buffer for KMAC-KDF.
jadephilipoom May 23, 2025
2c5818c
[crypto] Randomize plaintext and key buffers in RSA code.
jadephilipoom May 23, 2025
55b1d2c
[crypto] Improve side-channel defenses in DRBG.
jadephilipoom May 23, 2025
5f1db57
[crypto] Randomize sensitive buffers in AES.
jadephilipoom May 23, 2025
4cdbb07
[crypto] Randomize sensitive buffers in drivers.
jadephilipoom May 23, 2025
38251fa
[crypto] Randomize shares in separate loops in KMAC driver.
jadephilipoom May 28, 2025
64b3cb5
[crypto] Harden RSA padding selection against FI
nasahlpa Jul 10, 2025
b3255a8
[crypto] Fixed aes_gcm uninitialized output_len pointer
lucasbaizer Jul 21, 2025
eb29025
[crypto] Add OTBN DMEM wipes to RSA code
nasahlpa Jul 17, 2025
bbb58b8
[crypto] Wipe DMEM also in error cases
nasahlpa Jul 17, 2025
ef5a8f3
[crypto] Randomize HMAC key write order
nasahlpa Aug 26, 2025
173e216
[crypto] Read back HMAC config
nasahlpa Aug 26, 2025
6b44744
[crypto] Check if AES key length was set
nasahlpa Aug 25, 2025
1eea54d
[crypto] Read back the AES config as a FI countermeasure
nasahlpa Aug 25, 2025
2825dd7
[crypto] Check AES key integrity after writing to the core
nasahlpa Aug 27, 2025
05f3d39
[crypto] Check aes_key_t checksum for AES-GCM
nasahlpa Aug 27, 2025
82ee5a5
[crypto] Check aes_key_t checksum for AES-KWP
nasahlpa Aug 28, 2025
bad2439
[crypto] Harden AES & HMAC loops against FI
nasahlpa Aug 26, 2025
8e01704
[crypto] Randomize key write in AES driver
nasahlpa Aug 14, 2025
3a50ac8
[crypto] Randomize key read in keymgr driver
nasahlpa Aug 14, 2025
7813ad3
[crypto] Protect HMAC against FI
nasahlpa Jul 8, 2025
41795a7
[crypto] Protect AES against FI
nasahlpa Jul 10, 2025
2feaf21
[crypto] Check ECC key integrity after writing to OTBN
nasahlpa Sep 8, 2025
88aa763
[crypto] Check return value of HMAC functions
nasahlpa Sep 5, 2025
e37115a
[crypto] Return `status_t` for hardened_* functions
nasahlpa Sep 4, 2025
d826fc7
[crypto] Use `HARDENED_TRY()` for `hardenend*` functions
nasahlpa Sep 5, 2025
659b7f9
[crypto] Fix includes
nasahlpa Oct 1, 2025
8a41e98
[crypto] Improve switch case hardening in AES
nasahlpa Sep 9, 2025
5150427
[crypto] Add Verify-after-Sign for ECDSA as a FI countermeasure
nasahlpa Jul 10, 2025
d4c9889
[cryptotest] Switch ecdsa_*_sign to ecdsa_*sign_verify
nasahlpa Jul 10, 2025
68283a4
[pentest] Switch ecdsa_*_sign to ecdsa_*sign_verify
nasahlpa Jul 11, 2025
06fe52f
[pentest] Add KeyGen to P256/P384 Sign
nasahlpa Jul 11, 2025
7d850f3
[crypto] Add new P256/P384 API calls to the API documentation
nasahlpa Aug 14, 2025
2bb3865
[crypto] Harden AES-GCM against FI
nasahlpa Jul 15, 2025
3188a26
[cryptotest] Set kOtcryptoKeySecurityLevelHigh for AES-GCM
nasahlpa Jul 15, 2025
3e17364
[pentest] se kOtcryptoKeySecurityLevelHigh for AES-GCM
nasahlpa Jul 15, 2025
185f677
[crypto] Check HMAC key integrity
nasahlpa Sep 1, 2025
9a3d92d
[crypto] AES-GCM SCA hardening
nasahlpa Aug 20, 2025
10c6bbd
[crypto] Rename hardened_xor
nasahlpa Aug 29, 2025
4da0106
[crypto] Add improved hardened_xor function
nasahlpa Aug 29, 2025
3ba3872
[crypto] Avoid overriding shares in AES-GCM
nasahlpa Aug 29, 2025
b76af53
[crypto] Add functions to disable/enable iCache
nasahlpa Jul 22, 2025
21c2b12
[crypto] Disable iCache for AES-GCM
nasahlpa Jul 22, 2025
1759e5d
[crypto] Use hardened_xor in HMAC
nasahlpa Sep 19, 2025
0b7678d
[crypto] Remove decoys in hardened_* functions
nasahlpa Aug 22, 2025
f85abb4
[crypto] eg100 specific fixes
nasahlpa Oct 2, 2025
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
2 changes: 2 additions & 0 deletions doc/security/cryptolib/cryptolib_api.md
Original file line number Diff line number Diff line change
Expand Up @@ -357,10 +357,12 @@ For ECDSA, the cryptography library supports keypair generation, signing, and si

{{#header-snippet sw/device/lib/crypto/include/ecc_p256.h otcrypto_ecdsa_p256_keygen }}
{{#header-snippet sw/device/lib/crypto/include/ecc_p256.h otcrypto_ecdsa_p256_sign }}
{{#header-snippet sw/device/lib/crypto/include/ecc_p256.h otcrypto_ecdsa_p256_sign_verify }}
{{#header-snippet sw/device/lib/crypto/include/ecc_p256.h otcrypto_ecdsa_p256_verify }}

{{#header-snippet sw/device/lib/crypto/include/ecc_p384.h otcrypto_ecdsa_p384_keygen }}
{{#header-snippet sw/device/lib/crypto/include/ecc_p384.h otcrypto_ecdsa_p384_sign }}
{{#header-snippet sw/device/lib/crypto/include/ecc_p384.h otcrypto_ecdsa_p384_sign_verify }}
{{#header-snippet sw/device/lib/crypto/include/ecc_p384.h otcrypto_ecdsa_p384_verify }}

#### ECDH
Expand Down
1 change: 1 addition & 0 deletions sw/device/lib/base/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -286,6 +286,7 @@ cc_library(
":macros",
":memory",
":random_order",
"//sw/device/lib/crypto/impl:status",
],
)

Expand Down
164 changes: 75 additions & 89 deletions sw/device/lib/base/hardened_memory.c
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,8 @@

// NOTE: The three hardened_mem* functions have similar contents, but the parts
// that are shared between them are commented only in `memcpy()`.
void hardened_memcpy(uint32_t *restrict dest, const uint32_t *restrict src,
size_t word_len) {
status_t hardened_memcpy(uint32_t *restrict dest, const uint32_t *restrict src,
size_t word_len) {
random_order_t order;
random_order_init(&order, word_len);

Expand All @@ -24,19 +24,8 @@ void hardened_memcpy(uint32_t *restrict dest, const uint32_t *restrict src,
uintptr_t src_addr = (uintptr_t)src;
uintptr_t dest_addr = (uintptr_t)dest;

// `decoys` is a small stack array that is filled with uninitialized memory.
// It is scratch space for us to do "extra" operations, when the number of
// iteration indices the chosen random order is different from `word_len`.
//
// These extra operations also introduce noise that an attacker must do work
// to filter, such as by applying side-channel analysis to obtain an address
// trace.
uint32_t decoys[8];
uintptr_t decoy_addr = (uintptr_t)&decoys;

// We need to launder `count`, so that the SW.LOOP-COMPLETION check is not
// deleted by the compiler.
size_t byte_len = word_len * sizeof(uint32_t);
for (; launderw(count) < expected_count; count = launderw(count) + 1) {
// The order values themselves are in units of words, but we need `byte_idx`
// to be in units of bytes.
Expand All @@ -49,37 +38,20 @@ void hardened_memcpy(uint32_t *restrict dest, const uint32_t *restrict src,
// happens-before among indices consistent with `order`.
barrierw(byte_idx);

// Compute putative offsets into `src`, `dest`, and `decoys`. Some of these
// may go off the end of `src` and `dest`, but they will not be cast to
// pointers in that case. (Note that casting out-of-range addresses to
// pointers is UB.)
uintptr_t srcp = src_addr + byte_idx;
uintptr_t destp = dest_addr + byte_idx;
uintptr_t decoy1 = decoy_addr + (byte_idx % sizeof(decoys));
uintptr_t decoy2 =
decoy_addr +
((byte_idx + (sizeof(decoys) / 2) + sizeof(uint32_t)) % sizeof(decoys));

// Branchlessly select whether to do a "real" copy or a decoy copy,
// depending on whether we've gone off the end of the array or not.
//
// Pretty much everything needs to be laundered: we need to launder
// `byte_idx` for obvious reasons, and we need to launder the result of the
// select, so that the compiler cannot delete the resulting loads and
// stores. This is similar to having used `volatile uint32_t *`.
void *src = (void *)launderw(
ct_cmovw(ct_sltuw(launderw(byte_idx), byte_len), srcp, decoy1));
void *dest = (void *)launderw(
ct_cmovw(ct_sltuw(launderw(byte_idx), byte_len), destp, decoy2));
// Calculate pointers.
void *src = (void *)launderw(src_addr + byte_idx);
void *dest = (void *)launderw(dest_addr + byte_idx);

// Perform the copy, without performing a typed dereference operation.
write_32(read_32(src), dest);
}
RANDOM_ORDER_HARDENED_CHECK_DONE(order);
HARDENED_CHECK_EQ(count, expected_count);

return OTCRYPTO_OK;
}

void hardened_memshred(uint32_t *dest, size_t word_len) {
status_t hardened_memshred(uint32_t *dest, size_t word_len) {
random_order_t order;
random_order_init(&order, word_len);

Expand All @@ -88,26 +60,21 @@ void hardened_memshred(uint32_t *dest, size_t word_len) {

uintptr_t data_addr = (uintptr_t)dest;

uint32_t decoys[8];
uintptr_t decoy_addr = (uintptr_t)&decoys;

size_t byte_len = word_len * sizeof(uint32_t);
for (; count < expected_count; count = launderw(count) + 1) {
size_t byte_idx = launderw(random_order_advance(&order)) * sizeof(uint32_t);
barrierw(byte_idx);

uintptr_t datap = data_addr + byte_idx;
uintptr_t decoy = decoy_addr + (byte_idx % sizeof(decoys));

void *data = (void *)launderw(
ct_cmovw(ct_sltuw(launderw(byte_idx), byte_len), datap, decoy));
// Calculate pointer.
void *data = (void *)launderw(data_addr + byte_idx);

// Write a freshly-generated random word to `*data`.
write_32(hardened_memshred_random_word(), data);
}
RANDOM_ORDER_HARDENED_CHECK_DONE(order);

HARDENED_CHECK_EQ(count, expected_count);

return OTCRYPTO_OK;
}

hardened_bool_t hardened_memeq(const uint32_t *lhs, const uint32_t *rhs,
Expand All @@ -121,36 +88,18 @@ hardened_bool_t hardened_memeq(const uint32_t *lhs, const uint32_t *rhs,
uintptr_t lhs_addr = (uintptr_t)lhs;
uintptr_t rhs_addr = (uintptr_t)rhs;

// `decoys` needs to be filled with equal values this time around. It
// should be filled with values with a Hamming weight of around 16, which is
// the most common hamming weight among 32-bit words.
uint32_t decoys[8] = {
0xaaaaaaaa, 0xaaaaaaaa, 0xaaaaaaaa, 0xaaaaaaaa,
0xaaaaaaaa, 0xaaaaaaaa, 0xaaaaaaaa, 0xaaaaaaaa,
};
uintptr_t decoy_addr = (uintptr_t)&decoys;

uint32_t zeros = 0;
uint32_t ones = UINT32_MAX;

// The loop is almost token-for-token the one above, but the copy is
// replaced with something else.
size_t byte_len = word_len * sizeof(uint32_t);
for (; count < expected_count; count = launderw(count) + 1) {
size_t byte_idx = launderw(random_order_advance(&order)) * sizeof(uint32_t);
barrierw(byte_idx);

uintptr_t ap = lhs_addr + byte_idx;
uintptr_t bp = rhs_addr + byte_idx;
uintptr_t decoy1 = decoy_addr + (byte_idx % sizeof(decoys));
uintptr_t decoy2 =
decoy_addr +
((byte_idx + (sizeof(decoys) / 2) + sizeof(uint32_t)) % sizeof(decoys));

void *av = (void *)launderw(
ct_cmovw(ct_sltuw(launderw(byte_idx), byte_len), ap, decoy1));
void *bv = (void *)launderw(
ct_cmovw(ct_sltuw(launderw(byte_idx), byte_len), bp, decoy2));
// Calculate pointers.
void *av = (void *)launderw(lhs_addr + byte_idx);
void *bv = (void *)launderw(rhs_addr + byte_idx);

uint32_t a = read_32(av);
uint32_t b = read_32(bv);
Expand Down Expand Up @@ -178,50 +127,87 @@ hardened_bool_t hardened_memeq(const uint32_t *lhs, const uint32_t *rhs,
return kHardenedBoolFalse;
}

void hardened_xor(uint32_t *restrict x, const uint32_t *restrict y,
size_t word_len) {
status_t hardened_xor(const uint32_t *restrict x, const uint32_t *restrict y,
size_t word_len, uint32_t *restrict dest) {
// Randomize the content of the output buffer before writing to it.
hardened_memshred(dest, word_len);

// Create a random variable rand.
uint32_t rand[word_len];
hardened_memshred(rand, word_len);

// Cast pointers to `uintptr_t` to erase their provenance.
uintptr_t x_addr = (uintptr_t)x;
uintptr_t y_addr = (uintptr_t)y;
uintptr_t dest_addr = (uintptr_t)dest;
uintptr_t rand_addr = (uintptr_t)&rand;

// Generate a random ordering.
random_order_t order;
random_order_init(&order, word_len);
size_t count = 0;
size_t expected_count = random_order_len(&order);

// Create some random values for decoy operations.
uint32_t decoys[8];
hardened_memshred(decoys, ARRAYSIZE(decoys));
// XOR the mask with the first share. This loop is modelled off the one in
// `hardened_memcpy`; see the comments there for more details.
for (; launderw(count) < expected_count; count = launderw(count) + 1) {
size_t byte_idx = launderw(random_order_advance(&order)) * sizeof(uint32_t);

// Prevent the compiler from re-ordering the loop.
barrierw(byte_idx);

// Calculate pointers.
uintptr_t xp = x_addr + byte_idx;
uintptr_t yp = y_addr + byte_idx;
uintptr_t destp = dest_addr + byte_idx;
uintptr_t randp = rand_addr + byte_idx;

// Set the pointers.
void *xv = (void *)launderw(xp);
void *yv = (void *)launderw(yp);
void *destv = (void *)launderw(destp);
void *randv = (void *)launderw(randp);

// Perform the XORs: dest = ((x ^ rand) ^ y) ^ rand
write_32(read_32(xv) ^ read_32(randv), destv);
write_32(read_32(destv) ^ read_32(yv), destv);
write_32(read_32(destv) ^ read_32(randv), destv);
}
RANDOM_ORDER_HARDENED_CHECK_DONE(order);
HARDENED_CHECK_EQ(count, expected_count);

return OTCRYPTO_OK;
}

status_t hardened_xor_in_place(uint32_t *restrict x, const uint32_t *restrict y,
size_t word_len) {
// Generate a random ordering.
random_order_t order;
random_order_init(&order, word_len);
size_t count = 0;
size_t expected_count = random_order_len(&order);

// Cast pointers to `uintptr_t` to erase their provenance.
uintptr_t x_addr = (uintptr_t)x;
uintptr_t y_addr = (uintptr_t)y;
uintptr_t decoy_addr = (uintptr_t)&decoys;

// XOR the mask with the first share. This loop is modelled off the one in
// `hardened_memcpy`; see the comments there for more details.
size_t byte_len = word_len * sizeof(uint32_t);
for (; launderw(count) < expected_count; count = launderw(count) + 1) {
size_t byte_idx = launderw(random_order_advance(&order)) * sizeof(uint32_t);

// Prevent the compiler from re-ordering the loop.
barrierw(byte_idx);

// Calculate pointers. The x and y pointers might not be valid, but in this
// case they will not be selected.
uintptr_t xp = x_addr + byte_idx;
uintptr_t yp = y_addr + byte_idx;
uintptr_t decoy1 = decoy_addr + (byte_idx % sizeof(decoys));
uintptr_t decoy2 =
decoy_addr +
((byte_idx + (sizeof(decoys) / 2) + sizeof(uint32_t)) % sizeof(decoys));

// Select in constant-time either the real pointers or decoys.
void *xv = (void *)launderw(
ct_cmovw(ct_sltuw(launderw(byte_idx), byte_len), xp, decoy1));
void *yv = (void *)launderw(
ct_cmovw(ct_sltuw(launderw(byte_idx), byte_len), yp, decoy2));

// Perform an XOR in either the decoy array or the real array.
// Calculate pointers.
void *xv = (void *)launderw(x_addr + byte_idx);
void *yv = (void *)launderw(y_addr + byte_idx);

// Perform an XOR in the array.
write_32(read_32(xv) ^ read_32(yv), xv);
}
RANDOM_ORDER_HARDENED_CHECK_DONE(order);
HARDENED_CHECK_EQ(count, expected_count);

return OTCRYPTO_OK;
}
36 changes: 30 additions & 6 deletions sw/device/lib/base/hardened_memory.h
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@

#include "sw/device/lib/base/hardened.h"
#include "sw/device/lib/base/macros.h"
#include "sw/device/lib/crypto/impl/status.h"

#ifdef __cplusplus
extern "C" {
Expand Down Expand Up @@ -43,9 +44,10 @@ extern uint32_t hardened_memshred_random_word(void);
* @param dest The destination of the copy.
* @param src The source of the copy.
* @param word_len The number of words to copy.
* @return OK or error.
*/
void hardened_memcpy(uint32_t *OT_RESTRICT dest,
const uint32_t *OT_RESTRICT src, size_t word_len);
status_t hardened_memcpy(uint32_t *OT_RESTRICT dest,
const uint32_t *OT_RESTRICT src, size_t word_len);

/**
* Fills a 32-bit aligned region of memory with random data.
Expand All @@ -63,8 +65,9 @@ void hardened_memcpy(uint32_t *OT_RESTRICT dest,
*
* @param dest The destination of the set.
* @param word_len The number of words to write.
* @return OK or error.
*/
void hardened_memshred(uint32_t *dest, size_t word_len);
status_t hardened_memshred(uint32_t *dest, size_t word_len);

/**
* Compare two potentially-overlapping 32-bit aligned regions of memory for
Expand All @@ -91,7 +94,27 @@ hardened_bool_t hardened_memeq(const uint32_t *lhs, const uint32_t *rhs,
size_t word_len);

/**
* Combines two word buffers with XOR.
* Combines two word buffers with XOR and store the result in the dest. buffer.
*
* Performs dest = ((rand ^ x) ^ y) ^ rand
*
* Callers should ensure the entropy complex is up before calling this
* function. The implementation uses random-order hardening primitives for
* side-channel defense. Moreover, calles should ensure that the dest. buffer
* is different from the source buffers.
*
* @param x Pointer to the first operand.
* @param y Pointer to the second operand.
* @param word_len Length in words of each operand.
* @param dest[out] Pointer to the output buffer.
* @return OK or error.
*/
status_t hardened_xor(const uint32_t *OT_RESTRICT x,
const uint32_t *OT_RESTRICT y, size_t word_len,
uint32_t *OT_RESTRICT dest);

/**
* Combines two word buffers with XOR in-place.
*
* Callers should ensure the entropy complex is up before calling this
* function. The implementation uses random-order hardening primitives for
Expand All @@ -100,9 +123,10 @@ hardened_bool_t hardened_memeq(const uint32_t *lhs, const uint32_t *rhs,
* @param[in,out] x Pointer to the first operand (modified in-place).
* @param y Pointer to the second operand.
* @param word_len Length in words of each operand.
* @return OK or error.
*/
void hardened_xor(uint32_t *OT_RESTRICT x, const uint32_t *OT_RESTRICT y,
size_t word_len);
status_t hardened_xor_in_place(uint32_t *OT_RESTRICT x,
const uint32_t *OT_RESTRICT y, size_t word_len);

#ifdef __cplusplus
} // extern "C"
Expand Down
Loading
Loading