diff --git a/whitebox/api/BUILD b/whitebox/api/BUILD index fe0017e..b48cd16 100644 --- a/whitebox/api/BUILD +++ b/whitebox/api/BUILD @@ -17,8 +17,12 @@ cc_library( "//:is_old_api": [], "//:is_old_vmpra": [], "//conditions:default": [ # Chrome - "HAS_PROVIDER_KEYS", + # "HAS_PROVIDER_KEYS", "ENABLE_LICENSE_PROTOCOL_2_2", + # Needed when talking to server SDKs [v16.3.3, v16.4.3] with license + # protocol v2.2 enabled (b/177271059). + # TODO(kqyang): Remove the flag after deprecating v16.x server SDKs. + "WORKAROUND_STRIP_PADDING_BUG", ], }) + select({ "//:is_chromeos": ["WV_ENABLE_HW_VERIFICATION=1"], diff --git a/whitebox/api/license_whitebox_key_control_block_test.cc b/whitebox/api/license_whitebox_key_control_block_test.cc index f470417..b89885c 100644 --- a/whitebox/api/license_whitebox_key_control_block_test.cc +++ b/whitebox/api/license_whitebox_key_control_block_test.cc @@ -12,17 +12,19 @@ class LicenseWhiteboxKeyControlBlockTest : public LicenseWhiteboxTestBase, public testing::WithParamInterface< std::tuple> { + TestLicenseBuilder::OdkVersion, + bool>> { protected: void SetUp() override { LicenseWhiteboxTestBase::SetUp(); - std::tie(kcb_, odk_) = GetParam(); + std::tie(kcb_, odk_, simulate_strip_padding_bug_) = GetParam(); server_ = TestServer::CreateDualKey(); } TestLicenseBuilder::KeyControlBlock kcb_; TestLicenseBuilder::OdkVersion odk_; + bool simulate_strip_padding_bug_; // b/177271059 std::unique_ptr server_; }; @@ -35,6 +37,8 @@ TEST_P(LicenseWhiteboxKeyControlBlockTest, Decrypt) { TestLicenseBuilder builder; builder.GetSettings().key_control_block = kcb_; builder.GetSettings().odk_version = odk_; + builder.GetSettings().simulate_strip_padding_bug = + simulate_strip_padding_bug_; const auto& content = golden_data_.CBCContent(); const auto& key = content.software_crypto_key; @@ -111,7 +115,8 @@ INSTANTIATE_TEST_SUITE_P( TestLicenseBuilder::KeyControlBlock::kEncrypted), ::testing::Values(TestLicenseBuilder::OdkVersion::kNone, TestLicenseBuilder::OdkVersion::k16_3, - TestLicenseBuilder::OdkVersion::k16_5))); + TestLicenseBuilder::OdkVersion::k16_5), + ::testing::Bool())); class LicenseWhiteboxKeyControlBlockSingleTest : public LicenseWhiteboxTestBase { diff --git a/whitebox/api/license_whitebox_process_license_response_benchmark.cc b/whitebox/api/license_whitebox_process_license_response_benchmark.cc index 6bc9afc..8f125fa 100644 --- a/whitebox/api/license_whitebox_process_license_response_benchmark.cc +++ b/whitebox/api/license_whitebox_process_license_response_benchmark.cc @@ -242,14 +242,6 @@ INSTANTIATE_TEST_SUITE_P( SecurityLevel::kSoftwareSecureDecode, kNoProviderKeyId))); -INSTANTIATE_TEST_SUITE_P( - SingleKeyWithSingleKeyServerAndProviderKeyId, - LicenseWhiteboxProcessLicenseResponseBenchmark, - ::testing::Values(std::make_tuple(WB_LICENSE_KEY_MODE_SINGLE_KEY, - 1, - SecurityLevel::kSoftwareSecureCrypto, - kValidProviderKeyId))); - INSTANTIATE_TEST_SUITE_P( SingleKeyWithDualKeyServerAndProviderKeyId, LicenseWhiteboxProcessLicenseResponseBenchmark, @@ -258,4 +250,20 @@ INSTANTIATE_TEST_SUITE_P( SecurityLevel::kSoftwareSecureCrypto, kValidProviderKeyId))); +INSTANTIATE_TEST_SUITE_P( + FiveKeysWithDualKeyServerAndProviderKeyId, + LicenseWhiteboxProcessLicenseResponseBenchmark, + ::testing::Values(std::make_tuple(WB_LICENSE_KEY_MODE_DUAL_KEY, + 5, + SecurityLevel::kSoftwareSecureCrypto, + kValidProviderKeyId))); + +INSTANTIATE_TEST_SUITE_P( + FiveDecodeKeysWithDualKeyServerAndProviderKeyId, + LicenseWhiteboxProcessLicenseResponseBenchmark, + ::testing::Values(std::make_tuple(WB_LICENSE_KEY_MODE_DUAL_KEY, + 5, + SecurityLevel::kSoftwareSecureDecode, + kValidProviderKeyId))); + } // namespace widevine diff --git a/whitebox/api/license_whitebox_query_content_key_status_test.cc b/whitebox/api/license_whitebox_query_content_key_status_test.cc index 6a1ef8e..95fc391 100644 --- a/whitebox/api/license_whitebox_query_content_key_status_test.cc +++ b/whitebox/api/license_whitebox_query_content_key_status_test.cc @@ -176,6 +176,11 @@ TEST_P(LicenseWhiteboxQueryContentKeyStatus, InvalidKeyForShortIv) { ASSERT_EQ(key_state, WB_KEY_STATUS_INVALID); } +// This test verifies if the implementation fails when the content key data is +// not set. The result OEMCrypto core message is the same as with the strip +// padding bug, i.e. having key_data offset and size as 0 for content keys. +// Thus we need to remove the test when the workaround is enabled. +#ifndef WORKAROUND_STRIP_PADDING_BUG TEST_P(LicenseWhiteboxQueryContentKeyStatus, InvalidKeyForNoKeyData) { TestLicenseBuilder::Settings settings; settings.include_content_key_key = false; @@ -195,6 +200,7 @@ TEST_P(LicenseWhiteboxQueryContentKeyStatus, InvalidKeyForNoKeyData) { ASSERT_EQ(key_state, WB_KEY_STATUS_INVALID); } } +#endif TEST_P(LicenseWhiteboxQueryContentKeyStatus, InvalidKeyForShortKeyData) { TestLicenseBuilder::Settings settings; diff --git a/whitebox/api/test_license_builder.cc b/whitebox/api/test_license_builder.cc index 17a6c14..4d0a671 100644 --- a/whitebox/api/test_license_builder.cc +++ b/whitebox/api/test_license_builder.cc @@ -177,11 +177,19 @@ KeyControlBlock CreateKeyControlBlock( return key_control_block; } +uint32_t DecodeUint32BigEndian(const std::string& data, size_t offset) { + return static_cast(data[offset]) << 24 | + static_cast(data[offset + 1]) << 16 | + static_cast(data[offset + 2]) << 8 | + static_cast(data[offset + 3]); +} + std::string GenerateCoreMessage(const std::string& serialized_request, const std::string& serialized_license_response, uint16_t api_major_version, uint16_t api_minor_version, - bool use_padding) { + bool use_padding, + bool simulate_strip_padding_bug) { DCHECK_GE(api_major_version, ODK_FIRST_VERSION) << "Verify ODK library is compatible."; DCHECK_LE(api_major_version, ODK_MAJOR_VERSION) @@ -230,6 +238,30 @@ std::string GenerateCoreMessage(const std::string& serialized_request, CHECK(oemcrypto_core_message::serialize::CreateCoreLicenseResponseFromProto( features, serialized_license_response, core_request, core_message_hash, /* nonce_required= */ true, use_padding, &oemcrypto_core_message)); + +#ifdef WORKAROUND_STRIP_PADDING_BUG + // Simulate the core message serialization bug in b/177271059. + if (!use_padding && simulate_strip_padding_bug) { + const size_t kKeyArrayLengthOffset = 100; + const size_t kKeyArrayLengthSize = sizeof(uint32_t); + const size_t kOemcryptoSubstringSize = 4 + 4; + // `key_data` is the third `OEMCrypto_Substring` in `OEMCrypto_KeyObject`. + const size_t kKeyDataOffset = 2 * kOemcryptoSubstringSize; + // There are five `OEMCrypto_Substring` in every `OEMCrypto_KeyObject`. + const size_t kKeyObjectSize = 5 * kOemcryptoSubstringSize; + + size_t offset = kKeyArrayLengthOffset; + size_t key_array_length = + DecodeUint32BigEndian(oemcrypto_core_message, offset); + offset += kKeyArrayLengthSize; + for (size_t i = 0; i < key_array_length; i++) { + memset(&oemcrypto_core_message[offset + kKeyDataOffset], 0, + kOemcryptoSubstringSize); + offset += kKeyObjectSize; + } + } +#endif + return oemcrypto_core_message; } @@ -643,7 +675,8 @@ void TestLicenseBuilder::Build(const TestServer& server, uint16_t api_minor_version = GetOdkMinorVersion(settings_.odk_version); oemcrypto_core_message = GenerateCoreMessage( serialized_request_, message_str, api_major_version, api_minor_version, - settings_.padding != Padding::kNone); + settings_.padding != Padding::kNone, + settings_.simulate_strip_padding_bug); if (settings_.odk_version == OdkVersion::k99) { // Change the api_major_version field to be too large (invalid) diff --git a/whitebox/api/test_license_builder.h b/whitebox/api/test_license_builder.h index 60eb6fe..26c9e1e 100644 --- a/whitebox/api/test_license_builder.h +++ b/whitebox/api/test_license_builder.h @@ -69,6 +69,7 @@ class TestLicenseBuilder { struct Settings { Padding padding = Padding::kNone; + bool simulate_strip_padding_bug = false; OdkVersion odk_version = OdkVersion::kNone; KeyControlBlock key_control_block = KeyControlBlock::kClear; RemoteAttestation remote_attestation = RemoteAttestation::kUnavailable; diff --git a/whitebox/reference/impl/odk_license_parser.cc b/whitebox/reference/impl/odk_license_parser.cc index 6a37d9d..56204a4 100644 --- a/whitebox/reference/impl/odk_license_parser.cc +++ b/whitebox/reference/impl/odk_license_parser.cc @@ -212,7 +212,26 @@ InternalKey OdkLicenseParser::ParseInternalKey( return InternalKey(); } - std::string wrapped_key = ExtractItem(key.key_data, message); + auto key_data = key.key_data; +#ifdef WORKAROUND_STRIP_PADDING_BUG + // Workaround license server SDK bug that strips bytes unconditionally even + // if no padding is present. See b/177271059 for details. The bug exists in + // license server SDK version [16.3.3, 16.4.3]. + // This workaround assumes the protobuf message License.KeyContainer is + // deterministically serialized in the field definition order, which is true + // in the affected SDK versions. Thus `key_data` offset should be the offset + // of the previous field `key_data_iv` + the header size. + if (key_data.offset == 0 && key_data.length == 0) { + const size_t kKeyDataProtoHeaderSize = 2; + key_data.offset = key.key_data_iv.offset + key.key_data_iv.length + + kKeyDataProtoHeaderSize; + // To simplify the implementation, handles content keys only for now, which + // is always 16 bytes. + key_data.length = 16u; + } +#endif + + std::string wrapped_key = ExtractItem(key_data, message); // Unlike with protobufs, we don't need to handle padding here. The ODK will // not include the padding as part of the key's size. Additionally, generic