flashupdate: Implement BIOS key metrics writer for key rotation Introduce the BIOS key metrics writer logic in flashupdate. This writes telemetry data to `/run/bios_key/` containing four metrics: - bios_key_validation_method: EMBEDDED_KEY or PAYLOAD_KEY - bios_key_image_family: Decimal image family of the validating key - bios_key_version: The config package version from Hoth key rotation record - bios_key_validation_key_data: First 64 bits of the validating key's modulus (hex) The metrics writer is hooked into: 1. isBiosKeyRotationSupport(): Writes EMBEDDED_KEY when key rotation is not supported or there are zero keys defined. 2. trustKeyInCr51Signature(): Writes PAYLOAD_KEY, image family, version (queried via libhoth_key_rotation_get_version), and validation key data (extracted from the validating key's modulus) when a key match happens. Updated unit tests to align with this design and added mock coverage for libhoth_key_rotation_get_version. Google-Bug-Id: 459542518 Change-Id: Ibdf47909f85cf2ebd30fc9ed1bf02db3a543bc0d Signed-off-by: Manan J. Mehta <mjmehta@google.com>
diff --git a/subprojects/flashupdate/meson_options.txt b/subprojects/flashupdate/meson_options.txt index f3f8235..2bec758 100644 --- a/subprojects/flashupdate/meson_options.txt +++ b/subprojects/flashupdate/meson_options.txt
@@ -2,3 +2,5 @@ option('dev', type: 'boolean', value: false, description: 'Enable Development Workflow') option('cr51-mauv-path', type: 'string', description: 'CR51 MAUV Stored File') option('cr51-mauv-offset', type: 'integer', description: 'Offset to reset the MAUV data') +option('bios-key-metrics-dir', type: 'string', value: '/run/bios_key', description: 'Directory to write BIOS key metrics') +
diff --git a/subprojects/flashupdate/src/meson.build b/subprojects/flashupdate/src/meson.build index 9319b8f..2cd79b7 100644 --- a/subprojects/flashupdate/src/meson.build +++ b/subprojects/flashupdate/src/meson.build
@@ -49,6 +49,11 @@ conf_data = configuration_data() conf_data.set_quoted('CR51_MAUV_PATH', get_option('cr51-mauv-path')) conf_data.set('CR51_MAUV_OFFSET', get_option('cr51-mauv-offset')) +metrics_dir = get_option('bios-key-metrics-dir') +if not get_option('tests').disabled() + metrics_dir = '/tmp/flashupdate_test_bios_key' +endif +conf_data.set_quoted('BIOS_KEY_METRICS_DIR', metrics_dir) config_h = configure_file( output: 'config.h', configuration: conf_data
diff --git a/subprojects/flashupdate/src/validator/key_rotate_helper.cpp b/subprojects/flashupdate/src/validator/key_rotate_helper.cpp index d272628..a20ad26 100644 --- a/subprojects/flashupdate/src/validator/key_rotate_helper.cpp +++ b/subprojects/flashupdate/src/validator/key_rotate_helper.cpp
@@ -11,6 +11,8 @@ // 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 "config.h" + #include "flashupdate/validator/key_rotate_helper.hpp" #include "flashupdate/info.hpp" @@ -22,7 +24,9 @@ #include <libhoth/transports/libhoth_dbus.h> #include <cstdint> +#include <filesystem> #include <format> +#include <fstream> #include <string_view> namespace @@ -238,6 +242,111 @@ } return true; } + +void writeBiosKeyMetricFiles(std::string_view validation_method, + uint32_t image_family, uint32_t version, + uint64_t validation_key_data) +{ + std::string dir = BIOS_KEY_METRICS_DIR; + std::string tmp_dir = dir + ".tmp"; + std::error_code ec; + + // 1. Create and write to a .tmp directory first. We use the + // write-to-tmp-and-rename pattern to ensure that the metrics update is + // atomic. This prevents a telemetry reader from experiencing race + // conditions, such as reading partially-written files (partial writes) or a + // mix of old and new files (group inconsistency). + std::filesystem::create_directories(tmp_dir, ec); + if (ec) + { + LOG(flashupdate::LogLevel::Error, "Failed to create directory {}: {}", + tmp_dir, ec.message()); + return; + } + + auto write_file = [](const std::filesystem::path& path, + const std::string_view content) { + std::ofstream file(path, std::ios::out | std::ios::trunc); + if (!file.is_open()) + { + LOG(flashupdate::LogLevel::Error, + "Failed to open file for writing: {}", path.string()); + return; + } + file << content << "\n"; + }; + + std::filesystem::path tmp_dir_path(tmp_dir); + write_file(tmp_dir_path / "bios_key_validation_method", validation_method); + write_file(tmp_dir_path / "bios_key_image_family", + std::to_string(image_family)); + write_file(tmp_dir_path / "bios_key_version", std::to_string(version)); + write_file(tmp_dir_path / "bios_key_validation_key_data", + std::format("0x{:016x}", validation_key_data)); + + // 2. Delete the old target directory + std::filesystem::remove_all(dir, ec); + if (ec && ec != std::errc::no_such_file_or_directory) + { + LOG(flashupdate::LogLevel::Error, + "Failed to remove old directory {}: {}", dir, ec.message()); + return; + } + + // 3. Rename tmp directory to target directory + std::filesystem::rename(tmp_dir, dir, ec); + if (ec) + { + LOG(flashupdate::LogLevel::Error, + "Failed to rename directory {} to {}: {}", tmp_dir, dir, + ec.message()); + return; + } + + LOG(flashupdate::LogLevel::Notice, + "Write bios key metric files: {} {} {} {}", validation_method, + image_family, version, std::format("0x{:016x}", validation_key_data)); +} + +void clearBiosKeyMetricFiles() +{ + std::string dir = BIOS_KEY_METRICS_DIR; + std::error_code ec; + std::filesystem::remove_all(dir, ec); + if (ec && ec != std::errc::no_such_file_or_directory) + { + LOG(flashupdate::LogLevel::Error, + "Failed to clear metrics directory {}: {}", dir, ec.message()); + } +} + +const uint8_t* getModulusFromCr51Signature(enum signature_scheme scheme, + const void* cr51_signature) +{ + if (!cr51_signature) + { + return nullptr; + } + switch (scheme) + { + case SIGNATURE_RSA2048_PKCS15: + return reinterpret_cast<const struct signature_rsa2048_pkcs15*>( + cr51_signature) + ->modulus; + case SIGNATURE_RSA3072_PKCS15: + return reinterpret_cast<const struct signature_rsa3072_pkcs15*>( + cr51_signature) + ->modulus; + case SIGNATURE_RSA4096_PKCS15: + case SIGNATURE_RSA4096_PKCS15_SHA512: + return reinterpret_cast<const struct signature_rsa4096_pkcs15*>( + cr51_signature) + ->modulus; + default: + return nullptr; + } +} + } // namespace namespace google @@ -359,6 +468,7 @@ hoth_device, KEY_ROTATION_CHUNK_TYPE_CODE_BKEY); if (trusted_bios_key_count < 0) { + writeBiosKeyMetricFiles("EMBEDDED_KEY", 0, 0, 0); return false; } @@ -403,13 +513,47 @@ "Match trusted bios key finger print in {}_{}", rotConfigChunkName(KEY_ROTATION_CHUNK_TYPE_CODE_BKEY), chunk_index); + + uint32_t version = 0; + struct hoth_response_key_rotation_record_version record_version; + enum key_rotation_err version_err = + libhoth_key_rotation_get_version(hoth_device, &record_version); + if (version_err == KEY_ROTATION_CMD_SUCCESS) + { + version = record_version.version; + } + else + { + LOG(flashupdate::LogLevel::Error, + "Get key rotation version failed: {}", + static_cast<int>(version_err)); + } + + uint64_t val_key_data = 0; + const uint8_t* modulus = + getModulusFromCr51Signature(scheme, cr51_signature); + if (modulus) + { + for (size_t i = 0; i < 8; ++i) + { + val_key_data = (val_key_data << 8) | modulus[i]; + } + } + + writeBiosKeyMetricFiles("PAYLOAD_KEY", + trusted_bios_key->image_family, version, + val_key_data); + return true; } } LOG(flashupdate::LogLevel::Notice, "Not match any trusted bios key"); + writeBiosKeyMetricFiles("EMBEDDED_KEY", 0, 0, 0); + return false; } + // For Testing or debug bool alwaysTrustDescriptorHash(const void* ctx, const uint8_t* descriptor_hash, size_t hash_size) @@ -453,13 +597,19 @@ bool isBiosKeyRotationSupport() { struct libhoth_device* hoth_device = hothDevice(); - if (!hoth_device) - { - return false; - } - return chunkCountInHothRoTConfig(hoth_device, - KEY_ROTATION_CHUNK_TYPE_CODE_BKEY) > 0; + // Clear any stale metric files written by previous runs. + clearBiosKeyMetricFiles(); + + if (hoth_device && chunkCountInHothRoTConfig( + hoth_device, KEY_ROTATION_CHUNK_TYPE_CODE_BKEY) > 0) + { + return true; + } + // If rotation is not supported, we immediately record that we are + // using the EMBEDDED_KEY + writeBiosKeyMetricFiles("EMBEDDED_KEY", 0, 0, 0); + return false; } } // namespace cr51 } // namespace google
diff --git a/subprojects/flashupdate/test/validator/key_rotate_helper.cpp b/subprojects/flashupdate/test/validator/key_rotate_helper.cpp index da88f19..0f21131 100644 --- a/subprojects/flashupdate/test/validator/key_rotate_helper.cpp +++ b/subprojects/flashupdate/test/validator/key_rotate_helper.cpp
@@ -19,6 +19,8 @@ #include <flashupdate/validator/key_rotate_helper.hpp> +#include <filesystem> +#include <fstream> #include <memory> #include <gmock/gmock.h> @@ -46,6 +48,10 @@ 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( + enum key_rotation_err, libhoth_key_rotation_get_version, + (struct libhoth_device * dev, + struct hoth_response_key_rotation_record_version* record_version)); 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)); @@ -94,6 +100,17 @@ return KEY_ROTATION_ERR; } +enum key_rotation_err libhoth_key_rotation_get_version( + struct libhoth_device* dev, + struct hoth_response_key_rotation_record_version* record_version) +{ + if (mock_api) + { + return mock_api->libhoth_key_rotation_get_version(dev, record_version); + } + return KEY_ROTATION_ERR; +} + int hash_init(void* ctx, enum hash_type type) { if (mock_api) @@ -130,11 +147,15 @@ protected: void SetUp() override { + std::error_code ec; + std::filesystem::remove_all("/tmp/flashupdate_test_bios_key", ec); mock_api = std::make_unique<::testing::StrictMock<MockApi>>(); } void TearDown() override { + std::error_code ec; + std::filesystem::remove_all("/tmp/flashupdate_test_bios_key", ec); mock_api.reset(); } @@ -205,6 +226,8 @@ // TearDownTestSuite is called once after all tests in this suite are run. static void TearDownTestSuite() { + std::error_code ec; + std::filesystem::remove_all("/tmp/flashupdate_test_bios_key", ec); mock_api.reset(); } @@ -411,7 +434,10 @@ struct libcr51sign_ctx dummy_ctx; dummy_ctx.descriptor.image_family = 2; struct signature_rsa4096_pkcs15 sig = {}; - sha256 key_fingerprint{0x01, 0x02, 0x03, 0x04}; + uint8_t test_modulus[8] = {0x12, 0x34, 0x56, 0x78, 0xab, 0xcd, 0xef, 0x01}; + memcpy(sig.modulus, test_modulus, sizeof(test_modulus)); + + sha256 key_fingerprint = {0x12, 0x34, 0x56, 0x78, 0xab, 0xcd, 0xef, 0x01}; EXPECT_CALL(*mock_api, hash_init(_, HASH_SHA2_256)).WillOnce(Return(0)); EXPECT_CALL(*mock_api, hash_update(_, _, _)).WillRepeatedly(Return(0)); @@ -442,8 +468,54 @@ .WillOnce( DoAll(SetArgPointee<5>(response), SetArgPointee<6>(response_size), Return(KEY_ROTATION_CMD_SUCCESS))); + + struct hoth_response_key_rotation_record_version record_version = { + .version = 16}; + EXPECT_CALL(*mock_api, libhoth_key_rotation_get_version( + dummy_hoth_device_for_suite, _)) + .WillOnce(DoAll(SetArgPointee<1>(record_version), + Return(KEY_ROTATION_CMD_SUCCESS))); + + // Clean up any existing files before the test + std::error_code ec; + std::filesystem::remove_all("/tmp/flashupdate_test_bios_key", ec); + EXPECT_TRUE(trustKeyInCr51Signature(&dummy_ctx, SIGNATURE_RSA4096_PKCS15, &sig, sizeof(sig))); + + // Verify that the files were created and contain the correct values + EXPECT_TRUE(std::filesystem::exists( + "/tmp/flashupdate_test_bios_key/bios_key_validation_method")); + EXPECT_TRUE(std::filesystem::exists( + "/tmp/flashupdate_test_bios_key/bios_key_image_family")); + EXPECT_TRUE(std::filesystem::exists( + "/tmp/flashupdate_test_bios_key/bios_key_version")); + EXPECT_TRUE(std::filesystem::exists( + "/tmp/flashupdate_test_bios_key/bios_key_validation_key_data")); + + std::ifstream method_file( + "/tmp/flashupdate_test_bios_key/bios_key_validation_method"); + std::string method; + std::getline(method_file, method); + EXPECT_EQ(method, "PAYLOAD_KEY"); + + std::ifstream family_file( + "/tmp/flashupdate_test_bios_key/bios_key_image_family"); + std::string family; + std::getline(family_file, family); + EXPECT_EQ(family, "2"); + + std::ifstream version_file( + "/tmp/flashupdate_test_bios_key/bios_key_version"); + std::string version; + std::getline(version_file, version); + EXPECT_EQ(version, "16"); + + std::ifstream key_data_file( + "/tmp/flashupdate_test_bios_key/bios_key_validation_key_data"); + std::string key_data; + std::getline(key_data_file, key_data); + EXPECT_EQ(key_data, "0x12345678abcdef01"); } TEST_F(KeyRotateHelperWithDeviceTest, @@ -545,8 +617,54 @@ SetArgPointee<6>(matching_response_size), Return(KEY_ROTATION_CMD_SUCCESS))); + // Mock get_version failure to cover the version fallback path + EXPECT_CALL(*mock_api, libhoth_key_rotation_get_version( + dummy_hoth_device_for_suite, _)) + .WillOnce(Return(KEY_ROTATION_ERR)); + + // Clean up any existing files before the test + std::error_code ec; + std::filesystem::remove_all("/tmp/flashupdate_test_bios_key", ec); + + uint8_t test_modulus[8] = {0x12, 0x34, 0x56, 0x78, 0xab, 0xcd, 0xef, 0x01}; + memcpy(sig.modulus, test_modulus, sizeof(test_modulus)); + EXPECT_TRUE(trustKeyInCr51Signature(&dummy_ctx, SIGNATURE_RSA4096_PKCS15, &sig, sizeof(sig))); + + // Verify fallback files were created + EXPECT_TRUE(std::filesystem::exists( + "/tmp/flashupdate_test_bios_key/bios_key_validation_method")); + EXPECT_TRUE(std::filesystem::exists( + "/tmp/flashupdate_test_bios_key/bios_key_image_family")); + EXPECT_TRUE(std::filesystem::exists( + "/tmp/flashupdate_test_bios_key/bios_key_version")); + EXPECT_TRUE(std::filesystem::exists( + "/tmp/flashupdate_test_bios_key/bios_key_validation_key_data")); + + std::ifstream method_file( + "/tmp/flashupdate_test_bios_key/bios_key_validation_method"); + std::string method; + std::getline(method_file, method); + EXPECT_EQ(method, "PAYLOAD_KEY"); + + std::ifstream family_file( + "/tmp/flashupdate_test_bios_key/bios_key_image_family"); + std::string family; + std::getline(family_file, family); + EXPECT_EQ(family, "0"); + + std::ifstream version_file( + "/tmp/flashupdate_test_bios_key/bios_key_version"); + std::string version; + std::getline(version_file, version); + EXPECT_EQ(version, "0"); + + std::ifstream key_data_file( + "/tmp/flashupdate_test_bios_key/bios_key_validation_key_data"); + std::string key_data; + std::getline(key_data_file, key_data); + EXPECT_EQ(key_data, "0x12345678abcdef01"); } TEST_F(KeyRotateHelperWithDeviceTest, TrustKeyInCr51SignatureMatchOnThirdChunk) @@ -610,6 +728,11 @@ SetArgPointee<6>(matching_response_size), Return(KEY_ROTATION_CMD_SUCCESS))); + // Mock get_version failure to cover the version fallback path + EXPECT_CALL(*mock_api, libhoth_key_rotation_get_version( + dummy_hoth_device_for_suite, _)) + .WillOnce(Return(KEY_ROTATION_ERR)); + EXPECT_TRUE(trustKeyInCr51Signature(&dummy_ctx, SIGNATURE_RSA4096_PKCS15, &sig, sizeof(sig))); } @@ -666,7 +789,17 @@ // 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()); + + // Verify that the files were created and contain the correct values + EXPECT_TRUE(std::filesystem::exists( + "/tmp/flashupdate_test_bios_key/bios_key_validation_method")); + std::ifstream method_file( + "/tmp/flashupdate_test_bios_key/bios_key_validation_method"); + std::string method; + std::getline(method_file, method); + EXPECT_EQ(method, "EMBEDDED_KEY"); } TEST_F(KeyRotateHelperWithDeviceTest, IsBiosKeyRotationSupportChunkCountFails) @@ -678,15 +811,6 @@ 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( @@ -696,4 +820,187 @@ EXPECT_TRUE(isBiosKeyRotationSupport()); } +TEST_F(KeyRotateHelperWithDeviceTest, IsBiosKeyRotationSupportClearsMetrics) +{ + // First, write some files + std::error_code ec; + std::string dir = "/tmp/flashupdate_test_bios_key"; + std::filesystem::create_directories(dir, ec); + ASSERT_FALSE(ec); + + auto write_dummy = [&](const std::string& name) { + std::ofstream file(std::filesystem::path(dir) / name); + file << "dummy\n"; + }; + write_dummy("bios_key_validation_method"); + write_dummy("bios_key_image_family"); + write_dummy("bios_key_version"); + write_dummy("bios_key_validation_key_data"); + + // Verify they exist + EXPECT_TRUE(std::filesystem::exists(dir + "/bios_key_validation_method")); + EXPECT_TRUE(std::filesystem::exists(dir + "/bios_key_image_family")); + EXPECT_TRUE(std::filesystem::exists(dir + "/bios_key_version")); + EXPECT_TRUE(std::filesystem::exists(dir + "/bios_key_validation_key_data")); + + // Mock Hoth call to succeed so isBiosKeyRotationSupport returns true + 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))); + + // Call isBiosKeyRotationSupport, which should clear the files + EXPECT_TRUE(isBiosKeyRotationSupport()); + + // Verify they are gone + EXPECT_FALSE(std::filesystem::exists(dir + "/bios_key_validation_method")); + EXPECT_FALSE(std::filesystem::exists(dir + "/bios_key_image_family")); + EXPECT_FALSE(std::filesystem::exists(dir + "/bios_key_version")); + EXPECT_FALSE( + std::filesystem::exists(dir + "/bios_key_validation_key_data")); +} + +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))); + + // Clean up any existing files before the test + std::error_code ec; + std::filesystem::remove_all("/tmp/flashupdate_test_bios_key", ec); + + EXPECT_FALSE(isBiosKeyRotationSupport()); + + // Verify that the files were created and contain the correct values + EXPECT_TRUE(std::filesystem::exists( + "/tmp/flashupdate_test_bios_key/bios_key_validation_method")); + EXPECT_TRUE(std::filesystem::exists( + "/tmp/flashupdate_test_bios_key/bios_key_image_family")); + EXPECT_TRUE(std::filesystem::exists( + "/tmp/flashupdate_test_bios_key/bios_key_version")); + EXPECT_TRUE(std::filesystem::exists( + "/tmp/flashupdate_test_bios_key/bios_key_validation_key_data")); + + std::ifstream method_file( + "/tmp/flashupdate_test_bios_key/bios_key_validation_method"); + std::string method; + std::getline(method_file, method); + EXPECT_EQ(method, "EMBEDDED_KEY"); + + std::ifstream family_file( + "/tmp/flashupdate_test_bios_key/bios_key_image_family"); + std::string family; + std::getline(family_file, family); + EXPECT_EQ(family, "0"); + + std::ifstream version_file( + "/tmp/flashupdate_test_bios_key/bios_key_version"); + std::string version; + std::getline(version_file, version); + EXPECT_EQ(version, "0"); + + std::ifstream key_data_file( + "/tmp/flashupdate_test_bios_key/bios_key_validation_key_data"); + std::string key_data; + std::getline(key_data_file, key_data); + EXPECT_EQ(key_data, "0x0000000000000000"); +} + +TEST_F(KeyRotateHelperWithDeviceTest, TrustKeyInCr51SignatureChunkCountError) +{ + struct libcr51sign_ctx dummy_ctx; + struct signature_rsa4096_pkcs15 sig = {}; + + sha256 key_fingerprint = {0x12, 0x34, 0x56, 0x78, 0xab, 0xcd, 0xef, 0x01}; + + 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(Return(KEY_ROTATION_ERR)); + + // Clean up any existing files before the test + std::error_code ec; + std::filesystem::remove_all("/tmp/flashupdate_test_bios_key", ec); + + EXPECT_FALSE(trustKeyInCr51Signature(&dummy_ctx, SIGNATURE_RSA4096_PKCS15, + &sig, sizeof(sig))); + + // Verify that the files were created and contain the correct values + EXPECT_TRUE(std::filesystem::exists( + "/tmp/flashupdate_test_bios_key/bios_key_validation_method")); + std::ifstream method_file( + "/tmp/flashupdate_test_bios_key/bios_key_validation_method"); + std::string method; + std::getline(method_file, method); + EXPECT_EQ(method, "EMBEDDED_KEY"); +} + +TEST_F(KeyRotateHelperWithDeviceTest, TrustKeyInCr51SignatureNoMatch) +{ + struct libcr51sign_ctx dummy_ctx; + struct signature_rsa4096_pkcs15 sig = {}; + sha256 key_fingerprint = {0x12, 0x34, 0x56, 0x78, 0xab, 0xcd, 0xef, 0x01}; + + 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))); + + // Define 1 trusted key in Hoth, but it won't match + 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); + trusted_key->image_family = 2; + // Different fingerprint + sha256 different_fingerprint = {0xff, 0xff, 0xff, 0xff}; + memcpy(trusted_key->key_fingerprint, &different_fingerprint, + sizeof(different_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))); + + // Clean up any existing files before the test + std::error_code ec; + std::filesystem::remove_all("/tmp/flashupdate_test_bios_key", ec); + + EXPECT_FALSE(trustKeyInCr51Signature(&dummy_ctx, SIGNATURE_RSA4096_PKCS15, + &sig, sizeof(sig))); + + // Verify that the files were created and contain EMBEDDED_KEY + EXPECT_TRUE(std::filesystem::exists( + "/tmp/flashupdate_test_bios_key/bios_key_validation_method")); + std::ifstream method_file( + "/tmp/flashupdate_test_bios_key/bios_key_validation_method"); + std::string method; + std::getline(method_file, method); + EXPECT_EQ(method, "EMBEDDED_KEY"); +} + } // namespace google::cr51