Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions sw/device/lib/crypto/drivers/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,7 @@ cc_library(
"//sw/device/lib/base:abs_mmio",
"//sw/device/lib/base:bitfield",
"//sw/device/lib/base:hardened",
"//sw/device/lib/base:hardened_memory",
"//sw/device/lib/base:macros",
"//sw/device/lib/crypto/impl:status",
],
Expand Down
78 changes: 32 additions & 46 deletions sw/device/lib/crypto/drivers/kmac.c
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
#include "hw/top/dt/kmac.h"
#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/drivers/rv_core_ibex.h"
Expand Down Expand Up @@ -566,27 +567,18 @@ 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(kBase + 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(
kBase + KMAC_KEY_SHARE0_0_REG_OFFSET + i * sizeof(uint32_t),
ibex_rnd32_read());
}
for (size_t i = 0; i * sizeof(uint32_t) < key->len; i++) {
abs_mmio_write32(
kBase + KMAC_KEY_SHARE0_0_REG_OFFSET + i * sizeof(uint32_t),
Copy link
Member Author

Choose a reason for hiding this comment

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

Hm no I don't think so, we want to access byte 0 of share0:
https://opentitan.org/book/hw/ip/kmac/doc/registers.html#key_share0

Copy link
Contributor

@johannheyszl johannheyszl Jan 20, 2026

Choose a reason for hiding this comment

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

thanks sorry

key->share0[i]);
}
for (size_t i = 0; i * sizeof(uint32_t) < key->len; i++) {
abs_mmio_write32(
kBase + KMAC_KEY_SHARE1_0_REG_OFFSET + i * sizeof(uint32_t),
ibex_rnd32_read());
}
for (size_t i = 0; i * sizeof(uint32_t) < key->len; i++) {
abs_mmio_write32(
kBase + KMAC_KEY_SHARE1_0_REG_OFFSET + i * sizeof(uint32_t),
key->share1[i]);
}
// Write random words to the key registers and use hardened_memcpy
// for SCA defense. Using the hardened_mem* functions is fine as
// we are always operating on multiples of 32-bit words.
uint32_t share0_addr = kBase + KMAC_KEY_SHARE0_0_REG_OFFSET;
uint32_t share1_addr = kBase + KMAC_KEY_SHARE1_0_REG_OFFSET;
size_t key_len_words = key->len / sizeof(uint32_t);
HARDENED_TRY(hardened_memshred((uint32_t *)share0_addr, key_len_words));
HARDENED_TRY(
hardened_memcpy((uint32_t *)share0_addr, key->share0, key_len_words));
HARDENED_TRY(hardened_memshred((uint32_t *)share1_addr, key_len_words));
HARDENED_TRY(
hardened_memcpy((uint32_t *)share1_addr, key->share1, key_len_words));

return OTCRYPTO_OK;
}
Expand Down Expand Up @@ -706,41 +698,35 @@ static status_t kmac_process_msg_blocks(kmac_operation_t operation,
// Read words from the state registers (either `digest_len_words` or the
// maximum number of words available).
size_t offset = 0;
uint32_t offset_share0 = kBase + KMAC_STATE_REG_OFFSET;
uint32_t offset_share1 =
kBase + KMAC_STATE_REG_OFFSET + kKmacStateShareSize;
size_t read_len_words = keccak_rate_words;
if (idx + read_len_words >= digest_len_words) {
read_len_words = digest_len_words - idx;
}
if (launder32(masked_digest) == kHardenedBoolTrue) {
HARDENED_CHECK_EQ(masked_digest, kHardenedBoolTrue);
// Read the digest into each share in turn. Do this in separate loops so
// corresponding shares aren't handled close together.
for (offset = 0; launder32(idx + offset) < digest_len_words &&
offset < keccak_rate_words;
offset++) {
digest[idx + offset] = abs_mmio_read32(kBase + KMAC_STATE_REG_OFFSET +
offset * sizeof(uint32_t));
}
for (offset = 0; launder32(idx + offset) < digest_len_words &&
offset < keccak_rate_words;
offset++) {
digest[idx + offset + digest_len_words] =
abs_mmio_read32(kBase + KMAC_STATE_REG_OFFSET +
kKmacStateShareSize + offset * sizeof(uint32_t));
}
idx += offset;
// Read the digest into each share in turn. Do this by using the SCA
// resilient hardened_memcpy function.
HARDENED_TRY(hardened_memcpy(
&digest[idx], (const uint32_t *)offset_share0, read_len_words));
HARDENED_TRY(hardened_memcpy(&digest[idx + digest_len_words],
(const uint32_t *)offset_share1,
read_len_words));
} else {
// Skip right to the hardened check here instead of returning
// `OTCRYPTO_BAD_ARGS` if the value is not `kHardenedBoolFalse`; this
// value always comes from within the cryptolib, so we expect it to be
// valid and should be suspicious if it's not.
HARDENED_CHECK_EQ(masked_digest, kHardenedBoolFalse);
// Unmask the digest as we read it.
for (; launder32(idx) < digest_len_words && offset < keccak_rate_words;
offset++) {
digest[idx] = abs_mmio_read32(kBase + KMAC_STATE_REG_OFFSET +
offset * sizeof(uint32_t));
digest[idx] ^=
abs_mmio_read32(kBase + KMAC_STATE_REG_OFFSET +
kKmacStateShareSize + offset * sizeof(uint32_t));
idx++;
}
HARDENED_TRY(hardened_xor((const uint32_t *)offset_share0,
(const uint32_t *)offset_share1, read_len_words,
&digest[idx]));
}
offset += read_len_words;
idx += read_len_words;

// If we read all the remaining words and still need more digest, issue
// `CMD.RUN` to generate more state.
Expand Down
Loading