diff --git a/doc/security/cryptolib/cryptolib_api.md b/doc/security/cryptolib/cryptolib_api.md index b1f98660ae2c1..dce876937b6e0 100644 --- a/doc/security/cryptolib/cryptolib_api.md +++ b/doc/security/cryptolib/cryptolib_api.md @@ -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 diff --git a/sw/device/lib/base/BUILD b/sw/device/lib/base/BUILD index ac33ec252f4aa..77a9007717405 100644 --- a/sw/device/lib/base/BUILD +++ b/sw/device/lib/base/BUILD @@ -286,6 +286,7 @@ cc_library( ":macros", ":memory", ":random_order", + "//sw/device/lib/crypto/impl:status", ], ) diff --git a/sw/device/lib/base/hardened_memory.c b/sw/device/lib/base/hardened_memory.c index 91397065b6ef2..a720a094acb83 100644 --- a/sw/device/lib/base/hardened_memory.c +++ b/sw/device/lib/base/hardened_memory.c @@ -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); @@ -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. @@ -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); @@ -88,19 +60,12 @@ 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); @@ -108,6 +73,8 @@ void hardened_memshred(uint32_t *dest, size_t word_len) { 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, @@ -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); @@ -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; } diff --git a/sw/device/lib/base/hardened_memory.h b/sw/device/lib/base/hardened_memory.h index c527da48c94fb..1edeb9f7f4801 100644 --- a/sw/device/lib/base/hardened_memory.h +++ b/sw/device/lib/base/hardened_memory.h @@ -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" { @@ -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. @@ -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 @@ -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 @@ -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" diff --git a/sw/device/lib/crypto/drivers/BUILD b/sw/device/lib/crypto/drivers/BUILD index 6efaf66dae362..a5c9e636f845b 100644 --- a/sw/device/lib/crypto/drivers/BUILD +++ b/sw/device/lib/crypto/drivers/BUILD @@ -31,11 +31,14 @@ cc_library( hdrs = ["aes.h"], deps = [ ":entropy", + ":rv_core_ibex", "//hw/ip/aes/data:aes_c_regs", "//hw/top_earlgrey/sw/autogen:top_earlgrey", "//sw/device/lib/base:abs_mmio", "//sw/device/lib/base:bitfield", + "//sw/device/lib/base:crc32", "//sw/device/lib/base:hardened", + "//sw/device/lib/base:hardened_memory", "//sw/device/lib/base:macros", "//sw/device/lib/crypto/impl:status", ], @@ -50,6 +53,7 @@ opentitan_test( ), deps = [ ":aes", + ":rv_core_ibex", "//sw/device/lib/base:macros", "//sw/device/lib/base:memory", "//sw/device/lib/crypto/impl:status", @@ -66,10 +70,12 @@ cc_library( ], deps = [ ":entropy", + ":rv_core_ibex", "//hw/ip/keymgr/data:keymgr_c_regs", "//hw/top_earlgrey/sw/autogen:top_earlgrey", "//sw/device/lib/base:abs_mmio", "//sw/device/lib/base:bitfield", + "//sw/device/lib/base:hardened_memory", "//sw/device/lib/base:macros", "//sw/device/lib/crypto/impl:status", "//sw/device/lib/runtime:hart", @@ -112,13 +118,13 @@ cc_library( ], deps = [ ":entropy", + ":rv_core_ibex", "//hw/ip/kmac/data:kmac_c_regs", "//hw/top_earlgrey/sw/autogen:top_earlgrey", "//sw/device/lib/base:abs_mmio", "//sw/device/lib/base:bitfield", "//sw/device/lib/base:hardened", "//sw/device/lib/base:macros", - "//sw/device/lib/crypto/drivers:rv_core_ibex", "//sw/device/lib/crypto/impl:status", ], ) @@ -192,11 +198,14 @@ cc_library( srcs = ["hmac.c"], hdrs = ["hmac.h"], deps = [ + ":entropy", "//hw/ip/hmac/data:hmac_c_regs", "//hw/top_earlgrey/sw/autogen:top_earlgrey", "//sw/device/lib/base:abs_mmio", "//sw/device/lib/base:bitfield", + "//sw/device/lib/base:crc32", "//sw/device/lib/base:hardened", + "//sw/device/lib/base:hardened_memory", "//sw/device/lib/base:macros", "//sw/device/lib/base:memory", "//sw/device/lib/crypto/drivers:rv_core_ibex", @@ -221,6 +230,10 @@ dual_cc_library( "//sw/device/lib/base:abs_mmio", "//sw/device/lib/base:csr", ], + shared = [ + "//sw/device/lib/base:hardened", + "//sw/device/lib/crypto/impl:status", + ], ), ) diff --git a/sw/device/lib/crypto/drivers/aes.c b/sw/device/lib/crypto/drivers/aes.c index 33e25e8746f22..07ff97f6f2703 100644 --- a/sw/device/lib/crypto/drivers/aes.c +++ b/sw/device/lib/crypto/drivers/aes.c @@ -6,10 +6,13 @@ #include "sw/device/lib/base/abs_mmio.h" #include "sw/device/lib/base/bitfield.h" +#include "sw/device/lib/base/crc32.h" #include "sw/device/lib/base/hardened.h" +#include "sw/device/lib/base/hardened_memory.h" #include "sw/device/lib/base/macros.h" #include "sw/device/lib/base/memory.h" #include "sw/device/lib/crypto/drivers/entropy.h" +#include "sw/device/lib/crypto/drivers/rv_core_ibex.h" #include "sw/device/lib/crypto/impl/status.h" #include "aes_regs.h" // Generated. @@ -24,6 +27,7 @@ enum { kAesKeyWordLen128 = 128 / (sizeof(uint32_t) * 8), kAesKeyWordLen192 = 192 / (sizeof(uint32_t) * 8), kAesKeyWordLen256 = 256 / (sizeof(uint32_t) * 8), + kAesKeyWordLenMax = kAesKeyWordLen256, }; /** @@ -47,6 +51,8 @@ static status_t spin_until(uint32_t bit) { * * If the key is sideloaded, this is a no-op. * + * Consumes randomness; the caller must ensure the entropy complex is up. + * * @param key AES key. * @return result, OK or error. */ @@ -61,26 +67,18 @@ static status_t aes_write_key(aes_key_t key) { uint32_t share0 = kBase + AES_KEY_SHARE0_0_REG_OFFSET; uint32_t share1 = kBase + AES_KEY_SHARE1_0_REG_OFFSET; - // Handle key shares in two separate loops to avoid dealing with + // Handle key shares in two separate hardened_memcpys to avoid dealing with // corresponding parts too close together, which could risk power - // side-channel leakage in the ALU. - // TODO: randomize iteration order. - size_t i = 0; - for (; i < key.key_len; ++i) { - abs_mmio_write32(share0 + i * sizeof(uint32_t), key.key_shares[0][i]); - } - HARDENED_CHECK_EQ(i, key.key_len); - for (i = 0; i < key.key_len; ++i) { - abs_mmio_write32(share1 + i * sizeof(uint32_t), key.key_shares[1][i]); - } - HARDENED_CHECK_EQ(i, key.key_len); + // side-channel leakage in the ALU. Before writing the key shares, initialize + // the registers with random data. + hardened_memshred((uint32_t *)share0, kAesKeyWordLenMax); + hardened_memcpy((uint32_t *)share0, key.key_shares[0], key.key_len); + hardened_memshred((uint32_t *)share1, kAesKeyWordLenMax); + hardened_memcpy((uint32_t *)share1, key.key_shares[1], key.key_len); + + // Check the integrity of the key written to the AES core. + HARDENED_CHECK_EQ(aes_key_integrity_checksum_check(&key), kHardenedBoolTrue); - // NOTE: all eight share registers must be written; in the case we don't have - // enough key data, we fill it with zeroes. - for (size_t i = key.key_len; i < 8; ++i) { - abs_mmio_write32(share0 + i * sizeof(uint32_t), 0); - abs_mmio_write32(share1 + i * sizeof(uint32_t), 0); - } return spin_until(AES_STATUS_IDLE_BIT); } @@ -104,102 +102,113 @@ static status_t aes_begin(aes_key_t key, const aes_block_t *iv, uint32_t ctrl_reg = AES_CTRL_SHADOWED_REG_RESVAL; // Set the operation (encrypt or decrypt). - hardened_bool_t operation_written = kHardenedBoolFalse; + hardened_bool_t operation_enc = launder32(0); switch (encrypt) { case kHardenedBoolTrue: ctrl_reg = bitfield_field32_write(ctrl_reg, AES_CTRL_SHADOWED_OPERATION_FIELD, AES_CTRL_SHADOWED_OPERATION_VALUE_AES_ENC); - operation_written = launder32(kHardenedBoolTrue); + operation_enc = launder32(operation_enc) | kHardenedBoolTrue; break; case kHardenedBoolFalse: ctrl_reg = bitfield_field32_write(ctrl_reg, AES_CTRL_SHADOWED_OPERATION_FIELD, AES_CTRL_SHADOWED_OPERATION_VALUE_AES_DEC); - operation_written = launder32(kHardenedBoolTrue); + operation_enc = launder32(operation_enc) | kHardenedBoolFalse; break; default: // Invalid value. return OTCRYPTO_BAD_ARGS; } - HARDENED_CHECK_EQ(operation_written, kHardenedBoolTrue); + // Check if we landed in the correct case statement. Use ORs for this to + // avoid that multiple cases were executed. + HARDENED_CHECK_EQ(launder32(operation_enc), encrypt); // Indicate whether the key will be sideloaded. - hardened_bool_t sideload_written = kHardenedBoolFalse; + hardened_bool_t which_sideload = launder32(0); switch (key.sideload) { case kHardenedBoolTrue: ctrl_reg = bitfield_bit32_write(ctrl_reg, AES_CTRL_SHADOWED_SIDELOAD_BIT, true); - sideload_written = launder32(kHardenedBoolTrue); + which_sideload = launder32(which_sideload) | kHardenedBoolTrue; break; case kHardenedBoolFalse: ctrl_reg = bitfield_bit32_write(ctrl_reg, AES_CTRL_SHADOWED_SIDELOAD_BIT, false); - sideload_written = launder32(kHardenedBoolTrue); + which_sideload = launder32(which_sideload) | kHardenedBoolFalse; break; default: // Invalid value. return OTCRYPTO_BAD_ARGS; } - HARDENED_CHECK_EQ(sideload_written, kHardenedBoolTrue); + // Check if we landed in the correct case statement. Use ORs for this to + // avoid that multiple cases were executed. + HARDENED_CHECK_EQ(launder32(which_sideload), key.sideload); // Translate the cipher mode to the hardware-encoding value and write the // control reg field. - aes_cipher_mode_t mode_written; + aes_cipher_mode_t mode_written = launder32(0); switch (launder32(key.mode)) { case kAesCipherModeEcb: ctrl_reg = bitfield_field32_write(ctrl_reg, AES_CTRL_SHADOWED_MODE_FIELD, AES_CTRL_SHADOWED_MODE_VALUE_AES_ECB); - mode_written = launder32(kAesCipherModeEcb); + mode_written = launder32(mode_written) | kAesCipherModeEcb; break; case kAesCipherModeCbc: ctrl_reg = bitfield_field32_write(ctrl_reg, AES_CTRL_SHADOWED_MODE_FIELD, AES_CTRL_SHADOWED_MODE_VALUE_AES_CBC); - mode_written = launder32(kAesCipherModeCbc); + mode_written = launder32(mode_written) | kAesCipherModeCbc; break; case kAesCipherModeCfb: ctrl_reg = bitfield_field32_write(ctrl_reg, AES_CTRL_SHADOWED_MODE_FIELD, AES_CTRL_SHADOWED_MODE_VALUE_AES_CFB); - mode_written = launder32(kAesCipherModeCfb); + mode_written = launder32(mode_written) | kAesCipherModeCfb; break; case kAesCipherModeOfb: ctrl_reg = bitfield_field32_write(ctrl_reg, AES_CTRL_SHADOWED_MODE_FIELD, AES_CTRL_SHADOWED_MODE_VALUE_AES_OFB); - mode_written = launder32(kAesCipherModeOfb); + mode_written = launder32(mode_written) | kAesCipherModeOfb; break; case kAesCipherModeCtr: ctrl_reg = bitfield_field32_write(ctrl_reg, AES_CTRL_SHADOWED_MODE_FIELD, AES_CTRL_SHADOWED_MODE_VALUE_AES_CTR); - mode_written = launder32(kAesCipherModeCtr); + mode_written = launder32(mode_written) | kAesCipherModeCtr; break; default: // Invalid value. return OTCRYPTO_BAD_ARGS; } - HARDENED_CHECK_EQ(mode_written, key.mode); + // Check if we landed in the correct case statement. Use ORs for this to + // avoid that multiple cases were executed. + HARDENED_CHECK_EQ(launder32(mode_written), key.mode); // Translate the key length to the hardware-encoding value and write the // control reg field. + size_t key_len_written; switch (key.key_len) { case kAesKeyWordLen128: ctrl_reg = bitfield_field32_write(ctrl_reg, AES_CTRL_SHADOWED_KEY_LEN_FIELD, AES_CTRL_SHADOWED_KEY_LEN_VALUE_AES_128); + key_len_written = launder32(kAesKeyWordLen128); break; case kAesKeyWordLen192: ctrl_reg = bitfield_field32_write(ctrl_reg, AES_CTRL_SHADOWED_KEY_LEN_FIELD, AES_CTRL_SHADOWED_KEY_LEN_VALUE_AES_192); + key_len_written = launder32(kAesKeyWordLen192); break; case kAesKeyWordLen256: ctrl_reg = bitfield_field32_write(ctrl_reg, AES_CTRL_SHADOWED_KEY_LEN_FIELD, AES_CTRL_SHADOWED_KEY_LEN_VALUE_AES_256); + key_len_written = launder32(kAesKeyWordLen256); break; default: // Invalid value. return OTCRYPTO_BAD_ARGS; } + HARDENED_CHECK_EQ(key_len_written, key.key_len); // Never enable manual operation. ctrl_reg = bitfield_bit32_write( @@ -225,6 +234,10 @@ static status_t aes_begin(aes_key_t key, const aes_block_t *iv, } } + // Read back the AES configuration and compare to the expected configuration. + HARDENED_CHECK_EQ(abs_mmio_read32(kBase + AES_CTRL_SHADOWED_REG_OFFSET), + launder32(ctrl_reg)); + // Check that AES is ready to receive input data. uint32_t status = abs_mmio_read32(kBase + AES_STATUS_REG_OFFSET); if (!bitfield_bit32_read(launder32(status), AES_STATUS_INPUT_READY_BIT)) { @@ -260,18 +273,24 @@ status_t aes_update(aes_block_t *dest, const aes_block_t *src) { HARDENED_TRY(spin_until(AES_STATUS_OUTPUT_VALID_BIT)); uint32_t offset = kBase + AES_DATA_OUT_0_REG_OFFSET; - for (size_t i = 0; i < ARRAYSIZE(dest->data); ++i) { + size_t i; + for (i = 0; launder32(i) < ARRAYSIZE(dest->data); ++i) { dest->data[i] = abs_mmio_read32(offset + i * sizeof(uint32_t)); } + // Check that the loop ran for the correct number of iterations. + HARDENED_CHECK_EQ(i, ARRAYSIZE(dest->data)); } if (src != NULL) { HARDENED_TRY(spin_until(AES_STATUS_INPUT_READY_BIT)); uint32_t offset = kBase + AES_DATA_IN_0_REG_OFFSET; - for (size_t i = 0; i < ARRAYSIZE(src->data); ++i) { + size_t i; + for (i = 0; launder32(i) < ARRAYSIZE(src->data); ++i) { abs_mmio_write32(offset + i * sizeof(uint32_t), src->data[i]); } + // Check that the loop ran for the correct number of iterations. + HARDENED_CHECK_EQ(i, ARRAYSIZE(src->data)); } return OTCRYPTO_OK; @@ -286,9 +305,12 @@ status_t aes_end(aes_block_t *iv) { if (iv != NULL) { // Read back the current IV from the hardware. uint32_t iv_offset = kBase + AES_IV_0_REG_OFFSET; - for (size_t i = 0; i < ARRAYSIZE(iv->data); ++i) { + size_t i; + for (i = 0; launder32(i) < ARRAYSIZE(iv->data); ++i) { iv->data[i] = abs_mmio_read32(iv_offset + i * sizeof(uint32_t)); } + // Check that the loop ran for the correct number of iterations. + HARDENED_CHECK_EQ(i, ARRAYSIZE(iv->data)); } uint32_t trigger_reg = 0; @@ -300,3 +322,26 @@ status_t aes_end(aes_block_t *iv) { return spin_until(AES_STATUS_IDLE_BIT); } + +uint32_t aes_key_integrity_checksum(const aes_key_t *key) { + uint32_t ctx; + crc32_init(&ctx); + crc32_add32(&ctx, key->mode); + crc32_add32(&ctx, key->sideload); + crc32_add32(&ctx, key->key_len); + // 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 *)key->key_shares[0], key->key_len); + return crc32_finish(&ctx); +} + +hardened_bool_t aes_key_integrity_checksum_check(const aes_key_t *key) { + if (key->checksum == launder32(aes_key_integrity_checksum(key))) { + HARDENED_CHECK_EQ(key->checksum, aes_key_integrity_checksum(key)); + return kHardenedBoolTrue; + } + return kHardenedBoolFalse; +} diff --git a/sw/device/lib/crypto/drivers/aes.h b/sw/device/lib/crypto/drivers/aes.h index 5e2e9396775d2..ca90d882636cc 100644 --- a/sw/device/lib/crypto/drivers/aes.h +++ b/sw/device/lib/crypto/drivers/aes.h @@ -73,6 +73,11 @@ typedef struct aes_key { * of sufficient length. */ const uint32_t *key_shares[2]; + + /** + * The checksum of the key structure. + */ + uint32_t checksum; } aes_key_t; /** @@ -152,6 +157,28 @@ status_t aes_update(aes_block_t *dest, const aes_block_t *src); OT_WARN_UNUSED_RESULT status_t aes_end(aes_block_t *iv); +/** + * Compute the checksum of an AES key. + * + * Call this routine after creating or modifying the aes key structure. + * + * @param key AES key. + * @returns Checksum value. + */ +uint32_t aes_key_integrity_checksum(const aes_key_t *key); + +/** + * Perform an integrity check on the AES key. + * + * Returns `kHardenedBoolTrue` if the check passed and `kHardenedBoolFalse` + * otherwise. + * + * @param key AES key. + * @returns Whether the integrity check passed. + */ +OT_WARN_UNUSED_RESULT +hardened_bool_t aes_key_integrity_checksum_check(const aes_key_t *key); + #ifdef __cplusplus } #endif diff --git a/sw/device/lib/crypto/drivers/aes_test.c b/sw/device/lib/crypto/drivers/aes_test.c index a05e5807055ba..33808eb0b785a 100644 --- a/sw/device/lib/crypto/drivers/aes_test.c +++ b/sw/device/lib/crypto/drivers/aes_test.c @@ -88,6 +88,9 @@ static status_t run_aes_test(void) { .key_len = 4, .key_shares = {share0, share1}, }; + // Create the checksum of the key and store it in the key structure. + key.checksum = aes_key_integrity_checksum(&key); + TRY(aes_encrypt_begin(key, &kIv)); aes_block_t ciphertext[ARRAYSIZE(kCiphertext)] = {0}; diff --git a/sw/device/lib/crypto/drivers/hmac.c b/sw/device/lib/crypto/drivers/hmac.c index 78fecac4c6407..b4842d4971a05 100644 --- a/sw/device/lib/crypto/drivers/hmac.c +++ b/sw/device/lib/crypto/drivers/hmac.c @@ -6,8 +6,11 @@ #include "sw/device/lib/base/abs_mmio.h" #include "sw/device/lib/base/bitfield.h" +#include "sw/device/lib/base/crc32.h" #include "sw/device/lib/base/hardened.h" +#include "sw/device/lib/base/hardened_memory.h" #include "sw/device/lib/base/memory.h" +#include "sw/device/lib/crypto/drivers/entropy.h" #include "sw/device/lib/crypto/drivers/rv_core_ibex.h" #include "sw/device/lib/crypto/impl/status.h" #include "sw/device/lib/runtime/ibex.h" @@ -155,15 +158,19 @@ static status_t hmac_idle_wait(void) { * * It also clears the internal state of HMAC HWIP by overwriting sensitive * values with 1s. + * + * @return Result of the operation. */ -static void clear(void) { +static status_t clear(void) { // Do not clear the config yet, we just need to deassert sha_en, see #23014. uint32_t cfg = abs_mmio_read32(kHmacBaseAddr + HMAC_CFG_REG_OFFSET); cfg = bitfield_bit32_write(cfg, HMAC_CFG_SHA_EN_BIT, false); abs_mmio_write32(kHmacBaseAddr + HMAC_CFG_REG_OFFSET, cfg); - // TODO(#23191): Use a random value from EDN to wipe. - abs_mmio_write32(kHmacBaseAddr + HMAC_WIPE_SECRET_REG_OFFSET, UINT32_MAX); + // Use a random value from EDN to wipe HMAC. + abs_mmio_write32(kHmacBaseAddr + HMAC_WIPE_SECRET_REG_OFFSET, + (uint32_t)ibex_rnd32_read()); + return OTCRYPTO_OK; } /** @@ -174,17 +181,32 @@ static void clear(void) { * caller must ensure that HMAC HWIP is in an idle state that accepts writing * key words. * - * The key may be NULL if `key_wordlen` is zero; in that case this function is - * a no-op. + * The key may be NULL. * - * @param key The buffer that points to the key. - * @param key_wordlen The length of the key in words. + * @param key The buffer that points to the hmac_key_t key structure. + * @return Result of the operation. */ -static void key_write(const uint32_t *key, size_t key_wordlen) { - for (size_t i = 0; i < key_wordlen; i++) { - abs_mmio_write32( - kHmacBaseAddr + HMAC_KEY_0_REG_OFFSET + sizeof(uint32_t) * i, key[i]); +static status_t key_write(const hmac_key_t *key) { + if (key != NULL) { + uint32_t key_reg = kHmacBaseAddr + HMAC_KEY_0_REG_OFFSET; + HARDENED_TRY( + hardened_memcpy((uint32_t *)key_reg, key->key_block, key->key_len)); + // We only check the integrity of the key when entering the CryptoLib. This + // check here will catch any manipulations of the key or the pointer to the + // key that might have happend in the meanwhile. We do it at this point as + // the key just got written into the HMAC core in the `hardened_memcpy()` + // above. + if (launder32(key->key_len) != 0) { + HARDENED_CHECK_EQ(hmac_key_integrity_checksum_check(key), + kHardenedBoolTrue); + } else { + HARDENED_CHECK_EQ(key->key_len, 0); + } + } else { + HARDENED_CHECK_EQ(key, NULL); } + + return OTCRYPTO_OK; } /** @@ -210,11 +232,12 @@ static void digest_read(uint32_t *digest, size_t digest_wordlen) { * Resume a streaming operation from a saved context. * * @param ctx Context object from which to restore. + * @return Result of the operation. */ -static void context_restore(hmac_ctx_t *ctx) { +static status_t context_restore(hmac_ctx_t *ctx) { // The previous caller should have left it clean, but it doesn't hurt to // clear again. - clear(); + HARDENED_TRY(clear()); // Restore CFG register from `ctx->cfg_reg`. We need to keep `sha_en` low // until context is restored, see #23014. @@ -223,7 +246,7 @@ static void context_restore(hmac_ctx_t *ctx) { // Write to KEY registers for HMAC operations. If the operation is SHA-2, // `key_wordlen` is set to 0 during `ctx` initialization. - key_write(ctx->key, ctx->key_wordlen); + HARDENED_TRY(key_write(&ctx->key)); uint32_t cmd = HMAC_CMD_REG_RESVAL; // Decide if we need to invoke `start` or `continue` command. @@ -234,11 +257,14 @@ static void context_restore(hmac_ctx_t *ctx) { // For SHA-256 and HMAC-256, we do not need to write to the second half of // DIGEST registers, but we do it anyway to keep the driver simple. - for (size_t i = 0; i < kHmacMaxDigestWords; i++) { + size_t i = 0; + for (; launder32(i) < kHmacMaxDigestWords; i++) { abs_mmio_write32( kHmacBaseAddr + HMAC_DIGEST_0_REG_OFFSET + sizeof(uint32_t) * i, ctx->H[i]); } + // Check that the loop ran for the correct number of iterations. + HARDENED_CHECK_EQ(i, kHmacMaxDigestWords); abs_mmio_write32(kHmacBaseAddr + HMAC_MSG_LENGTH_LOWER_REG_OFFSET, ctx->lower); abs_mmio_write32(kHmacBaseAddr + HMAC_MSG_LENGTH_UPPER_REG_OFFSET, @@ -253,6 +279,8 @@ static void context_restore(hmac_ctx_t *ctx) { // Now we can finally write the command to the register to actually issue // `start` or `continue`. abs_mmio_write32(kHmacBaseAddr + HMAC_CMD_REG_OFFSET, cmd); + + return OTCRYPTO_OK; } /** @@ -273,32 +301,68 @@ static void context_save(hmac_ctx_t *ctx) { abs_mmio_read32(kHmacBaseAddr + HMAC_MSG_LENGTH_UPPER_REG_OFFSET); } +/** + * Wipes the ctx struct by replacing sensitive data with randomness from the + * Ibex EDN interface. Non-sensitive variables are zeroized. + * + * @param[out] ctx Initialized context object for SHA2/HMAC-SHA2 operations. + * @return Result of the operation. + */ +static status_t hmac_context_wipe(hmac_ctx_t *ctx) { + // Ensure entropy complex is initialized. + HARDENED_TRY(entropy_complex_check()); + + // Randomize sensitive data. + HARDENED_TRY(hardened_memshred(ctx->key.key_block, kHmacMaxBlockWords)); + HARDENED_TRY(hardened_memshred(ctx->H, kHmacMaxDigestWords)); + HARDENED_TRY(hardened_memshred((uint32_t *)(ctx->partial_block), + kHmacMaxBlockBytes / sizeof(uint32_t))); + // Zero the remaining ctx fields. + ctx->cfg_reg = 0; + ctx->key.key_len = 0; + ctx->key.checksum = 0; + ctx->msg_block_wordlen = 0; + ctx->digest_wordlen = 0; + ctx->lower = 0; + ctx->upper = 0; + ctx->partial_block_bytelen = 0; + + return OTCRYPTO_OK; +} + /** * Write given byte array into the `MSG_FIFO`. This function should only be * called when HMAC HWIP is already running and expecting further message bytes. * * @param message The incoming message buffer to be fed into HMAC_FIFO. * @param message_len The length of `message` in bytes. + * @return Result of the operation. */ -static void msg_fifo_write(const uint8_t *message, size_t message_len) { +static status_t msg_fifo_write(const uint8_t *message, size_t message_len) { // TODO(#23191): Should we handle backpressure here? // Begin by writing a one byte at a time until the data is aligned. size_t i = 0; - for (; misalignment32_of((uintptr_t)(&message[i])) > 0 && i < message_len; + for (; misalignment32_of((uintptr_t)(&message[i])) > 0 && + launder32(i) < message_len; i++) { abs_mmio_write8(kHmacBaseAddr + HMAC_MSG_FIFO_REG_OFFSET, message[i]); } // Write one word at a time as long as there is a full word available. - for (; i + sizeof(uint32_t) <= message_len; i += sizeof(uint32_t)) { + for (; launder32(i + sizeof(uint32_t)) <= message_len; + i += sizeof(uint32_t)) { uint32_t next_word = read_32(&message[i]); abs_mmio_write32(kHmacBaseAddr + HMAC_MSG_FIFO_REG_OFFSET, next_word); } // For the last few bytes, we need to write one byte at a time again. - for (; i < message_len; i++) { + for (; launder32(i) < message_len; i++) { abs_mmio_write8(kHmacBaseAddr + HMAC_MSG_FIFO_REG_OFFSET, message[i]); } + // Check that the loops ran for the correct number of iterations. + HARDENED_CHECK_EQ(i, message_len); + + return OTCRYPTO_OK; } /** @@ -385,14 +449,13 @@ static status_t ensure_idle(void) { * * @param cfg HMAC block configuration register. * @param key Key input for HMAC (may be NULL if `key_wordlen` is 0). - * @param key_wordlen Length of key input in words. * @param msg Message data. * @param msg_len Length of message data in bytes. * @param digest_wordlen Digest length in 32-bit words. * @param[out] digest Buffer for the digest. */ -static status_t oneshot(const uint32_t cfg, const uint32_t *key, - size_t key_wordlen, const uint8_t *msg, size_t msg_len, +static status_t oneshot(const uint32_t cfg, const hmac_key_t *key, + const uint8_t *msg, size_t msg_len, size_t digest_wordlen, uint32_t *digest) { // Check that the block is idle. HARDENED_TRY(ensure_idle()); @@ -401,7 +464,11 @@ static status_t oneshot(const uint32_t cfg, const uint32_t *key, abs_mmio_write32(kHmacBaseAddr + HMAC_CFG_REG_OFFSET, cfg); // Write the key (no-op if the key length is 0, e.g. for hashing). - key_write(key, key_wordlen); + HARDENED_TRY(key_write(key)); + + // Read back the HMAC configuration and compare to the expected configuration. + HARDENED_CHECK_EQ(abs_mmio_read32(kHmacBaseAddr + HMAC_CFG_REG_OFFSET), + launder32(cfg)); // Send the START command. uint32_t cmd = @@ -409,7 +476,7 @@ static status_t oneshot(const uint32_t cfg, const uint32_t *key, abs_mmio_write32(kHmacBaseAddr + HMAC_CMD_REG_OFFSET, cmd); // Write the message. - msg_fifo_write(msg, msg_len); + HARDENED_TRY(msg_fifo_write(msg, msg_len)); // Send the PROCESS command. cmd = bitfield_bit32_write(HMAC_CMD_REG_RESVAL, HMAC_CMD_HASH_PROCESS_BIT, 1); @@ -419,7 +486,7 @@ static status_t oneshot(const uint32_t cfg, const uint32_t *key, HARDENED_TRY(hmac_idle_wait()); digest_read(digest, digest_wordlen); - clear(); + HARDENED_TRY(clear()); return OTCRYPTO_OK; } @@ -427,48 +494,158 @@ status_t hmac_hash_sha256(const uint8_t *msg, size_t msg_len, uint32_t *digest) { uint32_t cfg = cfg_get(/*hmac_en=*/false, kDigestLengthSha256, kKeyLengthNone); - return oneshot(cfg, /*key=*/NULL, /*key_wordlen=*/0, msg, msg_len, - kHmacSha256DigestWords, digest); + return oneshot(cfg, /*key=*/NULL, msg, msg_len, kHmacSha256DigestWords, + digest); } status_t hmac_hash_sha384(const uint8_t *msg, size_t msg_len, uint32_t *digest) { uint32_t cfg = cfg_get(/*hmac_en=*/false, kDigestLengthSha384, kKeyLengthNone); - return oneshot(cfg, /*key=*/NULL, /*key_wordlen=*/0, msg, msg_len, - kHmacSha384DigestWords, digest); + return oneshot(cfg, /*key=*/NULL, msg, msg_len, kHmacSha384DigestWords, + digest); } status_t hmac_hash_sha512(const uint8_t *msg, size_t msg_len, uint32_t *digest) { uint32_t cfg = cfg_get(/*hmac_en=*/false, kDigestLengthSha512, kKeyLengthNone); - return oneshot(cfg, /*key=*/NULL, /*key_wordlen=*/0, msg, msg_len, - kHmacSha512DigestWords, digest); + return oneshot(cfg, /*key=*/NULL, msg, msg_len, kHmacSha512DigestWords, + digest); } -status_t hmac_hmac_sha256_cl(const uint32_t *key_block, const uint8_t *msg, +status_t hmac_hmac_sha256_cl(const hmac_key_t *key, const uint8_t *msg, size_t msg_len, uint32_t *tag) { // Always configure the key length as the underlying message block size. uint32_t cfg = cfg_get(/*hmac_en=*/true, kDigestLengthSha256, kKeyLength512); - return oneshot(cfg, key_block, kHmacSha256BlockWords, msg, msg_len, - kHmacSha256DigestWords, tag); + return oneshot(cfg, key, msg, msg_len, kHmacSha256DigestWords, tag); +} + +status_t hmac_hmac_sha256_redundant(const hmac_key_t *key, const uint8_t *msg, + size_t msg_len, uint32_t *tag) { + uint32_t o_key_pad[kHmacSha256BlockWords]; + uint32_t i_key_pad[kHmacSha256BlockWords]; + memset(o_key_pad, 0, kHmacSha256BlockBytes); + memset(i_key_pad, 0, kHmacSha256BlockBytes); + + // XOR the key K with the outer (opad) and inner (ipad) padding. + uint32_t opad[kHmacSha256BlockWords]; + uint32_t ipad[kHmacSha256BlockWords]; + memset(opad, 0x5c5c5c5c, kHmacSha256BlockBytes); + memset(ipad, 0x36363636, kHmacSha256BlockBytes); + TRY(hardened_xor(key->key_block, opad, kHmacSha256BlockWords, o_key_pad)); + TRY(hardened_xor(key->key_block, ipad, kHmacSha256BlockWords, i_key_pad)); + + // Concatenate the message with the inner padded key. + uint8_t i_key_pad_msg[kHmacSha256BlockBytes + msg_len]; + memset(i_key_pad_msg, 0, sizeof(i_key_pad_msg)); + memcpy(i_key_pad_msg, i_key_pad, kHmacSha256BlockBytes); + memcpy(i_key_pad_msg + kHmacSha256BlockBytes, msg, msg_len); + + // h_i_key_pad_msg = H(i_key_pad || m). + uint32_t h_i_key_pad_msg[kHmacSha256DigestWords]; + memset(h_i_key_pad_msg, 0, sizeof(h_i_key_pad_msg)); + HARDENED_TRY(hmac_hash_sha256(i_key_pad_msg, kHmacSha256BlockBytes + msg_len, + h_i_key_pad_msg)); + + // Concatenate the outer padded key with h_i_key_pad_msg. + uint8_t o_key_pad_hash[kHmacSha256BlockBytes + kHmacSha256DigestBytes]; + memset(o_key_pad_hash, 0, sizeof(o_key_pad_hash)); + memcpy(o_key_pad_hash, o_key_pad, kHmacSha256BlockBytes); + memcpy(o_key_pad_hash + kHmacSha256BlockBytes, h_i_key_pad_msg, + kHmacSha256DigestBytes); + + // hmac = H(o_key_pad || h_i_key_pad_msg). + return hmac_hash_sha256(o_key_pad_hash, + kHmacSha256BlockBytes + kHmacSha256DigestBytes, tag); } -status_t hmac_hmac_sha384(const uint32_t *key_block, const uint8_t *msg, +status_t hmac_hmac_sha384(const hmac_key_t *key, const uint8_t *msg, size_t msg_len, uint32_t *tag) { // Always configure the key length as the underlying message block size. uint32_t cfg = cfg_get(/*hmac_en=*/true, kDigestLengthSha384, kKeyLength1024); - return oneshot(cfg, key_block, kHmacSha384BlockWords, msg, msg_len, - kHmacSha384DigestWords, tag); + return oneshot(cfg, key, msg, msg_len, kHmacSha384DigestWords, tag); } -status_t hmac_hmac_sha512(const uint32_t *key_block, const uint8_t *msg, +status_t hmac_hmac_sha384_redundant(const hmac_key_t *key, const uint8_t *msg, + size_t msg_len, uint32_t *tag) { + uint32_t o_key_pad[kHmacSha384BlockWords]; + uint32_t i_key_pad[kHmacSha384BlockWords]; + memset(o_key_pad, 0, kHmacSha384BlockBytes); + memset(i_key_pad, 0, kHmacSha384BlockBytes); + + // XOR the key K with the outer (opad) and inner (ipad) padding. + for (size_t it = 0; it < kHmacSha384BlockWords; it++) { + o_key_pad[it] = key->key_block[it] ^ 0x5c5c5c5c; + i_key_pad[it] = key->key_block[it] ^ 0x36363636; + } + + // Concatenate the message with the inner padded key. + uint8_t i_key_pad_msg[kHmacSha384BlockBytes + msg_len]; + memset(i_key_pad_msg, 0, sizeof(i_key_pad_msg)); + memcpy(i_key_pad_msg, i_key_pad, kHmacSha384BlockBytes); + memcpy(i_key_pad_msg + kHmacSha384BlockBytes, msg, msg_len); + + // h_i_key_pad_msg = H(i_key_pad || m). + uint32_t h_i_key_pad_msg[kHmacSha384DigestWords]; + memset(h_i_key_pad_msg, 0, sizeof(h_i_key_pad_msg)); + HARDENED_TRY(hmac_hash_sha384(i_key_pad_msg, kHmacSha384BlockBytes + msg_len, + h_i_key_pad_msg)); + + // Concatenate the outer padded key with h_i_key_pad_msg. + uint8_t o_key_pad_hash[kHmacSha384BlockBytes + kHmacSha384DigestBytes]; + memset(o_key_pad_hash, 0, sizeof(o_key_pad_hash)); + memcpy(o_key_pad_hash, o_key_pad, kHmacSha384BlockBytes); + memcpy(o_key_pad_hash + kHmacSha384BlockBytes, h_i_key_pad_msg, + kHmacSha384DigestBytes); + + // hmac = H(o_key_pad || h_i_key_pad_msg). + return hmac_hash_sha384(o_key_pad_hash, + kHmacSha384BlockBytes + kHmacSha384DigestBytes, tag); +} + +status_t hmac_hmac_sha512(const hmac_key_t *key, const uint8_t *msg, size_t msg_len, uint32_t *tag) { // Always configure the key length as the underlying message block size. uint32_t cfg = cfg_get(/*hmac_en=*/true, kDigestLengthSha512, kKeyLength1024); - return oneshot(cfg, key_block, kHmacSha512BlockWords, msg, msg_len, - kHmacSha512DigestWords, tag); + return oneshot(cfg, key, msg, msg_len, kHmacSha512DigestWords, tag); +} + +status_t hmac_hmac_sha512_redundant(const hmac_key_t *key, const uint8_t *msg, + size_t msg_len, uint32_t *tag) { + uint32_t o_key_pad[kHmacSha512BlockWords]; + uint32_t i_key_pad[kHmacSha512BlockWords]; + memset(o_key_pad, 0, kHmacSha512BlockBytes); + memset(i_key_pad, 0, kHmacSha512BlockBytes); + + // XOR the key K with the outer (opad) and inner (ipad) padding. + for (size_t it = 0; it < kHmacSha512BlockWords; it++) { + o_key_pad[it] = key->key_block[it] ^ 0x5c5c5c5c; + i_key_pad[it] = key->key_block[it] ^ 0x36363636; + } + + // Concatenate the message with the inner padded key. + uint8_t i_key_pad_msg[kHmacSha512BlockBytes + msg_len]; + memset(i_key_pad_msg, 0, sizeof(i_key_pad_msg)); + memcpy(i_key_pad_msg, i_key_pad, kHmacSha512BlockBytes); + memcpy(i_key_pad_msg + kHmacSha512BlockBytes, msg, msg_len); + + // h_i_key_pad_msg = H(i_key_pad || m). + uint32_t h_i_key_pad_msg[kHmacSha512DigestWords]; + memset(h_i_key_pad_msg, 0, sizeof(h_i_key_pad_msg)); + HARDENED_TRY(hmac_hash_sha512(i_key_pad_msg, kHmacSha512BlockBytes + msg_len, + h_i_key_pad_msg)); + + // Concatenate the outer padded key with h_i_key_pad_msg. + uint8_t o_key_pad_hash[kHmacSha512BlockBytes + kHmacSha512DigestBytes]; + memset(o_key_pad_hash, 0, sizeof(o_key_pad_hash)); + memcpy(o_key_pad_hash, o_key_pad, kHmacSha512BlockBytes); + memcpy(o_key_pad_hash + kHmacSha512BlockBytes, h_i_key_pad_msg, + kHmacSha512DigestBytes); + + // hmac = H(o_key_pad || h_i_key_pad_msg). + return hmac_hash_sha512(o_key_pad_hash, + kHmacSha512BlockBytes + kHmacSha512DigestBytes, tag); } /** @@ -484,7 +661,8 @@ status_t hmac_hmac_sha512(const uint32_t *key_block, const uint8_t *msg, */ static void sha2_init(hmac_digest_length_t digest_len, hmac_ctx_t *ctx) { ctx->cfg_reg = cfg_get(/*hmac_en=*/false, digest_len, kKeyLengthNone); - ctx->key_wordlen = 0; + ctx->key.key_len = 0; + ctx->key.checksum = 0; ctx->lower = 0; ctx->upper = 0; ctx->partial_block_bytelen = 0; @@ -526,30 +704,49 @@ void hmac_hash_sha512_init(hmac_ctx_t *ctx) { sha2_init(kDigestLengthSha512, ctx); } -void hmac_hmac_sha256_init_cl(const uint32_t *key_block, hmac_ctx_t *ctx) { +void hmac_hmac_sha256_init_cl(const hmac_key_t key, hmac_ctx_t *ctx) { ctx->msg_block_wordlen = kHmacSha256BlockWords, - ctx->digest_wordlen = kHmacSha256DigestWords, - ctx->key_wordlen = kHmacSha256BlockWords; - memcpy(ctx->key, key_block, ctx->key_wordlen * sizeof(uint32_t)); + ctx->digest_wordlen = kHmacSha256DigestWords; + ctx->key.key_len = key.key_len; + ctx->key.checksum = key.checksum; + hardened_memcpy(ctx->key.key_block, key.key_block, key.key_len); hmac_init(kKeyLength512, kDigestLengthSha256, ctx); } -void hmac_hmac_sha384_init(const uint32_t *key_block, hmac_ctx_t *ctx) { +void hmac_hmac_sha384_init(const hmac_key_t key, hmac_ctx_t *ctx) { ctx->msg_block_wordlen = kHmacSha384BlockWords, - ctx->digest_wordlen = kHmacSha384DigestWords, - ctx->key_wordlen = kHmacSha384BlockWords; - memcpy(ctx->key, key_block, ctx->key_wordlen * sizeof(uint32_t)); + ctx->digest_wordlen = kHmacSha384DigestWords; + ctx->key.key_len = key.key_len; + ctx->key.checksum = key.checksum; + hardened_memcpy(ctx->key.key_block, key.key_block, key.key_len); hmac_init(kKeyLength1024, kDigestLengthSha384, ctx); } -void hmac_hmac_sha512_init(const uint32_t *key_block, hmac_ctx_t *ctx) { +void hmac_hmac_sha512_init(const hmac_key_t key, hmac_ctx_t *ctx) { ctx->msg_block_wordlen = kHmacSha512BlockWords, - ctx->digest_wordlen = kHmacSha512DigestWords, - ctx->key_wordlen = kHmacSha512BlockWords; - memcpy(ctx->key, key_block, ctx->key_wordlen * sizeof(uint32_t)); + ctx->digest_wordlen = kHmacSha512DigestWords; + ctx->key.key_len = key.key_len; + ctx->key.checksum = key.checksum; + hardened_memcpy(ctx->key.key_block, key.key_block, key.key_len); hmac_init(kKeyLength1024, kDigestLengthSha512, ctx); } +uint32_t hmac_key_integrity_checksum(const hmac_key_t *key) { + uint32_t ctx; + crc32_init(&ctx); + crc32_add32(&ctx, key->key_len); + crc32_add(&ctx, (unsigned char *)key->key_block, key->key_len); + return crc32_finish(&ctx); +} + +hardened_bool_t hmac_key_integrity_checksum_check(const hmac_key_t *key) { + if (key->checksum == launder32(hmac_key_integrity_checksum(key))) { + HARDENED_CHECK_EQ(key->checksum, hmac_key_integrity_checksum(key)); + return kHardenedBoolTrue; + } + return kHardenedBoolFalse; +} + status_t hmac_update(hmac_ctx_t *ctx, const uint8_t *data, size_t len) { // If we don't have enough new bytes to fill a block, just update the partial // block and return. @@ -569,12 +766,12 @@ status_t hmac_update(hmac_ctx_t *ctx, const uint8_t *data, size_t len) { // Retore context will restore the context and also hit start or continue // button as necessary. - context_restore(ctx); + HARDENED_TRY(context_restore(ctx)); // Write the partial block, then the new bytes. - msg_fifo_write((unsigned char *)ctx->partial_block, - ctx->partial_block_bytelen); - msg_fifo_write(data, len - leftover_len); + HARDENED_TRY(msg_fifo_write((unsigned char *)ctx->partial_block, + ctx->partial_block_bytelen)); + HARDENED_TRY(msg_fifo_write(data, len - leftover_len)); /* * TODO should be uncommented once the issue #24767 will be solved in the HW @@ -601,18 +798,18 @@ status_t hmac_update(hmac_ctx_t *ctx, const uint8_t *data, size_t len) { ctx->partial_block_bytelen = leftover_len; // Clean up. - clear(); + HARDENED_TRY(clear()); return OTCRYPTO_OK; } status_t hmac_final(hmac_ctx_t *ctx, uint32_t *digest) { // Retore context will restore the context and also hit start or continue // button as necessary. - context_restore(ctx); + HARDENED_TRY(context_restore(ctx)); // Feed the final leftover bytes to HMAC HWIP. - msg_fifo_write((unsigned char *)ctx->partial_block, - ctx->partial_block_bytelen); + HARDENED_TRY(msg_fifo_write((unsigned char *)ctx->partial_block, + ctx->partial_block_bytelen)); // All message bytes are fed, now hit the process button. uint32_t cmd = @@ -626,6 +823,6 @@ status_t hmac_final(hmac_ctx_t *ctx, uint32_t *digest) { // TODO(#23191): Destroy sensitive values in the ctx object. // Clean up. - clear(); + HARDENED_TRY(clear()); return OTCRYPTO_OK; } diff --git a/sw/device/lib/crypto/drivers/hmac.h b/sw/device/lib/crypto/drivers/hmac.h index da28c1e599ea3..17bcc0f262e70 100644 --- a/sw/device/lib/crypto/drivers/hmac.h +++ b/sw/device/lib/crypto/drivers/hmac.h @@ -52,6 +52,24 @@ enum { kHmacMaxBlockWords = kHmacMaxBlockBytes / sizeof(uint32_t), }; +/** + * The HMAC key structure. + */ +typedef struct hmac_key { + /** + * The length of the key (in 32-bit words). + */ + size_t key_len; + /** + * Key storage buffer. + */ + uint32_t key_block[kHmacMaxBlockWords]; + /** + * Checksum of this HMAC key structure. + */ + uint32_t checksum; +} hmac_key_t; + /** * A context struct maintained for streaming operations. */ @@ -59,8 +77,7 @@ typedef struct hmac_ctx { // A copy of `CFG` register used during resumption. uint32_t cfg_reg; // A copy of `KEY` to be used during start or resumption. - uint32_t key[kHmacMaxBlockWords]; - size_t key_wordlen; + hmac_key_t key; // The internal (message) block size of SHA-2 for this operation. size_t msg_block_wordlen; size_t digest_wordlen; @@ -112,48 +129,114 @@ status_t hmac_hash_sha512(const uint8_t *msg, size_t msg_len, uint32_t *digest); * The key should be pre-processed into a buffer the size of a full message * block, according to FIPS 198-1, section 4. * - * @param key_block Input key block (`kHmacSha256BlockWords` words). + * @param key HMAC key. * @param msg Input message. * @param msg_len Message length in bytes. * @param[out] tag Authentication tag (`kHmacSha256DigestWords` bytes). * @return OK or error. */ OT_WARN_UNUSED_RESULT -status_t hmac_hmac_sha256_cl(const uint32_t *key_block, const uint8_t *msg, +status_t hmac_hmac_sha256_cl(const hmac_key_t *key, const uint8_t *msg, size_t msg_len, uint32_t *tag); +/** + * Redundant implementation for a one-shot HMAC-SHA256 hash computation. + * + * The key should be pre-processed into a buffer the size of a full message + * block, according to FIPS 198-1, section 4. + * + * To be used together with hmac_hmac_sha256() to mitigate FI attacks. This + * implementation is different to hmac_hmac_sha256() as it manually assembles + * the HMAC functionality using SHA256. By using two different HMAC + * implementations, injecting two identical faults affect different parts during + * the HMAC compuation, which can be detected. + * + * @param key HMAC key. + * @param msg Input message. + * @param msg_len Message length in bytes. + * @param[out] tag Authentication tag (`kHmacSha256DigestWords` bytes). + * @return OK or error. + */ +OT_WARN_UNUSED_RESULT +status_t hmac_hmac_sha256_redundant(const hmac_key_t *key, const uint8_t *msg, + size_t msg_len, uint32_t *tag); + /** * One-shot HMAC-SHA384 hash computation. * * The key should be pre-processed into a buffer the size of a full message * block, according to FIPS 198-1, section 4. * - * @param key_block Input key block (`kHmacSha384BlockWords` words). + * @param key HMAC key. * @param msg Input message. * @param msg_len Message length in bytes. * @param[out] tag Authentication tag (`kHmacSha384DigestWords` bytes). * @return OK or error. */ OT_WARN_UNUSED_RESULT -status_t hmac_hmac_sha384(const uint32_t *key_block, const uint8_t *msg, +status_t hmac_hmac_sha384(const hmac_key_t *key, const uint8_t *msg, size_t msg_len, uint32_t *tag); +/** + * Redundant implementation for a one-shot HMAC-SHA384 hash computation. + * + * The key should be pre-processed into a buffer the size of a full message + * block, according to FIPS 198-1, section 4. + * + * To be used together with hmac_hmac_sha384() to mitigate FI attacks. This + * implementation is different to hmac_hmac_sha384() as it manually assembles + * the HMAC functionality using SHA384. By using two different HMAC + * implementations, injecting two identical faults affect different parts during + * the HMAC compuation, which can be detected. + * + * @param key HMAC key. + * @param msg Input message. + * @param msg_len Message length in bytes. + * @param[out] tag Authentication tag (`kHmacSha384DigestWords` bytes). + * @return OK or error. + */ +OT_WARN_UNUSED_RESULT +status_t hmac_hmac_sha384_redundant(const hmac_key_t *key, const uint8_t *msg, + size_t msg_len, uint32_t *tag); + /** * One-shot HMAC-SHA512 hash computation. * * The key should be pre-processed into a buffer the size of a full message * block, according to FIPS 198-1, section 4. * - * @param key_block Input key block (`kHmacSha512BlockWords` words). + * @param key HMAC key. * @param msg Input message. * @param msg_len Message length in bytes. * @param[out] tag Authentication tag (`kHmacSha512DigestWords` bytes). * @return OK or error. */ OT_WARN_UNUSED_RESULT -status_t hmac_hmac_sha512(const uint32_t *key_block, const uint8_t *msg, +status_t hmac_hmac_sha512(const hmac_key_t *key, const uint8_t *msg, size_t msg_len, uint32_t *tag); +/** + * Redundant implementation for a one-shot HMAC-SHA512 hash computation. + * + * The key should be pre-processed into a buffer the size of a full message + * block, according to FIPS 198-1, section 4. + * + * To be used together with hmac_hmac_sha512() to mitigate FI attacks. This + * implementation is different to hmac_hmac_sha512() as it manually assembles + * the HMAC functionality using SHA512. By using two different HMAC + * implementations, injecting two identical faults affect different parts during + * the HMAC compuation, which can be detected. + * + * @param key HMAC key. + * @param msg Input message. + * @param msg_len Message length in bytes. + * @param[out] tag Authentication tag (`kHmacSha512DigestWords` bytes). + * @return OK or error. + */ +OT_WARN_UNUSED_RESULT +status_t hmac_hmac_sha512_redundant(const hmac_key_t *key, const uint8_t *msg, + size_t msg_len, uint32_t *tag); + /** * Initializes the context for a streaming SHA256 hash computation. * @@ -181,10 +264,10 @@ void hmac_hash_sha512_init(hmac_ctx_t *ctx); * The key should be pre-processed into a buffer the size of a full message * block, according to FIPS 198-1, section 4. * - * @param key_block Input key block (`kHmacSha256BlockWords` words). + * @param key The key used for HMAC. * @param[out] ctx Initialized context object. */ -void hmac_hmac_sha256_init_cl(const uint32_t *key_block, hmac_ctx_t *ctx); +void hmac_hmac_sha256_init_cl(const hmac_key_t key, hmac_ctx_t *ctx); /** * Initializes the context for a streaming HMAC-SHA384 computation. @@ -192,10 +275,10 @@ void hmac_hmac_sha256_init_cl(const uint32_t *key_block, hmac_ctx_t *ctx); * The key should be pre-processed into a buffer the size of a full message * block, according to FIPS 198-1, section 4. * - * @param key_block Input key block (`kHmacSha384BlockWords` words). + * @param key The key used for HMAC. * @param[out] ctx Initialized context object. */ -void hmac_hmac_sha384_init(const uint32_t *key_block, hmac_ctx_t *ctx); +void hmac_hmac_sha384_init(const hmac_key_t key, hmac_ctx_t *ctx); /** * Initializes the context for a streaming HMAC-SHA512 computation. @@ -203,10 +286,31 @@ void hmac_hmac_sha384_init(const uint32_t *key_block, hmac_ctx_t *ctx); * The key should be pre-processed into a buffer the size of a full message * block, according to FIPS 198-1, section 4. * - * @param key_block Input key block (`kHmacSha512BlockWords` words). + * @param key The key used for HMAC. * @param[out] ctx Initialized context object. */ -void hmac_hmac_sha512_init(const uint32_t *key_block, hmac_ctx_t *ctx); +void hmac_hmac_sha512_init(const hmac_key_t key, hmac_ctx_t *ctx); + +/** + * Compute the checksum of an HMAC key. + * + * Call this routine after creating or modifying the HMAC key structure. + * + * @param key HMAC key. + * @returns Checksum value. + */ +uint32_t hmac_key_integrity_checksum(const hmac_key_t *key); + +/** + * Perform an integrity check on the HMAC key. + * + * Returns `kHardenedBoolTrue` if the check passed and `kHardenedBoolFalse` + * otherwise. + * + * @param key HMAC key. + * @returns Whether the integrity check passed. + */ +hardened_bool_t hmac_key_integrity_checksum_check(const hmac_key_t *key); /** * Update the context with additional messsage data. diff --git a/sw/device/lib/crypto/drivers/keymgr.c b/sw/device/lib/crypto/drivers/keymgr.c index c672de49bd9b6..5e7869259d0d9 100644 --- a/sw/device/lib/crypto/drivers/keymgr.c +++ b/sw/device/lib/crypto/drivers/keymgr.c @@ -6,6 +6,7 @@ #include "sw/device/lib/base/abs_mmio.h" #include "sw/device/lib/base/bitfield.h" +#include "sw/device/lib/base/hardened_memory.h" #include "sw/device/lib/base/memory.h" #include "sw/device/lib/crypto/drivers/entropy.h" #include "sw/device/lib/crypto/impl/status.h" @@ -152,18 +153,17 @@ status_t keymgr_generate_key_sw(keymgr_diversification_t diversification, keymgr_start(diversification); HARDENED_TRY(keymgr_wait_until_done()); - // Collect output. - // TODO: for SCA hardening, randomize the order of these reads. - for (size_t i = 0; i < kKeymgrOutputShareNumWords; i++) { - key->share0[i] = - abs_mmio_read32(kBaseAddr + KEYMGR_SW_SHARE0_OUTPUT_0_REG_OFFSET + - (i * sizeof(uint32_t))); - } - for (size_t i = 0; i < kKeymgrOutputShareNumWords; i++) { - key->share1[i] = - abs_mmio_read32(kBaseAddr + KEYMGR_SW_SHARE1_OUTPUT_0_REG_OFFSET + - (i * sizeof(uint32_t))); - } + // Collect the output. To avoid side-channel lekage, first randomize the + // destination buffers using memshred. Then copy the key using a hardened + // memcpy. + uint32_t share0 = kBaseAddr + KEYMGR_SW_SHARE0_OUTPUT_0_REG_OFFSET; + uint32_t share1 = kBaseAddr + KEYMGR_SW_SHARE1_OUTPUT_0_REG_OFFSET; + HARDENED_TRY(hardened_memshred(key->share0, kKeymgrOutputShareNumWords)); + HARDENED_TRY(hardened_memcpy(key->share0, (uint32_t *)share0, + kKeymgrOutputShareNumWords)); + HARDENED_TRY(hardened_memshred(key->share1, kKeymgrOutputShareNumWords)); + HARDENED_TRY(hardened_memcpy(key->share1, (uint32_t *)share1, + kKeymgrOutputShareNumWords)); return OTCRYPTO_OK; } diff --git a/sw/device/lib/crypto/drivers/kmac.c b/sw/device/lib/crypto/drivers/kmac.c index 988e9c070fde3..d6e5070f26df0 100644 --- a/sw/device/lib/crypto/drivers/kmac.c +++ b/sw/device/lib/crypto/drivers/kmac.c @@ -525,6 +525,9 @@ static status_t kmac_init(kmac_operation_t operation, * * If the key is hardware-backed, this is a no-op. * + * Uses hardening primitives internally that consume entropy; the caller must + * ensure the entropy complex is up before calling. + * * @param key The input key passed as a struct. * @return Error code. */ @@ -546,8 +549,19 @@ static status_t kmac_write_key_block(kmac_blinded_key_t *key) { KMAC_KEY_LEN_REG_RESVAL, KMAC_KEY_LEN_LEN_FIELD, key_len_enum); abs_mmio_write32(kKmacBaseAddr + KMAC_KEY_LEN_REG_OFFSET, key_len_reg); + // Write random words to the key registers first for SCA defense. + for (size_t i = 0; i * sizeof(uint32_t) < key->len; i++) { + abs_mmio_write32(kKmacKeyShare0Addr + i * sizeof(uint32_t), + ibex_rnd32_read()); + } for (size_t i = 0; i * sizeof(uint32_t) < key->len; i++) { abs_mmio_write32(kKmacKeyShare0Addr + i * sizeof(uint32_t), key->share0[i]); + } + for (size_t i = 0; i * sizeof(uint32_t) < key->len; i++) { + abs_mmio_write32(kKmacKeyShare1Addr + i * sizeof(uint32_t), + ibex_rnd32_read()); + } + for (size_t i = 0; i * sizeof(uint32_t) < key->len; i++) { abs_mmio_write32(kKmacKeyShare1Addr + i * sizeof(uint32_t), key->share1[i]); } diff --git a/sw/device/lib/crypto/drivers/otbn.h b/sw/device/lib/crypto/drivers/otbn.h index d19a396b98afc..becb1f4ddb7e0 100644 --- a/sw/device/lib/crypto/drivers/otbn.h +++ b/sw/device/lib/crypto/drivers/otbn.h @@ -15,6 +15,27 @@ extern "C" { #endif +/** + * Hardened version of the `TRY` macro that wipes DMEM() on an error. + * + * Returns an error if either expr_ represents an error, or if the OK code does + * not match the expected hardened value. + * + * @param expr_ An expression that evaluates to a `status_t`. + * @return The enclosed OK value. + */ +#define HARDENED_TRY_WIPE_DMEM(expr_) \ + do { \ + status_t status_ = expr_; \ + if (launder32(OT_UNSIGNED(status_.value)) != kHardenedBoolTrue) { \ + otbn_dmem_sec_wipe(); \ + return (status_t){ \ + .value = (int32_t)(OT_UNSIGNED(status_.value) | 0x80000000)}; \ + } \ + HARDENED_CHECK_EQ(status_.value, kHardenedBoolTrue); \ + status_.value; \ + } while (false) + /** * Constants related to OTBN wide words */ diff --git a/sw/device/lib/crypto/drivers/rv_core_ibex.c b/sw/device/lib/crypto/drivers/rv_core_ibex.c index c4a8a38991968..5c3977c139ff6 100644 --- a/sw/device/lib/crypto/drivers/rv_core_ibex.c +++ b/sw/device/lib/crypto/drivers/rv_core_ibex.c @@ -6,12 +6,19 @@ #include "sw/device/lib/base/abs_mmio.h" #include "sw/device/lib/base/bitfield.h" +#include "sw/device/lib/base/csr.h" +#include "sw/device/lib/base/hardened.h" +#include "sw/device/lib/crypto/impl/status.h" #include "hw/top_earlgrey/sw/autogen/top_earlgrey.h" #include "rv_core_ibex_regs.h" enum { kBaseAddr = TOP_EARLGREY_RV_CORE_IBEX_CFG_BASE_ADDR, + /** + * CSR_REG_CPUCTRL[0] is the iCache configuration field. + */ + kCpuctrlICacheMask = 1, }; /** @@ -27,6 +34,35 @@ static void wait_rnd_valid(void) { } } +status_t ibex_disable_icache(hardened_bool_t *icache_enabled) { + // Check if the instruction cache is already disabled. + uint32_t csr; + CSR_READ(CSR_REG_CPUCTRL, &csr); + if ((csr & kCpuctrlICacheMask) == 1) { + *icache_enabled = kHardenedBoolTrue; + } else { + *icache_enabled = kHardenedBoolFalse; + HARDENED_CHECK_EQ(launder32((csr & kCpuctrlICacheMask)), 0); + } + + // If the instruction cache is enabled, disable it. + if (*icache_enabled == kHardenedBoolTrue) { + CSR_CLEAR_BITS(CSR_REG_CPUCTRL, kCpuctrlICacheMask); + } else { + HARDENED_CHECK_EQ(launder32(*icache_enabled), kHardenedBoolFalse); + } + + return OTCRYPTO_OK; +} + +void ibex_restore_icache(hardened_bool_t icache_enabled) { + // If the instruction cache was enabled before the CL disabled it, enable it + // again. + if (icache_enabled == kHardenedBoolTrue) { + CSR_SET_BITS(CSR_REG_CPUCTRL, kCpuctrlICacheMask); + } +} + uint32_t ibex_rnd32_read(void) { wait_rnd_valid(); return abs_mmio_read32(kBaseAddr + RV_CORE_IBEX_RND_DATA_REG_OFFSET); diff --git a/sw/device/lib/crypto/drivers/rv_core_ibex.h b/sw/device/lib/crypto/drivers/rv_core_ibex.h index 5fe43bdc472fd..68e64a01ab7e2 100644 --- a/sw/device/lib/crypto/drivers/rv_core_ibex.h +++ b/sw/device/lib/crypto/drivers/rv_core_ibex.h @@ -8,6 +8,29 @@ #include #include +#include "sw/device/lib/base/hardened.h" +#include "sw/device/lib/crypto/impl/status.h" + +/** + * Disable the Ibex Instruction Cache if it is enabled. + * + * Reads out the current state of the instruction cache. If it is enabled, + * disable it for the crypto lib. + * + * @param[out] icache_enabled kHardenedBoolTrue if the iCache was enabled before + * we disabled it. + * @return Error status. + */ +status_t ibex_disable_icache(hardened_bool_t *icache_enabled); + +/** + * Enables the Ibex Instruction Cache if icache_enabled is set. + * + * If icache_enabled == kHardenedBoolTrue, this function enables the iCache by + * writing to CPUCTRL. + */ +void ibex_restore_icache(hardened_bool_t icache_enabled); + /** * Get random data from the EDN0 interface. * diff --git a/sw/device/lib/crypto/impl/BUILD b/sw/device/lib/crypto/impl/BUILD index 1df16b901e749..ec5bf193e7916 100644 --- a/sw/device/lib/crypto/impl/BUILD +++ b/sw/device/lib/crypto/impl/BUILD @@ -195,8 +195,10 @@ cc_library( ":integrity", ":keyblob", ":status", + "//sw/device/lib/base:hardened_memory", "//sw/device/lib/base:math", "//sw/device/lib/crypto/drivers:kmac", + "//sw/device/lib/crypto/drivers:rv_core_ibex", "//sw/device/lib/crypto/include:datatypes", ], ) @@ -301,6 +303,7 @@ cc_library( ":sha2", "//sw/device/lib/base:hardened", "//sw/device/lib/base:hardened_memory", + "//sw/device/lib/crypto/drivers:entropy", "//sw/device/lib/crypto/drivers:hmac", "//sw/device/lib/crypto/drivers:rv_core_ibex", ], diff --git a/sw/device/lib/crypto/impl/aes.c b/sw/device/lib/crypto/impl/aes.c index b3396b37b866f..adeeda195dad6 100644 --- a/sw/device/lib/crypto/impl/aes.c +++ b/sw/device/lib/crypto/impl/aes.c @@ -82,25 +82,40 @@ static status_t aes_key_construct(otcrypto_blinded_key_t *blinded_key, } // Set the block cipher mode based on the key mode. + otcrypto_key_mode_t blinded_key_mode_used = launder32(0); switch (blinded_key->config.key_mode) { case kOtcryptoKeyModeAesEcb: aes_key->mode = kAesCipherModeEcb; + blinded_key_mode_used = + launder32(blinded_key_mode_used) | kOtcryptoKeyModeAesEcb; break; case kOtcryptoKeyModeAesCbc: - aes_key->mode = kAesCipherModeCbc; + aes_key->mode = launder32(kAesCipherModeCbc); + blinded_key_mode_used = + launder32(blinded_key_mode_used) | kOtcryptoKeyModeAesCbc; break; case kOtcryptoKeyModeAesCfb: - aes_key->mode = kAesCipherModeCfb; + aes_key->mode = launder32(kAesCipherModeCfb); + blinded_key_mode_used = + launder32(blinded_key_mode_used) | kOtcryptoKeyModeAesCfb; break; case kOtcryptoKeyModeAesOfb: - aes_key->mode = kAesCipherModeOfb; + aes_key->mode = launder32(kAesCipherModeOfb); + blinded_key_mode_used = + launder32(blinded_key_mode_used) | kOtcryptoKeyModeAesOfb; break; case kOtcryptoKeyModeAesCtr: - aes_key->mode = kAesCipherModeCtr; + aes_key->mode = launder32(kAesCipherModeCtr); + blinded_key_mode_used = + launder32(blinded_key_mode_used) | kOtcryptoKeyModeAesCtr; break; default: return OTCRYPTO_BAD_ARGS; } + // Check if we landed in the correct case statement. Use ORs for this to + // avoid that multiple cases were executed. + HARDENED_CHECK_EQ(launder32(blinded_key_mode_used), + blinded_key->config.key_mode); // Check that the key mode matches the requested block cipher mode. if (memcmp(&aes_key->mode, &aes_mode, sizeof(aes_key->mode)) != 0) { @@ -111,6 +126,11 @@ static status_t aes_key_construct(otcrypto_blinded_key_t *blinded_key, // Set the AES key length (in words). aes_key->key_len = keyblob_share_num_words(blinded_key->config); + if (aes_key->sideload == kHardenedBoolFalse) { + // Create the checksum of the key and store it in the key structure. + aes_key->checksum = aes_key_integrity_checksum(aes_key); + } + return OTCRYPTO_OK; } @@ -140,18 +160,19 @@ static status_t aes_padding_apply(otcrypto_aes_padding_t padding_mode, // Pad a full block if the last block is full, otherwise just fill the last // block. size_t padding_len = kAesBlockNumBytes - partial_data_len; - hardened_bool_t padding_written = kHardenedBoolFalse; + otcrypto_aes_padding_t padding_written = launder32(0); switch (launder32(padding_mode)) { case kOtcryptoAesPaddingPkcs7: // Pads with value same as the number of padding bytes. memset(padding, (uint8_t)padding_len, padding_len); - padding_written = kHardenedBoolTrue; + padding_written = launder32(padding_written) | kOtcryptoAesPaddingPkcs7; break; case kOtcryptoAesPaddingIso9797M2: // Pads with 0x80 (0b10000000), followed by zero bytes. memset(padding, 0x0, padding_len); padding[0] = 0x80; - padding_written = kHardenedBoolTrue; + padding_written = + launder32(padding_written) | kOtcryptoAesPaddingIso9797M2; break; case kOtcryptoAesPaddingNull: // This routine should not be called if padding is not needed. @@ -160,7 +181,9 @@ static status_t aes_padding_apply(otcrypto_aes_padding_t padding_mode, // Unrecognized padding mode. return OTCRYPTO_BAD_ARGS; } - HARDENED_CHECK_EQ(padding_written, kHardenedBoolTrue); + // Check if we landed in the correct case statement. Use ORs for this to + // avoid that multiple cases were executed. + HARDENED_CHECK_EQ(launder32(padding_written), padding_mode); return OTCRYPTO_OK; } @@ -197,6 +220,9 @@ static status_t num_padded_blocks_get(size_t plaintext_len, /** * Return the block at index i for the given input and padding mode. * + * Uses hardening primitives internally that consume entropy; check the entropy + * complex is up before calling. + * * @param input Input data buffer. * @param padding Padding mode. * @param index Block index. @@ -214,6 +240,9 @@ static status_t get_block(otcrypto_const_byte_buf_t input, // padding. HARDENED_CHECK_LE(index, num_full_blocks + 1); + // Randomize the destination buffer. + HARDENED_TRY(hardened_memshred(block->data, ARRAYSIZE(block->data))); + if (launder32(index) < num_full_blocks) { HARDENED_CHECK_LT(index, num_full_blocks); // No need to worry about padding, just copy the data into the output @@ -245,22 +274,30 @@ otcrypto_status_t otcrypto_aes_padded_plaintext_length( return OTCRYPTO_OK; } -otcrypto_status_t otcrypto_aes(otcrypto_blinded_key_t *key, - otcrypto_word32_buf_t iv, - otcrypto_aes_mode_t aes_mode, - otcrypto_aes_operation_t aes_operation, - otcrypto_const_byte_buf_t cipher_input, - otcrypto_aes_padding_t aes_padding, - otcrypto_byte_buf_t cipher_output) { +/** + * Performs the AES operation. + * + * @param key Pointer to the blinded key struct with key shares. + * @param iv Initialization vector, used for CBC, CFB, OFB, CTR modes. May be + * NULL if mode is ECB. + * @param aes_mode Required AES mode of operation. + * @param aes_operation Required AES operation (encrypt or decrypt). + * @param cipher_input Input data to be ciphered. + * @param aes_padding Padding scheme to be used for the data. + * @param[out] cipher_output Output data after cipher operation. + * @return The result of the cipher operation. + */ +static otcrypto_status_t otcrypto_aes_impl( + otcrypto_blinded_key_t *key, otcrypto_word32_buf_t iv, + otcrypto_aes_mode_t aes_mode, otcrypto_aes_operation_t aes_operation, + otcrypto_const_byte_buf_t cipher_input, otcrypto_aes_padding_t aes_padding, + otcrypto_byte_buf_t cipher_output) { // Check for NULL pointers in input pointers and data buffers. if (key == NULL || (aes_mode != kOtcryptoAesModeEcb && iv.data == NULL) || cipher_input.data == NULL || cipher_output.data == NULL) { return OTCRYPTO_BAD_ARGS; } - // Ensure the entropy complex is initialized. - HARDENED_TRY(entropy_complex_check()); - // Calculate the number of blocks for the input, including the padding for // encryption. size_t input_nblocks; @@ -299,7 +336,7 @@ otcrypto_status_t otcrypto_aes(otcrypto_blinded_key_t *key, return OTCRYPTO_BAD_ARGS; } HARDENED_CHECK_EQ(iv.len, kAesBlockNumWords); - hardened_memcpy(aes_iv.data, iv.data, kAesBlockNumWords); + HARDENED_TRY(hardened_memcpy(aes_iv.data, iv.data, kAesBlockNumWords)); } // Parse the AES key. @@ -307,16 +344,24 @@ otcrypto_status_t otcrypto_aes(otcrypto_blinded_key_t *key, HARDENED_TRY(aes_key_construct(key, aes_mode, &aes_key)); // Start the operation (encryption or decryption). + otcrypto_aes_operation_t aes_operation_started = launder32(0); switch (aes_operation) { case kOtcryptoAesOperationEncrypt: HARDENED_TRY(aes_encrypt_begin(aes_key, &aes_iv)); + aes_operation_started = + launder32(aes_operation_started) | kOtcryptoAesOperationEncrypt; break; case kOtcryptoAesOperationDecrypt: HARDENED_TRY(aes_decrypt_begin(aes_key, &aes_iv)); + aes_operation_started = + launder32(aes_operation_started) | kOtcryptoAesOperationDecrypt; break; default: return OTCRYPTO_BAD_ARGS; } + // Check if we landed in the correct case statement. Use ORs for this to + // avoid that multiple cases were executed. + HARDENED_CHECK_EQ(launder32(aes_operation_started), aes_operation); // Perform the cipher operation for all full blocks. The input and output are // offset by `block_offset` number of blocks, where `block_offset` can be 1 @@ -352,7 +397,7 @@ otcrypto_status_t otcrypto_aes(otcrypto_blinded_key_t *key, // cipher. for (i = 0; launder32(i) < block_offset; ++i) { HARDENED_TRY(get_block(cipher_input, aes_padding, i, &block_in)); - TRY(aes_update(/*dest=*/NULL, &block_in)); + HARDENED_TRY(aes_update(/*dest=*/NULL, &block_in)); } // Check that the loop ran for the correct number of iterations. HARDENED_CHECK_EQ(i, block_offset); @@ -361,7 +406,8 @@ otcrypto_status_t otcrypto_aes(otcrypto_blinded_key_t *key, // output buffer. for (i = block_offset; launder32(i) < input_nblocks; ++i) { HARDENED_TRY(get_block(cipher_input, aes_padding, i, &block_in)); - TRY(aes_update(&block_out, &block_in)); + HARDENED_TRY(hardened_memshred(block_out.data, ARRAYSIZE(block_out.data))); + HARDENED_TRY(aes_update(&block_out, &block_in)); // TODO(#17711) Change to `hardened_memcpy`. memcpy(&cipher_output.data[(i - block_offset) * kAesBlockNumBytes], block_out.data, kAesBlockNumBytes); @@ -385,12 +431,86 @@ otcrypto_status_t otcrypto_aes(otcrypto_blinded_key_t *key, HARDENED_TRY(aes_end(NULL)); } else { HARDENED_TRY(aes_end(&aes_iv)); - hardened_memcpy(iv.data, aes_iv.data, kAesBlockNumWords); + HARDENED_TRY(hardened_memcpy(iv.data, aes_iv.data, kAesBlockNumWords)); + } + + // In case the key was sideloaded, clear it. + return keymgr_sideload_clear_aes(); +} + +otcrypto_status_t otcrypto_aes(otcrypto_blinded_key_t *key, + otcrypto_word32_buf_t iv, + otcrypto_aes_mode_t aes_mode, + otcrypto_aes_operation_t aes_operation, + otcrypto_const_byte_buf_t cipher_input, + otcrypto_aes_padding_t aes_padding, + otcrypto_byte_buf_t cipher_output) { + // Check for NULL pointers in input pointers and data buffers. + if (key == NULL || (aes_mode != kOtcryptoAesModeEcb && iv.data == NULL) || + cipher_input.data == NULL || cipher_output.data == NULL) { + return OTCRYPTO_BAD_ARGS; } - // If the key was sideloaded, clear it. - if (key->config.hw_backed == kHardenedBoolTrue) { - HARDENED_TRY(keymgr_sideload_clear_aes()); + // Ensure the entropy complex is initialized. + HARDENED_TRY(entropy_complex_check()); + + if (launder32(key->config.security_level) == kOtcryptoKeySecurityLevelLow) { + HARDENED_CHECK_EQ(key->config.security_level, kOtcryptoKeySecurityLevelLow); + // No additional FI protection. + return otcrypto_aes_impl(key, iv, aes_mode, aes_operation, cipher_input, + aes_padding, cipher_output); + } else { + HARDENED_CHECK_NE(key->config.security_level, kOtcryptoKeySecurityLevelLow); + // Protect the AES computation against faults. Recomputes the ciphertext or + // plaintexts after the actual AES operation and compares it to the input. + + // Copy the IV for the second AES computation. + uint32_t iv_data[iv.len]; + memcpy(iv_data, iv.data, sizeof(iv_data)); + otcrypto_word32_buf_t iv_redundant = { + .data = iv_data, + .len = iv.len, + }; + + // First AES operation using the intended AES mode (encryption or + // decryption). + HARDENED_TRY(otcrypto_aes_impl(key, iv, aes_mode, aes_operation, + cipher_input, aes_padding, cipher_output)); + + // Second AES operation using the counterpart of the AES mode (decryption or + // encryption). + otcrypto_aes_operation_t aes_operation_inverse; + size_t len_bytes; + if (aes_operation == kOtcryptoAesOperationEncrypt) { + aes_operation_inverse = kOtcryptoAesOperationDecrypt; + len_bytes = cipher_output.len; + } else { + aes_operation_inverse = kOtcryptoAesOperationEncrypt; + TRY(otcrypto_aes_padded_plaintext_length(cipher_output.len, aes_padding, + &len_bytes)); + } + + // Create the input buffer that contains the cipher_output of the first AES + // operation. + otcrypto_const_byte_buf_t cipher_input_redundant = { + .data = cipher_output.data, + .len = cipher_output.len, + }; + // Create the output buffer. + uint32_t output_buf[len_bytes / sizeof(uint32_t)]; + otcrypto_byte_buf_t cipher_input_recomputed = { + .data = (unsigned char *)output_buf, + .len = len_bytes, + }; + HARDENED_TRY(otcrypto_aes_impl( + key, iv_redundant, aes_mode, aes_operation_inverse, + cipher_input_redundant, aes_padding, cipher_input_recomputed)); + + // Comparison. + HARDENED_CHECK_EQ( + hardened_memeq((const uint32_t *)cipher_input.data, output_buf, + cipher_input.len / sizeof(uint32_t)), + kHardenedBoolTrue); } return OTCRYPTO_OK; diff --git a/sw/device/lib/crypto/impl/aes_gcm.c b/sw/device/lib/crypto/impl/aes_gcm.c index e229dc955bd7f..f797ce6fcf75a 100644 --- a/sw/device/lib/crypto/impl/aes_gcm.c +++ b/sw/device/lib/crypto/impl/aes_gcm.c @@ -47,11 +47,12 @@ enum { * * @param internal_ctx Internal context object to save. * @param[out] api_ctx Resulting API-facing context object. + * @return Result of the operation. */ -static inline void gcm_context_save(aes_gcm_context_t *internal_ctx, - otcrypto_aes_gcm_context_t *api_ctx) { - hardened_memcpy(api_ctx->data, (uint32_t *)internal_ctx, - kAesGcmContextNumWords); +static inline status_t gcm_context_save(aes_gcm_context_t *internal_ctx, + otcrypto_aes_gcm_context_t *api_ctx) { + return hardened_memcpy(api_ctx->data, (uint32_t *)internal_ctx, + kAesGcmContextNumWords); } /** @@ -59,11 +60,12 @@ static inline void gcm_context_save(aes_gcm_context_t *internal_ctx, * * @param api_ctx API-facing context object to restore from. * @param[out] internal_ctx Resulting internal context object. + * @return Result of the operation. */ -static inline void gcm_context_restore(otcrypto_aes_gcm_context_t *api_ctx, - aes_gcm_context_t *internal_ctx) { - hardened_memcpy((uint32_t *)internal_ctx, api_ctx->data, - kAesGcmContextNumWords); +static inline status_t gcm_context_restore(otcrypto_aes_gcm_context_t *api_ctx, + aes_gcm_context_t *internal_ctx) { + return hardened_memcpy((uint32_t *)internal_ctx, api_ctx->data, + kAesGcmContextNumWords); } /** @@ -80,13 +82,17 @@ status_t gcm_remask_key(aes_gcm_context_t *internal_ctx) { // Generate a fresh mask the size of one share. uint32_t mask[internal_ctx->key.key_len]; - hardened_memshred(mask, internal_ctx->key.key_len); + HARDENED_TRY(hardened_memshred(mask, internal_ctx->key.key_len)); // XOR each share with the mask. - hardened_xor((uint32_t *)internal_ctx->key.key_shares[0], mask, - internal_ctx->key.key_len); - hardened_xor((uint32_t *)internal_ctx->key.key_shares[1], mask, - internal_ctx->key.key_len); + HARDENED_TRY( + hardened_xor_in_place((uint32_t *)internal_ctx->key.key_shares[0], mask, + internal_ctx->key.key_len)); + HARDENED_TRY( + hardened_xor_in_place((uint32_t *)internal_ctx->key.key_shares[1], mask, + internal_ctx->key.key_len)); + // Update the checksum. + internal_ctx->key.checksum = aes_key_integrity_checksum(&internal_ctx->key); } else { HARDENED_CHECK_EQ(internal_ctx->key.sideload, kHardenedBoolTrue); } @@ -163,6 +169,9 @@ static status_t aes_gcm_key_construct(otcrypto_blinded_key_t *blinded_key, } HARDENED_CHECK_EQ(aes_key->sideload, blinded_key->config.hw_backed); + // Create the checksum of the key and store it in the key structure. + aes_key->checksum = aes_key_integrity_checksum(aes_key); + return OTCRYPTO_OK; } @@ -176,27 +185,31 @@ static status_t aes_gcm_key_construct(otcrypto_blinded_key_t *blinded_key, status_t aes_gcm_check_tag_length(size_t word_len, otcrypto_aes_gcm_tag_len_t tag_len) { size_t bit_len = 0; + otcrypto_aes_gcm_tag_len_t tag_len_set = launder32(0); switch (launder32(tag_len)) { case kOtcryptoAesGcmTagLen128: - HARDENED_CHECK_EQ(tag_len, kOtcryptoAesGcmTagLen128); bit_len = 128; + tag_len_set = launder32(tag_len_set) | kOtcryptoAesGcmTagLen128; break; case kOtcryptoAesGcmTagLen96: - HARDENED_CHECK_EQ(tag_len, kOtcryptoAesGcmTagLen96); bit_len = 96; + tag_len_set = launder32(tag_len_set) | kOtcryptoAesGcmTagLen96; break; case kOtcryptoAesGcmTagLen64: - HARDENED_CHECK_EQ(tag_len, kOtcryptoAesGcmTagLen64); bit_len = 64; + tag_len_set = launder32(tag_len_set) | kOtcryptoAesGcmTagLen64; break; case kOtcryptoAesGcmTagLen32: - HARDENED_CHECK_EQ(tag_len, kOtcryptoAesGcmTagLen32); bit_len = 32; + tag_len_set = launder32(tag_len_set) | kOtcryptoAesGcmTagLen32; break; default: // Invalid tag length. return OTCRYPTO_BAD_ARGS; } + // Check if we landed in the correct case statement. Use ORs for this to + // avoid that multiple cases were executed. + HARDENED_CHECK_EQ(launder32(tag_len_set), tag_len); HARDENED_CHECK_GT(bit_len, 0); HARDENED_CHECK_EQ(bit_len % 32, 0); @@ -297,6 +310,10 @@ otcrypto_status_t otcrypto_aes_gcm_encrypt(otcrypto_blinded_key_t *key, // Check the tag length. HARDENED_TRY(aes_gcm_check_tag_length(auth_tag.len, tag_len)); + // Store the iCache state (on or off) and disable it when it is on. + hardened_bool_t icache_saved_state; + HARDENED_TRY(ibex_disable_icache(&icache_saved_state)); + // Construct the AES key. aes_key_t aes_key; HARDENED_TRY(aes_gcm_key_construct(key, &aes_key)); @@ -308,6 +325,10 @@ otcrypto_status_t otcrypto_aes_gcm_encrypt(otcrypto_blinded_key_t *key, auth_tag.data, ciphertext.data)); HARDENED_TRY(clear_key_if_sideloaded(aes_key)); + + // Enable the iCache if it was previously enabled. + ibex_restore_icache(icache_saved_state); + return OTCRYPTO_OK; } @@ -333,6 +354,10 @@ otcrypto_status_t otcrypto_aes_gcm_decrypt( // Ensure entropy complex is initialized. HARDENED_TRY(entropy_complex_check()); + // Store the iCache state (on or off) and disable it when it is on. + hardened_bool_t icache_saved_state; + HARDENED_TRY(ibex_disable_icache(&icache_saved_state)); + // Construct the AES key. aes_key_t aes_key; HARDENED_TRY(aes_gcm_key_construct(key, &aes_key)); @@ -353,6 +378,10 @@ otcrypto_status_t otcrypto_aes_gcm_decrypt( auth_tag.data, plaintext.data, success)); HARDENED_TRY(clear_key_if_sideloaded(aes_key)); + + // Enable the iCache if it was previously enabled. + ibex_restore_icache(icache_saved_state); + return OTCRYPTO_OK; } @@ -366,6 +395,10 @@ otcrypto_status_t otcrypto_aes_gcm_encrypt_init( // Ensure entropy complex is initialized. HARDENED_TRY(entropy_complex_check()); + // Store the iCache state (on or off) and disable it when it is on. + hardened_bool_t icache_saved_state; + HARDENED_TRY(ibex_disable_icache(&icache_saved_state)); + // Construct the AES key. aes_key_t aes_key; HARDENED_TRY(aes_gcm_key_construct(key, &aes_key)); @@ -373,11 +406,16 @@ otcrypto_status_t otcrypto_aes_gcm_encrypt_init( // Call the internal init operation. aes_gcm_context_t internal_ctx; + internal_ctx.security_level = key->config.security_level; HARDENED_TRY(aes_gcm_encrypt_init(aes_key, iv.len, iv.data, &internal_ctx)); // Save the context and clear the key if needed. - gcm_context_save(&internal_ctx, ctx); + HARDENED_TRY(gcm_context_save(&internal_ctx, ctx)); HARDENED_TRY(clear_key_if_sideloaded(internal_ctx.key)); + + // Enable the iCache if it was previously enabled. + ibex_restore_icache(icache_saved_state); + return OTCRYPTO_OK; } @@ -391,6 +429,10 @@ otcrypto_status_t otcrypto_aes_gcm_decrypt_init( // Ensure entropy complex is initialized. HARDENED_TRY(entropy_complex_check()); + // Store the iCache state (on or off) and disable it when it is on. + hardened_bool_t icache_saved_state; + HARDENED_TRY(ibex_disable_icache(&icache_saved_state)); + // Construct the AES key. aes_key_t aes_key; HARDENED_TRY(aes_gcm_key_construct(key, &aes_key)); @@ -398,11 +440,16 @@ otcrypto_status_t otcrypto_aes_gcm_decrypt_init( // Call the internal init operation. aes_gcm_context_t internal_ctx; + internal_ctx.security_level = key->config.security_level; HARDENED_TRY(aes_gcm_decrypt_init(aes_key, iv.len, iv.data, &internal_ctx)); // Save the context and clear the key if needed. - gcm_context_save(&internal_ctx, ctx); + HARDENED_TRY(gcm_context_save(&internal_ctx, ctx)); HARDENED_TRY(clear_key_if_sideloaded(internal_ctx.key)); + + // Enable the iCache if it was previously enabled. + ibex_restore_icache(icache_saved_state); + return OTCRYPTO_OK; } @@ -420,17 +467,25 @@ otcrypto_status_t otcrypto_aes_gcm_update_aad(otcrypto_aes_gcm_context_t *ctx, return OTCRYPTO_OK; } + // Store the iCache state (on or off) and disable it when it is on. + hardened_bool_t icache_saved_state; + HARDENED_TRY(ibex_disable_icache(&icache_saved_state)); + // Restore the AES-GCM context object and load the key if needed. aes_gcm_context_t internal_ctx; - gcm_context_restore(ctx, &internal_ctx); + HARDENED_TRY(gcm_context_restore(ctx, &internal_ctx)); HARDENED_TRY(load_key_if_sideloaded(internal_ctx.key)); // Call the internal update operation. HARDENED_TRY(aes_gcm_update_aad(&internal_ctx, aad.len, aad.data)); // Save the context and clear the key if needed. - gcm_context_save(&internal_ctx, ctx); + HARDENED_TRY(gcm_context_save(&internal_ctx, ctx)); HARDENED_TRY(clear_key_if_sideloaded(internal_ctx.key)); + + // Enable the iCache if it was previously enabled. + ibex_restore_icache(icache_saved_state); + return OTCRYPTO_OK; } @@ -451,9 +506,13 @@ otcrypto_status_t otcrypto_aes_gcm_update_encrypted_data( return OTCRYPTO_OK; } + // Store the iCache state (on or off) and disable it when it is on. + hardened_bool_t icache_saved_state; + HARDENED_TRY(ibex_disable_icache(&icache_saved_state)); + // Restore the AES-GCM context object and load the key if needed. aes_gcm_context_t internal_ctx; - gcm_context_restore(ctx, &internal_ctx); + HARDENED_TRY(gcm_context_restore(ctx, &internal_ctx)); HARDENED_TRY(load_key_if_sideloaded(internal_ctx.key)); // Remask the key if it is not sideloaded. HARDENED_TRY(gcm_remask_key(&internal_ctx)); @@ -476,8 +535,12 @@ otcrypto_status_t otcrypto_aes_gcm_update_encrypted_data( &internal_ctx, input.len, input.data, output_bytes_written, output.data)); // Save the context and clear the key if needed. - gcm_context_save(&internal_ctx, ctx); + HARDENED_TRY(gcm_context_save(&internal_ctx, ctx)); HARDENED_TRY(clear_key_if_sideloaded(internal_ctx.key)); + + // Enable the iCache if it was previously enabled. + ibex_restore_icache(icache_saved_state); + return OTCRYPTO_OK; } @@ -497,12 +560,16 @@ otcrypto_status_t otcrypto_aes_gcm_encrypt_final( // Ensure entropy complex is initialized. HARDENED_TRY(entropy_complex_check()); + // Store the iCache state (on or off) and disable it when it is on. + hardened_bool_t icache_saved_state; + HARDENED_TRY(ibex_disable_icache(&icache_saved_state)); + // Check the tag length. HARDENED_TRY(aes_gcm_check_tag_length(auth_tag.len, tag_len)); // Restore the AES-GCM context object and load the key if needed. aes_gcm_context_t internal_ctx; - gcm_context_restore(ctx, &internal_ctx); + HARDENED_TRY(gcm_context_restore(ctx, &internal_ctx)); HARDENED_TRY(load_key_if_sideloaded(internal_ctx.key)); // Remask the key if it is not sideloaded. HARDENED_TRY(gcm_remask_key(&internal_ctx)); @@ -520,8 +587,12 @@ otcrypto_status_t otcrypto_aes_gcm_encrypt_final( ciphertext.data)); // Clear the context and the key if needed. - hardened_memshred(ctx->data, ARRAYSIZE(ctx->data)); + HARDENED_TRY(hardened_memshred(ctx->data, ARRAYSIZE(ctx->data))); HARDENED_TRY(clear_key_if_sideloaded(internal_ctx.key)); + + // Enable the iCache if it was previously enabled. + ibex_restore_icache(icache_saved_state); + return OTCRYPTO_OK; } @@ -542,12 +613,16 @@ otcrypto_status_t otcrypto_aes_gcm_decrypt_final( // Entropy complex needs to be initialized for `memshred`. HARDENED_TRY(entropy_complex_check()); + // Store the iCache state (on or off) and disable it when it is on. + hardened_bool_t icache_saved_state; + HARDENED_TRY(ibex_disable_icache(&icache_saved_state)); + // Check the tag length. HARDENED_TRY(aes_gcm_check_tag_length(auth_tag.len, tag_len)); // Restore the AES-GCM context object and load the key if needed. aes_gcm_context_t internal_ctx; - gcm_context_restore(ctx, &internal_ctx); + HARDENED_TRY(gcm_context_restore(ctx, &internal_ctx)); HARDENED_TRY(load_key_if_sideloaded(internal_ctx.key)); // Remask the key if it is not sideloaded. HARDENED_TRY(gcm_remask_key(&internal_ctx)); @@ -565,7 +640,11 @@ otcrypto_status_t otcrypto_aes_gcm_decrypt_final( success)); // Clear the context and the key if needed. - hardened_memshred(ctx->data, ARRAYSIZE(ctx->data)); + HARDENED_TRY(hardened_memshred(ctx->data, ARRAYSIZE(ctx->data))); HARDENED_TRY(clear_key_if_sideloaded(internal_ctx.key)); + + // Enable the iCache if it was previously enabled. + ibex_restore_icache(icache_saved_state); + return OTCRYPTO_OK; } diff --git a/sw/device/lib/crypto/impl/aes_gcm/BUILD b/sw/device/lib/crypto/impl/aes_gcm/BUILD index 272575a4a18ad..1f67983f4a02a 100644 --- a/sw/device/lib/crypto/impl/aes_gcm/BUILD +++ b/sw/device/lib/crypto/impl/aes_gcm/BUILD @@ -15,6 +15,7 @@ cc_library( "//sw/device/lib/base:memory", "//sw/device/lib/crypto/drivers:aes", "//sw/device/lib/crypto/drivers:rv_core_ibex", + "//sw/device/lib/crypto/include:datatypes", ], ) @@ -23,8 +24,10 @@ cc_library( srcs = ["ghash.c"], hdrs = ["ghash.h"], deps = [ + "//sw/device/lib/base:hardened_memory", "//sw/device/lib/base:macros", "//sw/device/lib/base:memory", + "//sw/device/lib/crypto/drivers:rv_core_ibex", ], ) diff --git a/sw/device/lib/crypto/impl/aes_gcm/aes_gcm.c b/sw/device/lib/crypto/impl/aes_gcm/aes_gcm.c index b2f982cbf23d4..e97c4f21ee44b 100644 --- a/sw/device/lib/crypto/impl/aes_gcm/aes_gcm.c +++ b/sw/device/lib/crypto/impl/aes_gcm/aes_gcm.c @@ -61,13 +61,38 @@ static inline void block_inc32(aes_block_t *block) { * One-shot version of the AES encryption API for a single block. */ OT_WARN_UNUSED_RESULT -static status_t aes_encrypt_block(const aes_key_t key, const aes_block_t *iv, - const aes_block_t *input, - aes_block_t *output) { - HARDENED_TRY(aes_encrypt_begin(key, iv)); - HARDENED_TRY(aes_update(/*dest=*/NULL, input)); - HARDENED_TRY(aes_update(output, /*src=*/NULL)); - return aes_end(NULL); +static status_t aes_encrypt_block( + const aes_key_t key, const aes_block_t *iv, const aes_block_t *input, + aes_block_t *output, otcrypto_key_security_level_t security_level) { + if (launder32(security_level) == kOtcryptoKeySecurityLevelLow) { + HARDENED_CHECK_EQ(security_level, kOtcryptoKeySecurityLevelLow); + // No additional FI protection. + HARDENED_TRY(aes_encrypt_begin(key, iv)); + HARDENED_TRY(aes_update(/*dest=*/NULL, input)); + HARDENED_TRY(aes_update(output, /*src=*/NULL)); + return aes_end(NULL); + } else { + HARDENED_CHECK_NE(security_level, kOtcryptoKeySecurityLevelLow); + // Additional FI hardening. + // First AES operation. Encrypt the input. + HARDENED_TRY(aes_encrypt_begin(key, iv)); + HARDENED_TRY(aes_update(/*dest=*/NULL, input)); + HARDENED_TRY(aes_update(output, /*src=*/NULL)); + + // Second AES operation. Decrypt the output of the first AES operation and + // check whether the same input is retrieved. + aes_block_t input_recalculated = (aes_block_t){.data = {0}}; + HARDENED_TRY(aes_decrypt_begin(key, iv)); + HARDENED_TRY(aes_update(/*dest=*/NULL, output)); + HARDENED_TRY(aes_update(&input_recalculated, /*src=*/NULL)); + HARDENED_TRY(aes_end(NULL)); + + HARDENED_CHECK_EQ( + hardened_memeq((const uint32_t *)input_recalculated.data, + (const uint32_t *)input->data, kAesBlockNumWords), + kHardenedBoolTrue); + return OTCRYPTO_OK; + } } /** @@ -78,12 +103,15 @@ static status_t aes_encrypt_block(const aes_key_t key, const aes_block_t *iv, * @param key The AES key * @param iv Initialization vector, 128 bits * @param input Input block + * @param security_level Security level configuration * @param[out] output Output block */ OT_WARN_UNUSED_RESULT static status_t gctr_process_block(const aes_key_t key, aes_block_t *iv, - aes_block_t *input, aes_block_t *output) { - HARDENED_TRY(aes_encrypt_block(key, iv, input, output)); + aes_block_t *input, + otcrypto_key_security_level_t security_level, + aes_block_t *output) { + HARDENED_TRY(aes_encrypt_block(key, iv, input, output, security_level)); block_inc32(iv); return OTCRYPTO_OK; } @@ -114,6 +142,7 @@ static status_t gctr_process_block(const aes_key_t key, aes_block_t *iv, * @param partial Partial AES block. * @param input_len Number of bytes for input and output * @param input Pointer to input buffer (may be NULL if `len` is 0) + * @param security_level Security level of the key * @param[out] output_len Number of output bytes written * @param[out] output Pointer to output buffer */ @@ -121,6 +150,7 @@ OT_WARN_UNUSED_RESULT static status_t aes_gcm_gctr(const aes_key_t key, aes_block_t *iv, size_t partial_len, aes_block_t *partial, size_t input_len, const uint8_t *input, + otcrypto_key_security_level_t security_level, size_t *output_len, uint8_t *output) { // Key must be intended for CTR mode. if (key.mode != kAesCipherModeCtr) { @@ -131,6 +161,7 @@ static status_t aes_gcm_gctr(const aes_key_t key, aes_block_t *iv, // Not enough data for a full block; copy into the partial block. unsigned char *partial_bytes = (unsigned char *)partial->data; memcpy(partial_bytes + partial_len, input, input_len); + *output_len = 0; } else { // Construct a block from the partial data and the start of the new data. unsigned char *partial_bytes = (unsigned char *)partial->data; @@ -140,7 +171,8 @@ static status_t aes_gcm_gctr(const aes_key_t key, aes_block_t *iv, // Process the block. aes_block_t block_out; - HARDENED_TRY(gctr_process_block(key, iv, partial, &block_out)); + HARDENED_TRY( + gctr_process_block(key, iv, partial, security_level, &block_out)); memcpy(output, block_out.data, kAesBlockNumBytes); output += kAesBlockNumBytes; *output_len = kAesBlockNumBytes; @@ -148,7 +180,8 @@ static status_t aes_gcm_gctr(const aes_key_t key, aes_block_t *iv, // Process any remaining full blocks of input. while (input_len >= kAesBlockNumBytes) { memcpy(partial->data, input, kAesBlockNumBytes); - HARDENED_TRY(gctr_process_block(key, iv, partial, &block_out)); + HARDENED_TRY( + gctr_process_block(key, iv, partial, security_level, &block_out)); memcpy(output, block_out.data, kAesBlockNumBytes); output += kAesBlockNumBytes; *output_len += kAesBlockNumBytes; @@ -170,21 +203,37 @@ static status_t aes_gcm_gctr(const aes_key_t key, aes_block_t *iv, * output should not be used. * * @param key AES key + * @param security_level The security level of the key * @param[out] ctx Destination GHASH context object * @return OK or error */ OT_WARN_UNUSED_RESULT -static status_t aes_gcm_hash_subkey(const aes_key_t key, ghash_context_t *ctx) { +static status_t aes_gcm_hash_subkey( + const aes_key_t key, otcrypto_key_security_level_t security_level, + ghash_context_t *ctx) { // Compute the initial hash subkey H = AES_K(0). Note that to get this // result from AES_CTR, we set both the IV and plaintext to zero; this way, // AES-CTR's final XOR with the plaintext does nothing. aes_block_t zero; memset(zero.data, 0, kAesBlockNumBytes); aes_block_t hash_subkey; - HARDENED_TRY(aes_encrypt_block(key, &zero, &zero, &hash_subkey)); + HARDENED_TRY( + aes_encrypt_block(key, &zero, &zero, &hash_subkey, security_level)); + + // Create two shares of the hash subkey. + aes_block_t hash_subkey_share0; + aes_block_t hash_subkey_share1; + + // Share 0: random data. + hardened_memshred(hash_subkey_share0.data, kAesBlockNumWords); + + // Share 1: hash_subkey ^ hash_subkey_share0 + hardened_xor(hash_subkey_share0.data, hash_subkey.data, kAesBlockNumWords, + hash_subkey_share1.data); // Set the key for the GHASH context. - ghash_init_subkey(hash_subkey.data, ctx); + ghash_init_subkey(hash_subkey_share0.data, ctx->tbl0); + ghash_init_subkey(hash_subkey_share1.data, ctx->tbl1); return OTCRYPTO_OK; } @@ -206,12 +255,34 @@ static status_t aes_gcm_counter(const size_t iv_len, const uint32_t *iv, ghash_context_t *ctx, aes_block_t *j0) { if (iv_len == 3) { // If the IV is 96 bits, then J0 = (IV || {0}^31 || 1). - hardened_memcpy(j0->data, iv, iv_len); + HARDENED_TRY(hardened_memcpy(j0->data, iv, iv_len)); // Set the last word to 1 (as a big-endian integer). j0->data[kAesBlockNumWords - 1] = __builtin_bswap32(1); } else if (iv_len == 4) { // If the IV is 128 bits, then J0 = GHASH(H, IV || {0}^120 || 0x80), where // {0}^120 means 120 zero bits (15 0x00 bytes). + + // As the encrypted initial counter block S only can be computed AFTER + // aes_gcm_counter(), set S0 and S1 to random. + aes_block_t enc_initial_counter_block; + hardened_memshred(enc_initial_counter_block.data, kAesBlockNumWords); + + // Split the initial counter block S into two shares S0 and S1. + // S0: random data. + aes_block_t enc_initial_counter_block0; + hardened_memshred(enc_initial_counter_block0.data, kAesBlockNumWords); + + // S1: S ^ S0 + aes_block_t enc_initial_counter_block1; + hardened_xor(enc_initial_counter_block0.data, + enc_initial_counter_block.data, kAesBlockNumWords, + enc_initial_counter_block1.data); + + // Calculate the masking correction terms and store the encrypted initial + // counter blocks S0 and S1. + ghash_handle_enc_initial_counter_block( + enc_initial_counter_block0.data, enc_initial_counter_block1.data, ctx); + ghash_init(ctx); ghash_update(ctx, iv_len * sizeof(uint32_t), (unsigned char *)iv); uint8_t buffer[kAesBlockNumBytes]; @@ -219,6 +290,11 @@ static status_t aes_gcm_counter(const size_t iv_len, const uint32_t *iv, buffer[kAesBlockNumBytes - 1] = 0x80; ghash_update(ctx, kAesBlockNumBytes, buffer); ghash_final(ctx, j0->data); + // In the masking scheme, the GHASH function now actually XORs the initial + // counter block S to the output. As we do not want to have this for J0, + // correct the output. + hardened_xor_in_place(j0->data, enc_initial_counter_block.data, + kAesBlockNumWords); } else { // Should not happen; invalid IV length. return OTCRYPTO_BAD_ARGS; @@ -248,29 +324,16 @@ static status_t aes_gcm_get_tag(aes_gcm_context_t *ctx, size_t tag_len, __builtin_bswap64(((uint64_t)ctx->input_len) * 8), }; - // Finish computing S by appending (len64(A) || len64(C)). + // Append (len64(A) || len64(C)) and XOR the result with S1 to get the final + // tag. ghash_update(&ctx->ghash_ctx, kAesBlockNumBytes, (unsigned char *)last_block); - aes_block_t s; - ghash_final(&ctx->ghash_ctx, s.data); - - // Compute the tag T = GCTR(K, J0, S). - uint32_t full_tag[kAesBlockNumWords]; - size_t full_tag_len; - aes_block_t empty = {.data = {0}}; - HARDENED_TRY(aes_gcm_gctr(ctx->key, &ctx->initial_counter_block, - /*partial_len=*/0, &empty, kAesBlockNumBytes, - (unsigned char *)s.data, &full_tag_len, - (unsigned char *)full_tag)); - - // Sanity check. - if (full_tag_len != kAesBlockNumBytes) { - return OTCRYPTO_FATAL_ERR; - } + aes_block_t full_tag; + ghash_final(&ctx->ghash_ctx, full_tag.data); // Truncate the tag if needed. NIST requires we take the most significant // bits in big-endian representation, which corresponds to the least // significant bits in Ibex's little-endian representation. - hardened_memcpy(tag, full_tag, tag_len); + HARDENED_TRY(hardened_memcpy(tag, full_tag.data, tag_len)); return OTCRYPTO_OK; } @@ -310,12 +373,36 @@ static status_t aes_gcm_init(const aes_key_t key, const size_t iv_len, } // Initialize the hash subkey H. - HARDENED_TRY(aes_gcm_hash_subkey(key, &ctx->ghash_ctx)); + HARDENED_TRY(aes_gcm_hash_subkey(key, ctx->security_level, &ctx->ghash_ctx)); // Compute the counter block (called J0 in the NIST specification). HARDENED_TRY(aes_gcm_counter(iv_len, iv, &ctx->ghash_ctx, &ctx->initial_counter_block)); + // Create the encrypted initial counter block S. + aes_block_t zero; + memset(zero.data, 0, kAesBlockNumBytes); + aes_block_t enc_initial_counter_block; + HARDENED_TRY(aes_encrypt_block(key, &ctx->initial_counter_block, &zero, + &enc_initial_counter_block, + ctx->security_level)); + + // Split the initial counter block S into two shares S0 and S1. + // S0: random data. + aes_block_t enc_initial_counter_block0; + hardened_memshred(enc_initial_counter_block0.data, kAesBlockNumWords); + + // S1: S ^ S0 + aes_block_t enc_initial_counter_block1; + hardened_xor(enc_initial_counter_block0.data, enc_initial_counter_block.data, + kAesBlockNumWords, enc_initial_counter_block1.data); + + // Calculate the masking correction terms and store the encrypted initial + // counter blocks. + ghash_handle_enc_initial_counter_block(enc_initial_counter_block0.data, + enc_initial_counter_block1.data, + &ctx->ghash_ctx); + // Set the initial IV for GCTR to inc32(J0). // The eventual ciphertext is C = GCTR(K, inc32(J0), plaintext). memcpy(ctx->gctr_iv.data, ctx->initial_counter_block.data, kAesBlockNumBytes); @@ -413,7 +500,7 @@ status_t aes_gcm_update_encrypted_data(aes_gcm_context_t *ctx, size_t input_len, size_t partial_aes_block_len = ctx->input_len % kAesBlockNumBytes; HARDENED_TRY(aes_gcm_gctr(ctx->key, &ctx->gctr_iv, partial_aes_block_len, &ctx->partial_aes_block, input_len, input, - output_len, output)); + ctx->security_level, output_len, output)); // Accumulate any new ciphertext to the GHASH context. The ciphertext is the // output for encryption, and the input for decryption. @@ -478,7 +565,8 @@ status_t aes_gcm_final(aes_gcm_context_t *ctx, size_t tag_len, uint32_t *tag, kAesBlockNumBytes - partial_aes_block_len); aes_block_t block_out; HARDENED_TRY(gctr_process_block(ctx->key, &ctx->gctr_iv, - &ctx->partial_aes_block, &block_out)); + &ctx->partial_aes_block, + ctx->security_level, &block_out)); memcpy(output, block_out.data, partial_aes_block_len); *output_len = partial_aes_block_len; } diff --git a/sw/device/lib/crypto/impl/aes_gcm/aes_gcm.h b/sw/device/lib/crypto/impl/aes_gcm/aes_gcm.h index 80273ce5ade82..3c98608f84f7a 100644 --- a/sw/device/lib/crypto/impl/aes_gcm/aes_gcm.h +++ b/sw/device/lib/crypto/impl/aes_gcm/aes_gcm.h @@ -12,6 +12,7 @@ #include "sw/device/lib/base/macros.h" #include "sw/device/lib/crypto/drivers/aes.h" #include "sw/device/lib/crypto/impl/aes_gcm/ghash.h" +#include "sw/device/lib/crypto/include/datatypes.h" #ifdef __cplusplus extern "C" { @@ -29,6 +30,10 @@ typedef struct aes_gcm_context { * Underlying AES-CTR key. */ aes_key_t key; + /** + * Security level of the underlying AES-CTR key. + */ + otcrypto_key_security_level_t security_level; /** * Initial counter block (J0 in the spec). */ diff --git a/sw/device/lib/crypto/impl/aes_gcm/ghash.c b/sw/device/lib/crypto/impl/aes_gcm/ghash.c index 0d4426d328ba3..d3f510ebb25e6 100644 --- a/sw/device/lib/crypto/impl/aes_gcm/ghash.c +++ b/sw/device/lib/crypto/impl/aes_gcm/ghash.c @@ -4,6 +4,7 @@ #include "sw/device/lib/crypto/impl/aes_gcm/ghash.h" +#include "sw/device/lib/base/hardened_memory.h" #include "sw/device/lib/base/macros.h" #include "sw/device/lib/base/memory.h" @@ -143,11 +144,11 @@ static uint8_t reverse_bits(uint8_t byte) { return out; } -void ghash_init_subkey(const uint32_t *hash_subkey, ghash_context_t *ctx) { +void ghash_init_subkey(const uint32_t *hash_subkey, ghash_block_t *tbl) { // Initialize 0 * H = 0. - memset(ctx->tbl[0].data, 0, kGhashBlockNumBytes); + memset(tbl[0].data, 0, kGhashBlockNumBytes); // Initialize 1 * H = H. - memcpy(ctx->tbl[0x8].data, hash_subkey, kGhashBlockNumBytes); + memcpy(tbl[0x8].data, hash_subkey, kGhashBlockNumBytes); // To get remaining entries, we use a variant of "shift and add"; in // polynomial terms, a shift is a multiplication by x. Note that, because the @@ -157,16 +158,19 @@ void ghash_init_subkey(const uint32_t *hash_subkey, ghash_context_t *ctx) { for (uint8_t i = 2; i < 16; i += 2) { // Find the product corresponding to (i >> 1) * H and multiply by x to // shift 1; this will be i * H. - galois_mulx(&ctx->tbl[reverse_bits(i >> 1)], &ctx->tbl[reverse_bits(i)]); + galois_mulx(&tbl[reverse_bits(i >> 1)], &tbl[reverse_bits(i)]); // Add H to i * H to get (i + 1) * H. - block_xor(&ctx->tbl[reverse_bits(i)], &ctx->tbl[0x8], - &ctx->tbl[reverse_bits(i + 1)]); + block_xor(&tbl[reverse_bits(i)], &tbl[0x8], &tbl[reverse_bits(i + 1)]); } } void ghash_init(ghash_context_t *ctx) { - memset(ctx->state.data, 0, kGhashBlockNumBytes); + // Randomize the initial state. + hardened_memshred(ctx->state0.data, kGhashBlockNumWords); + hardened_memshred(ctx->state1.data, kGhashBlockNumWords); + // Initialize the ghash block counter. + ctx->ghash_block_cnt = 0; } /** @@ -182,9 +186,12 @@ void ghash_init(ghash_context_t *ctx) { * This operation corresponds to multiplication in the Galois field with order * 2^128, modulo the polynomial x^128 + x^8 + x^2 + x + 1 * - * @param ctx GHASH context, updated in place. + * @param state GHASH state. + * @param tbl Product table for the masked hash subkey. + * @return Multiplication of the state and the hash subkey. */ -static void galois_mul_state_key(ghash_context_t *ctx) { +static ghash_block_t galois_mul_state_key(ghash_block_t state, + ghash_block_t tbl[16]) { // Initialize the multiplication result to 0. ghash_block_t result; memset(result.data, 0, kGhashBlockNumBytes); @@ -217,7 +224,7 @@ static void galois_mul_state_key(ghash_context_t *ctx) { // Add the product of the next window and H to `result`. We process the // windows starting with the most significant polynomial terms, which means // starting from the last byte and proceeding to the first. - uint8_t tbl_index = block_byte_get(&ctx->state, (kNumWindows - 1 - i) >> 1); + uint8_t tbl_index = block_byte_get(&state, (kNumWindows - 1 - i) >> 1); // Select the less significant 4 bits if i is even, or the more significant // 4 bits if i is odd. This does not need to be constant time, since the @@ -227,10 +234,9 @@ static void galois_mul_state_key(ghash_context_t *ctx) { } else { tbl_index &= 0x0f; } - block_xor(&result, &ctx->tbl[tbl_index], &result); + block_xor(&result, &tbl[tbl_index], &result); } - - memcpy(ctx->state.data, result.data, kGhashBlockNumBytes); + return result; } /** @@ -240,10 +246,67 @@ static void galois_mul_state_key(ghash_context_t *ctx) { * @param block Block to incorporate. */ static void ghash_process_block(ghash_context_t *ctx, ghash_block_t *block) { - // XOR `state` with the next input block. - block_xor(&ctx->state, block, &ctx->state); - // Multiply state by H in-place. - galois_mul_state_key(ctx); + ghash_block_t s0_tmp; + ghash_block_t s1_tmp; + if (ctx->ghash_block_cnt == 0) { + // Process share 0. + // share0_tmp = (S0 + T0) * H0 + hardened_memcpy(s0_tmp.data, block->data, kGhashBlockNumWords); + hardened_xor_in_place(s0_tmp.data, ctx->enc_initial_counter_block0.data, + kGhashBlockNumWords); + s0_tmp = galois_mul_state_key(s0_tmp, ctx->tbl0); + + // Apply the correction terms for state share 0. + // share0 = share0_tmp + (S0*(H0+1)) + hardened_memcpy(ctx->state0.data, s0_tmp.data, kGhashBlockNumWords); + hardened_xor_in_place(ctx->state0.data, ctx->correction_term0.data, + kGhashBlockNumWords); + + // TODO(#28013): randomize register file content before processing the + // second share. + + // Process share 1. + // share1_tmp = (S1 + T0) * H1 + hardened_memcpy(s1_tmp.data, block->data, kGhashBlockNumWords); + hardened_xor_in_place(s1_tmp.data, ctx->enc_initial_counter_block1.data, + kGhashBlockNumWords); + s1_tmp = galois_mul_state_key(s1_tmp, ctx->tbl1); + + // Apply the correction terms for state share 1. + // share1 = share1_tmp + correction_term1 + hardened_memcpy(ctx->state1.data, s1_tmp.data, kGhashBlockNumWords); + hardened_xor_in_place(ctx->state1.data, ctx->correction_term1_init.data, + kGhashBlockNumWords); + } else { + // Process share 0. + // tmp = (share0+TN-1)+share1 + ghash_block_t tmp; + hardened_memcpy(tmp.data, block->data, kGhashBlockNumWords); + hardened_xor_in_place(tmp.data, ctx->state0.data, kGhashBlockNumWords); + hardened_xor_in_place(tmp.data, ctx->state1.data, kGhashBlockNumWords); + + // s0_tmp = tmp * H0 + s0_tmp = galois_mul_state_key(tmp, ctx->tbl0); + + // Apply the correction terms for state share 0. + // share0 = share0_tmp + (S0*(H0+1)) + hardened_memcpy(ctx->state0.data, s0_tmp.data, kGhashBlockNumWords); + hardened_xor_in_place(ctx->state0.data, ctx->correction_term0.data, + kGhashBlockNumWords); + + // Process share 1. + // share1_tmp = tmp * H1 + s1_tmp = galois_mul_state_key(tmp, ctx->tbl1); + + // Apply the correction terms for state share 1. + // share1 = share1_tmp + (S0*H0) + hardened_memcpy(ctx->state1.data, s1_tmp.data, kGhashBlockNumWords); + hardened_xor_in_place(ctx->state1.data, ctx->correction_term1.data, + kGhashBlockNumWords); + } + + // Increment the number of processed ghash block counter. + ctx->ghash_block_cnt++; } void ghash_process_full_blocks(ghash_context_t *ctx, size_t partial_len, @@ -293,6 +356,39 @@ void ghash_update(ghash_context_t *ctx, size_t input_len, } } +void ghash_handle_enc_initial_counter_block( + const uint32_t *enc_initial_counter_block0, + const uint32_t *enc_initial_counter_block1, ghash_context_t *ctx) { + // correction_term0 = S0 * (H0 + 1). + ghash_block_t s0; + hardened_memcpy(s0.data, enc_initial_counter_block0, kGhashBlockNumWords); + ghash_block_t mul_tmp = galois_mul_state_key(s0, ctx->tbl0); + block_xor(&mul_tmp, &s0, &ctx->correction_term0); + + // correction_term1 = S0 * H1. + ctx->correction_term1 = galois_mul_state_key(s0, ctx->tbl1); + + // correction_term1_init = S1 * H1. + ghash_block_t s1; + hardened_memcpy(s1.data, enc_initial_counter_block1, kGhashBlockNumWords); + ctx->correction_term1_init = galois_mul_state_key(s1, ctx->tbl1); + + // Save the encrypted initial counter blocks into the ghash context as we + // need them throughout the ghash computations. + hardened_memcpy(ctx->enc_initial_counter_block0.data, + enc_initial_counter_block0, kGhashBlockNumWords); + hardened_memcpy(ctx->enc_initial_counter_block1.data, + enc_initial_counter_block1, kGhashBlockNumWords); +} + void ghash_final(ghash_context_t *ctx, uint32_t *result) { - memcpy(result, ctx->state.data, kGhashBlockNumBytes); + // Tag = (state0 + state1) + S1 + ghash_block_t tmp_block; + ghash_block_t final_block; + hardened_xor(ctx->state0.data, ctx->state1.data, kGhashBlockNumWords, + tmp_block.data); + hardened_xor(tmp_block.data, ctx->enc_initial_counter_block1.data, + kGhashBlockNumWords, final_block.data); + + memcpy(result, final_block.data, kGhashBlockNumBytes); } diff --git a/sw/device/lib/crypto/impl/aes_gcm/ghash.h b/sw/device/lib/crypto/impl/aes_gcm/ghash.h index d7f05638c910e..688cd2c7a9d1a 100644 --- a/sw/device/lib/crypto/impl/aes_gcm/ghash.h +++ b/sw/device/lib/crypto/impl/aes_gcm/ghash.h @@ -32,13 +32,45 @@ typedef struct ghash_block { typedef struct ghash_context { /** - * Precomputed product table for the hash subkey. + * Precomputed product table for the hash subkey share 0. */ - ghash_block_t tbl[16]; + ghash_block_t tbl0[16]; /** - * Cipher block representing the current GHASH state. + * Precomputed product table for the hash subkey share 1. */ - ghash_block_t state; + ghash_block_t tbl1[16]; + /** + * Cipher block representing the current GHASH state for share 0. + */ + ghash_block_t state0; + /** + * Cipher block representing the current GHASH state for share 1. + */ + ghash_block_t state1; + /** + * Precomputed correction term (S0 * (H0+1)) for state share 0. + */ + ghash_block_t correction_term0; + /** + * Precomputed correction term (S0 * H1) for state share 1. + */ + ghash_block_t correction_term1; + /** + * Precomputed initial correction term (S1 * H1) for state share 1. + */ + ghash_block_t correction_term1_init; + /** + * Encrypted initial counter block share 0. + */ + ghash_block_t enc_initial_counter_block0; + /** + * Encrypted initial counter block share 1. + */ + ghash_block_t enc_initial_counter_block1; + /** + * Number of processed ghash blocks. + */ + size_t ghash_block_cnt; } ghash_context_t; /** @@ -55,9 +87,9 @@ typedef struct ghash_context { * * @param hash_subkey Subkey for the GHASH operation (`kGhashBlockNumWords` * words). - * @param[out] ctx Context object with product table populated. + * @param[out] tbl The populated product table. */ -void ghash_init_subkey(const uint32_t *hash_subkey, ghash_context_t *ctx); +void ghash_init_subkey(const uint32_t *hash_subkey, ghash_block_t *tbl); /** * Start a GHASH operation. @@ -106,6 +138,21 @@ void ghash_process_full_blocks(ghash_context_t *ctx, size_t partial_len, */ void ghash_update(ghash_context_t *ctx, size_t input_len, const uint8_t *input); +/** + * Computes the correction terms needed for the masking scheme. + * + * correction_term0 = S0 * (H0 + 1). + * correction_term1 = S0 * H1. + * correction_term1_init = S1 * H1. + * + * @param enc_initial_counter_block0 Pointer to S0. + * @param enc_initial_counter_block1 Pointer to S1. + * @param ctx Context object. + */ +void ghash_handle_enc_initial_counter_block( + const uint32_t *enc_initial_counter_block0, + const uint32_t *enc_initial_counter_block1, ghash_context_t *ctx); + /** * Update the state of a GHASH operation. * diff --git a/sw/device/lib/crypto/impl/aes_gcm/ghash_unittest.cc b/sw/device/lib/crypto/impl/aes_gcm/ghash_unittest.cc index cb6061de0f31a..456dfebc86470 100644 --- a/sw/device/lib/crypto/impl/aes_gcm/ghash_unittest.cc +++ b/sw/device/lib/crypto/impl/aes_gcm/ghash_unittest.cc @@ -13,6 +13,14 @@ namespace ghash_unittest { namespace { using ::testing::ElementsAreArray; +// Zero block. +std::array Zero = { + 0x0, + 0x0, + 0x0, + 0x0, +}; + TEST(Ghash, McGrawViegaTestCase1) { // GHASH computation from test case 1 of: // https://csrc.nist.rip/groups/ST/toolkit/BCM/documents/proposedmodes/gcm/gcm-spec.pdf @@ -31,7 +39,9 @@ TEST(Ghash, McGrawViegaTestCase1) { // Compute GHASH(H, A, C). ghash_context_t ctx; - ghash_init_subkey(H.data(), &ctx); + ghash_init_subkey(H.data(), ctx.tbl0); + ghash_init_subkey(Zero.data(), ctx.tbl1); + ghash_handle_enc_initial_counter_block(Zero.data(), Zero.data(), &ctx); ghash_init(&ctx); uint32_t result[kGhashBlockNumWords]; ghash_final(&ctx, result); @@ -59,11 +69,11 @@ TEST(Ghash, ProcessFullBlocksOneByte) { // Initialize context. ghash_context_t ctx; - ghash_init_subkey(H.data(), &ctx); + ghash_init_subkey(H.data(), ctx.tbl0); + ghash_init_subkey(Zero.data(), ctx.tbl1); + ghash_handle_enc_initial_counter_block(Zero.data(), Zero.data(), &ctx); ghash_init(&ctx); - EXPECT_THAT(ctx.state.data, testing::ElementsAreArray(zero_block)); ghash_process_full_blocks(&ctx, partial_len, &partial, input_len, input); - EXPECT_THAT(ctx.state.data, testing::ElementsAreArray(zero_block)); EXPECT_EQ(partial.data[0], input_word); EXPECT_EQ(partial.data[1], 0); EXPECT_EQ(partial.data[2], 0); @@ -85,9 +95,10 @@ TEST(Ghash, Mul1) { uint8_t one = 0x80; ghash_context_t ctx; - ghash_init_subkey(H.data(), &ctx); + ghash_init_subkey(H.data(), ctx.tbl0); + ghash_init_subkey(Zero.data(), ctx.tbl1); + ghash_handle_enc_initial_counter_block(Zero.data(), Zero.data(), &ctx); ghash_init(&ctx); - EXPECT_THAT(ctx.state.data, testing::ElementsAreArray(zero)); ghash_block_t partial = {.data = {0}}; size_t partial_len = 0; @@ -96,10 +107,9 @@ TEST(Ghash, Mul1) { ghash_process_full_blocks(&ctx, partial_len, &partial, input_len, input); EXPECT_LT(input_len, kGhashBlockNumBytes - partial_len); EXPECT_EQ(partial.data[0], one); - EXPECT_THAT(ctx.state.data, testing::ElementsAreArray(zero)); ghash_update(&ctx, 1, &one); - EXPECT_THAT(ctx.state.data, testing::ElementsAreArray(H)); + EXPECT_THAT(ctx.state0.data, testing::ElementsAreArray(H)); uint32_t result[kGhashBlockNumWords]; ghash_final(&ctx, result); @@ -144,7 +154,9 @@ TEST(Ghash, McGrawViegaTestCase2) { // Compute GHASH(H, A, C). ghash_context_t ctx; - ghash_init_subkey(H.data(), &ctx); + ghash_init_subkey(H.data(), ctx.tbl0); + ghash_init_subkey(Zero.data(), ctx.tbl1); + ghash_handle_enc_initial_counter_block(Zero.data(), Zero.data(), &ctx); ghash_init(&ctx); ghash_update(&ctx, A.size() * sizeof(uint32_t), (unsigned char *)A.data()); ghash_update(&ctx, C.size() * sizeof(uint32_t), (unsigned char *)C.data()); @@ -190,7 +202,9 @@ TEST(Ghash, ContextReset) { // Initialize the hash subkey (should only need to do this once). ghash_context_t ctx; - ghash_init_subkey(H.data(), &ctx); + ghash_init_subkey(H.data(), ctx.tbl0); + ghash_init_subkey(Zero.data(), ctx.tbl1); + ghash_handle_enc_initial_counter_block(Zero.data(), Zero.data(), &ctx); // Compute GHASH(H, A, C). ghash_init(&ctx); @@ -254,7 +268,9 @@ TEST(Ghash, McGrawViegaTestCase18) { // Compute GHASH(H, A, C). ghash_context_t ctx; - ghash_init_subkey(H.data(), &ctx); + ghash_init_subkey(H.data(), ctx.tbl0); + ghash_init_subkey(Zero.data(), ctx.tbl1); + ghash_handle_enc_initial_counter_block(Zero.data(), Zero.data(), &ctx); ghash_init(&ctx); ghash_update(&ctx, A.size() * sizeof(uint32_t), (unsigned char *)A.data()); ghash_update(&ctx, C.size() * sizeof(uint32_t), (unsigned char *)C.data()); diff --git a/sw/device/lib/crypto/impl/aes_kwp/aes_kwp.c b/sw/device/lib/crypto/impl/aes_kwp/aes_kwp.c index ccc9a2f353f24..04315ca4c188f 100644 --- a/sw/device/lib/crypto/impl/aes_kwp/aes_kwp.c +++ b/sw/device/lib/crypto/impl/aes_kwp/aes_kwp.c @@ -56,8 +56,9 @@ status_t aes_kwp_wrap(const aes_key_t kek, const uint32_t *plaintext, // Initialize the output buffer with (A || plaintext || padding). size_t plaintext_words = ceil_div(plaintext_len, sizeof(uint32_t)); - hardened_memcpy(ciphertext, block.data, kSemiblockWords); - hardened_memcpy(ciphertext + kSemiblockWords, plaintext, plaintext_words); + HARDENED_TRY(hardened_memcpy(ciphertext, block.data, kSemiblockWords)); + HARDENED_TRY(hardened_memcpy(ciphertext + kSemiblockWords, plaintext, + plaintext_words)); unsigned char *pad_start = ((unsigned char *)ciphertext) + kSemiblockBytes + plaintext_len; memset(pad_start, 0, pad_len); @@ -66,8 +67,9 @@ status_t aes_kwp_wrap(const aes_key_t kek, const uint32_t *plaintext, for (size_t j = 0; j < 6; j++) { for (size_t i = 1; i <= plaintext_semiblocks; i++) { // Copy R[i] into the block (A should already be present). - hardened_memcpy(block.data + kSemiblockWords, - ciphertext + i * kSemiblockWords, kSemiblockWords); + HARDENED_TRY(hardened_memcpy(block.data + kSemiblockWords, + ciphertext + i * kSemiblockWords, + kSemiblockWords)); HARDENED_TRY(aes_update(/*dest=*/NULL, &block)); HARDENED_TRY(aes_update(&block, /*src=*/NULL)); @@ -78,13 +80,14 @@ status_t aes_kwp_wrap(const aes_key_t kek, const uint32_t *plaintext, t++; // Copy the last two words back into R[i]. - hardened_memcpy(ciphertext + i * kSemiblockWords, - block.data + kSemiblockWords, kSemiblockWords); + HARDENED_TRY(hardened_memcpy(ciphertext + i * kSemiblockWords, + block.data + kSemiblockWords, + kSemiblockWords)); } } // Copy A into the first semiblock of the ciphertext. - hardened_memcpy(ciphertext, block.data, kSemiblockWords); + HARDENED_TRY(hardened_memcpy(ciphertext, block.data, kSemiblockWords)); return OTCRYPTO_OK; } @@ -120,7 +123,7 @@ status_t aes_kwp_unwrap(const aes_key_t kek, const uint32_t *ciphertext, // Initialize the working buffer, R. uint32_t r[(ciphertext_semiblocks - 1) * kSemiblockWords]; - hardened_memcpy(r, ciphertext + kSemiblockWords, ARRAYSIZE(r)); + HARDENED_TRY(hardened_memcpy(r, ciphertext + kSemiblockWords, ARRAYSIZE(r))); uint64_t t = 6 * ((uint64_t)ciphertext_semiblocks - 1); for (size_t j = 0; j < 6; j++) { @@ -131,14 +134,16 @@ status_t aes_kwp_unwrap(const aes_key_t kek, const uint32_t *ciphertext, t--; // Copy R[i] into the block (A ^ t should already be present). - hardened_memcpy(block.data + kSemiblockWords, - r + (i - 1) * kSemiblockWords, kSemiblockWords); + HARDENED_TRY(hardened_memcpy(block.data + kSemiblockWords, + r + (i - 1) * kSemiblockWords, + kSemiblockWords)); HARDENED_TRY(aes_update(/*dest=*/NULL, &block)); HARDENED_TRY(aes_update(&block, /*src=*/NULL)); // Copy the last two words back into R[i]. - hardened_memcpy(r + (i - 1) * kSemiblockWords, - block.data + kSemiblockWords, kSemiblockWords); + HARDENED_TRY(hardened_memcpy(r + (i - 1) * kSemiblockWords, + block.data + kSemiblockWords, + kSemiblockWords)); } } @@ -174,7 +179,7 @@ status_t aes_kwp_unwrap(const aes_key_t kek, const uint32_t *ciphertext, // Copy the plaintext into the destination buffer. size_t plaintext_words = ceil_div(plaintext_len, sizeof(uint32_t)); - hardened_memcpy(plaintext, r, plaintext_words); + HARDENED_TRY(hardened_memcpy(plaintext, r, plaintext_words)); // Return success. *success = kHardenedBoolTrue; diff --git a/sw/device/lib/crypto/impl/drbg.c b/sw/device/lib/crypto/impl/drbg.c index 103feb5b6bb13..3f8639a461773 100644 --- a/sw/device/lib/crypto/impl/drbg.c +++ b/sw/device/lib/crypto/impl/drbg.c @@ -25,7 +25,7 @@ * @param[out] seed_material Resulting entropy complex seed. * @return OK or error. */ -static otcrypto_status_t seed_material_construct( +static status_t seed_material_construct( otcrypto_const_byte_buf_t value, entropy_seed_material_t *seed_material) { if (value.len > kEntropySeedBytes) { return OTCRYPTO_BAD_ARGS; @@ -34,17 +34,14 @@ static otcrypto_status_t seed_material_construct( size_t nwords = ceil_div(value.len, sizeof(uint32_t)); seed_material->len = nwords; - // Initialize the set words to zero. - memset(seed_material->data, 0, nwords * sizeof(uint32_t)); - - if (value.len == 0) { - return OTCRYPTO_OK; - } - // Copy seed data. // TODO(#17711) Change to `hardened_memcpy`. memcpy(seed_material->data, value.data, value.len); + // Set any unset bytes to zero. + size_t unset_bytes = nwords * sizeof(uint32_t) - value.len; + memset(((unsigned char *)seed_material->data) + value.len, 0, unset_bytes); + return OTCRYPTO_OK; } @@ -95,6 +92,8 @@ otcrypto_status_t otcrypto_drbg_instantiate( HARDENED_TRY(entropy_complex_check()); entropy_seed_material_t seed_material; + HARDENED_TRY( + hardened_memshred(seed_material.data, ARRAYSIZE(seed_material.data))); seed_material_construct(perso_string, &seed_material); HARDENED_TRY(entropy_csrng_uninstantiate()); @@ -113,6 +112,8 @@ otcrypto_status_t otcrypto_drbg_reseed( HARDENED_TRY(entropy_complex_check()); entropy_seed_material_t seed_material; + HARDENED_TRY( + hardened_memshred(seed_material.data, ARRAYSIZE(seed_material.data))); seed_material_construct(additional_input, &seed_material); return entropy_csrng_reseed(/*disable_trng_input=*/kHardenedBoolFalse, @@ -195,6 +196,12 @@ static otcrypto_status_t generate(hardened_bool_t fips_check, otcrypto_status_t otcrypto_drbg_generate( otcrypto_const_byte_buf_t additional_input, otcrypto_word32_buf_t drbg_output) { + // Ensure the entropy complex is initialized. + HARDENED_TRY(entropy_complex_check()); + + // Randomize destination buffer. + HARDENED_TRY(hardened_memshred(drbg_output.data, drbg_output.len)); + return generate(/*fips_check=*/kHardenedBoolTrue, additional_input, drbg_output); } diff --git a/sw/device/lib/crypto/impl/ecc/p256.c b/sw/device/lib/crypto/impl/ecc/p256.c index d3fadcde901b1..26d91a3998f6b 100644 --- a/sw/device/lib/crypto/impl/ecc/p256.c +++ b/sw/device/lib/crypto/impl/ecc/p256.c @@ -125,17 +125,19 @@ status_t p256_sideload_keygen_start(void) { status_t p256_keygen_finalize(p256_masked_scalar_t *private_key, p256_point_t *public_key) { // Spin here waiting for OTBN to complete. - HARDENED_TRY(otbn_busy_wait_for_done()); + HARDENED_TRY_WIPE_DMEM(otbn_busy_wait_for_done()); // Read the masked private key from OTBN dmem. - HARDENED_TRY(otbn_dmem_read(kP256MaskedScalarShareWords, kOtbnVarD0, - private_key->share0)); - HARDENED_TRY(otbn_dmem_read(kP256MaskedScalarShareWords, kOtbnVarD1, - private_key->share1)); + HARDENED_TRY_WIPE_DMEM(otbn_dmem_read(kP256MaskedScalarShareWords, kOtbnVarD0, + private_key->share0)); + HARDENED_TRY_WIPE_DMEM(otbn_dmem_read(kP256MaskedScalarShareWords, kOtbnVarD1, + private_key->share1)); // Read the public key from OTBN dmem. - HARDENED_TRY(otbn_dmem_read(kP256CoordWords, kOtbnVarX, public_key->x)); - HARDENED_TRY(otbn_dmem_read(kP256CoordWords, kOtbnVarY, public_key->y)); + HARDENED_TRY_WIPE_DMEM( + otbn_dmem_read(kP256CoordWords, kOtbnVarX, public_key->x)); + HARDENED_TRY_WIPE_DMEM( + otbn_dmem_read(kP256CoordWords, kOtbnVarY, public_key->y)); // Wipe DMEM. HARDENED_TRY(otbn_dmem_sec_wipe()); @@ -145,11 +147,13 @@ status_t p256_keygen_finalize(p256_masked_scalar_t *private_key, status_t p256_sideload_keygen_finalize(p256_point_t *public_key) { // Spin here waiting for OTBN to complete. - HARDENED_TRY(otbn_busy_wait_for_done()); + HARDENED_TRY_WIPE_DMEM(otbn_busy_wait_for_done()); // Read the public key from OTBN dmem. - HARDENED_TRY(otbn_dmem_read(kP256CoordWords, kOtbnVarX, public_key->x)); - HARDENED_TRY(otbn_dmem_read(kP256CoordWords, kOtbnVarY, public_key->y)); + HARDENED_TRY_WIPE_DMEM( + otbn_dmem_read(kP256CoordWords, kOtbnVarX, public_key->x)); + HARDENED_TRY_WIPE_DMEM( + otbn_dmem_read(kP256CoordWords, kOtbnVarY, public_key->y)); // Wipe DMEM. HARDENED_TRY(otbn_dmem_sec_wipe()); @@ -218,13 +222,15 @@ status_t p256_ecdsa_sideload_sign_start( status_t p256_ecdsa_sign_finalize(p256_ecdsa_signature_t *result) { // Spin here waiting for OTBN to complete. - HARDENED_TRY(otbn_busy_wait_for_done()); + HARDENED_TRY_WIPE_DMEM(otbn_busy_wait_for_done()); // Read signature R out of OTBN dmem. - HARDENED_TRY(otbn_dmem_read(kP256ScalarWords, kOtbnVarR, result->r)); + HARDENED_TRY_WIPE_DMEM( + otbn_dmem_read(kP256ScalarWords, kOtbnVarR, result->r)); // Read signature S out of OTBN dmem. - HARDENED_TRY(otbn_dmem_read(kP256ScalarWords, kOtbnVarS, result->s)); + HARDENED_TRY_WIPE_DMEM( + otbn_dmem_read(kP256ScalarWords, kOtbnVarS, result->s)); // Wipe DMEM. HARDENED_TRY(otbn_dmem_sec_wipe()); @@ -264,20 +270,21 @@ status_t p256_ecdsa_verify_start(const p256_ecdsa_signature_t *signature, status_t p256_ecdsa_verify_finalize(const p256_ecdsa_signature_t *signature, hardened_bool_t *result) { // Spin here waiting for OTBN to complete. - HARDENED_TRY(otbn_busy_wait_for_done()); + HARDENED_TRY_WIPE_DMEM(otbn_busy_wait_for_done()); // Read the status code out of DMEM (false if basic checks on the validity of // the signature and public key failed). uint32_t ok; - HARDENED_TRY(otbn_dmem_read(1, kOtbnVarOk, &ok)); + HARDENED_TRY_WIPE_DMEM(otbn_dmem_read(1, kOtbnVarOk, &ok)); if (launder32(ok) != kHardenedBoolTrue) { + HARDENED_TRY(otbn_dmem_sec_wipe()); return OTCRYPTO_BAD_ARGS; } HARDENED_CHECK_EQ(ok, kHardenedBoolTrue); // Read x_r (recovered R) out of OTBN dmem. uint32_t x_r[kP256ScalarWords]; - HARDENED_TRY(otbn_dmem_read(kP256ScalarWords, kOtbnVarXr, x_r)); + HARDENED_TRY_WIPE_DMEM(otbn_dmem_read(kP256ScalarWords, kOtbnVarXr, x_r)); *result = hardened_memeq(x_r, signature->r, kP256ScalarWords); @@ -311,19 +318,22 @@ status_t p256_ecdh_start(const p256_masked_scalar_t *private_key, status_t p256_ecdh_finalize(p256_ecdh_shared_key_t *shared_key) { // Spin here waiting for OTBN to complete. - HARDENED_TRY(otbn_busy_wait_for_done()); + HARDENED_TRY_WIPE_DMEM(otbn_busy_wait_for_done()); // Read the code indicating if the public key is valid. uint32_t ok; - HARDENED_TRY(otbn_dmem_read(1, kOtbnVarOk, &ok)); + HARDENED_TRY_WIPE_DMEM(otbn_dmem_read(1, kOtbnVarOk, &ok)); if (launder32(ok) != kHardenedBoolTrue) { + HARDENED_TRY(otbn_dmem_sec_wipe()); return OTCRYPTO_BAD_ARGS; } HARDENED_CHECK_EQ(ok, kHardenedBoolTrue); // Read the shares of the key from OTBN dmem (at vars x and y). - HARDENED_TRY(otbn_dmem_read(kP256CoordWords, kOtbnVarX, shared_key->share0)); - HARDENED_TRY(otbn_dmem_read(kP256CoordWords, kOtbnVarY, shared_key->share1)); + HARDENED_TRY_WIPE_DMEM( + otbn_dmem_read(kP256CoordWords, kOtbnVarX, shared_key->share0)); + HARDENED_TRY_WIPE_DMEM( + otbn_dmem_read(kP256CoordWords, kOtbnVarY, shared_key->share1)); // Wipe DMEM. HARDENED_TRY(otbn_dmem_sec_wipe()); diff --git a/sw/device/lib/crypto/impl/ecc/p384.c b/sw/device/lib/crypto/impl/ecc/p384.c index 8c429b5324bb7..4d54eb94515b5 100644 --- a/sw/device/lib/crypto/impl/ecc/p384.c +++ b/sw/device/lib/crypto/impl/ecc/p384.c @@ -166,17 +166,19 @@ status_t p384_keygen_start(void) { status_t p384_keygen_finalize(p384_masked_scalar_t *private_key, p384_point_t *public_key) { // Spin here waiting for OTBN to complete. - HARDENED_TRY(otbn_busy_wait_for_done()); + HARDENED_TRY_WIPE_DMEM(otbn_busy_wait_for_done()); // Read the masked private key from OTBN dmem. - HARDENED_TRY(otbn_dmem_read(kP384MaskedScalarShareWords, kOtbnVarD0, - private_key->share0)); - HARDENED_TRY(otbn_dmem_read(kP384MaskedScalarShareWords, kOtbnVarD1, - private_key->share1)); + HARDENED_TRY_WIPE_DMEM(otbn_dmem_read(kP384MaskedScalarShareWords, kOtbnVarD0, + private_key->share0)); + HARDENED_TRY_WIPE_DMEM(otbn_dmem_read(kP384MaskedScalarShareWords, kOtbnVarD1, + private_key->share1)); // Read the public key from OTBN dmem. - HARDENED_TRY(otbn_dmem_read(kP384CoordWords, kOtbnVarX, public_key->x)); - HARDENED_TRY(otbn_dmem_read(kP384CoordWords, kOtbnVarY, public_key->y)); + HARDENED_TRY_WIPE_DMEM( + otbn_dmem_read(kP384CoordWords, kOtbnVarX, public_key->x)); + HARDENED_TRY_WIPE_DMEM( + otbn_dmem_read(kP384CoordWords, kOtbnVarY, public_key->y)); // Wipe DMEM. HARDENED_TRY(otbn_dmem_sec_wipe()); @@ -198,11 +200,13 @@ status_t p384_sideload_keygen_start(void) { status_t p384_sideload_keygen_finalize(p384_point_t *public_key) { // Spin here waiting for OTBN to complete. - HARDENED_TRY(otbn_busy_wait_for_done()); + HARDENED_TRY_WIPE_DMEM(otbn_busy_wait_for_done()); // Read the public key from OTBN dmem. - HARDENED_TRY(otbn_dmem_read(kP384CoordWords, kOtbnVarX, public_key->x)); - HARDENED_TRY(otbn_dmem_read(kP384CoordWords, kOtbnVarY, public_key->y)); + HARDENED_TRY_WIPE_DMEM( + otbn_dmem_read(kP384CoordWords, kOtbnVarX, public_key->x)); + HARDENED_TRY_WIPE_DMEM( + otbn_dmem_read(kP384CoordWords, kOtbnVarY, public_key->y)); // Wipe DMEM. HARDENED_TRY(otbn_dmem_sec_wipe()); @@ -247,13 +251,15 @@ status_t p384_ecdsa_sideload_sign_start( status_t p384_ecdsa_sign_finalize(p384_ecdsa_signature_t *result) { // Spin here waiting for OTBN to complete. - HARDENED_TRY(otbn_busy_wait_for_done()); + HARDENED_TRY_WIPE_DMEM(otbn_busy_wait_for_done()); // Read signature R out of OTBN dmem. - HARDENED_TRY(otbn_dmem_read(kP384ScalarWords, kOtbnVarR, result->r)); + HARDENED_TRY_WIPE_DMEM( + otbn_dmem_read(kP384ScalarWords, kOtbnVarR, result->r)); // Read signature S out of OTBN dmem. - HARDENED_TRY(otbn_dmem_read(kP384ScalarWords, kOtbnVarS, result->s)); + HARDENED_TRY_WIPE_DMEM( + otbn_dmem_read(kP384ScalarWords, kOtbnVarS, result->s)); // Wipe DMEM. HARDENED_TRY(otbn_dmem_sec_wipe()); @@ -290,20 +296,21 @@ status_t p384_ecdsa_verify_start(const p384_ecdsa_signature_t *signature, status_t p384_ecdsa_verify_finalize(const p384_ecdsa_signature_t *signature, hardened_bool_t *result) { // Spin here waiting for OTBN to complete. - HARDENED_TRY(otbn_busy_wait_for_done()); + HARDENED_TRY_WIPE_DMEM(otbn_busy_wait_for_done()); // Read the status code out of DMEM (false if basic checks on the validity of // the signature and public key failed). uint32_t ok; - HARDENED_TRY(otbn_dmem_read(1, kOtbnVarOk, &ok)); + HARDENED_TRY_WIPE_DMEM(otbn_dmem_read(1, kOtbnVarOk, &ok)); if (launder32(ok) != kHardenedBoolTrue) { + HARDENED_TRY(otbn_dmem_sec_wipe()); return OTCRYPTO_BAD_ARGS; } HARDENED_CHECK_EQ(ok, kHardenedBoolTrue); // Read x_r (recovered R) out of OTBN dmem. uint32_t x_r[kP384ScalarWords]; - HARDENED_TRY(otbn_dmem_read(kP384ScalarWords, kOtbnVarXr, x_r)); + HARDENED_TRY_WIPE_DMEM(otbn_dmem_read(kP384ScalarWords, kOtbnVarXr, x_r)); *result = hardened_memeq(x_r, signature->r, kP384ScalarWords); @@ -334,20 +341,23 @@ status_t p384_ecdh_start(const p384_masked_scalar_t *private_key, status_t p384_ecdh_finalize(p384_ecdh_shared_key_t *shared_key) { // Spin here waiting for OTBN to complete. - HARDENED_TRY(otbn_busy_wait_for_done()); + HARDENED_TRY_WIPE_DMEM(otbn_busy_wait_for_done()); // Read the status code out of DMEM (false if basic checks on the validity of // the signature and public key failed). uint32_t ok; - HARDENED_TRY(otbn_dmem_read(1, kOtbnVarOk, &ok)); + HARDENED_TRY_WIPE_DMEM(otbn_dmem_read(1, kOtbnVarOk, &ok)); if (launder32(ok) != kHardenedBoolTrue) { + HARDENED_TRY(otbn_dmem_sec_wipe()); return OTCRYPTO_BAD_ARGS; } HARDENED_CHECK_EQ(ok, kHardenedBoolTrue); // Read the shares of the key from OTBN dmem (at vars x and y). - HARDENED_TRY(otbn_dmem_read(kP384CoordWords, kOtbnVarX, shared_key->share0)); - HARDENED_TRY(otbn_dmem_read(kP384CoordWords, kOtbnVarY, shared_key->share1)); + HARDENED_TRY_WIPE_DMEM( + otbn_dmem_read(kP384CoordWords, kOtbnVarX, shared_key->share0)); + HARDENED_TRY_WIPE_DMEM( + otbn_dmem_read(kP384CoordWords, kOtbnVarY, shared_key->share1)); // Wipe DMEM. HARDENED_TRY(otbn_dmem_sec_wipe()); diff --git a/sw/device/lib/crypto/impl/ecc_p256.c b/sw/device/lib/crypto/impl/ecc_p256.c index be0fd32cc6c32..5fe4fd8cf4bb4 100644 --- a/sw/device/lib/crypto/impl/ecc_p256.c +++ b/sw/device/lib/crypto/impl/ecc_p256.c @@ -4,6 +4,7 @@ #include "sw/device/lib/crypto/include/ecc_p256.h" +#include "sw/device/lib/base/hardened_memory.h" #include "sw/device/lib/crypto/drivers/entropy.h" #include "sw/device/lib/crypto/drivers/hmac.h" #include "sw/device/lib/crypto/impl/ecc/p256.h" @@ -40,6 +41,29 @@ otcrypto_status_t otcrypto_ecdsa_p256_verify( verification_result); } +otcrypto_status_t otcrypto_ecdsa_p256_sign_verify( + const otcrypto_blinded_key_t *private_key, + const otcrypto_unblinded_key_t *public_key, + const otcrypto_hash_digest_t message_digest, + otcrypto_word32_buf_t signature) { + // Signature generation. + HARDENED_TRY( + otcrypto_ecdsa_p256_sign(private_key, message_digest, signature)); + + // Verify signature before releasing it. + otcrypto_const_word32_buf_t signature_check = { + .data = signature.data, + .len = signature.len, + }; + hardened_bool_t verification_result = kHardenedBoolFalse; + HARDENED_TRY(otcrypto_ecdsa_p256_verify( + public_key, message_digest, signature_check, &verification_result)); + + // Trap if signature verification failed. + HARDENED_CHECK_EQ(verification_result, kHardenedBoolTrue); + return OTCRYPTO_OK; +} + otcrypto_status_t otcrypto_ecdh_p256_keygen( otcrypto_blinded_key_t *private_key, otcrypto_unblinded_key_t *public_key) { HARDENED_TRY(otcrypto_ecdh_p256_keygen_async_start(private_key)); @@ -202,14 +226,19 @@ static status_t internal_p256_keygen_finalize( HARDENED_TRY(p256_sideload_keygen_finalize(pk)); } else if (launder32(private_key->config.hw_backed) == kHardenedBoolFalse) { HARDENED_CHECK_EQ(private_key->config.hw_backed, kHardenedBoolFalse); - p256_masked_scalar_t *sk = (p256_masked_scalar_t *)private_key->keyblob; - HARDENED_TRY(p256_keygen_finalize(sk, pk)); - private_key->checksum = integrity_blinded_checksum(private_key); + + // Randomize the keyblob before writing secret data. + HARDENED_TRY(hardened_memshred(private_key->keyblob, + keyblob_num_words(private_key->config))); + + HARDENED_TRY( + p256_keygen_finalize((p256_masked_scalar_t *)private_key->keyblob, pk)); } else { return OTCRYPTO_BAD_ARGS; } - // Prepare the public key. + // Set the key checksums. + private_key->checksum = integrity_blinded_checksum(private_key); public_key->checksum = integrity_unblinded_checksum(public_key); // Clear the OTBN sideload slot (in case the seed was sideloaded). @@ -271,17 +300,26 @@ otcrypto_status_t otcrypto_ecdsa_p256_sign_async_start( if (launder32(private_key->config.hw_backed) == kHardenedBoolFalse) { // Start the asynchronous signature-generation routine. HARDENED_CHECK_EQ(private_key->config.hw_backed, kHardenedBoolFalse); - p256_masked_scalar_t *sk = (p256_masked_scalar_t *)private_key->keyblob; - return p256_ecdsa_sign_start(message_digest.data, sk); + HARDENED_TRY(p256_ecdsa_sign_start( + message_digest.data, (p256_masked_scalar_t *)private_key->keyblob)); } else if (launder32(private_key->config.hw_backed) == kHardenedBoolTrue) { // Load the key and start in sideloaded-key mode. HARDENED_CHECK_EQ(private_key->config.hw_backed, kHardenedBoolTrue); HARDENED_TRY(keyblob_sideload_key_otbn(private_key)); - return p256_ecdsa_sideload_sign_start(message_digest.data); + HARDENED_TRY(p256_ecdsa_sideload_sign_start(message_digest.data)); + } else { + // Invalid value for private_key->hw_backed. + return OTCRYPTO_BAD_ARGS; } - // Invalid value for private_key->hw_backed. - return OTCRYPTO_BAD_ARGS; + // To detect forgeries of the pointer to the private key that we have passed + // to the ECC implementation, check again its integrity. If the pointer would + // have been tampered with between the first integrity check we did when + // entering the CryptoLib and here, we would detect this now. + HARDENED_CHECK_EQ(integrity_blinded_key_check(private_key), + kHardenedBoolTrue); + + return OTCRYPTO_OK; } /** @@ -365,7 +403,15 @@ otcrypto_status_t otcrypto_ecdsa_p256_verify_async_start( p256_ecdsa_signature_t *sig = (p256_ecdsa_signature_t *)signature.data; // Start the asynchronous signature-verification routine. - return p256_ecdsa_verify_start(sig, message_digest.data, pk); + HARDENED_TRY(p256_ecdsa_verify_start(sig, message_digest.data, pk)); + + // To detect forgeries of the pointer to the public key that we have passed + // to the ECC implementation, check again its integrity. If the pointer would + // have been tampered with between the first integrity check we did when + // entering the CryptoLib and here, we would detect this now. + HARDENED_CHECK_EQ(integrity_unblinded_key_check(public_key), + kHardenedBoolTrue); + return OTCRYPTO_OK; } otcrypto_status_t otcrypto_ecdsa_p256_verify_async_finalize( @@ -452,15 +498,26 @@ otcrypto_status_t otcrypto_ecdh_p256_async_start( if (launder32(private_key->config.hw_backed) == kHardenedBoolTrue) { HARDENED_CHECK_EQ(private_key->config.hw_backed, kHardenedBoolTrue); HARDENED_TRY(keyblob_sideload_key_otbn(private_key)); - return p256_sideload_ecdh_start(pk); + HARDENED_TRY(p256_sideload_ecdh_start(pk)); } else if (launder32(private_key->config.hw_backed) == kHardenedBoolFalse) { HARDENED_CHECK_EQ(private_key->config.hw_backed, kHardenedBoolFalse); - p256_masked_scalar_t *sk = (p256_masked_scalar_t *)private_key->keyblob; - return p256_ecdh_start(sk, pk); + HARDENED_TRY( + p256_ecdh_start((p256_masked_scalar_t *)private_key->keyblob, pk)); + } else { + // Invalid value for `hw_backed`. + return OTCRYPTO_BAD_ARGS; } - // Invalid value for `hw_backed`. - return OTCRYPTO_BAD_ARGS; + // To detect forgeries of the pointer to the public key that we have passed + // to the ECC implementation, check again its integrity. If the pointer would + // have been tampered with between the first integrity check we did when + // entering the CryptoLib and here, we would detect this now. + HARDENED_CHECK_EQ(integrity_blinded_key_check(private_key), + kHardenedBoolTrue); + HARDENED_CHECK_EQ(integrity_unblinded_key_check(public_key), + kHardenedBoolTrue); + + return OTCRYPTO_OK; } otcrypto_status_t otcrypto_ecdh_p256_async_finalize( @@ -495,6 +552,8 @@ otcrypto_status_t otcrypto_ecdh_p256_async_finalize( // occurs after this point then the keys would be unrecoverable. This should // be the last potentially error-causing line before returning to the caller. p256_ecdh_shared_key_t ss; + HARDENED_TRY(hardened_memshred(ss.share0, ARRAYSIZE(ss.share0))); + HARDENED_TRY(hardened_memshred(ss.share1, ARRAYSIZE(ss.share1))); HARDENED_TRY(p256_ecdh_finalize(&ss)); HARDENED_TRY(keyblob_from_shares(ss.share0, ss.share1, shared_secret->config, diff --git a/sw/device/lib/crypto/impl/ecc_p384.c b/sw/device/lib/crypto/impl/ecc_p384.c index 34a9c7bccbc3b..e1047f88fa9c4 100644 --- a/sw/device/lib/crypto/impl/ecc_p384.c +++ b/sw/device/lib/crypto/impl/ecc_p384.c @@ -4,6 +4,7 @@ #include "sw/device/lib/crypto/include/ecc_p384.h" +#include "sw/device/lib/base/hardened_memory.h" #include "sw/device/lib/crypto/drivers/entropy.h" #include "sw/device/lib/crypto/drivers/hmac.h" #include "sw/device/lib/crypto/impl/ecc/p384.h" @@ -40,6 +41,29 @@ otcrypto_status_t otcrypto_ecdsa_p384_verify( verification_result); } +otcrypto_status_t otcrypto_ecdsa_p384_sign_verify( + const otcrypto_blinded_key_t *private_key, + const otcrypto_unblinded_key_t *public_key, + const otcrypto_hash_digest_t message_digest, + otcrypto_word32_buf_t signature) { + // Signature generation. + HARDENED_TRY( + otcrypto_ecdsa_p384_sign(private_key, message_digest, signature)); + + // Verify signature before releasing it. + otcrypto_const_word32_buf_t signature_check = { + .data = signature.data, + .len = signature.len, + }; + hardened_bool_t verification_result = kHardenedBoolFalse; + HARDENED_TRY(otcrypto_ecdsa_p384_verify( + public_key, message_digest, signature_check, &verification_result)); + + // Trap if signature verification failed. + HARDENED_CHECK_EQ(verification_result, kHardenedBoolTrue); + return OTCRYPTO_OK; +} + otcrypto_status_t otcrypto_ecdh_p384_keygen( otcrypto_blinded_key_t *private_key, otcrypto_unblinded_key_t *public_key) { HARDENED_TRY(otcrypto_ecdh_p384_keygen_async_start(private_key)); @@ -201,18 +225,23 @@ static status_t internal_p384_keygen_finalize( HARDENED_CHECK_EQ(private_key->config.hw_backed, kHardenedBoolTrue); HARDENED_TRY(p384_sideload_keygen_finalize(pk)); } else if (launder32(private_key->config.hw_backed) == kHardenedBoolFalse) { - p384_masked_scalar_t *sk = (p384_masked_scalar_t *)private_key->keyblob; + HARDENED_CHECK_EQ(private_key->config.hw_backed, kHardenedBoolFalse); + + // Randomize the keyblob before writing secret data. + HARDENED_TRY(hardened_memshred(private_key->keyblob, + keyblob_num_words(private_key->config))); + // Note: This operation wipes DMEM after retrieving the keys, so if an error // occurs after this point then the keys would be unrecoverable. This should // be the last potentially error-causing line before returning to the // caller. - HARDENED_CHECK_EQ(private_key->config.hw_backed, kHardenedBoolFalse); - HARDENED_TRY(p384_keygen_finalize(sk, pk)); - private_key->checksum = integrity_blinded_checksum(private_key); + HARDENED_TRY( + p384_keygen_finalize((p384_masked_scalar_t *)private_key->keyblob, pk)); } else { return OTCRYPTO_BAD_ARGS; } + private_key->checksum = integrity_blinded_checksum(private_key); public_key->checksum = integrity_unblinded_checksum(public_key); return OTCRYPTO_OK; } @@ -275,17 +304,26 @@ otcrypto_status_t otcrypto_ecdsa_p384_sign_async_start( if (launder32(private_key->config.hw_backed) == kHardenedBoolFalse) { // Start the asynchronous signature-generation routine. HARDENED_CHECK_EQ(private_key->config.hw_backed, kHardenedBoolFalse); - p384_masked_scalar_t *sk = (p384_masked_scalar_t *)private_key->keyblob; - return p384_ecdsa_sign_start(message_digest.data, sk); + HARDENED_TRY(p384_ecdsa_sign_start( + message_digest.data, (p384_masked_scalar_t *)private_key->keyblob)); } else if (launder32(private_key->config.hw_backed) == kHardenedBoolTrue) { // Load the key and start in sideloaded-key mode. HARDENED_CHECK_EQ(private_key->config.hw_backed, kHardenedBoolTrue); HARDENED_TRY(keyblob_sideload_key_otbn(private_key)); - return p384_ecdsa_sideload_sign_start(message_digest.data); + HARDENED_TRY(p384_ecdsa_sideload_sign_start(message_digest.data)); + } else { + // Invalid value for private_key->hw_backed. + return OTCRYPTO_BAD_ARGS; } - // Invalid value for private_key->hw_backed. - return OTCRYPTO_BAD_ARGS; + // To detect forgeries of the pointer to the private key that we have passed + // to the ECC implementation, check again its integrity. If the pointer would + // have been tampered with between the first integrity check we did when + // entering the CryptoLib and here, we would detect this now. + HARDENED_CHECK_EQ(integrity_blinded_key_check(private_key), + kHardenedBoolTrue); + + return OTCRYPTO_OK; } /** @@ -368,7 +406,15 @@ otcrypto_status_t otcrypto_ecdsa_p384_verify_async_start( p384_ecdsa_signature_t *sig = (p384_ecdsa_signature_t *)signature.data; // Start the asynchronous signature-verification routine. - return p384_ecdsa_verify_start(sig, message_digest.data, pk); + HARDENED_TRY(p384_ecdsa_verify_start(sig, message_digest.data, pk)); + + // To detect forgeries of the pointer to the public key that we have passed + // to the ECC implementation, check again its integrity. If the pointer would + // have been tampered with between the first integrity check we did when + // entering the CryptoLib and here, we would detect this now. + HARDENED_CHECK_EQ(integrity_unblinded_key_check(public_key), + kHardenedBoolTrue); + return OTCRYPTO_OK; } otcrypto_status_t otcrypto_ecdsa_p384_verify_async_finalize( @@ -455,15 +501,26 @@ otcrypto_status_t otcrypto_ecdh_p384_async_start( if (launder32(private_key->config.hw_backed) == kHardenedBoolTrue) { HARDENED_CHECK_EQ(private_key->config.hw_backed, kHardenedBoolTrue); HARDENED_TRY(keyblob_sideload_key_otbn(private_key)); - return p384_sideload_ecdh_start(pk); + HARDENED_TRY(p384_sideload_ecdh_start(pk)); } else if (launder32(private_key->config.hw_backed) == kHardenedBoolFalse) { HARDENED_CHECK_EQ(private_key->config.hw_backed, kHardenedBoolFalse); - p384_masked_scalar_t *sk = (p384_masked_scalar_t *)private_key->keyblob; - return p384_ecdh_start(sk, pk); + HARDENED_TRY( + p384_ecdh_start((p384_masked_scalar_t *)private_key->keyblob, pk)); + } else { + // Invalid value for `hw_backed`. + return OTCRYPTO_BAD_ARGS; } - // Invalid value for `hw_backed`. - return OTCRYPTO_BAD_ARGS; + // To detect forgeries of the pointers to the public and private key that we + // have passed to the ECC implementation, check again their integrity. If the + // pointers would have been tampered with between the first integrity check we + // did when entering the CryptoLib and here, we would detect this now. + HARDENED_CHECK_EQ(integrity_blinded_key_check(private_key), + kHardenedBoolTrue); + HARDENED_CHECK_EQ(integrity_unblinded_key_check(public_key), + kHardenedBoolTrue); + + return OTCRYPTO_OK; } otcrypto_status_t otcrypto_ecdh_p384_async_finalize( @@ -498,6 +555,8 @@ otcrypto_status_t otcrypto_ecdh_p384_async_finalize( // occurs after this point then the keys would be unrecoverable. This should // be the last potentially error-causing line before returning to the caller. p384_ecdh_shared_key_t ss; + HARDENED_TRY(hardened_memshred(ss.share0, ARRAYSIZE(ss.share0))); + HARDENED_TRY(hardened_memshred(ss.share1, ARRAYSIZE(ss.share1))); HARDENED_TRY(p384_ecdh_finalize(&ss)); HARDENED_TRY(keyblob_from_shares(ss.share0, ss.share1, shared_secret->config, diff --git a/sw/device/lib/crypto/impl/hkdf.c b/sw/device/lib/crypto/impl/hkdf.c index c0abafc215da6..fb64d9ea19fb3 100644 --- a/sw/device/lib/crypto/impl/hkdf.c +++ b/sw/device/lib/crypto/impl/hkdf.c @@ -172,13 +172,9 @@ otcrypto_status_t otcrypto_hkdf_extract(const otcrypto_blinded_key_t *ikm, // struct. // Unmask the input key. - uint32_t *ikm_share0; - uint32_t *ikm_share1; - HARDENED_TRY(keyblob_to_shares(ikm, &ikm_share0, &ikm_share1)); uint32_t unmasked_ikm_data[keyblob_share_num_words(ikm->config)]; - for (size_t i = 0; i < ARRAYSIZE(unmasked_ikm_data); i++) { - unmasked_ikm_data[i] = ikm_share0[i] ^ ikm_share1[i]; - } + HARDENED_TRY( + keyblob_key_unmask(ikm, ARRAYSIZE(unmasked_ikm_data), unmasked_ikm_data)); otcrypto_const_byte_buf_t unmasked_ikm = { .data = (unsigned char *)unmasked_ikm_data, .len = ikm->config.key_length, diff --git a/sw/device/lib/crypto/impl/hmac.c b/sw/device/lib/crypto/impl/hmac.c index 97227ea60819b..81eb60583fcee 100644 --- a/sw/device/lib/crypto/impl/hmac.c +++ b/sw/device/lib/crypto/impl/hmac.c @@ -5,6 +5,7 @@ #include "sw/device/lib/crypto/include/hmac.h" #include "sw/device/lib/base/hardened_memory.h" +#include "sw/device/lib/crypto/drivers/entropy.h" #include "sw/device/lib/crypto/drivers/hmac.h" #include "sw/device/lib/crypto/drivers/rv_core_ibex.h" #include "sw/device/lib/crypto/impl/integrity.h" @@ -29,21 +30,24 @@ static_assert(sizeof(hmac_ctx_t) % sizeof(uint32_t) == 0, "`hardened_memcpy()`"); /** - * Compute the key block (see FIPS 198-1, Section 4, Steps 1-3). + * Compute the key block (see FIPS 198-1, Section 4, Steps 1-3) together with + * its checksum. * * Adds padding and in some cases pre-hashes the HMAC key to get a value the - * length of the underlying message block size. + * length of the underlying message block size. This length is then set in + * the key_len field of hmac_key_t. * * The caller must ensure that at least `key_block_wordlen` 32-bit words of * space is allocated at the destination `key_block` buffer. * * @param key The blinded input key. * @param key_block_wordlen Block size in 32-bit words. - * @param[out] key_block Destination buffer for the key block. + * @param[out] hmac_key Destination of the HMAC key struct. * @return Result of the operation. */ -static status_t key_block_get(const otcrypto_blinded_key_t *key, - size_t key_block_wordlen, uint32_t *key_block) { +static status_t hmac_key_construct(const otcrypto_blinded_key_t *key, + size_t key_block_wordlen, + hmac_key_t *hmac_key) { // HMAC HWIP does not support masking, so we need to unmask the key. size_t unmasked_key_len = keyblob_share_num_words(key->config); uint32_t unmasked_key[unmasked_key_len]; @@ -51,42 +55,61 @@ static status_t key_block_get(const otcrypto_blinded_key_t *key, // Pre-populate with 0s, in order to pad keys smaller than the internal // block size, according to FIPS 198-1, Section 4. - memset(key_block, 0, key_block_wordlen * sizeof(uint32_t)); + memset(hmac_key->key_block, 0, key_block_wordlen * sizeof(uint32_t)); // If the key is larger than the internal block size, we need to hash it // according to FIPS 198-1, Section 4, Step 2. if (launder32(key->config.key_length) > key_block_wordlen * sizeof(uint32_t)) { + otcrypto_hmac_key_mode_t used_key_mode; switch (key->config.key_mode) { case kOtcryptoKeyModeHmacSha256: - return hmac_hash_sha256((unsigned char *)unmasked_key, - key->config.key_length, key_block); + HARDENED_TRY(hmac_hash_sha256((unsigned char *)unmasked_key, + key->config.key_length, + hmac_key->key_block)); + used_key_mode = launder32(kOtcryptoKeyModeHmacSha256); + break; case kOtcryptoKeyModeHmacSha384: - return hmac_hash_sha384((unsigned char *)unmasked_key, - key->config.key_length, key_block); + HARDENED_TRY(hmac_hash_sha384((unsigned char *)unmasked_key, + key->config.key_length, + hmac_key->key_block)); + used_key_mode = launder32(kOtcryptoKeyModeHmacSha384); + break; case kOtcryptoKeyModeHmacSha512: - return hmac_hash_sha512((unsigned char *)unmasked_key, - key->config.key_length, key_block); + HARDENED_TRY(hmac_hash_sha512((unsigned char *)unmasked_key, + key->config.key_length, + hmac_key->key_block)); + used_key_mode = launder32(kOtcryptoKeyModeHmacSha512); + break; default: return OTCRYPTO_BAD_ARGS; } - // Should be unreachable. - HARDENED_TRAP(); - return OTCRYPTO_FATAL_ERR; + HARDENED_CHECK_EQ(used_key_mode, key->config.key_mode); } else { HARDENED_CHECK_LE(key->config.key_length, key_block_wordlen * sizeof(uint32_t)); - hardened_memcpy(key_block, unmasked_key, unmasked_key_len); + HARDENED_TRY( + hardened_memcpy(hmac_key->key_block, unmasked_key, unmasked_key_len)); // If the key size isn't a multiple of the word size, zero the last few // bytes. size_t offset = key->config.key_length % sizeof(uint32_t); if (offset != 0) { unsigned char *key_end_ptr = - (unsigned char *)(&key_block[unmasked_key_len]); + (unsigned char *)(&hmac_key->key_block[unmasked_key_len]); size_t num_zero_bytes = sizeof(uint32_t) - offset; memset(key_end_ptr - num_zero_bytes, 0, num_zero_bytes); } } + // Set the key length to the key block word length. + hmac_key->key_len = key_block_wordlen; + + // Create the checksum of the key and store it in the key structure. + if (launder32(hmac_key->key_len) > 0) { + hmac_key->checksum = hmac_key_integrity_checksum(hmac_key); + } else { + HARDENED_CHECK_EQ(hmac_key->key_len, 0); + } + return OTCRYPTO_OK; } @@ -101,10 +124,8 @@ static status_t check_key(const otcrypto_blinded_key_t *key) { return OTCRYPTO_BAD_ARGS; } - // The underlying HMAC hardware is unmasked, and does not have sideload - // support. - if (key->config.hw_backed != kHardenedBoolFalse || - key->config.security_level != kOtcryptoKeySecurityLevelLow) { + // The underlying HMAC hardware does not have sideload support. + if (key->config.hw_backed != kHardenedBoolFalse) { return OTCRYPTO_NOT_IMPLEMENTED; } @@ -130,28 +151,177 @@ otcrypto_status_t otcrypto_hmac(const otcrypto_blinded_key_t *key, // Check the key for null pointers or invalid configurations. HARDENED_TRY(check_key(key)); + if (key->config.security_level != kOtcryptoKeySecurityLevelLow) { + // Entropy complex must be initialized for `hardened_memeq`. + HARDENED_TRY(entropy_complex_check()); + } + // Call the appropriate function from the HMAC driver. + hmac_key_t hmac_key; switch (launder32(key->config.key_mode)) { case kOtcryptoKeyModeHmacSha256: { HARDENED_CHECK_EQ(key->config.key_mode, kOtcryptoKeyModeHmacSha256); - uint32_t key_block[kHmacSha256BlockWords]; - HARDENED_TRY(key_block_get(key, ARRAYSIZE(key_block), key_block)); - return hmac_hmac_sha256_cl(key_block, input_message.data, - input_message.len, tag.data); + HARDENED_TRY(hmac_key_construct(key, kHmacSha256BlockWords, &hmac_key)); + if (launder32(key->config.security_level) == + kOtcryptoKeySecurityLevelLow) { + // No protection against FI. + HARDENED_CHECK_EQ(key->config.security_level, + kOtcryptoKeySecurityLevelLow); + return hmac_hmac_sha256_cl(&hmac_key, input_message.data, + input_message.len, tag.data); + } else if (launder32(key->config.security_level) == + kOtcryptoKeySecurityLevelMedium) { + // Call the HMAC core twice and compare both tags. This serves as a FI + // countermeasure. + // First HMAC computation using the HMAC core. + HARDENED_CHECK_EQ(key->config.security_level, + kOtcryptoKeySecurityLevelMedium); + HARDENED_TRY(hmac_hmac_sha256_cl(&hmac_key, input_message.data, + input_message.len, tag.data)); + // Second HMAC computation using the HMAC core. + uint32_t tag_redundant[tag.len]; + hmac_key_t hmac_key_redundant; + HARDENED_TRY(hmac_key_construct(key, kHmacSha256BlockWords, + &hmac_key_redundant)); + HARDENED_TRY(hmac_hmac_sha256_cl(&hmac_key_redundant, + input_message.data, input_message.len, + tag_redundant)); + // Comparison of both tags. + HARDENED_CHECK_EQ( + hardened_memeq(&tag.data[0], &tag_redundant[0], tag.len), + kHardenedBoolTrue); + return OTCRYPTO_OK; + } else { + // Perform two HMAC operations. The first call uses the HMAC core. The + // second use uses a HMAC implementation that does not use the HMAC + // core. This serves as a FI countermeasure. + HARDENED_CHECK_EQ(key->config.security_level, + kOtcryptoKeySecurityLevelHigh); + // First HMAC computation using the HMAC core. + HARDENED_TRY(hmac_hmac_sha256_cl(&hmac_key, input_message.data, + input_message.len, tag.data)); + // Second HMAC computation without using the HMAC core. + uint32_t tag_redundant[tag.len]; + hmac_key_t hmac_key_redundant; + HARDENED_TRY(hmac_key_construct(key, kHmacSha256BlockWords, + &hmac_key_redundant)); + HARDENED_TRY( + hmac_hmac_sha256_redundant(&hmac_key_redundant, input_message.data, + input_message.len, tag_redundant)); + // Comparison of both tags. + HARDENED_CHECK_EQ( + hardened_memeq(&tag.data[0], &tag_redundant[0], tag.len), + kHardenedBoolTrue); + return OTCRYPTO_OK; + } } case kOtcryptoKeyModeHmacSha384: { HARDENED_CHECK_EQ(key->config.key_mode, kOtcryptoKeyModeHmacSha384); - uint32_t key_block[kHmacSha384BlockWords]; - HARDENED_TRY(key_block_get(key, ARRAYSIZE(key_block), key_block)); - return hmac_hmac_sha384(key_block, input_message.data, input_message.len, - tag.data); + HARDENED_TRY(hmac_key_construct(key, kHmacSha384BlockWords, &hmac_key)); + if (launder32(key->config.security_level) == + kOtcryptoKeySecurityLevelLow) { + HARDENED_CHECK_EQ(key->config.security_level, + kOtcryptoKeySecurityLevelLow); + return hmac_hmac_sha384(&hmac_key, input_message.data, + input_message.len, tag.data); + } else if (launder32(key->config.security_level) == + kOtcryptoKeySecurityLevelMedium) { + // Call the HMAC core twice and compare both tags. This serves as a FI + // countermeasure. + // First HMAC computation using the HMAC core. + HARDENED_CHECK_EQ(key->config.security_level, + kOtcryptoKeySecurityLevelMedium); + HARDENED_TRY(hmac_hmac_sha384(&hmac_key, input_message.data, + input_message.len, tag.data)); + // Second HMAC computation using the HMAC core. + uint32_t tag_redundant[tag.len]; + hmac_key_t hmac_key_redundant; + HARDENED_TRY(hmac_key_construct(key, kHmacSha384BlockWords, + &hmac_key_redundant)); + HARDENED_TRY(hmac_hmac_sha384(&hmac_key_redundant, input_message.data, + input_message.len, tag_redundant)); + // Comparison of both tags. + HARDENED_CHECK_EQ( + hardened_memeq(&tag.data[0], &tag_redundant[0], tag.len), + kHardenedBoolTrue); + return OTCRYPTO_OK; + } else { + // Perform two HMAC operations. The first call uses the HMAC core. The + // second use uses a HMAC implementation that does not use the HMAC + // core. This serves as a FI countermeasure. + HARDENED_CHECK_EQ(key->config.security_level, + kOtcryptoKeySecurityLevelHigh); + // First HMAC computation using the HMAC core. + HARDENED_TRY(hmac_hmac_sha384(&hmac_key, input_message.data, + input_message.len, tag.data)); + // Second HMAC computation without using the HMAC core. + uint32_t tag_redundant[tag.len]; + hmac_key_t hmac_key_redundant; + HARDENED_TRY(hmac_key_construct(key, kHmacSha384BlockWords, + &hmac_key_redundant)); + HARDENED_TRY( + hmac_hmac_sha384_redundant(&hmac_key_redundant, input_message.data, + input_message.len, tag_redundant)); + // Comparison of both tags. + HARDENED_CHECK_EQ( + hardened_memeq(&tag.data[0], &tag_redundant[0], tag.len), + kHardenedBoolTrue); + return OTCRYPTO_OK; + } } case kOtcryptoKeyModeHmacSha512: { HARDENED_CHECK_EQ(key->config.key_mode, kOtcryptoKeyModeHmacSha512); - uint32_t key_block[kHmacSha512BlockWords]; - HARDENED_TRY(key_block_get(key, ARRAYSIZE(key_block), key_block)); - return hmac_hmac_sha512(key_block, input_message.data, input_message.len, - tag.data); + HARDENED_TRY(hmac_key_construct(key, kHmacSha512BlockWords, &hmac_key)); + if (launder32(key->config.security_level) == + kOtcryptoKeySecurityLevelLow) { + HARDENED_CHECK_EQ(key->config.security_level, + kOtcryptoKeySecurityLevelLow); + return hmac_hmac_sha512(&hmac_key, input_message.data, + input_message.len, tag.data); + } else if (launder32(key->config.security_level) == + kOtcryptoKeySecurityLevelMedium) { + // Call the HMAC core twice and compare both tags. This serves as a FI + // countermeasure. + // First HMAC computation using the HMAC core. + HARDENED_CHECK_EQ(key->config.security_level, + kOtcryptoKeySecurityLevelMedium); + HARDENED_TRY(hmac_hmac_sha512(&hmac_key, input_message.data, + input_message.len, tag.data)); + // Second HMAC computation using the HMAC core. + uint32_t tag_redundant[tag.len]; + hmac_key_t hmac_key_redundant; + HARDENED_TRY(hmac_key_construct(key, kHmacSha512BlockWords, + &hmac_key_redundant)); + HARDENED_TRY(hmac_hmac_sha512(&hmac_key_redundant, input_message.data, + input_message.len, tag_redundant)); + // Comparison of both tags. + HARDENED_CHECK_EQ( + hardened_memeq(&tag.data[0], &tag_redundant[0], tag.len), + kHardenedBoolTrue); + return OTCRYPTO_OK; + } else { + // Perform two HMAC operations. The first call uses the HMAC core. The + // second use uses a HMAC implementation that does not use the HMAC + // core. This serves as a FI countermeasure. + HARDENED_CHECK_EQ(key->config.security_level, + kOtcryptoKeySecurityLevelHigh); + // First HMAC computation using the HMAC core. + HARDENED_TRY(hmac_hmac_sha512(&hmac_key, input_message.data, + input_message.len, tag.data)); + // Second HMAC computation without using the HMAC core. + uint32_t tag_redundant[tag.len]; + hmac_key_t hmac_key_redundant; + HARDENED_TRY(hmac_key_construct(key, kHmacSha512BlockWords, + &hmac_key_redundant)); + HARDENED_TRY( + hmac_hmac_sha512_redundant(&hmac_key_redundant, input_message.data, + input_message.len, tag_redundant)); + // Comparison of both tags. + HARDENED_CHECK_EQ( + hardened_memeq(&tag.data[0], &tag_redundant[0], tag.len), + kHardenedBoolTrue); + return OTCRYPTO_OK; + } } default: return OTCRYPTO_BAD_ARGS; @@ -171,24 +341,29 @@ otcrypto_status_t otcrypto_hmac_init(otcrypto_hmac_context_t *ctx, // Check the key for null pointers or invalid configurations. HARDENED_TRY(check_key(key)); + // Only security level low is supported for the streaming mode. + if (key->config.security_level != kOtcryptoKeySecurityLevelLow) { + return OTCRYPTO_NOT_IMPLEMENTED; + } + // Call the appropriate function from the HMAC driver. hmac_ctx_t hmac_ctx; - uint32_t key_block[kHmacMaxBlockWords]; + hmac_key_t hmac_key; switch (launder32(key->config.key_mode)) { case kOtcryptoKeyModeHmacSha256: HARDENED_CHECK_EQ(key->config.key_mode, kOtcryptoKeyModeHmacSha256); - HARDENED_TRY(key_block_get(key, kHmacSha256BlockWords, key_block)); - hmac_hmac_sha256_init_cl(key_block, &hmac_ctx); + HARDENED_TRY(hmac_key_construct(key, kHmacSha256BlockWords, &hmac_key)); + hmac_hmac_sha256_init_cl(hmac_key, &hmac_ctx); break; case kOtcryptoKeyModeHmacSha384: HARDENED_CHECK_EQ(key->config.key_mode, kOtcryptoKeyModeHmacSha384); - HARDENED_TRY(key_block_get(key, kHmacSha384BlockWords, key_block)); - hmac_hmac_sha384_init(key_block, &hmac_ctx); + HARDENED_TRY(hmac_key_construct(key, kHmacSha384BlockWords, &hmac_key)); + hmac_hmac_sha384_init(hmac_key, &hmac_ctx); break; case kOtcryptoKeyModeHmacSha512: HARDENED_CHECK_EQ(key->config.key_mode, kOtcryptoKeyModeHmacSha512); - HARDENED_TRY(key_block_get(key, kHmacSha512BlockWords, key_block)); - hmac_hmac_sha512_init(key_block, &hmac_ctx); + HARDENED_TRY(hmac_key_construct(key, kHmacSha512BlockWords, &hmac_key)); + hmac_hmac_sha512_init(hmac_key, &hmac_ctx); break; default: return OTCRYPTO_BAD_ARGS; diff --git a/sw/device/lib/crypto/impl/key_transport.c b/sw/device/lib/crypto/impl/key_transport.c index 8a68113fa362e..8f3c6bafcf2a4 100644 --- a/sw/device/lib/crypto/impl/key_transport.c +++ b/sw/device/lib/crypto/impl/key_transport.c @@ -6,6 +6,7 @@ #include "sw/device/lib/base/hardened_memory.h" #include "sw/device/lib/base/memory.h" +#include "sw/device/lib/crypto/drivers/entropy.h" #include "sw/device/lib/crypto/impl/aes_kwp/aes_kwp.h" #include "sw/device/lib/crypto/impl/integrity.h" #include "sw/device/lib/crypto/impl/keyblob.h" @@ -26,6 +27,9 @@ otcrypto_status_t otcrypto_symmetric_keygen( // hardware-backed or non-symmetric keys. HARDENED_TRY(keyblob_ensure_xor_masked(key->config)); + // Ensure that the entropy complex is initialized. + HARDENED_TRY(entropy_complex_check()); + // Get pointers to the shares within the keyblob. Fails if the key length // doesn't match the mode. uint32_t *share0; @@ -42,6 +46,10 @@ otcrypto_status_t otcrypto_symmetric_keygen( .len = keyblob_share_num_words(key->config), }; + // Randomize the memory before writing the shares. + HARDENED_TRY(hardened_memshred(share0_buf.data, share0_buf.len)); + HARDENED_TRY(hardened_memshred(share1_buf.data, share1_buf.len)); + // Construct an empty buffer for the "additional input" to the DRBG generate // function. otcrypto_const_byte_buf_t empty = {.data = NULL, .len = 0}; @@ -158,6 +166,9 @@ static status_t aes_kwp_key_construct(const otcrypto_blinded_key_t *key_kek, aes_key->key_shares[0] = share0; aes_key->key_shares[1] = share1; + // Create the checksum of the key and store it in the key structure. + aes_key->checksum = aes_key_integrity_checksum(aes_key); + return OTCRYPTO_OK; } @@ -169,6 +180,9 @@ otcrypto_status_t otcrypto_key_wrap(const otcrypto_blinded_key_t *key_to_wrap, return OTCRYPTO_BAD_ARGS; } + // Ensure the entropy complex is initialized. + HARDENED_TRY(entropy_complex_check()); + // Check the integrity of the key material we are wrapping. if (launder32(integrity_blinded_key_check(key_to_wrap)) != kHardenedBoolTrue) { @@ -205,11 +219,13 @@ otcrypto_status_t otcrypto_key_wrap(const otcrypto_blinded_key_t *key_to_wrap, uint32_t config_words = sizeof(otcrypto_key_config_t) / sizeof(uint32_t); size_t plaintext_num_words = config_words + 2 + keyblob_words; uint32_t plaintext[plaintext_num_words]; - hardened_memcpy(plaintext, (uint32_t *)&key_to_wrap->config, config_words); + HARDENED_TRY(hardened_memshred(plaintext, ARRAYSIZE(plaintext))); + HARDENED_TRY(hardened_memcpy(plaintext, (uint32_t *)&key_to_wrap->config, + config_words)); plaintext[config_words] = key_to_wrap->checksum; plaintext[config_words + 1] = keyblob_words; - hardened_memcpy(plaintext + config_words + 2, key_to_wrap->keyblob, - keyblob_words); + HARDENED_TRY(hardened_memcpy(plaintext + config_words + 2, + key_to_wrap->keyblob, keyblob_words)); // Wrap the key. return aes_kwp_wrap(kek, plaintext, sizeof(plaintext), wrapped_key.data); @@ -227,6 +243,9 @@ otcrypto_status_t otcrypto_key_unwrap(otcrypto_const_word32_buf_t wrapped_key, return OTCRYPTO_BAD_ARGS; } + // Ensure the entropy complex is initialized. + HARDENED_TRY(entropy_complex_check()); + // Check the integrity/lengths/mode of the key encryption key, and construct // an internal AES key. aes_key_t kek; @@ -239,6 +258,7 @@ otcrypto_status_t otcrypto_key_unwrap(otcrypto_const_word32_buf_t wrapped_key, // Unwrap the key. uint32_t plaintext[wrapped_key.len]; + HARDENED_TRY(hardened_memshred(plaintext, ARRAYSIZE(plaintext))); HARDENED_TRY(aes_kwp_unwrap(kek, wrapped_key.data, wrapped_key.len * sizeof(uint32_t), success, plaintext)); @@ -254,7 +274,8 @@ otcrypto_status_t otcrypto_key_unwrap(otcrypto_const_word32_buf_t wrapped_key, // Extract the key configuration. uint32_t config_words = sizeof(otcrypto_key_config_t) / sizeof(uint32_t); - hardened_memcpy((uint32_t *)&unwrapped_key->config, plaintext, config_words); + HARDENED_TRY(hardened_memcpy((uint32_t *)&unwrapped_key->config, plaintext, + config_words)); // Extract the checksum and keyblob length. unwrapped_key->checksum = plaintext[config_words]; @@ -268,8 +289,8 @@ otcrypto_status_t otcrypto_key_unwrap(otcrypto_const_word32_buf_t wrapped_key, if (unwrapped_key->keyblob_length != keyblob_words * sizeof(uint32_t)) { return OTCRYPTO_BAD_ARGS; } - hardened_memcpy(unwrapped_key->keyblob, plaintext + config_words + 2, - keyblob_words); + HARDENED_TRY(hardened_memcpy(unwrapped_key->keyblob, + plaintext + config_words + 2, keyblob_words)); // Finally, check the integrity of the key material we unwrapped. *success = integrity_blinded_key_check(unwrapped_key); @@ -323,6 +344,9 @@ otcrypto_status_t otcrypto_export_blinded_key( return OTCRYPTO_BAD_ARGS; } + // Ensure the entropy complex is initialized. + HARDENED_TRY(entropy_complex_check()); + // Check key integrity. if (launder32(integrity_blinded_key_check(blinded_key)) != kHardenedBoolTrue) { @@ -351,6 +375,10 @@ otcrypto_status_t otcrypto_export_blinded_key( HARDENED_CHECK_EQ(key_share1.len, keyblob_share_num_words(blinded_key->config)); + // Randomize the destination buffers. + HARDENED_TRY(hardened_memshred(key_share0.data, key_share0.len)); + HARDENED_TRY(hardened_memshred(key_share1.data, key_share1.len)); + // Check the length of the keyblob. size_t keyblob_words = launder32(keyblob_num_words(blinded_key->config)); if ((blinded_key->keyblob_length % sizeof(uint32_t) != 0) || @@ -365,7 +393,9 @@ otcrypto_status_t otcrypto_export_blinded_key( uint32_t *keyblob_share1; HARDENED_TRY( keyblob_to_shares(blinded_key, &keyblob_share0, &keyblob_share1)); - hardened_memcpy(key_share0.data, keyblob_share0, key_share0.len); - hardened_memcpy(key_share1.data, keyblob_share1, key_share1.len); + HARDENED_TRY( + hardened_memcpy(key_share0.data, keyblob_share0, key_share0.len)); + HARDENED_TRY( + hardened_memcpy(key_share1.data, keyblob_share1, key_share1.len)); return OTCRYPTO_OK; } diff --git a/sw/device/lib/crypto/impl/keyblob.c b/sw/device/lib/crypto/impl/keyblob.c index 7183ee6b1def1..de3efa0a8107b 100644 --- a/sw/device/lib/crypto/impl/keyblob.c +++ b/sw/device/lib/crypto/impl/keyblob.c @@ -97,9 +97,12 @@ status_t keyblob_from_shares(const uint32_t *share0, const uint32_t *share1, // Entropy complex must be initialized for `hardened_memcpy`. HARDENED_TRY(entropy_complex_check()); + // Randomize the keyblob contents before writing shares. + HARDENED_TRY(hardened_memshred(keyblob, keyblob_num_words(config))); + size_t share_words = keyblob_share_num_words(config); - hardened_memcpy(keyblob, share0, share_words); - hardened_memcpy(keyblob + share_words, share1, share_words); + HARDENED_TRY(hardened_memcpy(keyblob, share0, share_words)); + HARDENED_TRY(hardened_memcpy(keyblob + share_words, share1, share_words)); return OTCRYPTO_OK; } @@ -113,7 +116,8 @@ status_t keyblob_buffer_to_keymgr_diversification( HARDENED_TRY(entropy_complex_check()); // Copy the remainder of the keyblob into the salt. - hardened_memcpy(diversification->salt, &keyblob[1], kKeymgrSaltNumWords - 1); + HARDENED_TRY(hardened_memcpy(diversification->salt, &keyblob[1], + kKeymgrSaltNumWords - 1)); // Set the key mode as the last word of the salt. diversification->salt[kKeymgrSaltNumWords - 1] = launder32(mode); @@ -221,11 +225,11 @@ status_t keyblob_remask(otcrypto_blinded_key_t *key) { // Generate a fresh mask the size of one share. size_t key_share_words = keyblob_share_num_words(key->config); uint32_t mask[key_share_words]; - hardened_memshred(mask, key_share_words); + HARDENED_TRY(hardened_memshred(mask, key_share_words)); // XOR each share with the mask. - hardened_xor(share0, mask, key_share_words); - hardened_xor(share1, mask, key_share_words); + HARDENED_TRY(hardened_xor_in_place(share0, mask, key_share_words)); + HARDENED_TRY(hardened_xor_in_place(share1, mask, key_share_words)); // Update the key checksum. key->checksum = integrity_blinded_checksum(key); diff --git a/sw/device/lib/crypto/impl/kmac_kdf.c b/sw/device/lib/crypto/impl/kmac_kdf.c index 2217c6ea41ac9..566c571bf7656 100644 --- a/sw/device/lib/crypto/impl/kmac_kdf.c +++ b/sw/device/lib/crypto/impl/kmac_kdf.c @@ -4,6 +4,7 @@ #include "sw/device/lib/crypto/include/kmac_kdf.h" +#include "sw/device/lib/base/hardened_memory.h" #include "sw/device/lib/base/math.h" #include "sw/device/lib/crypto/drivers/kmac.h" #include "sw/device/lib/crypto/impl/integrity.h" @@ -107,6 +108,11 @@ otcrypto_status_t otcrypto_kmac_kdf( return OTCRYPTO_BAD_ARGS; } + // Randomize the keyblob memory. + HARDENED_TRY( + hardened_memshred(output_key_material->keyblob, + keyblob_num_words(output_key_material->config))); + switch (launder32(key_derivation_key->config.key_mode)) { case kOtcryptoKeyModeKdfKmac128: { HARDENED_CHECK_EQ(key_derivation_key->config.key_mode, diff --git a/sw/device/lib/crypto/impl/rsa.c b/sw/device/lib/crypto/impl/rsa.c index a612208f1a77d..0505984a40ca5 100644 --- a/sw/device/lib/crypto/impl/rsa.c +++ b/sw/device/lib/crypto/impl/rsa.c @@ -5,6 +5,7 @@ #include "sw/device/lib/crypto/include/rsa.h" #include "sw/device/lib/base/hardened_memory.h" +#include "sw/device/lib/base/math.h" #include "sw/device/lib/crypto/drivers/entropy.h" #include "sw/device/lib/crypto/impl/integrity.h" #include "sw/device/lib/crypto/impl/rsa/rsa_encryption.h" @@ -87,7 +88,7 @@ otcrypto_status_t otcrypto_rsa_public_key_construct( } rsa_2048_public_key_t *pk = (rsa_2048_public_key_t *)public_key->key; pk->e = exponent; - hardened_memcpy(pk->n.data, modulus.data, modulus.len); + HARDENED_TRY(hardened_memcpy(pk->n.data, modulus.data, modulus.len)); break; } case kOtcryptoRsaSize3072: { @@ -97,7 +98,7 @@ otcrypto_status_t otcrypto_rsa_public_key_construct( } rsa_3072_public_key_t *pk = (rsa_3072_public_key_t *)public_key->key; pk->e = exponent; - hardened_memcpy(pk->n.data, modulus.data, modulus.len); + HARDENED_TRY(hardened_memcpy(pk->n.data, modulus.data, modulus.len)); break; } case kOtcryptoRsaSize4096: { @@ -107,7 +108,7 @@ otcrypto_status_t otcrypto_rsa_public_key_construct( } rsa_4096_public_key_t *pk = (rsa_4096_public_key_t *)public_key->key; pk->e = exponent; - hardened_memcpy(pk->n.data, modulus.data, modulus.len); + HARDENED_TRY(hardened_memcpy(pk->n.data, modulus.data, modulus.len)); break; } default: @@ -201,6 +202,11 @@ otcrypto_status_t otcrypto_rsa_private_key_from_exponents( // Check the mode and lengths for the private key. HARDENED_TRY(private_key_structural_check(size, private_key)); + // Randomize the keyblob. + HARDENED_TRY(hardened_memshred( + private_key->keyblob, + ceil_div(private_key->keyblob_length, sizeof(uint32_t)))); + switch (size) { case kOtcryptoRsaSize2048: { if (private_key->keyblob_length != sizeof(rsa_2048_private_key_t) || @@ -209,8 +215,8 @@ otcrypto_status_t otcrypto_rsa_private_key_from_exponents( } rsa_2048_private_key_t *sk = (rsa_2048_private_key_t *)private_key->keyblob; - hardened_memcpy(sk->n.data, modulus.data, modulus.len); - hardened_memcpy(sk->d.data, d_share0.data, d_share0.len); + HARDENED_TRY(hardened_memcpy(sk->n.data, modulus.data, modulus.len)); + HARDENED_TRY(hardened_memcpy(sk->d.data, d_share0.data, d_share0.len)); // TODO: RSA keys are currently unblinded, so combine the shares. for (size_t i = 0; i < d_share1.len; i++) { sk->d.data[i] ^= d_share1.data[i]; @@ -224,8 +230,8 @@ otcrypto_status_t otcrypto_rsa_private_key_from_exponents( } rsa_3072_private_key_t *sk = (rsa_3072_private_key_t *)private_key->keyblob; - hardened_memcpy(sk->n.data, modulus.data, modulus.len); - hardened_memcpy(sk->d.data, d_share0.data, d_share0.len); + HARDENED_TRY(hardened_memcpy(sk->n.data, modulus.data, modulus.len)); + HARDENED_TRY(hardened_memcpy(sk->d.data, d_share0.data, d_share0.len)); // TODO: RSA keys are currently unblinded, so combine the shares. for (size_t i = 0; i < d_share1.len; i++) { sk->d.data[i] ^= d_share1.data[i]; @@ -239,8 +245,8 @@ otcrypto_status_t otcrypto_rsa_private_key_from_exponents( } rsa_4096_private_key_t *sk = (rsa_4096_private_key_t *)private_key->keyblob; - hardened_memcpy(sk->n.data, modulus.data, modulus.len); - hardened_memcpy(sk->d.data, d_share0.data, d_share0.len); + HARDENED_TRY(hardened_memcpy(sk->n.data, modulus.data, modulus.len)); + HARDENED_TRY(hardened_memcpy(sk->d.data, d_share0.data, d_share0.len)); // TODO: RSA keys are currently unblinded, so combine the shares. for (size_t i = 0; i < d_share1.len; i++) { sk->d.data[i] ^= d_share1.data[i]; @@ -460,6 +466,11 @@ otcrypto_status_t otcrypto_rsa_keygen_async_finalize( // Check the caller-provided private key buffer. HARDENED_TRY(private_key_structural_check(size, private_key)); + // Randomize the keyblob memory. + HARDENED_TRY(hardened_memshred( + private_key->keyblob, + ceil_div(private_key->keyblob_length, sizeof(uint32_t)))); + // Call the required finalize() operation. switch (size) { case kOtcryptoRsaSize2048: { @@ -530,7 +541,7 @@ otcrypto_status_t otcrypto_rsa_keypair_from_cofactor_async_start( cf->data[i] ^= cofactor_share1.data[i]; } rsa_2048_public_key_t pk; - hardened_memcpy(pk.n.data, modulus.data, modulus.len); + HARDENED_TRY(hardened_memcpy(pk.n.data, modulus.data, modulus.len)); pk.e = e; return rsa_keygen_from_cofactor_2048_start(&pk, cf); } @@ -570,6 +581,11 @@ otcrypto_status_t otcrypto_rsa_keypair_from_cofactor_async_finalize( // Check the caller-provided private key buffer. HARDENED_TRY(private_key_structural_check(size, private_key)); + // Randomize the keyblob memory. + HARDENED_TRY(hardened_memshred( + private_key->keyblob, + ceil_div(private_key->keyblob_length, sizeof(uint32_t)))); + // Call the required finalize() operation. switch (size) { case kOtcryptoRsaSize2048: { diff --git a/sw/device/lib/crypto/impl/rsa/rsa_3072_verify.c b/sw/device/lib/crypto/impl/rsa/rsa_3072_verify.c index f445dd9e0f82f..bb68e4f627101 100644 --- a/sw/device/lib/crypto/impl/rsa/rsa_3072_verify.c +++ b/sw/device/lib/crypto/impl/rsa/rsa_3072_verify.c @@ -138,16 +138,18 @@ status_t rsa_3072_compute_constants(const rsa_3072_public_key_t *public_key, HARDENED_TRY(otbn_execute()); // Spin here waiting for OTBN to complete. - HARDENED_TRY(otbn_busy_wait_for_done()); + HARDENED_TRY_WIPE_DMEM(otbn_busy_wait_for_done()); // Read constant rr out of DMEM. - HARDENED_TRY(read_rsa_3072_int_from_otbn(kOtbnVarRsaRR, &result->rr)); + HARDENED_TRY_WIPE_DMEM( + read_rsa_3072_int_from_otbn(kOtbnVarRsaRR, &result->rr)); // Read constant m0_inv out of DMEM. - HARDENED_TRY( + HARDENED_TRY_WIPE_DMEM( otbn_dmem_read(kOtbnWideWordNumWords, kOtbnVarRsaM0Inv, result->m0_inv)); - return OTCRYPTO_OK; + // Wipe DMEM. + return otbn_dmem_sec_wipe(); } status_t rsa_3072_verify_start(const rsa_3072_int_t *signature, @@ -197,11 +199,11 @@ status_t rsa_3072_verify_finalize(const rsa_3072_int_t *message, *result = kHardenedBoolFalse; // Spin here waiting for OTBN to complete. - HARDENED_TRY(otbn_busy_wait_for_done()); + HARDENED_TRY_WIPE_DMEM(otbn_busy_wait_for_done()); // Read recovered message out of OTBN dmem. rsa_3072_int_t recoveredMessage; - HARDENED_TRY( + HARDENED_TRY_WIPE_DMEM( read_rsa_3072_int_from_otbn(kOtbnVarRsaOutBuf, &recoveredMessage)); // TODO: harden this memory comparison @@ -213,7 +215,8 @@ status_t rsa_3072_verify_finalize(const rsa_3072_int_t *message, } } - return OTCRYPTO_OK; + // Wipe DMEM. + return otbn_dmem_sec_wipe(); } status_t rsa_3072_verify(const rsa_3072_int_t *signature, diff --git a/sw/device/lib/crypto/impl/rsa/rsa_encryption.c b/sw/device/lib/crypto/impl/rsa/rsa_encryption.c index d6c0d15981b49..479ecb46bdeb9 100644 --- a/sw/device/lib/crypto/impl/rsa/rsa_encryption.c +++ b/sw/device/lib/crypto/impl/rsa/rsa_encryption.c @@ -7,6 +7,7 @@ #include "sw/device/lib/base/hardened.h" #include "sw/device/lib/base/hardened_memory.h" #include "sw/device/lib/base/math.h" +#include "sw/device/lib/base/memory.h" #include "sw/device/lib/crypto/drivers/entropy.h" #include "sw/device/lib/crypto/impl/rsa/rsa_modexp.h" #include "sw/device/lib/crypto/impl/rsa/rsa_padding.h" @@ -20,6 +21,8 @@ status_t rsa_encrypt_2048_start(const rsa_2048_public_key_t *public_key, const uint8_t *label, size_t label_bytelen) { // Encode the message. rsa_2048_int_t encoded_message; + HARDENED_TRY( + hardened_memshred(encoded_message.data, ARRAYSIZE(encoded_message.data))); HARDENED_TRY(rsa_padding_oaep_encode( hash_mode, message, message_bytelen, label, label_bytelen, ARRAYSIZE(encoded_message.data), encoded_message.data)); @@ -43,27 +46,31 @@ status_t rsa_decrypt_2048_start(const rsa_2048_private_key_t *private_key, status_t rsa_decrypt_finalize(const otcrypto_hash_mode_t hash_mode, const uint8_t *label, size_t label_bytelen, - size_t plaintext_max_wordlen, uint8_t *plaintext, + size_t plaintext_max_bytelen, uint8_t *plaintext, size_t *plaintext_len) { // Wait for OTBN to complete and get the size for the last RSA operation. size_t num_words; HARDENED_TRY(rsa_modexp_wait(&num_words)); - // Guard against overflow in the plaintext length checks. - if (plaintext_max_wordlen > UINT32_MAX / sizeof(uint32_t)) { - return OTCRYPTO_BAD_ARGS; - } - // Check that enough space has been allocated for the plaintext. size_t max_plaintext_bytelen = 0; HARDENED_TRY(rsa_padding_oaep_max_message_bytelen(hash_mode, num_words, &max_plaintext_bytelen)); - if (plaintext_max_wordlen < - ceil_div(max_plaintext_bytelen, sizeof(uint32_t))) { + if (plaintext_max_bytelen < max_plaintext_bytelen) { return OTCRYPTO_BAD_ARGS; } - HARDENED_CHECK_GE(plaintext_max_wordlen * sizeof(uint32_t), - max_plaintext_bytelen); + HARDENED_CHECK_GE(plaintext_max_bytelen, max_plaintext_bytelen); + + // Randomize the plaintext destination buffer as best we can, considering its + // alignment. + ptrdiff_t misalignment = misalignment32_of((uintptr_t)plaintext); + size_t aligned_offset = + (sizeof(uint32_t) - (size_t)misalignment) % sizeof(uint32_t); + size_t num_aligned_full_words = + (plaintext_max_bytelen - aligned_offset) / sizeof(uint32_t); + HARDENED_TRY( + hardened_memshred((uint32_t *)((uintptr_t)plaintext + aligned_offset), + num_aligned_full_words)); // Call the appropriate `finalize()` operation to get the recovered encoded // message. @@ -105,6 +112,8 @@ status_t rsa_encrypt_3072_start(const rsa_3072_public_key_t *public_key, const uint8_t *label, size_t label_bytelen) { // Encode the message. rsa_3072_int_t encoded_message; + HARDENED_TRY( + hardened_memshred(encoded_message.data, ARRAYSIZE(encoded_message.data))); HARDENED_TRY(rsa_padding_oaep_encode( hash_mode, message, message_bytelen, label, label_bytelen, ARRAYSIZE(encoded_message.data), encoded_message.data)); @@ -132,6 +141,8 @@ status_t rsa_encrypt_4096_start(const rsa_4096_public_key_t *public_key, const uint8_t *label, size_t label_bytelen) { // Encode the message. rsa_4096_int_t encoded_message; + HARDENED_TRY( + hardened_memshred(encoded_message.data, ARRAYSIZE(encoded_message.data))); HARDENED_TRY(rsa_padding_oaep_encode( hash_mode, message, message_bytelen, label, label_bytelen, ARRAYSIZE(encoded_message.data), encoded_message.data)); diff --git a/sw/device/lib/crypto/impl/rsa/rsa_keygen.c b/sw/device/lib/crypto/impl/rsa/rsa_keygen.c index 280f6bea27695..a86cb1b36620c 100644 --- a/sw/device/lib/crypto/impl/rsa/rsa_keygen.c +++ b/sw/device/lib/crypto/impl/rsa/rsa_keygen.c @@ -79,25 +79,24 @@ static status_t keygen_start(uint32_t mode) { static status_t keygen_finalize(uint32_t exp_mode, size_t num_words, uint32_t *n, uint32_t *d) { // Spin here waiting for OTBN to complete. - HARDENED_TRY(otbn_busy_wait_for_done()); + HARDENED_TRY_WIPE_DMEM(otbn_busy_wait_for_done()); // Read the mode from OTBN dmem and panic if it's not as expected. uint32_t act_mode = 0; - HARDENED_TRY(otbn_dmem_read(1, kOtbnVarRsaMode, &act_mode)); + HARDENED_TRY_WIPE_DMEM(otbn_dmem_read(1, kOtbnVarRsaMode, &act_mode)); if (act_mode != exp_mode) { + HARDENED_TRY(otbn_dmem_sec_wipe()); return OTCRYPTO_FATAL_ERR; } // Read the public modulus (n) from OTBN dmem. - HARDENED_TRY(otbn_dmem_read(num_words, kOtbnVarRsaN, n)); + HARDENED_TRY_WIPE_DMEM(otbn_dmem_read(num_words, kOtbnVarRsaN, n)); // Read the private exponent (d) from OTBN dmem. - HARDENED_TRY(otbn_dmem_read(num_words, kOtbnVarRsaD, d)); + HARDENED_TRY_WIPE_DMEM(otbn_dmem_read(num_words, kOtbnVarRsaD, d)); // Wipe DMEM. - HARDENED_TRY(otbn_dmem_sec_wipe()); - - return OTCRYPTO_OK; + return otbn_dmem_sec_wipe(); } status_t rsa_keygen_2048_start(void) { @@ -110,8 +109,8 @@ status_t rsa_keygen_2048_finalize(rsa_2048_public_key_t *public_key, private_key->n.data, private_key->d.data)); // Copy the modulus to the public key. - hardened_memcpy(public_key->n.data, private_key->n.data, - ARRAYSIZE(private_key->n.data)); + HARDENED_TRY(hardened_memcpy(public_key->n.data, private_key->n.data, + ARRAYSIZE(private_key->n.data))); // Set the public exponent to F4, the only exponent our key generation // algorithm supports. @@ -130,8 +129,8 @@ status_t rsa_keygen_3072_finalize(rsa_3072_public_key_t *public_key, private_key->n.data, private_key->d.data)); // Copy the modulus to the public key. - hardened_memcpy(public_key->n.data, private_key->n.data, - ARRAYSIZE(private_key->n.data)); + HARDENED_TRY(hardened_memcpy(public_key->n.data, private_key->n.data, + ARRAYSIZE(private_key->n.data))); // Set the public exponent to F4, the only exponent our key generation // algorithm supports. @@ -150,8 +149,8 @@ status_t rsa_keygen_4096_finalize(rsa_4096_public_key_t *public_key, private_key->n.data, private_key->d.data)); // Copy the modulus to the public key. - hardened_memcpy(public_key->n.data, private_key->n.data, - ARRAYSIZE(private_key->n.data)); + HARDENED_TRY(hardened_memcpy(public_key->n.data, private_key->n.data, + ARRAYSIZE(private_key->n.data))); // Set the public exponent to F4, the only exponent our key generation // algorithm supports. @@ -189,8 +188,8 @@ status_t rsa_keygen_from_cofactor_2048_finalize( private_key->n.data, private_key->d.data)); // Copy the modulus to the public key. - hardened_memcpy(public_key->n.data, private_key->n.data, - ARRAYSIZE(private_key->n.data)); + HARDENED_TRY(hardened_memcpy(public_key->n.data, private_key->n.data, + ARRAYSIZE(private_key->n.data))); // Set the public exponent to F4, the only exponent our key generation // algorithm supports. diff --git a/sw/device/lib/crypto/impl/rsa/rsa_modexp.c b/sw/device/lib/crypto/impl/rsa/rsa_modexp.c index ba30c5de72ebb..c1d738301124a 100644 --- a/sw/device/lib/crypto/impl/rsa/rsa_modexp.c +++ b/sw/device/lib/crypto/impl/rsa/rsa_modexp.c @@ -45,11 +45,11 @@ enum { status_t rsa_modexp_wait(size_t *num_words) { // Spin here waiting for OTBN to complete. - HARDENED_TRY(otbn_busy_wait_for_done()); + HARDENED_TRY_WIPE_DMEM(otbn_busy_wait_for_done()); // Read the application mode. uint32_t mode; - HARDENED_TRY(otbn_dmem_read(1, kOtbnVarRsaMode, &mode)); + HARDENED_TRY_WIPE_DMEM(otbn_dmem_read(1, kOtbnVarRsaMode, &mode)); *num_words = 0; switch (mode) { @@ -70,6 +70,8 @@ status_t rsa_modexp_wait(size_t *num_words) { break; default: // Unrecognized mode. + // Wipe DMEM. + HARDENED_TRY(otbn_dmem_sec_wipe()); return OTCRYPTO_FATAL_ERR; } @@ -89,15 +91,17 @@ status_t rsa_modexp_wait(size_t *num_words) { static status_t rsa_modexp_finalize(const size_t num_words, uint32_t *result) { // Wait for OTBN to complete and get the result size. size_t num_words_inferred; - HARDENED_TRY(rsa_modexp_wait(&num_words_inferred)); + HARDENED_TRY_WIPE_DMEM(rsa_modexp_wait(&num_words_inferred)); // Check that the inferred result size matches expectations. if (num_words != num_words_inferred) { + // Wipe DMEM. + HARDENED_TRY(otbn_dmem_sec_wipe()); return OTCRYPTO_FATAL_ERR; } // Read the result. - HARDENED_TRY(otbn_dmem_read(num_words, kOtbnVarRsaInOut, result)); + HARDENED_TRY_WIPE_DMEM(otbn_dmem_read(num_words, kOtbnVarRsaInOut, result)); // Wipe DMEM. return otbn_dmem_sec_wipe(); diff --git a/sw/device/lib/crypto/impl/rsa/rsa_padding.c b/sw/device/lib/crypto/impl/rsa/rsa_padding.c index 416c3f34e7c07..995aef4d80b9e 100644 --- a/sw/device/lib/crypto/impl/rsa/rsa_padding.c +++ b/sw/device/lib/crypto/impl/rsa/rsa_padding.c @@ -342,7 +342,8 @@ static status_t mgf1(otcrypto_hash_mode_t hash_mode, const uint8_t *seed, // Digest won't fit in mask (last iteration). Use a temporary buffer. uint32_t digest[digest_wordlen]; HARDENED_TRY(hash(hash_mode, hash_input, sizeof(hash_input), digest)); - hardened_memcpy(mask, digest, ceil_div(mask_len, sizeof(uint32_t))); + HARDENED_TRY( + hardened_memcpy(mask, digest, ceil_div(mask_len, sizeof(uint32_t)))); mask_len = 0; } else { HARDENED_TRY(hash(hash_mode, hash_input, sizeof(hash_input), mask)); @@ -396,9 +397,10 @@ static status_t pss_construct_h(const otcrypto_hash_digest_t message_digest, m_prime[1] = 0; uint32_t *digest_dst = &m_prime[2]; uint32_t *salt_dst = digest_dst + message_digest.len; - hardened_memcpy(digest_dst, message_digest.data, message_digest.len); + HARDENED_TRY( + hardened_memcpy(digest_dst, message_digest.data, message_digest.len)); if (salt_len > 0) { - hardened_memcpy(salt_dst, salt, salt_len); + HARDENED_TRY(hardened_memcpy(salt_dst, salt, salt_len)); } // Construct H = Hash(M'). @@ -450,7 +452,7 @@ status_t rsa_padding_pss_encode(const otcrypto_hash_digest_t message_digest, // Compute the final encoded message and reverse the byte-order. // EM = maskedDB || H || 0xbc unsigned char *encoded_message_bytes = (unsigned char *)encoded_message; - hardened_memcpy(encoded_message, db, ARRAYSIZE(db)); + HARDENED_TRY(hardened_memcpy(encoded_message, db, ARRAYSIZE(db))); memcpy(encoded_message_bytes + db_bytelen, h, sizeof(h)); encoded_message_bytes[encoded_message_bytelen - 1] = 0xbc; reverse_bytes(encoded_message_len, encoded_message); diff --git a/sw/device/lib/crypto/impl/rsa/rsa_signature.c b/sw/device/lib/crypto/impl/rsa/rsa_signature.c index 9185fce68bb71..7506c797b61e1 100644 --- a/sw/device/lib/crypto/impl/rsa/rsa_signature.c +++ b/sw/device/lib/crypto/impl/rsa/rsa_signature.c @@ -74,11 +74,13 @@ static status_t message_encode(const otcrypto_hash_digest_t message_digest, // Check that the digest length is OK. HARDENED_TRY(digest_check(message_digest)); - switch (padding_mode) { + switch (launder32(padding_mode)) { case kRsaSignaturePaddingPkcs1v15: + HARDENED_CHECK_EQ(padding_mode, kRsaSignaturePaddingPkcs1v15); return rsa_padding_pkcs1v15_encode(message_digest, encoded_message_len, encoded_message); case kRsaSignaturePaddingPss: { + HARDENED_CHECK_EQ(padding_mode, kRsaSignaturePaddingPss); // Generate a random salt value whose length matches the digest length. uint32_t salt[message_digest.len]; HARDENED_TRY(entropy_complex_check()); @@ -126,11 +128,13 @@ static status_t encoded_message_verify( // Check that the digest length is OK. HARDENED_TRY(digest_check(message_digest)); - switch (padding_mode) { + switch (launder32(padding_mode)) { case kRsaSignaturePaddingPkcs1v15: + HARDENED_CHECK_EQ(padding_mode, kRsaSignaturePaddingPkcs1v15); return rsa_padding_pkcs1v15_verify(message_digest, encoded_message, encoded_message_len, result); case kRsaSignaturePaddingPss: + HARDENED_CHECK_EQ(padding_mode, kRsaSignaturePaddingPss); return rsa_padding_pss_verify(message_digest, encoded_message, encoded_message_len, result); default: diff --git a/sw/device/lib/crypto/impl/sha2/sha256.c b/sw/device/lib/crypto/impl/sha2/sha256.c index 9ace990dcb2db..e06d150d55535 100644 --- a/sw/device/lib/crypto/impl/sha2/sha256.c +++ b/sw/device/lib/crypto/impl/sha2/sha256.c @@ -65,13 +65,16 @@ static const otbn_addr_t kOtbnVarSha256Msg = OTBN_ADDR_T_INIT(run_sha256, msg); static const otbn_addr_t kOtbnVarSha256NumMsgChunks = OTBN_ADDR_T_INIT(run_sha256, num_msg_chunks); -void sha256_init(sha256_state_t *state) { +status_t sha256_init(sha256_state_t *state) { // Set the initial state. - hardened_memcpy(state->H, kSha256InitialState, kSha256StateWords); + HARDENED_TRY( + hardened_memcpy(state->H, kSha256InitialState, kSha256StateWords)); // Set the partial block to 0 (the value is ignored). memset(state->partial_block, 0, kSha256MessageBlockBytes); // Set the message length so far to 0. state->total_len = 0ull; + + return OTCRYPTO_OK; } /** @@ -87,7 +90,7 @@ static status_t process_message_buffer(sha256_otbn_ctx_t *ctx) { // Run the OTBN program. HARDENED_TRY(otbn_execute()); - HARDENED_TRY(otbn_busy_wait_for_done()); + HARDENED_TRY_WIPE_DMEM(otbn_busy_wait_for_done()); // Reset the message buffer counter. ctx->num_blocks = 0; @@ -245,7 +248,7 @@ static status_t process_message(sha256_state_t *state, const uint8_t *msg, } // Read the final state from OTBN dmem. - HARDENED_TRY( + HARDENED_TRY_WIPE_DMEM( otbn_dmem_read(kSha256StateWords, kOtbnVarSha256State, new_state.H)); // Clear OTBN's memory. @@ -253,8 +256,9 @@ static status_t process_message(sha256_state_t *state, const uint8_t *msg, // At this point, no more errors are possible; it is safe to update the // context object. - hardened_memcpy(state->H, new_state.H, kSha256StateWords); - hardened_memcpy(state->partial_block, block.data, kSha256MessageBlockWords); + HARDENED_TRY(hardened_memcpy(state->H, new_state.H, kSha256StateWords)); + HARDENED_TRY(hardened_memcpy(state->partial_block, block.data, + kSha256MessageBlockWords)); state->total_len = new_state.total_len; return OTCRYPTO_OK; } @@ -271,11 +275,15 @@ status_t sha256_update(sha256_state_t *state, const uint8_t *msg, * The entropy complex must be initialized before calling this function. * * @param state The context object to shred. + * @return OK or error. */ -static void state_shred(sha256_state_t *state) { - hardened_memshred(state->H, kSha256StateWords); - hardened_memshred(state->partial_block, kSha256MessageBlockWords); +static status_t state_shred(sha256_state_t *state) { + HARDENED_TRY(hardened_memshred(state->H, kSha256StateWords)); + HARDENED_TRY( + hardened_memshred(state->partial_block, kSha256MessageBlockWords)); state->total_len = 0; + + return OTCRYPTO_OK; } /** @@ -290,14 +298,17 @@ static void state_shred(sha256_state_t *state) { * * @param state Context object. * @param[out] digest Destination buffer for digest. + * @return OK or error. */ -static void digest_get(sha256_state_t *state, uint32_t *digest) { +static status_t digest_get(sha256_state_t *state, uint32_t *digest) { for (size_t i = 0; i < kSha256StateWords / 2; i++) { uint32_t tmp = __builtin_bswap32(state->H[i]); state->H[i] = __builtin_bswap32(state->H[kSha256StateWords - 1 - i]); state->H[kSha256StateWords - 1 - i] = tmp; } - hardened_memcpy(digest, state->H, kSha256StateWords); + HARDENED_TRY(hardened_memcpy(digest, state->H, kSha256StateWords)); + + return OTCRYPTO_OK; } status_t sha256_final(sha256_state_t *state, uint32_t *digest) { @@ -308,8 +319,8 @@ status_t sha256_final(sha256_state_t *state, uint32_t *digest) { HARDENED_TRY(process_message(state, NULL, 0, kHardenedBoolTrue)); // Retrieve the final digest and destroy the state. - digest_get(state, digest); - state_shred(state); + HARDENED_TRY(digest_get(state, digest)); + HARDENED_TRY(state_shred(state)); return OTCRYPTO_OK; } @@ -318,13 +329,13 @@ status_t sha256(const uint8_t *msg, const size_t msg_len, uint32_t *digest) { HARDENED_TRY(entropy_complex_check()); sha256_state_t state; - sha256_init(&state); + HARDENED_TRY(sha256_init(&state)); // Process data with padding enabled. HARDENED_TRY(process_message(&state, msg, msg_len, kHardenedBoolTrue)); // Retrieve the final digest and destroy the state. - digest_get(&state, digest); - state_shred(&state); + HARDENED_TRY(digest_get(&state, digest)); + HARDENED_TRY(state_shred(&state)); return OTCRYPTO_OK; } diff --git a/sw/device/lib/crypto/impl/sha2/sha256.h b/sw/device/lib/crypto/impl/sha2/sha256.h index bf77b649331b5..e070f2eee54f5 100644 --- a/sw/device/lib/crypto/impl/sha2/sha256.h +++ b/sw/device/lib/crypto/impl/sha2/sha256.h @@ -105,7 +105,7 @@ status_t sha256(const uint8_t *msg, const size_t msg_len, uint32_t *digest); * @param[out] state Hash context object to initialize. * @return Result of the operation (OK or error). */ -void sha256_init(sha256_state_t *state); +status_t sha256_init(sha256_state_t *state); /** * Process new message data for a SHA-256 hash computation. diff --git a/sw/device/lib/crypto/impl/sha2/sha512.c b/sw/device/lib/crypto/impl/sha2/sha512.c index d0154dd8e59bb..9bc9d605376a3 100644 --- a/sw/device/lib/crypto/impl/sha2/sha512.c +++ b/sw/device/lib/crypto/impl/sha2/sha512.c @@ -70,24 +70,30 @@ static const otbn_addr_t kOtbnVarSha512Msg = OTBN_ADDR_T_INIT(run_sha512, msg); static const otbn_addr_t kOtbnVarSha512NChunks = OTBN_ADDR_T_INIT(run_sha512, n_chunks); -void sha512_init(sha512_state_t *state) { +status_t sha512_init(sha512_state_t *state) { // Set the initial state. - hardened_memcpy(state->H, kSha512InitialState, kSha512StateWords); + HARDENED_TRY( + hardened_memcpy(state->H, kSha512InitialState, kSha512StateWords)); // Set the partial block to 0 (the value is ignored). memset(state->partial_block, 0, sizeof(state->partial_block)); // Set the message length so far to 0. state->total_len.lower = 0; state->total_len.upper = 0; + + return OTCRYPTO_OK; } -void sha384_init(sha512_state_t *state) { +status_t sha384_init(sha512_state_t *state) { // Set the initial state. - hardened_memcpy(state->H, kSha384InitialState, kSha512StateWords); + HARDENED_TRY( + hardened_memcpy(state->H, kSha384InitialState, kSha512StateWords)); // Set the partial block to 0 (the value is ignored). memset(state->partial_block, 0, sizeof(state->partial_block)); // Set the message length so far to 0. state->total_len.lower = 0; state->total_len.upper = 0; + + return OTCRYPTO_OK; } /** @@ -135,7 +141,7 @@ static status_t process_message_buffer(sha512_otbn_ctx_t *ctx) { // Run the OTBN program. HARDENED_TRY(otbn_execute()); - HARDENED_TRY(otbn_busy_wait_for_done()); + HARDENED_TRY_WIPE_DMEM(otbn_busy_wait_for_done()); // Reset the message buffer counter. ctx->num_blocks = 0; @@ -270,7 +276,8 @@ static status_t process_message(sha512_state_t *state, const uint8_t *msg, sha512_message_block_t block; size_t partial_block_len = (state->total_len.lower >> 3) % kSha512MessageBlockBytes; - hardened_memcpy(block.data, state->partial_block, kSha512MessageBlockWords); + HARDENED_TRY(hardened_memcpy(block.data, state->partial_block, + kSha512MessageBlockWords)); // Initialize the context for the OTBN message buffer. sha512_otbn_ctx_t ctx = {.num_blocks = 0}; @@ -307,8 +314,9 @@ static status_t process_message(sha512_state_t *state, const uint8_t *msg, // boundaries. otbn_addr_t state_read_addr = kOtbnVarSha512State; for (size_t i = 0; i + 1 < kSha512StateWords; i += 2) { - HARDENED_TRY(otbn_dmem_read(1, state_read_addr, &new_state.H[i + 1])); - HARDENED_TRY( + HARDENED_TRY_WIPE_DMEM( + otbn_dmem_read(1, state_read_addr, &new_state.H[i + 1])); + HARDENED_TRY_WIPE_DMEM( otbn_dmem_read(1, state_read_addr + sizeof(uint32_t), &new_state.H[i])); state_read_addr += kOtbnWideWordNumBytes; } @@ -318,8 +326,9 @@ static status_t process_message(sha512_state_t *state, const uint8_t *msg, // At this point, no more errors are possible; it is safe to update the // context object. - hardened_memcpy(state->H, new_state.H, kSha512StateWords); - hardened_memcpy(state->partial_block, block.data, kSha512MessageBlockWords); + HARDENED_TRY(hardened_memcpy(state->H, new_state.H, kSha512StateWords)); + HARDENED_TRY(hardened_memcpy(state->partial_block, block.data, + kSha512MessageBlockWords)); state->total_len.lower = new_state.total_len.lower; state->total_len.upper = new_state.total_len.upper; return OTCRYPTO_OK; @@ -335,10 +344,12 @@ status_t sha512_update(sha512_state_t *state, const uint8_t *msg, * Replace sensitive data in a SHA-512 context with non-sensitive values. * * @param state The context object to shred. + * @return OK or error. */ -static void state_shred(sha512_state_t *state) { - hardened_memshred(state->H, kSha512StateWords); - hardened_memshred(state->partial_block, kSha512MessageBlockWords); +static status_t state_shred(sha512_state_t *state) { + HARDENED_TRY(hardened_memshred(state->H, kSha512StateWords)); + HARDENED_TRY( + hardened_memshred(state->partial_block, kSha512MessageBlockWords)); state->total_len.lower = 0; state->total_len.upper = 0; } @@ -352,13 +363,16 @@ static void state_shred(sha512_state_t *state) { * * @param state Context object. * @param[out] digest Destination buffer for digest. + * @return OK or error. */ -static void sha512_digest_get(sha512_state_t *state, uint32_t *digest) { +static status_t sha512_digest_get(sha512_state_t *state, uint32_t *digest) { // Reverse the bytes in each word to match FIPS 180-4. for (size_t i = 0; i < kSha512StateWords; i++) { state->H[i] = __builtin_bswap32(state->H[i]); } - hardened_memcpy(digest, state->H, kSha512DigestWords); + HARDENED_TRY(hardened_memcpy(digest, state->H, kSha512DigestWords)); + + return OTCRYPTO_OK; } /** @@ -369,13 +383,16 @@ static void sha512_digest_get(sha512_state_t *state, uint32_t *digest) { * * @param state Context object. * @param[out] digest Destination buffer for digest. + * @return OK or error. */ -static void sha384_digest_get(sha512_state_t *state, uint32_t *digest) { +static status_t sha384_digest_get(sha512_state_t *state, uint32_t *digest) { // Reverse the bytes in each word to match FIPS 180-4. for (size_t i = 0; i < kSha512StateWords; i++) { state->H[i] = __builtin_bswap32(state->H[i]); } - hardened_memcpy(digest, state->H, kSha384DigestWords); + HARDENED_TRY(hardened_memcpy(digest, state->H, kSha384DigestWords)); + + return OTCRYPTO_OK; } status_t sha512_final(sha512_state_t *state, uint32_t *digest) { @@ -386,8 +403,8 @@ status_t sha512_final(sha512_state_t *state, uint32_t *digest) { HARDENED_TRY(process_message(state, NULL, 0, kHardenedBoolTrue)); // Copy the digest and then destroy the state. - sha512_digest_get(state, digest); - state_shred(state); + HARDENED_TRY(sha512_digest_get(state, digest)); + HARDENED_TRY(state_shred(state)); return OTCRYPTO_OK; } @@ -396,12 +413,12 @@ status_t sha512(const uint8_t *msg, const size_t msg_len, uint32_t *digest) { HARDENED_TRY(entropy_complex_check()); sha512_state_t state; - sha512_init(&state); + HARDENED_TRY(sha512_init(&state)); // Process data with padding enabled. HARDENED_TRY(process_message(&state, msg, msg_len, kHardenedBoolTrue)); - sha512_digest_get(&state, digest); - state_shred(&state); + HARDENED_TRY(sha512_digest_get(&state, digest)); + HARDENED_TRY(state_shred(&state)); return OTCRYPTO_OK; } @@ -419,8 +436,8 @@ status_t sha384_final(sha384_state_t *state, uint32_t *digest) { HARDENED_TRY(process_message(state, NULL, 0, kHardenedBoolTrue)); // Copy the digest and then destroy the state. - sha384_digest_get(state, digest); - state_shred(state); + HARDENED_TRY(sha384_digest_get(state, digest)); + HARDENED_TRY(state_shred(state)); return OTCRYPTO_OK; } @@ -429,11 +446,11 @@ status_t sha384(const uint8_t *msg, const size_t msg_len, uint32_t *digest) { HARDENED_TRY(entropy_complex_check()); sha384_state_t state; - sha384_init(&state); + HARDENED_TRY(sha384_init(&state)); // Process data with padding enabled. HARDENED_TRY(process_message(&state, msg, msg_len, kHardenedBoolTrue)); - sha384_digest_get(&state, digest); - state_shred(&state); + HARDENED_TRY(sha384_digest_get(&state, digest)); + HARDENED_TRY(state_shred(&state)); return OTCRYPTO_OK; } diff --git a/sw/device/lib/crypto/impl/sha2/sha512.h b/sw/device/lib/crypto/impl/sha2/sha512.h index 3605e75c22e4a..a1cf2c829f44a 100644 --- a/sw/device/lib/crypto/impl/sha2/sha512.h +++ b/sw/device/lib/crypto/impl/sha2/sha512.h @@ -144,7 +144,7 @@ status_t sha384(const uint8_t *msg, const size_t msg_len, uint32_t *digest); * @param[out] state Hash context object to initialize. * @return Result of the operation (OK or error). */ -void sha384_init(sha384_state_t *state); +status_t sha384_init(sha384_state_t *state); /** * Process new message data for a SHA-384 hash computation. @@ -208,7 +208,7 @@ status_t sha512(const uint8_t *msg, const size_t msg_len, uint32_t *digest); * @param[out] state Hash context object to initialize. * @return Result of the operation (OK or error). */ -void sha512_init(sha512_state_t *state); +status_t sha512_init(sha512_state_t *state); /** * Process new message data for a SHA-512 hash computation. diff --git a/sw/device/lib/crypto/include/aes_gcm.h b/sw/device/lib/crypto/include/aes_gcm.h index 65eaf269bd39d..291e8f94c7ed1 100644 --- a/sw/device/lib/crypto/include/aes_gcm.h +++ b/sw/device/lib/crypto/include/aes_gcm.h @@ -39,7 +39,7 @@ typedef enum otcrypto_aes_gcm_tag_len { * change.  */ typedef struct otcrypto_aes_gcm_context { - uint32_t data[98]; + uint32_t data[192]; } otcrypto_aes_gcm_context_t; /** diff --git a/sw/device/lib/crypto/include/ecc_p256.h b/sw/device/lib/crypto/include/ecc_p256.h index d90697e966246..a3b69b0460b7c 100644 --- a/sw/device/lib/crypto/include/ecc_p256.h +++ b/sw/device/lib/crypto/include/ecc_p256.h @@ -40,7 +40,7 @@ otcrypto_status_t otcrypto_ecdsa_p256_keygen( * Generates an ECDSA signature with curve P-256. * * The message digest must be exactly 256 bits (32 bytes) long, but may use any - * hash mode. The caller is responsible for ensuring that the security + * hash mode. The caller is responsible for ensuring that the security * strength of the hash function is at least equal to the security strength of * the curve, but in some cases it may be truncated. See FIPS 186-5 for * details. @@ -56,11 +56,34 @@ otcrypto_status_t otcrypto_ecdsa_p256_sign( const otcrypto_hash_digest_t message_digest, otcrypto_word32_buf_t signature); +/** + * Generates an ECDSA signature with curve P-256 and verifies the signature + * before releasing it to mitigate fault injection attacks. + * + * The message digest must be exactly 256 bits (32 bytes) long, but may use any + * hash mode. The caller is responsible for ensuring that the security + * strength of the hash function is at least equal to the security strength of + * the curve, but in some cases it may be truncated. See FIPS 186-5 for + * details. + * + * @param private_key Pointer to the blinded private key (d) struct. + * @param public_key Pointer to the unblinded public key (Q) struct. + * @param message_digest Message digest to be signed (pre-hashed). + * @param[out] signature Pointer to the signature struct with (r,s) values. + * @return Result of the ECDSA signature generation. + */ +OT_WARN_UNUSED_RESULT +otcrypto_status_t otcrypto_ecdsa_p256_sign_verify( + const otcrypto_blinded_key_t *private_key, + const otcrypto_unblinded_key_t *public_key, + const otcrypto_hash_digest_t message_digest, + otcrypto_word32_buf_t signature); + /** * Verifies an ECDSA/P-256 signature. * * The message digest must be exactly 256 bits (32 bytes) long, but may use any - * hash mode. The caller is responsible for ensuring that the security + * hash mode. The caller is responsible for ensuring that the security * strength of the hash function is at least equal to the security strength of * the curve, but in some cases it may be truncated. See FIPS 186-5 for * details. diff --git a/sw/device/lib/crypto/include/ecc_p384.h b/sw/device/lib/crypto/include/ecc_p384.h index a6d0d9c3c63ec..96b84fa517a48 100644 --- a/sw/device/lib/crypto/include/ecc_p384.h +++ b/sw/device/lib/crypto/include/ecc_p384.h @@ -40,7 +40,7 @@ otcrypto_status_t otcrypto_ecdsa_p384_keygen( * Generates an ECDSA signature with curve P-384. * The message digest must be exactly 384 bits (48 bytes) long, but may use any - * hash mode. The caller is responsible for ensuring that the security + * hash mode. The caller is responsible for ensuring that the security * strength of the hash function is at least equal to the security strength of * the curve, but in some cases it may be truncated. See FIPS 186-5 for * details. @@ -56,11 +56,34 @@ otcrypto_status_t otcrypto_ecdsa_p384_sign( const otcrypto_hash_digest_t message_digest, otcrypto_word32_buf_t signature); +/** + * Generates an ECDSA signature with curve P-384 and verifies the signature + * before releasing it to mitigate fault injection attacks. + + * The message digest must be exactly 384 bits (48 bytes) long, but may use any + * hash mode. The caller is responsible for ensuring that the security + * strength of the hash function is at least equal to the security strength of + * the curve, but in some cases it may be truncated. See FIPS 186-5 for + * details. + * + * @param private_key Pointer to the blinded private key (d) struct. + * @param public_key Pointer to the unblinded public key (Q) struct. + * @param message_digest Message digest to be signed (pre-hashed). + * @param[out] signature Pointer to the signature struct with (r,s) values. + * @return Result of the ECDSA signature generation. + */ +OT_WARN_UNUSED_RESULT +otcrypto_status_t otcrypto_ecdsa_p384_sign_verify( + const otcrypto_blinded_key_t *private_key, + const otcrypto_unblinded_key_t *public_key, + const otcrypto_hash_digest_t message_digest, + otcrypto_word32_buf_t signature); + /** * Verifies an ECDSA/P-384 signature. * * The message digest must be exactly 384 bits (48 bytes) long, but may use any - * hash mode. The caller is responsible for ensuring that the security + * hash mode. The caller is responsible for ensuring that the security * strength of the hash function is at least equal to the security strength of * the curve, but in some cases it may be truncated. See FIPS 186-5 for * details. diff --git a/sw/device/lib/crypto/include/sha2.h b/sw/device/lib/crypto/include/sha2.h index ae98cb6fa1c31..f9491652d04b6 100644 --- a/sw/device/lib/crypto/include/sha2.h +++ b/sw/device/lib/crypto/include/sha2.h @@ -22,7 +22,7 @@ enum { * We assert that this value is large enough to host the internal HMAC driver * struct. */ - kOtcryptoSha2CtxStructWords = 87, + kOtcryptoSha2CtxStructWords = 88, }; /** diff --git a/sw/device/tests/crypto/aes_gcm_testutils.c b/sw/device/tests/crypto/aes_gcm_testutils.c index e5df22325e72f..fb2a335619121 100644 --- a/sw/device/tests/crypto/aes_gcm_testutils.c +++ b/sw/device/tests/crypto/aes_gcm_testutils.c @@ -129,7 +129,7 @@ status_t aes_gcm_testutils_encrypt(const aes_gcm_test_t *test, bool streaming, .key_mode = kOtcryptoKeyModeAesGcm, .key_length = test->key_len * sizeof(uint32_t), .hw_backed = kHardenedBoolFalse, - .security_level = kOtcryptoKeySecurityLevelLow, + .security_level = kOtcryptoKeySecurityLevelHigh, }; // Construct blinded key from the key and testing mask. @@ -226,7 +226,7 @@ status_t aes_gcm_testutils_decrypt(const aes_gcm_test_t *test, .key_mode = kOtcryptoKeyModeAesGcm, .key_length = test->key_len * sizeof(uint32_t), .hw_backed = kHardenedBoolFalse, - .security_level = kOtcryptoKeySecurityLevelLow, + .security_level = kOtcryptoKeySecurityLevelHigh, }; // Construct blinded key from the key and testing mask. diff --git a/sw/device/tests/crypto/aes_kwp_kat_functest.c b/sw/device/tests/crypto/aes_kwp_kat_functest.c index 812e9b3ddde20..b178740dd4ad7 100644 --- a/sw/device/tests/crypto/aes_kwp_kat_functest.c +++ b/sw/device/tests/crypto/aes_kwp_kat_functest.c @@ -52,6 +52,8 @@ static status_t aes_kwp_wrap_kat(const uint32_t *kek, size_t kek_words, const uint32_t *ctext, size_t ctext_words) { // Construct an AES key. aes_key_t aes_kek = make_aes_key(kek, kek_words); + // Create the checksum of the key and store it in the key structure. + aes_kek.checksum = aes_key_integrity_checksum(&aes_kek); // Run key wrapping and check the result. uint32_t act_ctext[ctext_words + 1]; @@ -87,6 +89,8 @@ static status_t aes_kwp_unwrap_kat(const uint32_t *kek, size_t kek_words, size_t ptext_bytes) { // Construct an AES key. aes_key_t aes_kek = make_aes_key(kek, kek_words); + // Create the checksum of the key and store it in the key structure. + aes_kek.checksum = aes_key_integrity_checksum(&aes_kek); // Run key unwrapping. size_t ptext_words = (ptext_bytes + sizeof(uint32_t) - 1) / sizeof(uint32_t); diff --git a/sw/device/tests/crypto/cryptotest/firmware/ecdsa.c b/sw/device/tests/crypto/cryptotest/firmware/ecdsa.c index fe5b25c2e415f..5fe599d999d77 100644 --- a/sw/device/tests/crypto/cryptotest/firmware/ecdsa.c +++ b/sw/device/tests/crypto/cryptotest/firmware/ecdsa.c @@ -190,6 +190,7 @@ status_t interpret_verify_status(ujson_t *uj, otcrypto_status_t status, } status_t p256_sign(ujson_t *uj, cryptotest_ecdsa_private_key_t *uj_private_key, + otcrypto_unblinded_key_t *public_key, otcrypto_hash_digest_t message_digest, otcrypto_word32_buf_t signature_mut, cryptotest_ecdsa_signature_t *uj_signature) { @@ -205,8 +206,8 @@ status_t p256_sign(ujson_t *uj, cryptotest_ecdsa_private_key_t *uj_private_key, memcpy(private_key_masked.share1, uj_private_key->d1, kP256ScalarBytes); private_key.checksum = integrity_blinded_checksum(&private_key); - otcrypto_status_t status = - otcrypto_ecdsa_p256_sign(&private_key, message_digest, signature_mut); + otcrypto_status_t status = otcrypto_ecdsa_p256_sign_verify( + &private_key, public_key, message_digest, signature_mut); if (status.value != kOtcryptoStatusValueOk) { return INTERNAL(status.value); } @@ -225,6 +226,7 @@ status_t p256_sign(ujson_t *uj, cryptotest_ecdsa_private_key_t *uj_private_key, } status_t p384_sign(ujson_t *uj, cryptotest_ecdsa_private_key_t *uj_private_key, + otcrypto_unblinded_key_t *public_key, otcrypto_hash_digest_t message_digest, otcrypto_word32_buf_t signature_mut, cryptotest_ecdsa_signature_t *uj_signature) { @@ -240,8 +242,8 @@ status_t p384_sign(ujson_t *uj, cryptotest_ecdsa_private_key_t *uj_private_key, memcpy(private_key_masked.share1, uj_private_key->d1, kP384ScalarBytes); private_key.checksum = integrity_blinded_checksum(&private_key); - otcrypto_status_t status = - otcrypto_ecdsa_p384_sign(&private_key, message_digest, signature_mut); + otcrypto_status_t status = otcrypto_ecdsa_p384_sign_verify( + &private_key, public_key, message_digest, signature_mut); if (status.value != kOtcryptoStatusValueOk) { return INTERNAL(status.value); } @@ -353,12 +355,12 @@ status_t handle_ecdsa(ujson_t *uj) { case kCryptotestEcdsaOperationSign: { switch (uj_curve) { case kCryptotestEcdsaCurveP256: { - return p256_sign(uj, &uj_private_key, message_digest, signature_mut, - &uj_signature); + return p256_sign(uj, &uj_private_key, &public_key, message_digest, + signature_mut, &uj_signature); } case kCryptotestEcdsaCurveP384: { - return p384_sign(uj, &uj_private_key, message_digest, signature_mut, - &uj_signature); + return p384_sign(uj, &uj_private_key, &public_key, message_digest, + signature_mut, &uj_signature); } default: LOG_ERROR("Unsupported ECC curve: %d", uj_curve); diff --git a/sw/device/tests/crypto/ecdsa_p256_functest.c b/sw/device/tests/crypto/ecdsa_p256_functest.c index 20c1353138945..dedf074c565cf 100644 --- a/sw/device/tests/crypto/ecdsa_p256_functest.c +++ b/sw/device/tests/crypto/ecdsa_p256_functest.c @@ -71,8 +71,8 @@ status_t sign_then_verify_test(hardened_bool_t *verification_result) { // Generate a signature for the message. LOG_INFO("Signing..."); - CHECK_STATUS_OK(otcrypto_ecdsa_p256_sign( - &private_key, msg_digest, + CHECK_STATUS_OK(otcrypto_ecdsa_p256_sign_verify( + &private_key, &public_key, msg_digest, (otcrypto_word32_buf_t){.data = sig, .len = ARRAYSIZE(sig)})); // Verify the signature. diff --git a/sw/device/tests/crypto/ecdsa_p256_sideload_functest.c b/sw/device/tests/crypto/ecdsa_p256_sideload_functest.c index 5d5f90a6cf7f4..e63afce4b9f14 100644 --- a/sw/device/tests/crypto/ecdsa_p256_sideload_functest.c +++ b/sw/device/tests/crypto/ecdsa_p256_sideload_functest.c @@ -86,8 +86,8 @@ status_t sign_then_verify_test(void) { // Generate a signature for the message. LOG_INFO("Signing..."); - CHECK_STATUS_OK(otcrypto_ecdsa_p256_sign( - &private_key, message_digest, + CHECK_STATUS_OK(otcrypto_ecdsa_p256_sign_verify( + &private_key, &public_key, message_digest, (otcrypto_word32_buf_t){.data = sig, .len = ARRAYSIZE(sig)})); // Verify the signature. diff --git a/sw/device/tests/crypto/ecdsa_p384_functest.c b/sw/device/tests/crypto/ecdsa_p384_functest.c index 294ba92e69712..f69bee8aafc26 100644 --- a/sw/device/tests/crypto/ecdsa_p384_functest.c +++ b/sw/device/tests/crypto/ecdsa_p384_functest.c @@ -71,8 +71,8 @@ status_t sign_then_verify_test(hardened_bool_t *verification_result) { // Generate a signature for the message. LOG_INFO("Signing..."); - CHECK_STATUS_OK(otcrypto_ecdsa_p384_sign( - &private_key, msg_digest, + CHECK_STATUS_OK(otcrypto_ecdsa_p384_sign_verify( + &private_key, &public_key, msg_digest, (otcrypto_word32_buf_t){.data = sig, .len = ARRAYSIZE(sig)})); // Verify the signature. diff --git a/sw/device/tests/crypto/ecdsa_p384_sideload_functest.c b/sw/device/tests/crypto/ecdsa_p384_sideload_functest.c index 72a89875f8c67..781ddfa36ce0c 100644 --- a/sw/device/tests/crypto/ecdsa_p384_sideload_functest.c +++ b/sw/device/tests/crypto/ecdsa_p384_sideload_functest.c @@ -84,8 +84,8 @@ status_t sign_then_verify_test(void) { // Generate a signature for the message. LOG_INFO("Signing..."); - CHECK_STATUS_OK(otcrypto_ecdsa_p384_sign( - &private_key, msg_digest, + CHECK_STATUS_OK(otcrypto_ecdsa_p384_sign_verify( + &private_key, &public_key, msg_digest, (otcrypto_word32_buf_t){.data = sig, .len = ARRAYSIZE(sig)})); // Verify the signature. diff --git a/sw/device/tests/penetrationtests/firmware/fi/cryptolib_fi_asym_impl.c b/sw/device/tests/penetrationtests/firmware/fi/cryptolib_fi_asym_impl.c index 1c41e175af36b..c309e9011f9a7 100644 --- a/sw/device/tests/penetrationtests/firmware/fi/cryptolib_fi_asym_impl.c +++ b/sw/device/tests/penetrationtests/firmware/fi/cryptolib_fi_asym_impl.c @@ -679,15 +679,21 @@ status_t cryptolib_fi_p256_sign_impl( memset(private_key_masked.share1, 0, kP256MaskedScalarShareBytes); private_key.checksum = integrity_blinded_checksum(&private_key); - // Allocate space for a public key. - uint32_t pk[kPentestP256Words * 2] = {0}; + // Create the public key. + p256_point_t pub_p256; otcrypto_unblinded_key_t public_key = { .key_mode = kOtcryptoKeyModeEcdsaP256, - .key_length = sizeof(pk), - .key = pk, + .key_length = sizeof(p256_point_t), + .key = (uint32_t *)&pub_p256, }; + memset(pub_p256.x, 0, kP256CoordBytes); + memcpy(pub_p256.x, uj_input.pubx, P256_CMD_BYTES); + memset(pub_p256.y, 0, kP256CoordBytes); + memcpy(pub_p256.y, uj_input.puby, P256_CMD_BYTES); + public_key.checksum = integrity_unblinded_checksum(&public_key); // Create a key pair if requested. + // This will overwrite the private and public key above. if (uj_input.cfg == 1) { // Trigger window 0. if (uj_input.trigger == 0) { @@ -718,10 +724,16 @@ status_t cryptolib_fi_p256_sign_impl( .len = ARRAYSIZE(sig), }; - // Trigger window. - pentest_set_trigger_high(); - TRY(otcrypto_ecdsa_p256_sign(&private_key, message_digest, signature_mut)); - pentest_set_trigger_low(); + // Trigger window 1. + if (uj_input.trigger == 1) { + pentest_set_trigger_high(); + } + // Sign the message. + TRY(otcrypto_ecdsa_p256_sign_verify(&private_key, &public_key, message_digest, + signature_mut)); + if (uj_input.trigger == 1) { + pentest_set_trigger_low(); + } // Return data back to host. uj_output->cfg = 0; @@ -733,9 +745,9 @@ status_t cryptolib_fi_p256_sign_impl( memcpy(uj_output->s, signature_p256->s, kP256ScalarBytes); // Return the public key. - p256_point_t *pub_p256 = (p256_point_t *)public_key.key; - memcpy(uj_output->pubx, pub_p256->x, P256_CMD_BYTES); - memcpy(uj_output->puby, pub_p256->y, P256_CMD_BYTES); + p256_point_t *pub = (p256_point_t *)public_key.key; + memcpy(uj_output->pubx, pub->x, P256_CMD_BYTES); + memcpy(uj_output->puby, pub->y, P256_CMD_BYTES); return OK_STATUS(); } @@ -893,15 +905,21 @@ status_t cryptolib_fi_p384_sign_impl( memset(private_key_masked.share1, 0, kP384MaskedScalarShareBytes); private_key.checksum = integrity_blinded_checksum(&private_key); - // Allocate space for a public key. - uint32_t pk[kPentestP384Words * 2] = {0}; + // Create the public key. + p384_point_t pub_p384; otcrypto_unblinded_key_t public_key = { .key_mode = kOtcryptoKeyModeEcdsaP384, - .key_length = sizeof(pk), - .key = pk, + .key_length = sizeof(p384_point_t), + .key = (uint32_t *)&pub_p384, }; + memset(pub_p384.x, 0, kP384CoordBytes); + memcpy(pub_p384.x, uj_input.pubx, P384_CMD_BYTES); + memset(pub_p384.y, 0, kP384CoordBytes); + memcpy(pub_p384.y, uj_input.puby, P384_CMD_BYTES); + public_key.checksum = integrity_unblinded_checksum(&public_key); // Create a key pair if requested. + // This will overwrite the private and public key above. if (uj_input.cfg == 1) { // Trigger window 0. if (uj_input.trigger == 0) { @@ -932,10 +950,15 @@ status_t cryptolib_fi_p384_sign_impl( .len = ARRAYSIZE(sig), }; - // Trigger window. - pentest_set_trigger_high(); - TRY(otcrypto_ecdsa_p384_sign(&private_key, message_digest, signature_mut)); - pentest_set_trigger_low(); + // Trigger window 1. + if (uj_input.trigger == 1) { + pentest_set_trigger_high(); + } + TRY(otcrypto_ecdsa_p384_sign_verify(&private_key, &public_key, message_digest, + signature_mut)); + if (uj_input.trigger == 1) { + pentest_set_trigger_low(); + } // Return data back to host. uj_output->cfg = 0; @@ -947,9 +970,9 @@ status_t cryptolib_fi_p384_sign_impl( memcpy(uj_output->s, signature_p384->s, kP384ScalarBytes); // Return the public key. - p384_point_t *pub_p384 = (p384_point_t *)public_key.key; - memcpy(uj_output->pubx, pub_p384->x, P384_CMD_BYTES); - memcpy(uj_output->puby, pub_p384->y, P384_CMD_BYTES); + p384_point_t *pub = (p384_point_t *)public_key.key; + memcpy(uj_output->pubx, pub->x, P384_CMD_BYTES); + memcpy(uj_output->puby, pub->y, P384_CMD_BYTES); return OK_STATUS(); } diff --git a/sw/device/tests/penetrationtests/firmware/fi/cryptolib_fi_sym_impl.c b/sw/device/tests/penetrationtests/firmware/fi/cryptolib_fi_sym_impl.c index 2d81f9792ae6c..2e9c48d9f4fc1 100644 --- a/sw/device/tests/penetrationtests/firmware/fi/cryptolib_fi_sym_impl.c +++ b/sw/device/tests/penetrationtests/firmware/fi/cryptolib_fi_sym_impl.c @@ -216,7 +216,7 @@ status_t cryptolib_fi_gcm_impl(cryptolib_fi_sym_gcm_in_t uj_input, .key_mode = kOtcryptoKeyModeAesGcm, .key_length = uj_input.key_len, .hw_backed = kHardenedBoolFalse, - .security_level = kOtcryptoKeySecurityLevelLow, + .security_level = kOtcryptoKeySecurityLevelHigh, }; // Construct blinded key from the key and testing mask. diff --git a/sw/device/tests/penetrationtests/firmware/sca/cryptolib_sca_asym_impl.c b/sw/device/tests/penetrationtests/firmware/sca/cryptolib_sca_asym_impl.c index bc37b05d25c1c..f0a40fa031a18 100644 --- a/sw/device/tests/penetrationtests/firmware/sca/cryptolib_sca_asym_impl.c +++ b/sw/device/tests/penetrationtests/firmware/sca/cryptolib_sca_asym_impl.c @@ -466,15 +466,21 @@ status_t cryptolib_sca_p256_sign_impl( memset(private_key_masked.share1, 0, kP256MaskedScalarShareBytes); private_key.checksum = integrity_blinded_checksum(&private_key); - // Allocate space for a public key. - uint32_t pk[kPentestP256Words * 2] = {0}; + // Create the public key. + p256_point_t pub_p256; otcrypto_unblinded_key_t public_key = { .key_mode = kOtcryptoKeyModeEcdsaP256, - .key_length = sizeof(pk), - .key = pk, + .key_length = sizeof(p256_point_t), + .key = (uint32_t *)&pub_p256, }; + memset(pub_p256.x, 0, kP256CoordBytes); + memcpy(pub_p256.x, uj_input.pubx, P256_CMD_BYTES); + memset(pub_p256.y, 0, kP256CoordBytes); + memcpy(pub_p256.y, uj_input.puby, P256_CMD_BYTES); + public_key.checksum = integrity_unblinded_checksum(&public_key); // Create a key pair if requested. + // This will overwrite the private and public key above. if (uj_input.cfg == 1) { // Trigger window 0. if (uj_input.trigger == 0) { @@ -506,9 +512,14 @@ status_t cryptolib_sca_p256_sign_impl( }; // Trigger window 1. - pentest_set_trigger_high(); - TRY(otcrypto_ecdsa_p256_sign(&private_key, message_digest, signature_mut)); - pentest_set_trigger_low(); + if (uj_input.trigger == 1) { + pentest_set_trigger_high(); + } + TRY(otcrypto_ecdsa_p256_sign_verify(&private_key, &public_key, message_digest, + signature_mut)); + if (uj_input.trigger == 1) { + pentest_set_trigger_low(); + } // Return data back to host. uj_output->cfg = 0; @@ -520,9 +531,9 @@ status_t cryptolib_sca_p256_sign_impl( memcpy(uj_output->s, signature_p256->s, kP256ScalarBytes); // Return the public key. - p256_point_t *pub_p256 = (p256_point_t *)public_key.key; - memcpy(uj_output->pubx, pub_p256->x, P256_CMD_BYTES); - memcpy(uj_output->puby, pub_p256->y, P256_CMD_BYTES); + p256_point_t *pub = (p256_point_t *)public_key.key; + memcpy(uj_output->pubx, pub->x, P256_CMD_BYTES); + memcpy(uj_output->puby, pub->y, P256_CMD_BYTES); return OK_STATUS(); } @@ -627,15 +638,21 @@ status_t cryptolib_sca_p384_sign_impl( memset(private_key_masked.share1, 0, kP384MaskedScalarShareBytes); private_key.checksum = integrity_blinded_checksum(&private_key); - // Allocate space for a public key. - uint32_t pk[kPentestP384Words * 2] = {0}; + // Create the public key. + p384_point_t pub_p384; otcrypto_unblinded_key_t public_key = { .key_mode = kOtcryptoKeyModeEcdsaP384, - .key_length = sizeof(pk), - .key = pk, + .key_length = sizeof(p384_point_t), + .key = (uint32_t *)&pub_p384, }; + memset(pub_p384.x, 0, kP384CoordBytes); + memcpy(pub_p384.x, uj_input.pubx, P384_CMD_BYTES); + memset(pub_p384.y, 0, kP384CoordBytes); + memcpy(pub_p384.y, uj_input.puby, P384_CMD_BYTES); + public_key.checksum = integrity_unblinded_checksum(&public_key); // Create a key pair if requested. + // This will overwrite the private and public key above. if (uj_input.cfg == 1) { // Trigger window 0. if (uj_input.trigger == 0) { @@ -666,10 +683,15 @@ status_t cryptolib_sca_p384_sign_impl( .len = ARRAYSIZE(sig), }; - // Trigger window. - pentest_set_trigger_high(); - TRY(otcrypto_ecdsa_p384_sign(&private_key, message_digest, signature_mut)); - pentest_set_trigger_low(); + // Trigger window 1. + if (uj_input.trigger == 1) { + pentest_set_trigger_high(); + } + TRY(otcrypto_ecdsa_p384_sign_verify(&private_key, &public_key, message_digest, + signature_mut)); + if (uj_input.trigger == 1) { + pentest_set_trigger_low(); + } // Return data back to host. uj_output->cfg = 0; @@ -681,9 +703,9 @@ status_t cryptolib_sca_p384_sign_impl( memcpy(uj_output->s, signature_p384->s, kP384ScalarBytes); // Return the public key. - p384_point_t *pub_p384 = (p384_point_t *)public_key.key; - memcpy(uj_output->pubx, pub_p384->x, P384_CMD_BYTES); - memcpy(uj_output->puby, pub_p384->y, P384_CMD_BYTES); + p384_point_t *pub = (p384_point_t *)public_key.key; + memcpy(uj_output->pubx, pub->x, P384_CMD_BYTES); + memcpy(uj_output->puby, pub->y, P384_CMD_BYTES); return OK_STATUS(); } diff --git a/sw/device/tests/penetrationtests/firmware/sca/cryptolib_sca_sym_impl.c b/sw/device/tests/penetrationtests/firmware/sca/cryptolib_sca_sym_impl.c index 11b32e4e004e6..3c6af084f792c 100644 --- a/sw/device/tests/penetrationtests/firmware/sca/cryptolib_sca_sym_impl.c +++ b/sw/device/tests/penetrationtests/firmware/sca/cryptolib_sca_sym_impl.c @@ -233,7 +233,7 @@ status_t cryptolib_sca_gcm_impl( .key_mode = kOtcryptoKeyModeAesGcm, .key_length = key_len, .hw_backed = kHardenedBoolFalse, - .security_level = kOtcryptoKeySecurityLevelLow, + .security_level = kOtcryptoKeySecurityLevelHigh, }; // Construct blinded key from the key and mask. diff --git a/sw/host/penetrationtests/testvectors/data/fi_asym_cryptolib.json b/sw/host/penetrationtests/testvectors/data/fi_asym_cryptolib.json index 7d36a8a6b8b7a..bc4ea67fa05ee 100644 --- a/sw/host/penetrationtests/testvectors/data/fi_asym_cryptolib.json +++ b/sw/host/penetrationtests/testvectors/data/fi_asym_cryptolib.json @@ -73,7 +73,7 @@ { "test_case_id": 9, "command": "P256Sign", - "input": "{\"scalar\": [240,45,145,122,29,206,131,106,94,161,109,193,195,45,18,242,85,226,125,202,25,195,63,78,53,21,158,78,28,204,101,150], \"pubx\": [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0], \"puby\": [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0], \"message\": [1194,91,234,230,56,255,141,205,55,14,3,166,248,156,89,76,85,190,209,39,126,225,77,131,187,176,239,120,58,5,23,199], \"cfg\": 0, \"trigger\": 0}", + "input": "{\"scalar\": [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0], \"pubx\": [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0], \"puby\": [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0], \"message\": [1194,91,234,230,56,255,141,205,55,14,3,166,248,156,89,76,85,190,209,39,126,225,77,131,187,176,239,120,58,5,23,199], \"cfg\": 1, \"trigger\": 0}", "expected_output": [ "{\"status\":0, \"r\": [0], \"s\": [0], \"pubx\": [0], \"puby\": [0], \"cfg\": 0}" ] @@ -113,7 +113,7 @@ { "test_case_id": 14, "command": "P384Sign", - "input": "{\"scalar\": [79,23,6,68,39,20,240,136,77,158,31,9,214,176,7,116,188,27,87,246,120,46,150,173,248,118,125,25,54,24,59,136,52,94,113,112,248,157,196,94,226,243,220,105,77,5,109,75], \"pubx\": [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0], \"puby\": [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0], \"message\": [249,177,39,240,216,30,188,209,123,123,160,234,19,28,102,13,52,11,5,206,85,124,130,22,14,15,121,61,224,125,56,23,144,35,148,40,113,172,183,0,45,250,253,255,252,141,234,206], \"cfg\": 0, \"trigger\": 1}", + "input": "{\"scalar\": [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0], \"pubx\": [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0], \"puby\": [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0], \"message\": [249,177,39,240,216,30,188,209,123,123,160,234,19,28,102,13,52,11,5,206,85,124,130,22,14,15,121,61,224,125,56,23,144,35,148,40,113,172,183,0,45,250,253,255,252,141,234,206], \"cfg\": 1, \"trigger\": 1}", "expected_output": [ "{\"status\":0, \"r\": [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0], \"s\": [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0], \"pubx\": [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0], \"puby\": [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0], \"cfg\": 0}" ] diff --git a/sw/host/penetrationtests/testvectors/data/sca_asym_cryptolib.json b/sw/host/penetrationtests/testvectors/data/sca_asym_cryptolib.json index 683fc7bfcda36..092c02419025f 100644 --- a/sw/host/penetrationtests/testvectors/data/sca_asym_cryptolib.json +++ b/sw/host/penetrationtests/testvectors/data/sca_asym_cryptolib.json @@ -70,7 +70,7 @@ { "test_case_id": 9, "command": "P256Sign", - "input": "{\"scalar\": [252, 193, 29, 106, 119, 65, 232, 228, 197, 32, 209, 68, 87, 194, 158, 104, 210, 136, 182, 102, 211, 128, 157, 43, 41, 70, 247, 236, 240, 17, 28, 116], \"pubx\": [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0], \"puby\": [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0], \"message\": [136,252,30,125,132,151,148,252,81,177,53,250,19,93,238,192,219,2,184,108,60,216,206,189,170,121,232,104,158,91,40,152], \"cfg\": 0, \"trigger\": 0}", + "input": "{\"scalar\": [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0], \"pubx\": [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0], \"puby\": [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0], \"message\": [136,252,30,125,132,151,148,252,81,177,53,250,19,93,238,192,219,2,184,108,60,216,206,189,170,121,232,104,158,91,40,152], \"cfg\": 1, \"trigger\": 0}", "expected_output": [ "{\"status\":0, \"r\": [0], \"s\": [0], \"pubx\": [0], \"puby\": [0], \"cfg\": 0}" ] @@ -110,7 +110,7 @@ { "test_case_id": 14, "command": "P384Sign", - "input": "{\"scalar\": [79,23,6,68,39,20,240,136,77,158,31,9,214,176,7,116,188,27,87,246,120,46,150,173,248,118,125,25,54,24,59,136,52,94,113,112,248,157,196,94,226,243,220,105,77,5,109,75], \"pubx\": [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0], \"puby\": [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0], \"message\": [249,177,39,240,216,30,188,209,123,123,160,234,19,28,102,13,52,11,5,206,85,124,130,22,14,15,121,61,224,125,56,23,144,35,148,40,113,172,183,0,45,250,253,255,252,141,234,206], \"cfg\": 0, \"trigger\": 1}", + "input": "{\"scalar\": [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0], \"pubx\": [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0], \"puby\": [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0], \"message\": [249,177,39,240,216,30,188,209,123,123,160,234,19,28,102,13,52,11,5,206,85,124,130,22,14,15,121,61,224,125,56,23,144,35,148,40,113,172,183,0,45,250,253,255,252,141,234,206], \"cfg\": 1, \"trigger\": 1}", "expected_output": [ "{\"status\":0, \"r\": [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0], \"s\": [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0], \"pubx\": [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0], \"puby\": [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0], \"cfg\": 0}" ]