Skip to content
Open
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
57 changes: 46 additions & 11 deletions sw/device/silicon_creator/lib/drivers/kmac.c
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,10 @@ typedef struct kmac_config {
* The algorithm: SHA3, SHAKE or cSHAKE
*/
uint8_t mode;
/**
* Hashing strength: L128, L224, L256, L384 or L512
*/
uint8_t kstrength;
} kmac_config_t;

/**
Expand Down Expand Up @@ -173,9 +177,9 @@ static rom_error_t kmac_configure(kmac_config_t config) {
// poll `STATUS.fifo_depth` to avoid a specific EDN-KMAC-Ibex deadlock
// scenario. See `absorb()` and the KMAC documentation for details.
cfg_reg = bitfield_bit32_write(cfg_reg, KMAC_CFG_SHADOWED_KMAC_EN_BIT, 0);
// Set `CFG.KSTRENGTH` field to 256-bit strength.
// Set `CFG.KSTRENGTH` field.
cfg_reg = bitfield_field32_write(cfg_reg, KMAC_CFG_SHADOWED_KSTRENGTH_FIELD,
KMAC_CFG_SHADOWED_KSTRENGTH_VALUE_L256);
config.kstrength);
// Set `CFG.MODE` field to SHAKE.
cfg_reg = bitfield_field32_write(cfg_reg, KMAC_CFG_SHADOWED_MODE_FIELD,
config.mode);
Expand Down Expand Up @@ -246,6 +250,7 @@ rom_error_t kmac_keymgr_configure(void) {
.sideload = true,
.kmac_en = false,
.mode = KMAC_CFG_SHADOWED_MODE_VALUE_SHAKE,
.kstrength = KMAC_CFG_SHADOWED_KSTRENGTH_VALUE_L256,
});
}

Expand All @@ -257,6 +262,7 @@ rom_error_t kmac_kmac256_sw_configure(void) {
.sideload = false,
.kmac_en = true,
.mode = KMAC_CFG_SHADOWED_MODE_VALUE_CSHAKE,
.kstrength = KMAC_CFG_SHADOWED_KSTRENGTH_VALUE_L256,
});
}

Expand All @@ -268,6 +274,18 @@ rom_error_t kmac_kmac256_hw_configure(void) {
.sideload = true,
.kmac_en = true,
.mode = KMAC_CFG_SHADOWED_MODE_VALUE_CSHAKE,
.kstrength = KMAC_CFG_SHADOWED_KSTRENGTH_VALUE_L256,
});
}

rom_error_t kmac_shake128_configure(void) {
return kmac_configure((kmac_config_t){
.entropy_fast_process = false,
.msg_mask = false,
.sideload = false,
.kmac_en = false,
.mode = KMAC_CFG_SHADOWED_MODE_VALUE_SHAKE,
.kstrength = KMAC_CFG_SHADOWED_KSTRENGTH_VALUE_L128,
});
}

Expand All @@ -278,6 +296,7 @@ rom_error_t kmac_shake256_configure(void) {
.sideload = false,
.kmac_en = false,
.mode = KMAC_CFG_SHADOWED_MODE_VALUE_SHAKE,
.kstrength = KMAC_CFG_SHADOWED_KSTRENGTH_VALUE_L256,
});
}

Expand Down Expand Up @@ -350,20 +369,26 @@ void kmac_shake256_squeeze_start(void) {
}

rom_error_t kmac_shake256_squeeze_end(uint32_t *out, size_t outlen) {
HARDENED_RETURN_IF_ERROR(
kmac_squeeze_words(out, outlen, kShake256KeccakRateWords));
return kmac_done();
}

rom_error_t kmac_squeeze_words(uint32_t *out, size_t out_words,
size_t rate_words) {
size_t idx = 0;
while (launder32(idx) < outlen) {
// Since we always read in increments of the SHAKE-256 rate, the index at
// start should always be a multiple of the rate.
HARDENED_CHECK_EQ(idx % kShake256KeccakRateWords, 0);
while (launder32(idx) < out_words) {
// Since we always read in increments of the rate, the index at the start
// of each squeeze cycle must be a multiple of the rate.
HARDENED_CHECK_EQ(idx % rate_words, 0);

// Poll the status register until in the 'squeeze' state.
HARDENED_RETURN_IF_ERROR(poll_state(KMAC_STATUS_SHA3_SQUEEZE_BIT));

// Read words from the state registers (either `outlen` or the maximum
// number of words available).
size_t offset = 0;
for (; launder32(idx) < outlen && offset < kShake256KeccakRateWords;
++offset) {
for (; launder32(idx) < out_words && offset < rate_words; ++offset) {
uint32_t share0 =
abs_mmio_read32(kAddrStateShare0 + offset * sizeof(uint32_t));
uint32_t share1 =
Expand All @@ -372,15 +397,18 @@ rom_error_t kmac_shake256_squeeze_end(uint32_t *out, size_t outlen) {
++idx;
}

if (launder32(offset) == kShake256KeccakRateWords) {
if (launder32(offset) == rate_words) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

In cryptolib, we have:

if (launder32(offset) == keccak_rate_words && idx < digest_len_words) {

So here we are running an additional CMD.RUN after a last full block. This is fine.

// If we read all the remaining words, issue `CMD.RUN` to generate more
// state.
HARDENED_CHECK_EQ(offset, kShake256KeccakRateWords);
HARDENED_CHECK_EQ(offset, rate_words);
issue_command(KMAC_CMD_CMD_VALUE_RUN);
}
}
HARDENED_CHECK_EQ(idx, outlen);
HARDENED_CHECK_EQ(idx, out_words);
return kErrorOk;
}

rom_error_t kmac_done(void) {
// Poll the status register until in the 'squeeze' state.
HARDENED_RETURN_IF_ERROR(poll_state(KMAC_STATUS_SHA3_SQUEEZE_BIT));

Expand All @@ -390,6 +418,13 @@ rom_error_t kmac_shake256_squeeze_end(uint32_t *out, size_t outlen) {
return kErrorOk;
}

bool kmac_is_squeezing(void) {
uint32_t status = abs_mmio_read32(kBase + KMAC_STATUS_REG_OFFSET);
bool sha3_idle = bitfield_bit32_read(status, KMAC_STATUS_SHA3_IDLE_BIT);
bool sha3_absorb = bitfield_bit32_read(status, KMAC_STATUS_SHA3_ABSORB_BIT);
return !sha3_idle && !sha3_absorb;
}

#define WORD_BITS (sizeof(uint32_t) * 8)
#define KEY_CASE(x_) \
case x_ / WORD_BITS: \
Expand Down
41 changes: 41 additions & 0 deletions sw/device/silicon_creator/lib/drivers/kmac.h
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,16 @@ rom_error_t kmac_kmac256_sw_configure(void);
*/
rom_error_t kmac_kmac256_hw_configure(void);

