blob: 61d7caad6050a76bebc95aed27c8b30ed1621fb1 [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_hmac.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 "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::MockHmac hmac_;
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));
} // namespace
} // namespace sigverify_unittest