Skip to content

Commit

Permalink
[rom_ext] Add SPX+ verification for owner payloads
Browse files Browse the repository at this point in the history
1. Add fake SPX+ keys to the test_owner configuration.
2. Use key_ids to distinguish between plain and hybrid keys (hybrid
   key_ids are the sum of the individual key_ids).
3. Detect the SPX manifest extensions and update the search key_id
   accordingly.
4. Verify hybrid signatures by checking both ECDSA and SPX signatures.

Signed-off-by: Chris Frantz <[email protected]>
  • Loading branch information
cfrantz committed Nov 12, 2024
1 parent 01fcb50 commit 1e19338
Show file tree
Hide file tree
Showing 9 changed files with 222 additions and 26 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 @@ -218,6 +218,7 @@ enum module_ {
X(kErrorOwnershipKeyNotFound, ERROR_(12, kModuleOwnership, kNotFound)), \
X(kErrorOwnershipInvalidDin, ERROR_(13, kModuleOwnership, kInvalidArgument)), \
X(kErrorOwnershipUnlockDenied, ERROR_(14, kModuleOwnership, kPermissionDenied)), \
X(kErrorOwnershipInvalidAlgorithm, ERROR_(15, kModuleOwnership, kInvalidArgument)), \
/* Group all of the tag version error codes together */ \
X(kErrorOwnershipOWNRVersion, ERROR_(0x70, kModuleOwnership, kInvalidArgument)), \
X(kErrorOwnershipAPPKVersion, ERROR_(0x71, kModuleOwnership, kInvalidArgument)), \
Expand Down
7 changes: 7 additions & 0 deletions sw/device/silicon_creator/lib/ownership/datatypes.h
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,13 @@ typedef enum ownership_key_alg {
kOwnershipKeyAlgHybridEcdsaSq20Pure = 0x75507148,
// Key algorithm Hybrid P256 & SPX+ Prehashed SHA256: `HqS2`
kOwnershipKeyAlgHybridEcdsaSq20Prehash = 0x32537148,

/** Key algorithm category mask */
kOwnershipKeyAlgCategoryMask = 0xFF,
/** Key algorithm category for Sphincs+: `S...` */
kOwnershipKeyAlgCategorySpx = 0x53,
/** Key algorithm category for Hybrid: `H...` */
kOwnershipKeyAlgCategoryHybridEcdsaSpx = 0x48,
} ownership_key_alg_t;

typedef enum ownership_update_mode {
Expand Down
12 changes: 8 additions & 4 deletions sw/device/silicon_creator/lib/ownership/owner_block.c
Original file line number Diff line number Diff line change
Expand Up @@ -256,11 +256,15 @@ rom_error_t owner_block_info_apply(const owner_flash_info_config_t *info) {
}

rom_error_t owner_keyring_find_key(const owner_application_keyring_t *keyring,
uint32_t key_alg, uint32_t key_id,
size_t *index) {
uint32_t key_id, size_t *index) {
for (size_t i = 0; i < keyring->length; ++i) {
if (keyring->key[i]->key_alg == key_alg &&
keyring->key[i]->data.id == key_id) {
uint32_t id = keyring->key[i]->data.id;
if ((keyring->key[i]->key_alg & kOwnershipKeyAlgCategoryMask) ==
kOwnershipKeyAlgCategoryHybridEcdsaSpx) {
// The ID of a hybrid key is the sum of the IDs of each key.
id += keyring->key[i]->data.hybrid_ecdsa_spx.spx.data[0];
}
if (id == key_id) {
*index = i;
return kErrorOk;
}
Expand Down
3 changes: 1 addition & 2 deletions sw/device/silicon_creator/lib/ownership/owner_block.h
Original file line number Diff line number Diff line change
Expand Up @@ -118,8 +118,7 @@ rom_error_t owner_block_flash_apply(const owner_flash_config_t *flash,
rom_error_t owner_block_info_apply(const owner_flash_info_config_t *info);

rom_error_t owner_keyring_find_key(const owner_application_keyring_t *keyring,
uint32_t key_alg, uint32_t key_id,
size_t *index);
uint32_t key_id, size_t *index);

/**
* Determine whether the given key is on owner page 0 or page 1.
Expand Down
46 changes: 46 additions & 0 deletions sw/device/silicon_creator/lib/ownership/test_owner.c
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,9 @@
#include "sw/device/silicon_creator/lib/error.h"
#include "sw/device/silicon_creator/lib/ownership/keys/fake/activate_ecdsa_p256.h"
#include "sw/device/silicon_creator/lib/ownership/keys/fake/app_dev_ecdsa_p256.h"
#include "sw/device/silicon_creator/lib/ownership/keys/fake/app_dev_spx.h"
#include "sw/device/silicon_creator/lib/ownership/keys/fake/app_prod_ecdsa_p256.h"
#include "sw/device/silicon_creator/lib/ownership/keys/fake/app_prod_spx.h"
#include "sw/device/silicon_creator/lib/ownership/keys/fake/app_test_ecdsa_p256.h"
#include "sw/device/silicon_creator/lib/ownership/keys/fake/owner_ecdsa_p256.h"
#include "sw/device/silicon_creator/lib/ownership/keys/fake/unlock_ecdsa_p256.h"
Expand Down Expand Up @@ -131,6 +133,50 @@ rom_error_t sku_creator_owner_init(boot_data_t *bootdata,
},
};

app = (owner_application_key_t *)((uintptr_t)app + app->header.length);
*app = (owner_application_key_t){
.header =
{
.tag = kTlvTagApplicationKey,
.length = kTlvLenApplicationKeyHybrid,
},
.key_alg = kOwnershipKeyAlgHybridEcdsaSpxPure,
.key_domain = kOwnerAppDomainProd,
.key_diversifier = {0},
.usage_constraint = 0,
.data =
{
.hybrid_ecdsa_spx =
{
.ecdsa = APP_PROD_ECDSA_P256,
.spx = APP_PROD_SPX,
},
},
};

app = (owner_application_key_t *)((uintptr_t)app + app->header.length);
*app = (owner_application_key_t){
.header =
{
.tag = kTlvTagApplicationKey,
.length = kTlvLenApplicationKeyHybrid,
},
// TODO(cfrantz): Change this to Prehash after putting
// the prehash infrastructure in place.
.key_alg = kOwnershipKeyAlgHybridEcdsaSpxPure,
.key_domain = kOwnerAppDomainDev,
.key_diversifier = {0},
.usage_constraint = 0,
.data =
{
.hybrid_ecdsa_spx =
{
.ecdsa = APP_DEV_ECDSA_P256,
.spx = APP_DEV_SPX,
},
},
};

// Fill the remainder of the data segment with the end tag (0x5a5a5a5a).
app = (owner_application_key_t *)((uintptr_t)app + app->header.length);
size_t len = (uintptr_t)(owner_page[0].data + sizeof(owner_page[0].data)) -
Expand Down
11 changes: 7 additions & 4 deletions sw/device/silicon_creator/lib/sigverify/spx_verify.c
Original file line number Diff line number Diff line change
Expand Up @@ -70,10 +70,11 @@ static const uint32_t kSpxVerifyShares[kSigverifySpxRootNumWords] = {
*
* In our case, `ctx` is always the empty string, so the length is 0.
*/
static const uint8_t kSpxVerifyPureDomainSep[] = {
const uint8_t kSpxVerifyPureDomainSep[] = {
0x00,
0x00,
};
const size_t kSpxVerifyPureDomainSepSize = sizeof(kSpxVerifyPureDomainSep);

/**
* Domain-separation prefix for SPHINCS+ with SHA256 prehashing.
Expand All @@ -84,9 +85,11 @@ static const uint8_t kSpxVerifyPureDomainSep[] = {
* 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};
const uint8_t kSpxVerifyPrehashDomainSep[] = {0x01, 0x00, 0x06, 0x09, 0x60,
0x86, 0x48, 0x01, 0x65, 0x03,
0x04, 0x02, 0x01};
const size_t kSpxVerifyPrehashDomainSepSize =
sizeof(kSpxVerifyPrehashDomainSep);

rom_error_t sigverify_spx_verify(
const sigverify_spx_signature_t *signature, const sigverify_spx_key_t *key,
Expand Down
5 changes: 5 additions & 0 deletions sw/device/silicon_creator/lib/sigverify/spx_verify.h
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,11 @@
extern "C" {
#endif // __cplusplus

extern const uint8_t kSpxVerifyPureDomainSep[];
extern const size_t kSpxVerifyPureDomainSepSize;
extern const uint8_t kSpxVerifyPrehashDomainSep[];
extern const size_t kSpxVerifyPrehashDomainSepSize;

enum {
/**
* A non-trivial constant chosen such that `kSigverifySpxSuccess ^
Expand Down
27 changes: 22 additions & 5 deletions sw/device/silicon_creator/rom_ext/e2e/verified_boot/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -145,22 +145,38 @@ opentitan_test(

_KEYS = {
"dev": {
"key": {"//sw/device/silicon_creator/lib/ownership/keys/fake:app_dev_ecdsa": "dev_key_0"},
"ecdsa": {"//sw/device/silicon_creator/lib/ownership/keys/fake:app_dev_ecdsa": "dev_key_0"},
"spx": {},
"exit_success": DEFAULT_TEST_SUCCESS_MSG,
"exit_failure": DEFAULT_TEST_FAILURE_MSG,
},
"dev_hybrid": {
"ecdsa": {"//sw/device/silicon_creator/lib/ownership/keys/fake:app_dev_ecdsa": "dev_key_0"},
"spx": {"//sw/device/silicon_creator/lib/ownership/keys/fake:app_dev_spx": "dev_key_0"},
"exit_success": DEFAULT_TEST_SUCCESS_MSG,
"exit_failure": DEFAULT_TEST_FAILURE_MSG,
},
"prod": {
"key": {"//sw/device/silicon_creator/lib/ownership/keys/fake:app_prod_ecdsa": "prod_key_0"},
"ecdsa": {"//sw/device/silicon_creator/lib/ownership/keys/fake:app_prod_ecdsa": "prod_key_0"},
"spx": {},
"exit_success": DEFAULT_TEST_SUCCESS_MSG,
"exit_failure": DEFAULT_TEST_FAILURE_MSG,
},
"prod_hybrid": {
"ecdsa": {"//sw/device/silicon_creator/lib/ownership/keys/fake:app_prod_ecdsa": "prod_key_0"},
"spx": {"//sw/device/silicon_creator/lib/ownership/keys/fake:app_prod_spx": "prod_key_0"},
"exit_success": DEFAULT_TEST_SUCCESS_MSG,
"exit_failure": DEFAULT_TEST_FAILURE_MSG,
},
"test": {
"key": {"//sw/device/silicon_creator/lib/ownership/keys/fake:app_test_ecdsa": "test_key_0"},
"ecdsa": {"//sw/device/silicon_creator/lib/ownership/keys/fake:app_test_ecdsa": "test_key_0"},
"spx": {},
"exit_success": DEFAULT_TEST_SUCCESS_MSG,
"exit_failure": DEFAULT_TEST_FAILURE_MSG,
},
"unauthorized": {
"key": {"//sw/device/silicon_creator/lib/ownership/keys/fake:app_unauthorized_ecdsa": "unauthorized_key_0"},
"ecdsa": {"//sw/device/silicon_creator/lib/ownership/keys/fake:app_unauthorized_ecdsa": "unauthorized_key_0"},
"spx": {},
"exit_success": DEFAULT_TEST_FAILURE_MSG,
"exit_failure": DEFAULT_TEST_SUCCESS_MSG,
},
Expand All @@ -170,14 +186,15 @@ _KEYS = {
opentitan_test(
name = "key_{}".format(name),
srcs = [":boot_test"],
ecdsa_key = keyinfo["key"],
ecdsa_key = keyinfo["ecdsa"],
exec_env = {
"//hw/top_earlgrey:fpga_hyper310_rom_ext": None,
},
fpga = fpga_params(
exit_failure = keyinfo["exit_failure"],
exit_success = keyinfo["exit_success"],
),
spx_key = keyinfo["spx"],
deps = [
"//sw/device/lib/base:status",
"//sw/device/lib/testing/test_framework:ottf_main",
Expand Down
136 changes: 125 additions & 11 deletions sw/device/silicon_creator/rom_ext/rom_ext.c
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@
#include "sw/device/silicon_creator/lib/sigverify/ecdsa_p256_key.h"
#include "sw/device/silicon_creator/lib/sigverify/rsa_verify.h"
#include "sw/device/silicon_creator/lib/sigverify/sigverify.h"
#include "sw/device/silicon_creator/lib/sigverify/sphincsplus/verify.h"
#include "sw/device/silicon_creator/manuf/base/perso_tlv_data.h"
#include "sw/device/silicon_creator/rom_ext/rescue.h"
#include "sw/device/silicon_creator/rom_ext/rom_ext_boot_policy.h"
Expand Down Expand Up @@ -257,18 +258,112 @@ void rom_ext_sram_exec(owner_sram_exec_mode_t mode) {
}
}

// A version of spx_verify that is tailored to the ROM_EXT needs.
// In particular:
// - We don't care about the OTP setting for SPX+ in the ROM_EXT.
// - We don't care about flash_exec in the ROM_EXT.
// - We have a different series of algorithm identifier values to accommodate
// hybrid signature schemes.
OT_WARN_UNUSED_RESULT
static rom_error_t rom_ext_spx_verify(
const sigverify_spx_signature_t *signature, const sigverify_spx_key_t *key,
uint32_t key_alg, 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) {
/*
* Shares for producing kErrorOk if SPHINCS+ verification succeeds. The first
* three shares are generated using the `sparse-fsm-encode` script while the
* last share is
* `kErrorOk ^ shares[0] ^ ... ^ shares[2]`.
*
* Encoding generated with:
* $ ./util/design/sparse-fsm-encode.py -d 5 -m 3 -n 32 \
* -s 1069420 --language=c
*
* Minimum Hamming distance: 14
* Maximum Hamming distance: 20
* Minimum Hamming weight: 14
* Maximum Hamming weight: 16
*/

const uint32_t shares[] = {
0x11deb806,
0x06457f69,
0x647f10c4,
0x73e4d092,
};

key_alg &= ~(uint32_t)kOwnershipKeyAlgCategoryMask;
key_alg |= (uint32_t)kOwnershipKeyAlgCategorySpx;

sigverify_spx_root_t actual_root;
sigverify_spx_root_t expected_root;
spx_public_key_root(key->data, expected_root.data);
size_t i;
for (i = 0; launder32(i) < kSigverifySpxRootNumWords; ++i) {
expected_root.data[i] ^= shares[i];
}

switch (key_alg) {
case kOwnershipKeyAlgSpxPure:
HARDENED_RETURN_IF_ERROR(spx_verify(
signature->data, kSpxVerifyPureDomainSep, kSpxVerifyPureDomainSepSize,
msg_prefix_1, msg_prefix_1_len, msg_prefix_2, msg_prefix_2_len, msg,
msg_len, key->data, actual_root.data));
break;

case kOwnershipKeyAlgSpxPrehash:
HARDENED_RETURN_IF_ERROR(
spx_verify(signature->data, kSpxVerifyPrehashDomainSep,
kSpxVerifyPrehashDomainSepSize,
/*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));
break;
default:
return kErrorSigverifyBadSpxConfig;
}
uint32_t result = 0;
for (--i; launder32(i) < kSigverifySpxRootNumWords; --i) {
result ^= expected_root.data[i] ^ actual_root.data[i];
}
HARDENED_CHECK_EQ(i, SIZE_MAX);
if (result != kErrorOk) {
return kErrorSigverifyBadSpxSignature;
}
return result;
}

OT_WARN_UNUSED_RESULT
static rom_error_t rom_ext_verify(const manifest_t *manifest,
const boot_data_t *boot_data) {
RETURN_IF_ERROR(rom_ext_boot_policy_manifest_check(manifest, boot_data));
ownership_key_alg_t key_alg = kOwnershipKeyAlgEcdsaP256;
RETURN_IF_ERROR(owner_keyring_find_key(
&keyring, key_alg,
sigverify_ecdsa_p256_key_id_get(&manifest->ecdsa_public_key),
&verify_key));

dbg_printf("app_verify: key=%u alg=%C domain=%C\r\n", verify_key,
keyring.key[verify_key]->key_alg,

uint32_t key_id =
sigverify_ecdsa_p256_key_id_get(&manifest->ecdsa_public_key);
// Check if there is an SPX+ key.
const manifest_ext_spx_key_t *ext_spx_key;
const manifest_ext_spx_signature_t *ext_spx_signature;
rom_error_t spx_err = manifest_ext_get_spx_key(manifest, &ext_spx_key);
spx_err += manifest_ext_get_spx_signature(manifest, &ext_spx_signature);
switch ((uint32_t)spx_err) {
case kErrorOk * 2:
// Both extensions present: valid SPX+ signature.
key_id += sigverify_spx_key_id_get(&ext_spx_key->key);
break;
case kErrorManifestBadExtension * 2:
// Both extensions absent: ECDSA only.
break;
default:
// One present, one absent: bad configuration.
return kErrorManifestBadExtension;
}

RETURN_IF_ERROR(owner_keyring_find_key(&keyring, key_id, &verify_key));
uint32_t key_alg = keyring.key[verify_key]->key_alg;

dbg_printf("app_verify: key=%u alg=%C domain=%C\r\n", verify_key, key_alg,
keyring.key[verify_key]->key_domain);

memset(boot_measurements.bl0.data, (int)rnd_uint32(),
Expand Down Expand Up @@ -296,9 +391,28 @@ static rom_error_t rom_ext_verify(const manifest_t *manifest,
memcpy(&boot_measurements.bl0, &act_digest, sizeof(boot_measurements.bl0));

uint32_t flash_exec = 0;
return sigverify_ecdsa_p256_verify(&manifest->ecdsa_signature,
&keyring.key[verify_key]->data.ecdsa,
&act_digest, &flash_exec);
if (key_alg == kOwnershipKeyAlgEcdsaP256) {
return sigverify_ecdsa_p256_verify(&manifest->ecdsa_signature,
&keyring.key[verify_key]->data.ecdsa,
&act_digest, &flash_exec);
} else if ((key_alg & kOwnershipKeyAlgCategoryMask) ==
kOwnershipKeyAlgCategoryHybridEcdsaSpx) {
// Hybrid signatures check both ECDSA and SPX+ signatures.
// TODO: as a future optimization, start the ECDSA verify operation on
// OTBN and compute the SPX+ verify in parallel on Ibex.
HARDENED_RETURN_IF_ERROR(sigverify_ecdsa_p256_verify(
&manifest->ecdsa_signature,
&keyring.key[verify_key]->data.hybrid_ecdsa_spx.ecdsa, &act_digest,
&flash_exec));
return rom_ext_spx_verify(
&ext_spx_signature->signature,
&keyring.key[verify_key]->data.hybrid_ecdsa_spx.spx, key_alg,
&usage_constraints_from_hw, sizeof(usage_constraints_from_hw), NULL, 0,
digest_region.start, digest_region.length, &act_digest);
} else {
// TODO: consider whether an SPX+-only verify is sufficent.
return kErrorOwnershipInvalidAlgorithm;
}
}

/**
Expand Down

0 comments on commit 1e19338

Please sign in to comment.