Workaround server bug that strips padding unconditionally

License server SDK between [v16.3.3, v16.4.3] strips 16 bytes from
wrapped content keys unconditionally when generating the OEMCrypto
core message even when the padding is not present, which happens
when license protocol version 2.2 is used. As a result, both key
data offset and key data length would be zero in the license
response OEMCrypto core message.

This CL workaround the problem by assuming deterministic in order
serialization of the protobuf fields and deriving the key data offset
from the previous field key_data_iv offset.

Entitlement keys and generic crypto keys are not handled in this CL
intentionally to reduce the implementation complexity.

Renewal signing keys do not need to be handled as the paddings are not
stripped from the signing keys.

The workaround is defined under flag WORKAROUND_STRIP_PADDING_BUG.

Also disabled HAS_PROVIDER_KEYS temporarily.

Bug: 280521253
This commit is contained in:
KongQun Yang
2023-05-03 07:30:19 +00:00
parent d6ef4e1133
commit f753fd4084
7 changed files with 91 additions and 15 deletions

View File

@@ -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"],

View File

@@ -12,17 +12,19 @@ class LicenseWhiteboxKeyControlBlockTest
: public LicenseWhiteboxTestBase,
public testing::WithParamInterface<
std::tuple<TestLicenseBuilder::KeyControlBlock,
TestLicenseBuilder::OdkVersion>> {
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<TestServer> 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 {

View File

@@ -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

View File

@@ -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;

View File

@@ -177,11 +177,19 @@ KeyControlBlock CreateKeyControlBlock(
return key_control_block;
}
uint32_t DecodeUint32BigEndian(const std::string& data, size_t offset) {
return static_cast<uint32_t>(data[offset]) << 24 |
static_cast<uint32_t>(data[offset + 1]) << 16 |
static_cast<uint32_t>(data[offset + 2]) << 8 |
static_cast<uint32_t>(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)

View File

@@ -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;

View File

@@ -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