/**
* Configure the KMAC block at startup for SHAKE-128 operation.
*
* Sets the KMAC block to use software entropy and sets the mode to SHAKE-128.
*
* @return Error code indicating if the operation succeeded.
*/
OT_WARN_UNUSED_RESULT
rom_error_t kmac_shake128_configure(void);

/**
* Configure the KMAC block at startup.
*
Expand Down Expand Up @@ -145,6 +155,37 @@ void kmac_shake256_squeeze_start(void);
OT_WARN_UNUSED_RESULT
rom_error_t kmac_shake256_squeeze_end(uint32_t *out, size_t outlen);

/**
* Squeeze arbitrary number of words from the Keccak state.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Is it really an arbitrary number of words or should it be a multiple of rate_words? If a first call would ask for non-rate multiple, the next call to this function would then re-read those words? I think it might be worth documenting this here.

*
* This function will read out_words from the Keccak state, and if necessary
* triggers additional hardware permutation runs using the rate_words.
* Unlike squeeze_end, it does not mark the operation as DONE.
*
* @param out Output buffer.
* @param out_words Desired length of output in 32-bit words.
* @param rate_words Keccak rate in 32-bit words.
* @return Error code indicating if the operation succeeded.
*/
OT_WARN_UNUSED_RESULT
rom_error_t kmac_squeeze_words(uint32_t *out, size_t out_words,
size_t rate_words);

