| // Copyright 2025 Google LLC |
| // |
| // Licensed under the Apache License, Version 2.0 (the "License"); |
| // you may not use this file except in compliance with the License. |
| // You may obtain a copy of the License at |
| // |
| // http://www.apache.org/licenses/LICENSE-2.0 |
| // |
| // Unless required by applicable law or agreed to in writing, software |
| // distributed under the License is distributed on an "AS IS" BASIS, |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| // See the License for the specific language governing permissions and |
| // limitations under the License. |
| |
| #include <libcr51sign/cr51_image_descriptor.h> |
| #include <libcr51sign/libcr51sign.h> |
| #include <libhoth/protocol/key_rotation.h> |
| #include <libhoth/transports/libhoth_dbus.h> |
| |
| #include <flashupdate/validator/key_rotate_helper.hpp> |
| |
| #include <memory> |
| |
| #include <gmock/gmock.h> |
| #include <gtest/gtest.h> |
| |
| using ::testing::_; |
| using ::testing::DoAll; |
| using ::testing::Return; |
| using ::testing::SetArgPointee; |
| using ::testing::SetArrayArgument; |
| |
| // Mocking C functions from libhoth and libcr51sign |
| class MockApi |
| { |
| public: |
| virtual ~MockApi() = default; |
| MOCK_METHOD(int, libhoth_dbus_open, |
| (const struct libhoth_dbus_device_init_options* opts, |
| struct libhoth_device** hoth_device)); |
| MOCK_METHOD(enum key_rotation_err, libhoth_key_rotation_chunk_type_count, |
| (struct libhoth_device * hoth_device, uint32_t chunk_typecode, |
| uint16_t* chunk_count)); |
| MOCK_METHOD(enum key_rotation_err, libhoth_key_rotation_read_chunk_type, |
| (struct libhoth_device * dev, uint32_t chunk_typecode, |
| uint32_t chunk_index, uint16_t offset, uint16_t size, |
| struct hoth_response_key_rotation_record_read* read_response, |
| uint16_t* response_size)); |
| MOCK_METHOD(int, hash_init, (void* ctx, enum hash_type type)); |
| MOCK_METHOD(int, hash_update, (void* ctx, const uint8_t* data, size_t len)); |
| MOCK_METHOD(int, hash_final, (void* ctx, uint8_t* digest)); |
| }; |
| |
| // Global mock object |
| static std::unique_ptr<MockApi> mock_api; |
| |
| // C function trampolines to the mock object |
| extern "C" |
| { |
| int libhoth_dbus_open(const struct libhoth_dbus_device_init_options* opts, |
| struct libhoth_device** hoth_device) |
| { |
| if (mock_api) |
| { |
| return mock_api->libhoth_dbus_open(opts, hoth_device); |
| } |
| return -1; // Should not happen in tests |
| } |
| |
| enum key_rotation_err libhoth_key_rotation_chunk_type_count( |
| struct libhoth_device* hoth_device, uint32_t chunk_typecode, |
| uint16_t* chunk_count) |
| { |
| if (mock_api) |
| { |
| return mock_api->libhoth_key_rotation_chunk_type_count( |
| hoth_device, chunk_typecode, chunk_count); |
| } |
| return KEY_ROTATION_ERR; |
| } |
| |
| enum key_rotation_err libhoth_key_rotation_read_chunk_type( |
| struct libhoth_device* dev, uint32_t chunk_typecode, uint32_t chunk_index, |
| uint16_t offset, uint16_t size, |
| struct hoth_response_key_rotation_record_read* read_response, |
| uint16_t* response_size) |
| { |
| if (mock_api) |
| { |
| return mock_api->libhoth_key_rotation_read_chunk_type( |
| dev, chunk_typecode, chunk_index, offset, size, read_response, |
| response_size); |
| } |
| return KEY_ROTATION_ERR; |
| } |
| |
| int hash_init(void* ctx, enum hash_type type) |
| { |
| if (mock_api) |
| { |
| return mock_api->hash_init(ctx, type); |
| } |
| return -1; |
| } |
| |
| int hash_update(void* ctx, const uint8_t* data, size_t len) |
| { |
| if (mock_api) |
| { |
| return mock_api->hash_update(ctx, data, len); |
| } |
| return -1; |
| } |
| |
| int hash_final(void* ctx, uint8_t* digest) |
| { |
| if (mock_api) |
| { |
| return mock_api->hash_final(ctx, digest); |
| } |
| return -1; |
| } |
| } // extern "C" |
| |
| namespace google::cr51 |
| { |
| |
| class KeyRotateHelperTest : public ::testing::Test |
| { |
| protected: |
| void SetUp() override |
| { |
| mock_api = std::make_unique<::testing::StrictMock<MockApi>>(); |
| } |
| |
| void TearDown() override |
| { |
| mock_api.reset(); |
| } |
| |
| // A dummy device pointer. The value doesn't matter as it's just passed |
| // around. |
| struct libhoth_device* dummy_hoth_device = |
| reinterpret_cast<struct libhoth_device*>(0xDEADBEEF); |
| }; |
| |
| TEST_F(KeyRotateHelperTest, SetHothId) |
| { |
| std::string id = "my-hoth-device"; |
| setHothId(id); |
| |
| // We can't read back HothId, but we can check if it's used correctly. |
| // This test has to run before any other test that successfully opens a hoth |
| // device, due to the static variable in hothDevice(). |
| EXPECT_CALL( |
| *mock_api, |
| libhoth_dbus_open( |
| ::testing::Truly( |
| [&](const struct libhoth_dbus_device_init_options* opts) { |
| return strcmp(opts->hoth_id, id.c_str()) == 0; |
| }), |
| _)) |
| .WillOnce(Return(-1)); // Return error to not cache the device. |
| sha256 hash{}; |
| trustDescriptorHash(nullptr, reinterpret_cast<uint8_t*>(&hash), |
| sizeof(hash)); |
| } |
| |
| TEST_F(KeyRotateHelperTest, TrustDescriptorHashWrongHashSize) |
| { |
| sha256 hash{}; |
| EXPECT_FALSE(trustDescriptorHash(nullptr, reinterpret_cast<uint8_t*>(&hash), |
| sizeof(hash) - 1)); |
| } |
| |
| TEST_F(KeyRotateHelperTest, TrustDescriptorHashHothOpenFails) |
| { |
| sha256 hash{}; |
| EXPECT_CALL(*mock_api, libhoth_dbus_open(_, _)).WillOnce(Return(-1)); |
| EXPECT_FALSE(trustDescriptorHash(nullptr, reinterpret_cast<uint8_t*>(&hash), |
| sizeof(hash))); |
| } |
| |
| // Grouping tests that need a valid hoth device to avoid issues with static |
| // hoth_device in hothDevice(). |
| class KeyRotateHelperWithDeviceTest : public KeyRotateHelperTest |
| { |
| protected: |
| // This static pointer is needed because SetUpTestSuite is static. |
| static struct libhoth_device* dummy_hoth_device_for_suite; |
| |
| // SetUpTestSuite is called once before any tests in this suite are run. |
| static void SetUpTestSuite() |
| { |
| // Create the mock API once for the whole suite. |
| mock_api = std::make_unique<::testing::StrictMock<MockApi>>(); |
| |
| // Expect the dbus_open call once for the entire suite. This will be |
| // triggered by the first test that calls hothDevice(). |
| EXPECT_CALL(*mock_api, libhoth_dbus_open(_, _)) |
| .WillOnce(DoAll(SetArgPointee<1>(dummy_hoth_device_for_suite), |
| Return(0))); |
| } |
| |
| // TearDownTestSuite is called once after all tests in this suite are run. |
| static void TearDownTestSuite() |
| { |
| mock_api.reset(); |
| } |
| |
| // Override per-test SetUp and TearDown to do nothing, preventing |
| // interference with the suite-level setup. |
| void SetUp() override {} |
| void TearDown() override {} |
| }; |
| |
| struct libhoth_device* |
| KeyRotateHelperWithDeviceTest::dummy_hoth_device_for_suite = |
| reinterpret_cast<struct libhoth_device*>(0xDEADBEEF); |
| |
| TEST_F(KeyRotateHelperWithDeviceTest, TrustDescriptorHashGetChunkCountFails) |
| { |
| sha256 hash{}; |
| EXPECT_CALL(*mock_api, libhoth_key_rotation_chunk_type_count( |
| dummy_hoth_device_for_suite, |
| KEY_ROTATION_CHUNK_TYPE_CODE_BASH, _)) |
| .WillOnce(Return(KEY_ROTATION_ERR)); |
| |
| EXPECT_FALSE(trustDescriptorHash(nullptr, reinterpret_cast<uint8_t*>(&hash), |
| sizeof(hash))); |
| } |
| |
| TEST_F(KeyRotateHelperWithDeviceTest, TrustDescriptorHashNoChunks) |
| { |
| sha256 hash{}; |
| EXPECT_CALL(*mock_api, libhoth_key_rotation_chunk_type_count( |
| dummy_hoth_device_for_suite, |
| KEY_ROTATION_CHUNK_TYPE_CODE_BASH, _)) |
| .WillOnce(DoAll(SetArgPointee<2>(0), Return(KEY_ROTATION_CMD_SUCCESS))); |
| |
| EXPECT_FALSE(trustDescriptorHash(nullptr, reinterpret_cast<uint8_t*>(&hash), |
| sizeof(hash))); |
| } |
| |
| TEST_F(KeyRotateHelperWithDeviceTest, TrustDescriptorHashMatch) |
| { |
| sha256 hash{0x01, 0x02, 0x03, 0x04}; |
| struct hoth_response_key_rotation_record_read response = {}; |
| struct bios_allowed_hash_list* hash_list = |
| reinterpret_cast<struct bios_allowed_hash_list*>(&response.data); |
| hash_list->hash_count = 1; |
| memcpy(hash_list->hash_list[0], &hash, sizeof(hash)); |
| |
| uint16_t response_size = |
| sizeof(struct bios_allowed_hash_list) + sizeof(sha256); |
| |
| EXPECT_CALL(*mock_api, libhoth_key_rotation_chunk_type_count( |
| dummy_hoth_device_for_suite, |
| KEY_ROTATION_CHUNK_TYPE_CODE_BASH, _)) |
| .WillOnce(DoAll(SetArgPointee<2>(1), Return(KEY_ROTATION_CMD_SUCCESS))); |
| EXPECT_CALL(*mock_api, |
| libhoth_key_rotation_read_chunk_type( |
| dummy_hoth_device_for_suite, |
| KEY_ROTATION_CHUNK_TYPE_CODE_BASH, 0, _, _, _, _)) |
| .WillOnce( |
| DoAll(SetArgPointee<5>(response), SetArgPointee<6>(response_size), |
| Return(KEY_ROTATION_CMD_SUCCESS))); |
| EXPECT_TRUE(trustDescriptorHash(nullptr, reinterpret_cast<uint8_t*>(&hash), |
| sizeof(hash))); |
| } |
| |
| TEST_F(KeyRotateHelperWithDeviceTest, TrustDescriptorHashMatchOnSecondChunk) |
| { |
| sha256 hash{0x01, 0x02, 0x03, 0x04}; |
| |
| // Setup for chunk_count = 3 |
| EXPECT_CALL(*mock_api, libhoth_key_rotation_chunk_type_count( |
| dummy_hoth_device_for_suite, |
| KEY_ROTATION_CHUNK_TYPE_CODE_BASH, _)) |
| .WillOnce(DoAll(SetArgPointee<2>(3), Return(KEY_ROTATION_CMD_SUCCESS))); |
| |
| // Setup for first chunk (non-matching) |
| struct hoth_response_key_rotation_record_read non_matching_response = {}; |
| struct bios_allowed_hash_list* non_matching_hash_list = |
| reinterpret_cast<struct bios_allowed_hash_list*>( |
| &non_matching_response.data); |
| non_matching_hash_list->hash_count = 1; |
| sha256 non_matching_hash{0xff, 0xff, 0xff, 0xff}; |
| memcpy(non_matching_hash_list->hash_list[0], &non_matching_hash, |
| sizeof(non_matching_hash)); |
| uint16_t non_matching_response_size = |
| sizeof(struct bios_allowed_hash_list) + sizeof(sha256); |
| |
| EXPECT_CALL(*mock_api, libhoth_key_rotation_read_chunk_type( |
| dummy_hoth_device_for_suite, |
| KEY_ROTATION_CHUNK_TYPE_CODE_BASH, 0, |
| sizeof(key_rotation_chunk_header), 0, _, _)) |
| .WillOnce(DoAll(SetArgPointee<5>(non_matching_response), |
| SetArgPointee<6>(non_matching_response_size), |
| Return(KEY_ROTATION_CMD_SUCCESS))); |
| |
| // Setup for second chunk (matching) |
| struct hoth_response_key_rotation_record_read matching_response = {}; |
| struct bios_allowed_hash_list* matching_hash_list = |
| reinterpret_cast<struct bios_allowed_hash_list*>( |
| &matching_response.data); |
| matching_hash_list->hash_count = 1; |
| memcpy(matching_hash_list->hash_list[0], &hash, sizeof(hash)); |
| uint16_t matching_response_size = |
| sizeof(struct bios_allowed_hash_list) + sizeof(sha256); |
| |
| EXPECT_CALL(*mock_api, libhoth_key_rotation_read_chunk_type( |
| dummy_hoth_device_for_suite, |
| KEY_ROTATION_CHUNK_TYPE_CODE_BASH, 1, |
| sizeof(key_rotation_chunk_header), 0, _, _)) |
| .WillOnce(DoAll(SetArgPointee<5>(matching_response), |
| SetArgPointee<6>(matching_response_size), |
| Return(KEY_ROTATION_CMD_SUCCESS))); |
| |
| EXPECT_TRUE(trustDescriptorHash(nullptr, reinterpret_cast<uint8_t*>(&hash), |
| sizeof(hash))); |
| } |
| |
| TEST_F(KeyRotateHelperWithDeviceTest, TrustDescriptorHashMatchOnThirdChunk) |
| { |
| sha256 hash{0x01, 0x02, 0x03, 0x04}; |
| |
| // Setup for chunk_count = 3 |
| EXPECT_CALL(*mock_api, libhoth_key_rotation_chunk_type_count( |
| dummy_hoth_device_for_suite, |
| KEY_ROTATION_CHUNK_TYPE_CODE_BASH, _)) |
| .WillOnce(DoAll(SetArgPointee<2>(3), Return(KEY_ROTATION_CMD_SUCCESS))); |
| |
| // Setup for first and second chunks (non-matching) |
| for (int i = 0; i < 2; ++i) |
| { |
| struct hoth_response_key_rotation_record_read non_matching_response = |
| {}; |
| struct bios_allowed_hash_list* non_matching_hash_list = |
| reinterpret_cast<struct bios_allowed_hash_list*>( |
| &non_matching_response.data); |
| non_matching_hash_list->hash_count = 1; |
| sha256 non_matching_hash{0xff, 0xff, 0xff, (uint8_t)i}; |
| memcpy(non_matching_hash_list->hash_list[0], &non_matching_hash, |
| sizeof(non_matching_hash)); |
| uint16_t non_matching_response_size = |
| sizeof(struct bios_allowed_hash_list) + sizeof(sha256); |
| |
| EXPECT_CALL(*mock_api, |
| libhoth_key_rotation_read_chunk_type( |
| dummy_hoth_device_for_suite, |
| KEY_ROTATION_CHUNK_TYPE_CODE_BASH, i, _, _, _, _)) |
| .WillOnce(DoAll(SetArgPointee<5>(non_matching_response), |
| SetArgPointee<6>(non_matching_response_size), |
| Return(KEY_ROTATION_CMD_SUCCESS))); |
| } |
| |
| // Setup for third chunk (matching) |
| struct hoth_response_key_rotation_record_read matching_response = {}; |
| struct bios_allowed_hash_list* matching_hash_list = |
| reinterpret_cast<struct bios_allowed_hash_list*>( |
| &matching_response.data); |
| matching_hash_list->hash_count = 1; |
| memcpy(matching_hash_list->hash_list[0], &hash, sizeof(hash)); |
| uint16_t matching_response_size = |
| sizeof(struct bios_allowed_hash_list) + sizeof(sha256); |
| |
| EXPECT_CALL(*mock_api, libhoth_key_rotation_read_chunk_type( |
| dummy_hoth_device_for_suite, |
| KEY_ROTATION_CHUNK_TYPE_CODE_BASH, 2, |
| sizeof(key_rotation_chunk_header), 0, _, _)) |
| .WillOnce(DoAll(SetArgPointee<5>(matching_response), |
| SetArgPointee<6>(matching_response_size), |
| Return(KEY_ROTATION_CMD_SUCCESS))); |
| |
| EXPECT_TRUE(trustDescriptorHash(nullptr, reinterpret_cast<uint8_t*>(&hash), |
| sizeof(hash))); |
| } |
| |
| TEST_F(KeyRotateHelperTest, TrustKeyInCr51SignatureNullArgs) |
| { |
| EXPECT_FALSE( |
| trustKeyInCr51Signature(nullptr, SIGNATURE_RSA2048_PKCS15, nullptr, 0)); |
| char dummy_ctx; |
| EXPECT_FALSE(trustKeyInCr51Signature(&dummy_ctx, SIGNATURE_RSA2048_PKCS15, |
| nullptr, 0)); // NOLINT |
| struct signature_rsa2048_pkcs15 sig; |
| EXPECT_FALSE(trustKeyInCr51Signature(nullptr, SIGNATURE_RSA2048_PKCS15, |
| &sig, sizeof(sig))); |
| } |
| |
| TEST_F(KeyRotateHelperTest, TrustKeyInCr51SignatureUnsupportedScheme) |
| { |
| char dummy_ctx; |
| struct signature_rsa2048_pkcs15 sig{}; |
| EXPECT_FALSE(trustKeyInCr51Signature( |
| &dummy_ctx, static_cast<enum signature_scheme>(99), &sig, sizeof(sig))); |
| } |
| |
| TEST_F(KeyRotateHelperTest, TrustKeyInCr51SignatureHashFails) |
| { |
| char dummy_ctx; |
| struct signature_rsa2048_pkcs15 sig{}; |
| EXPECT_CALL(*mock_api, hash_init(_, HASH_SHA2_256)).WillOnce(Return(1)); |
| EXPECT_FALSE(trustKeyInCr51Signature(&dummy_ctx, SIGNATURE_RSA2048_PKCS15, |
| &sig, sizeof(sig))); |
| } |
| |
| TEST_F(KeyRotateHelperWithDeviceTest, TrustKeyInCr51SignatureMatch) |
| { |
| char dummy_ctx; |
| struct signature_rsa4096_pkcs15 sig = {}; |
| sha256 key_fingerprint{0x01, 0x02, 0x03, 0x04}; |
| |
| EXPECT_CALL(*mock_api, hash_init(_, HASH_SHA2_256)).WillOnce(Return(0)); |
| EXPECT_CALL(*mock_api, hash_update(_, _, _)).WillRepeatedly(Return(0)); |
| EXPECT_CALL(*mock_api, hash_final(_, _)) |
| .WillOnce(DoAll( |
| SetArrayArgument<1>(reinterpret_cast<uint8_t*>(&key_fingerprint), |
| reinterpret_cast<uint8_t*>(&key_fingerprint) + |
| sizeof(key_fingerprint)), |
| Return(0))); |
| |
| EXPECT_CALL(*mock_api, libhoth_key_rotation_chunk_type_count( |
| dummy_hoth_device_for_suite, |
| KEY_ROTATION_CHUNK_TYPE_CODE_BKEY, _)) |
| .WillOnce(DoAll(SetArgPointee<2>(1), Return(KEY_ROTATION_CMD_SUCCESS))); |
| struct hoth_response_key_rotation_record_read response = {}; |
| struct bios_verifiction_key_fingerprint* trusted_key = |
| reinterpret_cast<struct bios_verifiction_key_fingerprint*>( |
| &response.data); |
| memcpy(trusted_key->key_fingerprint, &key_fingerprint, |
| sizeof(key_fingerprint)); |
| uint16_t response_size = sizeof(struct bios_verifiction_key_fingerprint); |
| |
| EXPECT_CALL(*mock_api, |
| libhoth_key_rotation_read_chunk_type( |
| dummy_hoth_device_for_suite, |
| KEY_ROTATION_CHUNK_TYPE_CODE_BKEY, 0, _, _, _, _)) |
| .WillOnce( |
| DoAll(SetArgPointee<5>(response), SetArgPointee<6>(response_size), |
| Return(KEY_ROTATION_CMD_SUCCESS))); |
| EXPECT_TRUE(trustKeyInCr51Signature(&dummy_ctx, SIGNATURE_RSA4096_PKCS15, |
| &sig, sizeof(sig))); |
| } |
| |
| TEST_F(KeyRotateHelperWithDeviceTest, TrustKeyInCr51SignatureMatchOnSecondChunk) |
| { |
| char dummy_ctx; |
| struct signature_rsa4096_pkcs15 sig = {}; |
| sha256 key_fingerprint{0x01, 0x02, 0x03, 0x04}; |
| |
| EXPECT_CALL(*mock_api, hash_init(_, HASH_SHA2_256)).WillOnce(Return(0)); |
| EXPECT_CALL(*mock_api, hash_update(_, _, _)).WillRepeatedly(Return(0)); |
| EXPECT_CALL(*mock_api, hash_final(_, _)) |
| .WillOnce(DoAll( |
| SetArrayArgument<1>(reinterpret_cast<uint8_t*>(&key_fingerprint), |
| reinterpret_cast<uint8_t*>(&key_fingerprint) + |
| sizeof(key_fingerprint)), |
| Return(0))); |
| |
| EXPECT_CALL(*mock_api, libhoth_key_rotation_chunk_type_count( |
| dummy_hoth_device_for_suite, |
| KEY_ROTATION_CHUNK_TYPE_CODE_BKEY, _)) |
| .WillOnce(DoAll(SetArgPointee<2>(3), Return(KEY_ROTATION_CMD_SUCCESS))); |
| |
| // Setup for first chunk (non-matching) |
| struct hoth_response_key_rotation_record_read non_matching_response = {}; |
| struct bios_verifiction_key_fingerprint* non_matching_key = |
| reinterpret_cast<struct bios_verifiction_key_fingerprint*>( |
| &non_matching_response.data); |
| sha256 non_matching_fingerprint{0xff, 0xff, 0xff, 0xff}; |
| memcpy(non_matching_key->key_fingerprint, &non_matching_fingerprint, |
| sizeof(non_matching_fingerprint)); |
| uint16_t non_matching_response_size = |
| sizeof(struct bios_verifiction_key_fingerprint); |
| |
| EXPECT_CALL(*mock_api, libhoth_key_rotation_read_chunk_type( |
| dummy_hoth_device_for_suite, |
| KEY_ROTATION_CHUNK_TYPE_CODE_BKEY, 0, |
| sizeof(key_rotation_chunk_header), 0, _, _)) |
| .WillOnce(DoAll(SetArgPointee<5>(non_matching_response), |
| SetArgPointee<6>(non_matching_response_size), |
| Return(KEY_ROTATION_CMD_SUCCESS))); |
| |
| // Setup for second chunk (matching) |
| struct hoth_response_key_rotation_record_read matching_response = {}; |
| struct bios_verifiction_key_fingerprint* matching_key = |
| reinterpret_cast<struct bios_verifiction_key_fingerprint*>( |
| &matching_response.data); |
| memcpy(matching_key->key_fingerprint, &key_fingerprint, |
| sizeof(key_fingerprint)); |
| uint16_t matching_response_size = |
| sizeof(struct bios_verifiction_key_fingerprint); |
| |
| EXPECT_CALL(*mock_api, libhoth_key_rotation_read_chunk_type( |
| dummy_hoth_device_for_suite, |
| KEY_ROTATION_CHUNK_TYPE_CODE_BKEY, 1, |
| sizeof(key_rotation_chunk_header), 0, _, _)) |
| .WillOnce(DoAll(SetArgPointee<5>(matching_response), |
| SetArgPointee<6>(matching_response_size), |
| Return(KEY_ROTATION_CMD_SUCCESS))); |
| |
| EXPECT_TRUE(trustKeyInCr51Signature(&dummy_ctx, SIGNATURE_RSA4096_PKCS15, |
| &sig, sizeof(sig))); |
| } |
| |
| TEST_F(KeyRotateHelperWithDeviceTest, TrustKeyInCr51SignatureMatchOnThirdChunk) |
| { |
| char dummy_ctx; |
| struct signature_rsa4096_pkcs15 sig = {}; |
| sha256 key_fingerprint{0x01, 0x02, 0x03, 0x04}; |
| |
| EXPECT_CALL(*mock_api, hash_init(_, HASH_SHA2_256)).WillOnce(Return(0)); |
| EXPECT_CALL(*mock_api, hash_update(_, _, _)).WillRepeatedly(Return(0)); |
| EXPECT_CALL(*mock_api, hash_final(_, _)) |
| .WillOnce(DoAll( |
| SetArrayArgument<1>(reinterpret_cast<uint8_t*>(&key_fingerprint), |
| reinterpret_cast<uint8_t*>(&key_fingerprint) + |
| sizeof(key_fingerprint)), |
| Return(0))); |
| |
| EXPECT_CALL(*mock_api, libhoth_key_rotation_chunk_type_count( |
| dummy_hoth_device_for_suite, |
| KEY_ROTATION_CHUNK_TYPE_CODE_BKEY, _)) |
| .WillOnce(DoAll(SetArgPointee<2>(3), Return(KEY_ROTATION_CMD_SUCCESS))); |
| |
| // Setup for first and second chunks (non-matching) |
| for (int i = 0; i < 2; ++i) |
| { |
| struct hoth_response_key_rotation_record_read non_matching_response = |
| {}; |
| struct bios_verifiction_key_fingerprint* non_matching_key = |
| reinterpret_cast<struct bios_verifiction_key_fingerprint*>( |
| &non_matching_response.data); |
| sha256 non_matching_fingerprint{0xff, 0xff, 0xff, (uint8_t)i}; |
| memcpy(non_matching_key->key_fingerprint, &non_matching_fingerprint, |
| sizeof(non_matching_fingerprint)); |
| uint16_t non_matching_response_size = |
| sizeof(struct bios_verifiction_key_fingerprint); |
| |
| EXPECT_CALL(*mock_api, |
| libhoth_key_rotation_read_chunk_type( |
| dummy_hoth_device_for_suite, |
| KEY_ROTATION_CHUNK_TYPE_CODE_BKEY, i, _, _, _, _)) |
| .WillOnce(DoAll(SetArgPointee<5>(non_matching_response), |
| SetArgPointee<6>(non_matching_response_size), |
| Return(KEY_ROTATION_CMD_SUCCESS))); |
| } |
| |
| // Setup for third chunk (matching) |
| struct hoth_response_key_rotation_record_read matching_response = {}; |
| struct bios_verifiction_key_fingerprint* matching_key = |
| reinterpret_cast<struct bios_verifiction_key_fingerprint*>( |
| &matching_response.data); |
| memcpy(matching_key->key_fingerprint, &key_fingerprint, |
| sizeof(key_fingerprint)); |
| uint16_t matching_response_size = |
| sizeof(struct bios_verifiction_key_fingerprint); |
| |
| EXPECT_CALL(*mock_api, libhoth_key_rotation_read_chunk_type( |
| dummy_hoth_device_for_suite, |
| KEY_ROTATION_CHUNK_TYPE_CODE_BKEY, 2, |
| sizeof(key_rotation_chunk_header), 0, _, _)) |
| .WillOnce(DoAll(SetArgPointee<5>(matching_response), |
| SetArgPointee<6>(matching_response_size), |
| Return(KEY_ROTATION_CMD_SUCCESS))); |
| |
| EXPECT_TRUE(trustKeyInCr51Signature(&dummy_ctx, SIGNATURE_RSA4096_PKCS15, |
| &sig, sizeof(sig))); |
| } |
| |
| TEST_F(KeyRotateHelperTest, AlwaysTrustDescriptorHash) |
| { |
| sha256 hash{}; |
| // It should return true even if the underlying function returns false. |
| EXPECT_CALL(*mock_api, libhoth_dbus_open(_, _)).WillOnce(Return(-1)); |
| EXPECT_TRUE(alwaysTrustDescriptorHash( |
| nullptr, reinterpret_cast<uint8_t*>(&hash), sizeof(hash))); |
| } |
| |
| TEST_F(KeyRotateHelperTest, AlwaysTrustKeyInCr51Signature) |
| { |
| char dummy_ctx; |
| struct signature_rsa2048_pkcs15 sig{}; |
| // It should return true even if the underlying function returns false. |
| EXPECT_CALL(*mock_api, hash_init(_, _)).WillOnce(Return(1)); // make it fail |
| EXPECT_TRUE(alwaysTrustKeyInCr51Signature( |
| &dummy_ctx, SIGNATURE_RSA2048_PKCS15, &sig, sizeof(sig))); |
| } |
| |
| TEST_F(KeyRotateHelperWithDeviceTest, NeverTrustDescriptorHash) |
| { |
| sha256 hash{0x01, 0x02, 0x03, 0x04}; |
| // It should return false even if the underlying function returns true. |
| struct hoth_response_key_rotation_record_read response = {}; |
| struct bios_allowed_hash_list* hash_list = |
| reinterpret_cast<struct bios_allowed_hash_list*>(&response.data); |
| hash_list->hash_count = 1; |
| memcpy(hash_list->hash_list[0], &hash, sizeof(hash)); |
| uint16_t response_size = |
| sizeof(struct bios_allowed_hash_list) + sizeof(sha256); |
| |
| EXPECT_CALL(*mock_api, libhoth_key_rotation_chunk_type_count( |
| dummy_hoth_device_for_suite, |
| KEY_ROTATION_CHUNK_TYPE_CODE_BASH, _)) |
| .WillOnce(DoAll(SetArgPointee<2>(1), Return(KEY_ROTATION_CMD_SUCCESS))); |
| EXPECT_CALL(*mock_api, |
| libhoth_key_rotation_read_chunk_type( |
| dummy_hoth_device_for_suite, |
| KEY_ROTATION_CHUNK_TYPE_CODE_BASH, 0, _, _, _, _)) |
| .WillOnce( |
| DoAll(SetArgPointee<5>(response), SetArgPointee<6>(response_size), |
| Return(KEY_ROTATION_CMD_SUCCESS))); |
| |
| EXPECT_FALSE(neverTrustDescriptorHash( |
| nullptr, reinterpret_cast<uint8_t*>(&hash), sizeof(hash))); |
| } |
| |
| TEST_F(KeyRotateHelperTest, IsBiosKeyRotationSupportHothOpenFails) |
| { |
| // This test must run before any test that successfully opens a hoth device |
| // due to the static hoth_device cache in the production code. |
| EXPECT_CALL(*mock_api, libhoth_dbus_open(_, _)).WillOnce(Return(-1)); |
| EXPECT_FALSE(isBiosKeyRotationSupport()); |
| } |
| |
| TEST_F(KeyRotateHelperWithDeviceTest, IsBiosKeyRotationSupportChunkCountFails) |
| { |
| EXPECT_CALL(*mock_api, libhoth_key_rotation_chunk_type_count( |
| dummy_hoth_device_for_suite, |
| KEY_ROTATION_CHUNK_TYPE_CODE_BKEY, _)) |
| .WillOnce(Return(KEY_ROTATION_ERR)); |
| EXPECT_FALSE(isBiosKeyRotationSupport()); |
| } |
| |
| TEST_F(KeyRotateHelperWithDeviceTest, IsBiosKeyRotationSupportNoChunks) |
| { |
| EXPECT_CALL(*mock_api, libhoth_key_rotation_chunk_type_count( |
| dummy_hoth_device_for_suite, |
| KEY_ROTATION_CHUNK_TYPE_CODE_BKEY, _)) |
| .WillOnce(DoAll(SetArgPointee<2>(0), Return(KEY_ROTATION_CMD_SUCCESS))); |
| EXPECT_FALSE(isBiosKeyRotationSupport()); |
| } |
| |
| TEST_F(KeyRotateHelperWithDeviceTest, IsBiosKeyRotationSupportSuccess) |
| { |
| EXPECT_CALL(*mock_api, libhoth_key_rotation_chunk_type_count( |
| dummy_hoth_device_for_suite, |
| KEY_ROTATION_CHUNK_TYPE_CODE_BKEY, _)) |
| .WillOnce(DoAll(SetArgPointee<2>(1), Return(KEY_ROTATION_CMD_SUCCESS))); |
| EXPECT_TRUE(isBiosKeyRotationSupport()); |
| } |
| |
| } // namespace google::cr51 |