Skip to content

Commit

Permalink
[rom] Add "pure" and "pre-hashed" domain separators for SPHINCS+.
Browse files Browse the repository at this point in the history
Signed-off-by: Jade Philipoom <[email protected]>
  • Loading branch information
jadephilipoom committed Jun 25, 2024
1 parent 811991b commit 4c2272a
Show file tree
Hide file tree
Showing 9 changed files with 108 additions and 38 deletions.
1 change: 1 addition & 0 deletions sw/device/silicon_creator/lib/error.h
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,7 @@ enum module_ {
X(kErrorSigverifyBadEcdsaSignature, ERROR_(7, kModuleSigverify, kInvalidArgument)), \
X(kErrorSigverifyBadAuthPartition, ERROR_(8, kModuleSigverify, kInvalidArgument)), \
X(kErrorSigverifyBadEcdsaKey, ERROR_(9, kModuleSigverify, kInvalidArgument)), \
X(kErrorSigverifyBadSpxConfig, ERROR_(10, kModuleSigverify, kInvalidArgument)), \
\
X(kErrorKeymgrInternal, ERROR_(1, kModuleKeymgr, kInternal)), \
\
Expand Down
8 changes: 6 additions & 2 deletions sw/device/silicon_creator/lib/sigverify/spx_key.h
Original file line number Diff line number Diff line change
Expand Up @@ -62,17 +62,21 @@ enum {
* ./util/design/sparse-fsm-encode.py -d 6 -m 2 -n 32 -s 359186736 --language=c
*/
typedef enum sigverify_spx_config_id {
/** SPHINCS+-SHA2-128s */
/** SPHINCS+-SHA2-128s without pre-hashing. */
kSigverifySpxConfigIdSha2128s = 0x0142410e,
/**
* SPHINCS+-SHA2-128s-q20
* SPHINCS+-SHA2-128s-q20 without pre-hashing.
*
* As specified in https://eprint.iacr.org/2022/1725.pdf.
*
* n | h | d | b | k | w | bitsec | sigsize
* 16 | 18 | 1 | 24 | 6 | 16 | 128 | 3264
*/
kSigverifySpxConfigIdSha2128sQ20 = 0x9b28d8da,
/** SPHINCS+-SHA2-128s with SHA256 pre-hashing. */
kSigverifySpxConfigIdSha2128sPrehash = 0x4694e9cb,
/** SPHINCS+-SHA2-128s-q20 with SHA256 pre-hashing. */
kSigverifySpxConfigIdSha2128sQ20Prehash = 0xa3ed7f9a,
} sigverify_spx_config_id_t;

/**
Expand Down
45 changes: 37 additions & 8 deletions sw/device/silicon_creator/lib/sigverify/spx_verify.c
Original file line number Diff line number Diff line change
Expand Up @@ -75,22 +75,51 @@ static const uint8_t kSpxVerifyPureDomainSep[] = {
0x00,
};

/**
* Domain-separation prefix for SPHINCS+ with SHA256 prehashing.
*
* The domain separation prefix is the following byte sequence:
* 0x01 || len(ctx) || ctx || OID(PH)
*
* In our case, `ctx` is always the empty string and PH (the pre-hashing
* function) is always SHA256.
*/
static const uint8_t kSpxVerifyPrehashDomainSep[] = {
0x01, 0x00, 0x06, 0x09, 0x60, 0x86, 0x48,
0x01, 0x65, 0x03, 0x04, 0x02, 0x01};

rom_error_t sigverify_spx_verify(
const sigverify_spx_signature_t *signature, const sigverify_spx_key_t *key,
lifecycle_state_t lc_state, const void *msg_prefix_1,
size_t msg_prefix_1_len, const void *msg_prefix_2, size_t msg_prefix_2_len,
const void *msg, size_t msg_len, uint32_t *flash_exec) {
const sigverify_spx_config_id_t *config, lifecycle_state_t lc_state,
const void *msg_prefix_1, size_t msg_prefix_1_len, const void *msg_prefix_2,
size_t msg_prefix_2_len, const void *msg, size_t msg_len,
const hmac_digest_t *digest, uint32_t *flash_exec) {
uint32_t spx_en = launder32(sigverify_spx_verify_enabled(lc_state));
rom_error_t error = kErrorSigverifyBadSpxSignature;
if (launder32(spx_en) != kSigverifySpxDisabledOtp) {
sigverify_spx_root_t expected_root;
spx_public_key_root(key->data, expected_root.data);
sigverify_spx_root_t actual_root;
HARDENED_RETURN_IF_ERROR(
spx_verify(signature->data, kSpxVerifyPureDomainSep,
sizeof(kSpxVerifyPureDomainSep), msg_prefix_1,
msg_prefix_1_len, msg_prefix_2, msg_prefix_2_len, msg,
msg_len, key->data, actual_root.data));
if (launder32(*config) == kSigverifySpxConfigIdSha2128sPrehash) {
HARDENED_CHECK_EQ(*config, kSigverifySpxConfigIdSha2128sPrehash);
HARDENED_RETURN_IF_ERROR(
spx_verify(signature->data, kSpxVerifyPrehashDomainSep,
sizeof(kSpxVerifyPrehashDomainSep),
/*msg_prefix_2=*/NULL, /*msg_prefix_2_len=*/0,
/*msg_prefix_3=*/NULL, /*msg_prefix_3_len=*/0,
(unsigned char *)digest->digest, sizeof(digest->digest),
key->data, actual_root.data));
} else if (launder32(*config) == kSigverifySpxConfigIdSha2128s) {
HARDENED_CHECK_EQ(*config, kSigverifySpxConfigIdSha2128s);
HARDENED_RETURN_IF_ERROR(
spx_verify(signature->data, kSpxVerifyPureDomainSep,
sizeof(kSpxVerifyPureDomainSep), msg_prefix_1,
msg_prefix_1_len, msg_prefix_2, msg_prefix_2_len, msg,
msg_len, key->data, actual_root.data));
} else {
// Unsupported SPHINCS+ configuration.
return kErrorSigverifyBadSpxConfig;
}

size_t i = 0;
for (; launder32(i) < kSigverifySpxRootNumWords; ++i) {
Expand Down
16 changes: 13 additions & 3 deletions sw/device/silicon_creator/lib/sigverify/spx_verify.h
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
#ifndef OPENTITAN_SW_DEVICE_SILICON_CREATOR_LIB_SIGVERIFY_SPX_VERIFY_H_
#define OPENTITAN_SW_DEVICE_SILICON_CREATOR_LIB_SIGVERIFY_SPX_VERIFY_H_

#include "sw/device/silicon_creator/lib/drivers/hmac.h"
#include "sw/device/silicon_creator/lib/drivers/lifecycle.h"
#include "sw/device/silicon_creator/lib/error.h"
#include "sw/device/silicon_creator/lib/sigverify/spx_key.h"
Expand Down Expand Up @@ -46,24 +47,33 @@ uint32_t sigverify_spx_verify_enabled(lifecycle_state_t lc_state);
/**
* Verifies a SPHINCS+ signature.
*
* To accomodate both "pure" and "pre-hash" variants of SPHINCS+, this function
* takes both the raw message components and the precomputed SHA256 digest as
* parameters. If the OTP parameter `SIGVERIFY_SPX_PREHASH` is set, then the
* raw message is ignored and we sign the digest. If it is unset, we ignore the
* digest and sign the raw message.
*
* @param signature Signature to be verified.
* @param key Signer's SPHINCS+ public key.
* @param config Signer's SPHINCS+ key configuration.
* @param lc_state Life cycle state of the device.
* @param msg_prefix_1 Optional message prefix.
* @param msg_prefix_1_len Length of the first prefix.
* @param msg_prefix_2 Optional message prefix.
* @param msg_prefix_2_len Length of the second prefix.
* @param msg Start of the message.
* @param msg_len Length of the message.
* @param digest Precomputed SHA256 digest of the message.
* @param[out] flash_exec Value to write to the flash_ctrl EXEC register.
* @return Result of the operation.
*/
OT_WARN_UNUSED_RESULT
rom_error_t sigverify_spx_verify(
const sigverify_spx_signature_t *signature, const sigverify_spx_key_t *key,
lifecycle_state_t lc_state, const void *msg_prefix_1,
size_t msg_prefix_1_len, const void *msg_prefix_2, size_t msg_prefix_2_len,
const void *msg, size_t msg_len, uint32_t *flash_exec);
const sigverify_spx_config_id_t *config, lifecycle_state_t lc_state,
const void *msg_prefix_1, size_t msg_prefix_1_len, const void *msg_prefix_2,
size_t msg_prefix_2_len, const void *msg, size_t msg_len,
const hmac_digest_t *digest, uint32_t *flash_exec);

/**
* Transforms `kSigverifySpxSuccess` into `kErrorOk`.
Expand Down
35 changes: 19 additions & 16 deletions sw/device/silicon_creator/lib/sigverify/spx_verify_functest.c
Original file line number Diff line number Diff line change
Expand Up @@ -379,7 +379,10 @@ static sigverify_spx_signature_t kSignature = {

static uint32_t kSpxEnabled = 0;

static const uint8_t kSpxVerifyPureDomainSep[] = {
static const sigverify_spx_config_id_t kConfig = kSigverifySpxConfigIdSha2128s;

// Domain separator for pure (not pre-hashed) mode.
static const uint8_t kSpxVerifyDomainSep[] = {
0x00,
0x00,
};
Expand Down Expand Up @@ -425,9 +428,9 @@ static rom_error_t spx_verify_impl_test(void) {
sigverify_spx_root_t expected_root;
sigverify_spx_root_t actual_root;
spx_public_key_root(kPubKey.data, expected_root.data);
rom_error_t error = spx_verify(kSignature.data, kSpxVerifyPureDomainSep,
sizeof(kSpxVerifyPureDomainSep), NULL, 0, NULL,
0, (const uint8_t *)kMessage, kMessageLen,
rom_error_t error = spx_verify(kSignature.data, kSpxVerifyDomainSep,
sizeof(kSpxVerifyDomainSep), NULL, 0, NULL, 0,
(const uint8_t *)kMessage, kMessageLen,
kPubKey.data, actual_root.data);
if (memcmp(&expected_root, &actual_root, sizeof(sigverify_spx_root_t)) != 0) {
return kErrorUnknown;
Expand All @@ -441,9 +444,9 @@ static rom_error_t spx_verify_disabled_bad_signature_test(void) {
uint32_t flash_exec = 0;
uint32_t good_word = kSignature.data[0];
kSignature.data[0] = 0;
rom_error_t error =
sigverify_spx_verify(&kSignature, &kPubKey, kLcStateProd, NULL, 0, NULL,
0, &kMessage, kMessageLen, &flash_exec);
rom_error_t error = sigverify_spx_verify(
&kSignature, &kPubKey, &kConfig, kLcStateProd, NULL, 0, NULL, 0,
&kMessage, kMessageLen, NULL, &flash_exec);
kSignature.data[0] = good_word;

if (flash_exec != kSigverifySpxSuccess) {
Expand All @@ -458,9 +461,9 @@ static rom_error_t spx_verify_disabled_good_signature_test(void) {
disable_spx_verify();

uint32_t flash_exec = 0;
rom_error_t error =
sigverify_spx_verify(&kSignature, &kPubKey, kLcStateProd, NULL, 0, NULL,
0, &kMessage, kMessageLen, &flash_exec);
rom_error_t error = sigverify_spx_verify(
&kSignature, &kPubKey, &kConfig, kLcStateProd, NULL, 0, NULL, 0,
&kMessage, kMessageLen, NULL, &flash_exec);

if (flash_exec != kSigverifySpxSuccess) {
LOG_ERROR("flash_exec must be 0x%08x, not 0x%08x", kSigverifySpxSuccess,
Expand All @@ -476,9 +479,9 @@ static rom_error_t spx_verify_enabled_bad_signature_test(void) {
uint32_t flash_exec = 0;
uint32_t good_word = kSignature.data[0];
kSignature.data[0] = 0;
rom_error_t error =
sigverify_spx_verify(&kSignature, &kPubKey, kLcStateProd, NULL, 0, NULL,
0, &kMessage, kMessageLen, &flash_exec);
rom_error_t error = sigverify_spx_verify(
&kSignature, &kPubKey, &kConfig, kLcStateProd, NULL, 0, NULL, 0,
&kMessage, kMessageLen, NULL, &flash_exec);
kSignature.data[0] = good_word;

if (error == kErrorOk) {
Expand All @@ -497,9 +500,9 @@ static rom_error_t spx_verify_enabled_good_signature_test(void) {
enable_spx_verify();

uint32_t flash_exec = 0;
rom_error_t error =
sigverify_spx_verify(&kSignature, &kPubKey, kLcStateProd, NULL, 0, NULL,
0, &kMessage, kMessageLen, &flash_exec);
rom_error_t error = sigverify_spx_verify(
&kSignature, &kPubKey, &kConfig, kLcStateProd, NULL, 0, NULL, 0,
&kMessage, kMessageLen, NULL, &flash_exec);

if (flash_exec != kSigverifySpxSuccess) {
LOG_ERROR("flash_exec must be 0x%08x, not 0x%08x", kSigverifySpxSuccess,
Expand Down
17 changes: 10 additions & 7 deletions sw/device/silicon_creator/rom/rom.c
Original file line number Diff line number Diff line change
Expand Up @@ -330,14 +330,15 @@ static rom_error_t rom_verify(const manifest_t *manifest,
&ecdsa_key));
// SPX+ key.
const sigverify_spx_key_t *spx_key = NULL;
const sigverify_spx_config_id_t *spx_config = NULL;
const sigverify_spx_signature_t *spx_signature = NULL;
uint32_t sigverify_spx_en = sigverify_spx_verify_enabled(lc_state);
if (launder32(sigverify_spx_en) != kSigverifySpxDisabledOtp) {
const manifest_ext_spx_key_t *ext_spx_key;
HARDENED_RETURN_IF_ERROR(manifest_ext_get_spx_key(manifest, &ext_spx_key));
HARDENED_RETURN_IF_ERROR(sigverify_spx_key_get(
&sigverify_ctx, sigverify_spx_key_id_get(&ext_spx_key->key), lc_state,
&spx_key));
&spx_key, &spx_config));
const manifest_ext_spx_signature_t *ext_spx_signature;
HARDENED_RETURN_IF_ERROR(
manifest_ext_get_spx_signature(manifest, &ext_spx_signature));
Expand Down Expand Up @@ -386,14 +387,16 @@ static rom_error_t rom_verify(const manifest_t *manifest,
&manifest->ecdsa_signature, ecdsa_key, &act_digest, flash_exec));

return sigverify_spx_verify(
spx_signature, spx_key, lc_state, &usage_constraints_from_hw,
sizeof(usage_constraints_from_hw), anti_rollback, anti_rollback_len,
digest_region.start, digest_region.length, flash_exec);
spx_signature, spx_key, spx_config, lc_state,
&usage_constraints_from_hw, sizeof(usage_constraints_from_hw),
anti_rollback, anti_rollback_len, digest_region.start,
digest_region.length, &act_digest, flash_exec);
} else {
HARDENED_RETURN_IF_ERROR(sigverify_spx_verify(
spx_signature, spx_key, lc_state, &usage_constraints_from_hw,
sizeof(usage_constraints_from_hw), anti_rollback, anti_rollback_len,
digest_region.start, digest_region.length, flash_exec));
spx_signature, spx_key, spx_config, lc_state,
&usage_constraints_from_hw, sizeof(usage_constraints_from_hw),
anti_rollback, anti_rollback_len, digest_region.start,
digest_region.length, &act_digest, flash_exec));

return sigverify_ecdsa_p256_verify(&manifest->ecdsa_signature, ecdsa_key,
&act_digest, flash_exec);
Expand Down
5 changes: 4 additions & 1 deletion sw/device/silicon_creator/rom/sigverify_keys_spx.c
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,10 @@

rom_error_t sigverify_spx_key_get(const sigverify_otp_key_ctx_t *sigverify_ctx,
uint32_t key_id, lifecycle_state_t lc_state,
const sigverify_spx_key_t **key) {
const sigverify_spx_key_t **key,
const sigverify_spx_config_id_t **config) {
*key = NULL;
*config = NULL;
uint32_t spx_en = sigverify_spx_verify_enabled(lc_state);
rom_error_t error = kErrorSigverifyBadSpxKey;

Expand All @@ -31,6 +33,7 @@ rom_error_t sigverify_spx_key_get(const sigverify_otp_key_ctx_t *sigverify_ctx,
&rom_key);
if (error == kErrorOk) {
*key = &((const sigverify_rom_spx_key_t *)rom_key)->entry.key;
*config = &((const sigverify_rom_spx_key_t *)rom_key)->entry.config_id;
}
} else {
HARDENED_CHECK_EQ(spx_en, kSigverifySpxDisabledOtp);
Expand Down
4 changes: 3 additions & 1 deletion sw/device/silicon_creator/rom/sigverify_keys_spx.h
Original file line number Diff line number Diff line change
Expand Up @@ -45,12 +45,14 @@ extern const sigverify_rom_spx_key_t kSigverifySpxKeys[];
* @param key_id A key ID.
* @param lc_state Life cycle state of the device.
* @param key Key with the given ID, valid only if it exists.
* @param config Key configuration with the given ID, valid only if it exists.
* @return Result of the operation.
*/
OT_WARN_UNUSED_RESULT
rom_error_t sigverify_spx_key_get(const sigverify_otp_key_ctx_t *sigverify_ctx,
uint32_t key_id, lifecycle_state_t lc_state,
const sigverify_spx_key_t **key);
const sigverify_spx_key_t **key,
const sigverify_spx_config_id_t **config);

#ifdef __cplusplus
} // extern "C"
Expand Down
15 changes: 15 additions & 0 deletions sw/host/opentitanlib/src/crypto/spx.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,14 @@ pub trait SpxPublicKeyPart {
let full_message = &[domain_sep, message].concat();
Ok(sphincsplus::spx_verify(self.pk(), &sig.0, full_message)?)
}

// Verify a message signature in pre-hashed mode, returning Ok(()) if the
// signature matches.
fn verify_prehash(&self, oid: &[u8], message: &[u8], sig: &SpxSignature) -> Result<()> {
let domain_sep = &[1u8, 0u8];
let full_message = &[domain_sep, oid, message].concat();
Ok(sphincsplus::spx_verify(self.pk(), &sig.0, full_message)?)
}
}

#[derive(Clone)]
Expand Down Expand Up @@ -86,6 +94,13 @@ impl SpxKeypair {
SpxSignature(sphincsplus::spx_sign(&self.sk, full_message).unwrap())
}

/// Sign `message` using the secret key in "prehash" mode with an empty context.
pub fn sign_prehash(&self, oid: &[u8], message: &[u8]) -> SpxSignature {
let domain_sep = &[0u8, 0u8];
let full_message = &[domain_sep, oid, message].concat();
SpxSignature(sphincsplus::spx_sign(&self.sk, full_message).unwrap())
}

/// Consumes this keypair and returns the corrisponding public key.
pub fn into_public_key(self) -> SpxPublicKey {
SpxPublicKey(self.pk)
Expand Down

0 comments on commit 4c2272a

Please sign in to comment.