/**
* End the squeeze phase and release the KMAC hardware block.
*
* @return Error code indicating if the operation succeeded.
*/
OT_WARN_UNUSED_RESULT
rom_error_t kmac_done(void);

/**
* Check if the KMAC block is in the squeezing phase.
*
* @return True if KMAC is squeezing, false otherwise.
*/
bool kmac_is_squeezing(void);

/**
* Load an unmasked software key into KMAC.
*
Expand Down
46 changes: 46 additions & 0 deletions sw/device/tests/embedpqc/BUILD
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
# Copyright lowRISC contributors (OpenTitan project).
# Licensed under the Apache License, Version 2.0, see LICENSE for details.
# SPDX-License-Identifier: Apache-2.0

load(
"//rules/opentitan:defs.bzl",
"EARLGREY_TEST_ENVS",
"opentitan_test",
)

package(default_visibility = ["//visibility:public"])

cc_library(
name = "mldsa_test_utils",
srcs = [
"mldsa_test_utils.c",
],
hdrs = [
"mldsa_test_utils.h",
],
deps = [
"//sw/device/lib/runtime:log",
],
)

cc_library(
name = "mldsa_testvectors",
srcs = ["mldsa_testvectors.c"],
hdrs = ["mldsa_testvectors.h"],
deps = [
"//third_party/embedpqc:mldsa44_tiny",
],
)

opentitan_test(
name = "mldsa44_tiny_test",
srcs = ["mldsa44_tiny_test.c"],
exec_env = EARLGREY_TEST_ENVS,
deps = [
":mldsa_test_utils",
":mldsa_testvectors",
"//sw/device/lib/runtime:log",
"//sw/device/lib/testing/test_framework:ottf_main",
"//third_party/embedpqc/ports:mldsa44_tiny_caller",

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Suggested change
"//third_party/embedpqc/ports:mldsa44_tiny_caller",
"//third_party/embedpqc/ports:mldsa44_tiny_caller",
"//third_party/embedpqc:mldsa44_tiny",

I think this could be added to make the includes more robust.

],
)
66 changes: 66 additions & 0 deletions sw/device/tests/embedpqc/mldsa44_tiny_test.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
// Copyright lowRISC contributors (OpenTitan project).
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
// SPDX-License-Identifier: Apache-2.0

#include "third_party/embedpqc/mldsa44_tiny.h"

#include "sw/device/lib/runtime/log.h"
#include "sw/device/lib/testing/test_framework/check.h"
#include "sw/device/lib/testing/test_framework/ottf_main.h"
#include "sw/device/tests/embedpqc/mldsa_test_utils.h"
#include "sw/device/tests/embedpqc/mldsa_testvectors.h"
#include "third_party/embedpqc/ports/mldsa44_tiny_caller.h"

OTTF_DEFINE_TEST_CONFIG();

bool test_main(void) {
LOG_INFO("MLDSA44-TINY Test starting...");

// Allocate a single static buffer to reuse for all RAM outputs to save BSS
// space.
static uint8_t buf[MLDSA44_SIGNATURE_BYTES];

// 1. Keygen Test
paint_stack();
mldsa44_tiny_pub_from_seed_with_stack(buf, kMldsa44KeygenSeed,
&mldsa_stack[MLDSA_STACK_SIZE]);
size_t keygen_stack = get_max_stack_usage();
LOG_INFO("mldsa44_tiny_pub_from_seed Max Stack Usage: %u bytes",
(unsigned int)keygen_stack);

CHECK(check_arrays_eq_verbose(buf, kMldsa44ExpectedPublicKey,
MLDSA44_PUBLIC_KEY_BYTES),
"PublicKey mismatch!");
LOG_INFO("Keygen Test Passed.");

// 2. Sign Test
paint_stack();
mldsa44_tiny_sign_deterministic_with_stack(
buf, kMldsa44SignSeed, kMldsa44Message, sizeof(kMldsa44Message),
&mldsa_stack[MLDSA_STACK_SIZE]);
size_t sign_stack = get_max_stack_usage();
LOG_INFO("mldsa44_tiny_sign Max Stack Usage: %u bytes",
(unsigned int)sign_stack);

CHECK(check_arrays_eq_verbose(buf, kMldsa44ExpectedSignature,
MLDSA44_SIGNATURE_BYTES),
"Signature mismatch!");
LOG_INFO("Sign Test Passed.");

// 3. Verify Test
mldsa44_tiny_pub_from_seed_with_stack(buf, kMldsa44SignSeed,

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Should we compare here to a golden public key? On the other hand, if this fails, the verify below would also fail. So fine for me to leave it as it is.

&mldsa_stack[MLDSA_STACK_SIZE]);
paint_stack();
int verify_res = mldsa44_tiny_verify_with_stack(
buf, kMldsa44ExpectedSignature, kMldsa44Message, sizeof(kMldsa44Message),
&mldsa_stack[MLDSA_STACK_SIZE]);
size_t verify_stack = get_max_stack_usage();
LOG_INFO("mldsa44_tiny_verify Max Stack Usage: %u bytes",
(unsigned int)verify_stack);

CHECK(verify_res != 0, "mldsa44_tiny_verify failed!");
LOG_INFO("Verify Test Passed.");

LOG_INFO("MLDSA44-TINY Test completed successfully!");
return true;
}
38 changes: 38 additions & 0 deletions sw/device/tests/embedpqc/mldsa_test_utils.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
// Copyright lowRISC contributors (OpenTitan project).
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
// SPDX-License-Identifier: Apache-2.0

#include "sw/device/tests/embedpqc/mldsa_test_utils.h"

#include "sw/device/lib/runtime/log.h"

__attribute__((aligned(16))) uint8_t mldsa_stack[MLDSA_STACK_SIZE];

void paint_stack(void) {
for (size_t i = 0; i < MLDSA_STACK_SIZE; i++) {
mldsa_stack[i] = 0xA5;
}
}

size_t get_max_stack_usage(void) {
for (size_t i = 0; i < MLDSA_STACK_SIZE; i++) {
if (mldsa_stack[i] != 0xA5) {
return MLDSA_STACK_SIZE - i;
}
}
return 0;
}

bool check_arrays_eq_verbose(const uint8_t *got, const uint8_t *expected,
size_t len) {
bool match = true;
for (size_t i = 0; i < len; ++i) {
if (got[i] != expected[i]) {
LOG_INFO("Mismatch at index %u: got 0x%02x, expected 0x%02x",
(unsigned int)i, got[i], expected[i]);
match = false;
break;
}
}
return match;
}
41 changes: 41 additions & 0 deletions sw/device/tests/embedpqc/mldsa_test_utils.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
// Copyright lowRISC contributors (OpenTitan project).
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
// SPDX-License-Identifier: Apache-2.0

#ifndef OPENTITAN_SW_DEVICE_TESTS_EMBEDPQC_MLDSA_TEST_UTILS_H_
#define OPENTITAN_SW_DEVICE_TESTS_EMBEDPQC_MLDSA_TEST_UTILS_H_

#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>

#define MLDSA_STACK_SIZE (64 * 1024)
extern uint8_t mldsa_stack[MLDSA_STACK_SIZE];

/**
* Fills the custom stack buffer with a predefined pattern (0xA5) to allow
* stack high-watermark usage measurement.
*/
void paint_stack(void);

/**
* Analyzes the custom stack buffer to find the deepest modified address
* (non-0xA5) and returns the maximum stack usage in bytes.
*
* @return Deepest stack usage in bytes.
*/
size_t get_max_stack_usage(void);

/**
* Verbose array comparison helper that compares two byte buffers and logs
* the index, got value, and expected value of the first mismatch encountered.
*
* @param got The byte buffer obtained.
* @param expected The expected byte buffer.
* @param len Length of the buffers to compare in bytes.
* @return True if the buffers match exactly, False otherwise.
*/
bool check_arrays_eq_verbose(const uint8_t *got, const uint8_t *expected,
size_t len);

#endif // OPENTITAN_SW_DEVICE_TESTS_EMBEDPQC_MLDSA_TEST_UTILS_H_
Loading
Loading