blob: cb0c58e29ced3e41922353b0f0065e723685c387 [file] [log] [blame]
// Copyright lowRISC contributors.
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
// SPDX-License-Identifier: Apache-2.0
#include "sw/device/silicon_creator/lib/sigverify.h"
#include <cstring>
#include <unordered_set>
#include "gtest/gtest.h"
#include "sw/device/lib/base/hardened.h"
#include "sw/device/silicon_creator/lib/drivers/mock_lifecycle.h"
#include "sw/device/silicon_creator/lib/drivers/mock_otp.h"
#include "sw/device/silicon_creator/lib/mock_sigverify_mod_exp_ibex.h"
#include "sw/device/silicon_creator/lib/mock_sigverify_mod_exp_otbn.h"
#include "sw/device/silicon_creator/testing/mask_rom_test.h"
#include "hw/top_earlgrey/sw/autogen/top_earlgrey.h"
#include "otp_ctrl_regs.h"
namespace sigverify_unittest {
namespace {
using ::testing::DoAll;
using ::testing::NotNull;
using ::testing::Return;
using ::testing::SetArgPointee;
/**
* SHA2-256 digest of "test".
*
* Can be obtained using:
* ```
* echo -n "test" | openssl dgst -sha256 -binary | \
* xxd -p -c 4 | tac | sed 's|.*|0x&,|'
* ```
*/
constexpr hmac_digest_t kTestDigest{
.digest =
{
0xb0f00a08,
0xd15d6c15,
0x2b0b822c,
0xa3bf4f1b,
0xc55ad015,
0x9a2feaa0,
0x884c7d65,
0x9f86d081,
},
};
/**
* 3072-bit EMSA-PKCS1-v1_5 encoding of `kTestDigest`.
*
* Can be obtained using:
* ```
* # Create private key.
* openssl genrsa -out key.pem 3072
* # Sign.
* echo -n "test" | openssl dgst -sha256 -sign key.pem -out signature
* # Print encoded message.
* openssl rsautl -verify -in signature -inkey key.pem -raw | \
* xxd -p -c 4 | tac | sed 's|.*|0x&,|'
* ```
*/
constexpr sigverify_rsa_buffer_t kEncMsg{
.data = {
0xb0f00a08, 0xd15d6c15, 0x2b0b822c, 0xa3bf4f1b, 0xc55ad015, 0x9a2feaa0,
0x884c7d65, 0x9f86d081, 0x05000420, 0x03040201, 0x86480165, 0x0d060960,
0x00303130, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x0001ffff,
}};
// The value of `kSignature` is not significant since we use mocks for
// `sigverify_mod_exp_ibex()` and `sigverify_mod_exp_otbn()`.
constexpr sigverify_rsa_buffer_t kSignature{};
/**
* Life cycle states used in parameterized tests.
*/
constexpr std::array<lifecycle_state_t, 8> kLcStatesTest{
kLcStateTestUnlocked0, kLcStateTestUnlocked1, kLcStateTestUnlocked2,
kLcStateTestUnlocked3, kLcStateTestUnlocked4, kLcStateTestUnlocked5,
kLcStateTestUnlocked6, kLcStateTestUnlocked7,
};
constexpr std::array<lifecycle_state_t, 4> kLcStatesNonTestOperational{
kLcStateDev,
kLcStateProd,
kLcStateProdEnd,
kLcStateRma,
};
constexpr std::array<lifecycle_state_t, 12> kLcStatesNonOperational{
kLcStateRaw, kLcStateTestLocked0,
kLcStateTestLocked1, kLcStateTestLocked2,
kLcStateTestLocked3, kLcStateTestLocked4,
kLcStateTestLocked5, kLcStateTestLocked6,
kLcStateScrap, kLcStatePostTransition,
kLcStateEscalate, kLcStateInvalid,
};
const std::unordered_set<lifecycle_state_t> &LcStatesAll() {
static const std::unordered_set<lifecycle_state_t> *const kLcStatesAll =
[]() {
auto states = new std::unordered_set<lifecycle_state_t>();
states->insert(kLcStatesTest.begin(), kLcStatesTest.end());
states->insert(kLcStatesNonTestOperational.begin(),
kLcStatesNonTestOperational.end());
states->insert(kLcStatesNonOperational.begin(),
kLcStatesNonOperational.end());
return states;
}();
return *kLcStatesAll;
}
TEST(LcStateCount, IsCorrect) {
EXPECT_EQ(kLcStateNumStates, LcStatesAll().size());
}
class SigverifyInLcState
: public mask_rom_test::MaskRomTest,
public testing::WithParamInterface<lifecycle_state_t> {
protected:
mask_rom_test::MockSigverifyModExpIbex sigverify_mod_exp_ibex_;
mask_rom_test::MockSigverifyModExpOtbn sigverify_mod_exp_otbn_;
mask_rom_test::MockOtp otp_;
// The content of this key is not significant since we use mocks.
sigverify_rsa_key_t key_{};
};
class SigverifyInNonTestStates : public SigverifyInLcState {};
TEST_P(SigverifyInNonTestStates, BadOtpValue) {
EXPECT_CALL(otp_,
read32(OTP_CTRL_PARAM_CREATOR_SW_CFG_USE_SW_RSA_VERIFY_OFFSET))
.WillOnce(Return(0xA5A5A5A5));
EXPECT_EQ(sigverify_rsa_verify(&kSignature, &key_, &kTestDigest, GetParam()),
kErrorSigverifyBadOtpValue);
}
TEST_P(SigverifyInNonTestStates, GoodSignatureIbex) {
EXPECT_CALL(otp_,
read32(OTP_CTRL_PARAM_CREATOR_SW_CFG_USE_SW_RSA_VERIFY_OFFSET))
.WillOnce(Return(kHardenedBoolTrue));
EXPECT_CALL(sigverify_mod_exp_ibex_, mod_exp(&key_, &kSignature, NotNull()))
.WillOnce(DoAll(SetArgPointee<2>(kEncMsg), Return(kErrorOk)));
EXPECT_EQ(sigverify_rsa_verify(&kSignature, &key_, &kTestDigest, GetParam()),
kErrorOk);
}
TEST_P(SigverifyInNonTestStates, GoodSignatureOtbn) {
EXPECT_CALL(otp_,
read32(OTP_CTRL_PARAM_CREATOR_SW_CFG_USE_SW_RSA_VERIFY_OFFSET))
.WillOnce(Return(kHardenedBoolFalse));
EXPECT_CALL(sigverify_mod_exp_otbn_, mod_exp(&key_, &kSignature, NotNull()))
.WillOnce(DoAll(SetArgPointee<2>(kEncMsg), Return(kErrorOk)));
EXPECT_EQ(sigverify_rsa_verify(&kSignature, &key_, &kTestDigest, GetParam()),
kErrorOk);
}
TEST_P(SigverifyInNonTestStates, BadSignatureOtbn) {
// Corrupt the words of the encoded message by flipping their bits and check
// that signature verification fails.
for (size_t i = 0; i < kSigVerifyRsaNumWords; ++i) {
auto bad_enc_msg = kEncMsg;
bad_enc_msg.data[i] = ~bad_enc_msg.data[i];
EXPECT_CALL(otp_,
read32(OTP_CTRL_PARAM_CREATOR_SW_CFG_USE_SW_RSA_VERIFY_OFFSET))
.WillOnce(Return(kHardenedBoolFalse));
EXPECT_CALL(sigverify_mod_exp_otbn_, mod_exp(&key_, &kSignature, NotNull()))
.WillOnce(DoAll(SetArgPointee<2>(bad_enc_msg), Return(kErrorOk)));
EXPECT_EQ(
sigverify_rsa_verify(&kSignature, &key_, &kTestDigest, GetParam()),
kErrorSigverifyBadEncodedMessage);
}
}
INSTANTIATE_TEST_SUITE_P(NonTestOperationalStates, SigverifyInNonTestStates,
testing::ValuesIn(kLcStatesNonTestOperational));
class SigverifyInTestStates : public SigverifyInLcState {};
TEST_P(SigverifyInTestStates, GoodSignatureIbex) {
EXPECT_CALL(sigverify_mod_exp_ibex_, mod_exp(&key_, &kSignature, NotNull()))
.WillOnce(DoAll(SetArgPointee<2>(kEncMsg), Return(kErrorOk)));
EXPECT_EQ(sigverify_rsa_verify(&kSignature, &key_, &kTestDigest, GetParam()),
kErrorOk);
}
TEST_P(SigverifyInTestStates, BadSignatureIbex) {
// Corrupt the words of the encoded message by flipping their bits and check
// that signature verification fails.
for (size_t i = 0; i < kSigVerifyRsaNumWords; ++i) {
auto bad_enc_msg = kEncMsg;
bad_enc_msg.data[i] = ~bad_enc_msg.data[i];
EXPECT_CALL(sigverify_mod_exp_ibex_, mod_exp(&key_, &kSignature, NotNull()))
.WillOnce(DoAll(SetArgPointee<2>(bad_enc_msg), Return(kErrorOk)));
EXPECT_EQ(
sigverify_rsa_verify(&kSignature, &key_, &kTestDigest, GetParam()),
kErrorSigverifyBadEncodedMessage);
}
}
INSTANTIATE_TEST_SUITE_P(TestStates, SigverifyInTestStates,
testing::ValuesIn(kLcStatesTest));
class SigverifyInInvalidStates : public SigverifyInLcState {};
TEST_P(SigverifyInInvalidStates, BadLcState) {
EXPECT_EQ(sigverify_rsa_verify(&kSignature, &key_, &kTestDigest, GetParam()),
kErrorSigverifyBadLcState);
}
INSTANTIATE_TEST_SUITE_P(NonOperationalStates, SigverifyInInvalidStates,
testing::ValuesIn(kLcStatesNonOperational));
struct UsageConstraintsTestCase {
uint32_t selector_bits;
lifecycle_device_id_t exp_device_id;
uint32_t exp_manuf_state_creator;
uint32_t exp_manuf_state_owner;
uint32_t exp_life_cycle_state;
};
class SigverifyUsageConstraints
: public mask_rom_test::MaskRomTest,
public testing::WithParamInterface<UsageConstraintsTestCase> {
protected:
mask_rom_test::MockLifecycle lifecycle_;
mask_rom_test::MockOtp otp_;
};
/**
* Constants used in usage constraints tests.
*/
constexpr uint32_t kUnusedWord = MANIFEST_USAGE_CONSTRAINT_UNSELECTED_WORD_VAL;
constexpr uint32_t kManufStateCreator = 0x30303030;
constexpr uint32_t kManufStateOwner = 0x31313131;
constexpr uint32_t kLifeCycleState = 0x32323232;
constexpr lifecycle_device_id_t kDeviceId{
.device_id =
{
0x01020304,
0x05060708,
0x09100A0B,
0x0C0D0E0F,
0x10111213,
0x14151617,
0x18192021,
0x22232425,
},
};
TEST_F(SigverifyUsageConstraints, ConstantCheck) {
// Changing this constant breaks compatibility with the mask ROM, existing
// images and tools.
EXPECT_EQ(MANIFEST_USAGE_CONSTRAINT_UNSELECTED_WORD_VAL, 0xA5A5A5A5);
EXPECT_NE(kManufStateCreator, kUnusedWord);
EXPECT_NE(kManufStateOwner, kUnusedWord);
EXPECT_NE(kLifeCycleState, kUnusedWord);
for (size_t i = 0; i < kLifecycleDeviceIdNumWords; ++i) {
EXPECT_NE(kDeviceId.device_id[i], kUnusedWord);
}
}
constexpr std::array<UsageConstraintsTestCase, 4> kUsageConstraintsTestCases{{
{
.selector_bits = 0x7FF,
.exp_device_id = kDeviceId,
.exp_manuf_state_creator = kUnusedWord,
.exp_manuf_state_owner = kUnusedWord,
.exp_life_cycle_state = kLifeCycleState,
},
{
.selector_bits = 0x3AA,
.exp_device_id = {.device_id =
{
kUnusedWord,
kDeviceId.device_id[1],
kUnusedWord,
kDeviceId.device_id[3],
kUnusedWord,
kDeviceId.device_id[5],
kUnusedWord,
kDeviceId.device_id[7],
}},
.exp_manuf_state_creator = kUnusedWord,
.exp_manuf_state_owner = kUnusedWord,
.exp_life_cycle_state = kUnusedWord,
},
{
.selector_bits = 0x155,
.exp_device_id = {.device_id =
{
kDeviceId.device_id[0],
kUnusedWord,
kDeviceId.device_id[2],
kUnusedWord,
kDeviceId.device_id[4],
kUnusedWord,
kDeviceId.device_id[6],
kUnusedWord,
}},
.exp_manuf_state_creator = kUnusedWord,
.exp_manuf_state_owner = kUnusedWord,
.exp_life_cycle_state = kUnusedWord,
},
{
.selector_bits = 0,
.exp_device_id = {.device_id =
{
kUnusedWord,
kUnusedWord,
kUnusedWord,
kUnusedWord,
kUnusedWord,
kUnusedWord,
kUnusedWord,
kUnusedWord,
}},
.exp_manuf_state_creator = kUnusedWord,
.exp_manuf_state_owner = kUnusedWord,
.exp_life_cycle_state = kUnusedWord,
},
}};
TEST_P(SigverifyUsageConstraints, Read) {
EXPECT_CALL(lifecycle_, DeviceId(NotNull()))
.WillOnce(SetArgPointee<0>(kDeviceId));
// TODO(#7948): Define OTP entries for manufacturing states.
EXPECT_CALL(lifecycle_, State())
.WillOnce(Return(static_cast<lifecycle_state_t>(kLifeCycleState)));
manifest_usage_constraints_t usage_constraints;
sigverify_usage_constraints_get(GetParam().selector_bits, &usage_constraints);
EXPECT_EQ(usage_constraints.selector_bits, GetParam().selector_bits);
EXPECT_THAT(usage_constraints.device_id.device_id,
testing::ElementsAreArray(GetParam().exp_device_id.device_id));
EXPECT_EQ(usage_constraints.manuf_state_creator,
GetParam().exp_manuf_state_creator);
EXPECT_EQ(usage_constraints.manuf_state_owner,
GetParam().exp_manuf_state_owner);
EXPECT_EQ(usage_constraints.life_cycle_state,
GetParam().exp_life_cycle_state);
}
INSTANTIATE_TEST_SUITE_P(UsageConstraintsTestCases, SigverifyUsageConstraints,
testing::ValuesIn(kUsageConstraintsTestCases));
} // namespace
} // namespace sigverify_unittest