diff --git a/whitebox-dev/WORKSPACE b/whitebox-dev/WORKSPACE index 6bebbfa..da48843 100644 --- a/whitebox-dev/WORKSPACE +++ b/whitebox-dev/WORKSPACE @@ -70,7 +70,7 @@ http_archive( new_git_repository( name = "odk_repo", build_file = "@whitebox_api_repo//external:odk.BUILD", - commit = "bee799748752fac84d9c3ecd549aa54f72c88d02", + commit = "565237f8e6900e467eb236040374428387e90bd0", remote = "https://widevine-partner.googlesource.com/oemcrypto_core_message.git", repo_mapping = {"@whitebox" : "@whitebox_api_repo"} ) diff --git a/whitebox-prod/WORKSPACE b/whitebox-prod/WORKSPACE index 2fc85c7..a8eb63d 100644 --- a/whitebox-prod/WORKSPACE +++ b/whitebox-prod/WORKSPACE @@ -70,7 +70,7 @@ http_archive( new_git_repository( name = "odk_repo", build_file = "@whitebox_api_repo//external:odk.BUILD", - commit = "bee799748752fac84d9c3ecd549aa54f72c88d02", + commit = "565237f8e6900e467eb236040374428387e90bd0", remote = "https://widevine-partner.googlesource.com/oemcrypto_core_message.git", repo_mapping = {"@whitebox" : "@whitebox_api_repo"} ) diff --git a/whitebox/.gitignore b/whitebox/.gitignore new file mode 100644 index 0000000..ac51a05 --- /dev/null +++ b/whitebox/.gitignore @@ -0,0 +1 @@ +bazel-* diff --git a/whitebox/WORKSPACE b/whitebox/WORKSPACE index 7d8a4c3..bb7c458 100644 --- a/whitebox/WORKSPACE +++ b/whitebox/WORKSPACE @@ -65,7 +65,7 @@ http_archive( new_git_repository( name = "odk_repo", build_file = "//external:odk.BUILD", - commit = "bee799748752fac84d9c3ecd549aa54f72c88d02", + commit = "565237f8e6900e467eb236040374428387e90bd0", remote = "https://widevine-partner.googlesource.com/oemcrypto_core_message.git", ) diff --git a/whitebox/api/BUILD b/whitebox/api/BUILD index d1ef59d..8cc10ce 100644 --- a/whitebox/api/BUILD +++ b/whitebox/api/BUILD @@ -32,6 +32,14 @@ cc_library( ], ) +cc_library( + name = "test_key_types", + testonly = True, + hdrs = [ + "test_key_types.h", + ], +) + cc_library( name = "test_public_key", testonly = True, @@ -60,6 +68,7 @@ cc_library( "golden_data.h", ], deps = [ + ":test_key_types", "//chromium_deps/cdm/protos:license_protocol_proto", ], ) @@ -71,6 +80,7 @@ cc_library( hdrs = ["test_license_builder.h"], visibility = ["//visibility:public"], deps = [ + ":test_key_types", "//chromium_deps/cdm/keys:dev_certs", "//chromium_deps/cdm/protos:license_protocol_proto", "//crypto_utils:aes_cbc_encryptor", @@ -126,6 +136,7 @@ cc_library( "license_whitebox_get_secret_string_test.cc", "license_whitebox_masked_decrypt_test.cc", "license_whitebox_process_license_response_core_message_test.cc", + "license_whitebox_process_license_response_with_encrypted_key_control_block.cc", "license_whitebox_process_license_response_test.cc", "license_whitebox_sign_license_request_test.cc", "license_whitebox_sign_renewal_request_test.cc", @@ -137,7 +148,6 @@ cc_library( ], visibility = ["//visibility:public"], deps = [ - ":aead_test_data", ":golden_data", ":license_whitebox", ":test_license_builder", @@ -165,8 +175,8 @@ cc_library( ], visibility = ["//visibility:public"], deps = [ - ":aead_test_data", ":license_whitebox", + ":test_key_types", ":test_license_builder", ":test_public_key", "//benchmarking:data_source", diff --git a/whitebox/api/golden_data.cc b/whitebox/api/golden_data.cc index f75e470..3fddf30 100644 --- a/whitebox/api/golden_data.cc +++ b/whitebox/api/golden_data.cc @@ -83,10 +83,9 @@ GoldenData::GoldenData() { }; } -void GoldenData::MakeKeyIdDifferent(std::vector* key_id) const { - // All our internal key ids start with 0xFF, so pushing something that is not - // 0xFF to the front will ensure that they don't collide. - key_id->insert(key_id->begin(), 0xAB); +KeyId GoldenData::GetFreeId() { + // All our internal key ids start with 0xFF, so starting with 0xAB should + // avoid conflicts. + return {0xAB, 0, 0, next_id_++}; } - } // namespace widevine diff --git a/whitebox/api/golden_data.h b/whitebox/api/golden_data.h index dfb2b9d..9e94dbb 100644 --- a/whitebox/api/golden_data.h +++ b/whitebox/api/golden_data.h @@ -3,10 +3,11 @@ #ifndef WHITEBOX_API_GOLDEN_DATA_H_ #define WHITEBOX_API_GOLDEN_DATA_H_ -#include +#include #include #include +#include "api/test_key_types.h" #include "cdm/protos/license_protocol.pb.h" namespace widevine { @@ -16,13 +17,13 @@ class GoldenData { struct Content { std::vector plaintext; std::vector ciphertext; - std::vector key; - std::vector iv; + AesKey key; + AesIv iv; }; struct Key { video_widevine::License_KeyContainer_SecurityLevel level; - std::vector id; + KeyId id; const Content* content; }; @@ -38,11 +39,7 @@ class GoldenData { const Key& CTRDecodeKey() const { return ctr_decode_key_; } const Key& CTRHardwareKey() const { return ctr_hardware_key_; } - // When a test needs to define a key id that does not conflict with any key - // ids defined in the golden data, it should use this to update their key id - // by prepending a single byte to ensure it won't collide with any of the - // internal key ids. - void MakeKeyIdDifferent(std::vector* key_id) const; + KeyId GetFreeId(); private: Content cbc_content_; @@ -54,6 +51,8 @@ class GoldenData { Key ctr_crypto_key_; Key ctr_decode_key_; Key ctr_hardware_key_; + + uint8_t next_id_ = 0; }; } // namespace widevine diff --git a/whitebox/api/license_whitebox_benchmark.cc b/whitebox/api/license_whitebox_benchmark.cc index cf130c5..c72595a 100644 --- a/whitebox/api/license_whitebox_benchmark.cc +++ b/whitebox/api/license_whitebox_benchmark.cc @@ -2,9 +2,8 @@ #include "api/license_whitebox_benchmark.h" -#include -#include - +#include +#include #include #include @@ -21,9 +20,9 @@ constexpr size_t kBlockSize = 16; // This must align with the AES block size. } // namespace void LicenseWhiteboxBenchmark::SetUp() { - key_id_ = data_source_.Get(8); // The id size is not meaningful. - key_ = data_source_.Get(kBlockSize); - iv_ = data_source_.Get(kBlockSize); + key_id_ = data_source_.Get<4>(); // We use size=4 for all our test key ids. + key_ = data_source_.Get(); + iv_ = data_source_.Get(); const auto public_key_data = GetLicensePublicKey(); public_key_.reset(widevine::RsaPublicKey::Create( @@ -33,13 +32,12 @@ void LicenseWhiteboxBenchmark::SetUp() { License LicenseWhiteboxBenchmark::CreateLicense() const { widevine::TestLicenseBuilder license_builder; - license_builder.AddSigningKey(TestLicenseBuilder::DefaultSigningKey(), - TestLicenseBuilder::NoPadding()); + license_builder.AddSigningKey(TestLicenseBuilder::DefaultSigningKey()); // Use secure crypto as it will work with both Decrypt() and // MaskedDecrypt(). license_builder.AddContentKey( video_widevine::License_KeyContainer_SecurityLevel_SW_SECURE_CRYPTO, - key_id_, key_, TestLicenseBuilder::NoPadding()); + key_id_, key_); widevine::License license; license_builder.Build(*public_key_, &license); diff --git a/whitebox/api/license_whitebox_benchmark.h b/whitebox/api/license_whitebox_benchmark.h index 45acce4..d773f5e 100644 --- a/whitebox/api/license_whitebox_benchmark.h +++ b/whitebox/api/license_whitebox_benchmark.h @@ -9,6 +9,7 @@ #include #include "api/license_whitebox.h" +#include "api/test_key_types.h" #include "api/test_license_builder.h" #include "benchmarking/data_source.h" #include "crypto_utils/rsa_key.h" @@ -27,19 +28,19 @@ class LicenseWhiteboxBenchmark : public ::testing::Test { const RsaPublicKey* PublicKey() const { return public_key_.get(); } - const std::vector& ContentKeyId() const { return key_id_; } + const KeyId& ContentKeyId() const { return key_id_; } - const std::vector& ContentKey() const { return key_; } + const AesKey& ContentKey() const { return key_; } - const std::vector& ContentIV() const { return iv_; } + const AesIv& ContentIV() const { return iv_; } private: DataSource data_source_; std::unique_ptr public_key_; - std::vector key_id_; - std::vector key_; - std::vector iv_; + KeyId key_id_; + AesKey key_; + AesIv iv_; }; } // namespace widevine diff --git a/whitebox/api/license_whitebox_chromeos_test.cc b/whitebox/api/license_whitebox_chromeos_test.cc index 3afe0c5..86f96ca 100644 --- a/whitebox/api/license_whitebox_chromeos_test.cc +++ b/whitebox/api/license_whitebox_chromeos_test.cc @@ -56,21 +56,18 @@ class LicenseWhiteboxChromeOSTest // Only use CBC keys so that we can always use the CBC content. builder.AddContentKey(golden_data_.CBCCryptoKey().level, golden_data_.CBCCryptoKey().id, - golden_data_.CBCCryptoKey().content->key, - TestLicenseBuilder::NoPadding()); + golden_data_.CBCCryptoKey().content->key); builder.AddContentKey(golden_data_.CBCDecodeKey().level, golden_data_.CBCDecodeKey().id, - golden_data_.CBCDecodeKey().content->key, - TestLicenseBuilder::NoPadding()); + golden_data_.CBCDecodeKey().content->key); builder.AddContentKey(golden_data_.CBCHardwareKey().level, golden_data_.CBCHardwareKey().id, - golden_data_.CBCHardwareKey().content->key, - TestLicenseBuilder::NoPadding()); + golden_data_.CBCHardwareKey().content->key); - builder.SetRemoteAttestation(remote_attestation_); - builder.SetVerificationStatus(verification_status_); + builder.GetSettings().remote_attestation = remote_attestation_; + builder.GetSettings().verification_status = verification_status_; License license; builder.Build(*public_key_, &license); diff --git a/whitebox/api/license_whitebox_decrypt_test.cc b/whitebox/api/license_whitebox_decrypt_test.cc index 382a85e..c47b24d 100644 --- a/whitebox/api/license_whitebox_decrypt_test.cc +++ b/whitebox/api/license_whitebox_decrypt_test.cc @@ -25,28 +25,29 @@ class LicenseWhiteboxDecryptTest : public LicenseWhiteboxTestBase { golden_data_.CTRContent().ciphertext.size()); plaintext_.resize(plaintext_size_); - golden_data_.MakeKeyIdDifferent(&non_content_key_id_); - golden_data_.MakeKeyIdDifferent(&missing_key_id_); + non_content_key_id_ = golden_data_.GetFreeId(); + missing_key_id_ = golden_data_.GetFreeId(); } - void LoadLicense(const std::vector& padding) { + void LoadLicense(TestLicenseBuilder::Padding padding) { TestLicenseBuilder builder; + builder.GetSettings().padding = padding; builder.AddContentKey(golden_data_.CBCCryptoKey().level, golden_data_.CBCCryptoKey().id, - golden_data_.CBCCryptoKey().content->key, padding); + golden_data_.CBCCryptoKey().content->key); builder.AddContentKey(golden_data_.CTRCryptoKey().level, golden_data_.CTRCryptoKey().id, - golden_data_.CTRCryptoKey().content->key, padding); + golden_data_.CTRCryptoKey().content->key); builder.AddContentKey(golden_data_.CBCDecodeKey().level, golden_data_.CBCDecodeKey().id, - golden_data_.CBCDecodeKey().content->key, padding); + golden_data_.CBCDecodeKey().content->key); builder.AddContentKey(golden_data_.CBCHardwareKey().level, golden_data_.CBCHardwareKey().id, - golden_data_.CBCHardwareKey().content->key, padding); + golden_data_.CBCHardwareKey().content->key); builder.AddOperatorSessionKey(non_content_key_id_); @@ -65,8 +66,8 @@ class LicenseWhiteboxDecryptTest : public LicenseWhiteboxTestBase { // We need two special keys for this test, one that will be used for a // non-content key and one that will never be in the license. - std::vector non_content_key_id_ = {0, 0, 0}; - std::vector missing_key_id_ = {1, 0, 0}; + KeyId non_content_key_id_; + KeyId missing_key_id_; // This is the buffer used to store the output of each decrypt call. size_t plaintext_size_; @@ -74,7 +75,7 @@ class LicenseWhiteboxDecryptTest : public LicenseWhiteboxTestBase { }; TEST_F(LicenseWhiteboxDecryptTest, CryptoKeyWithCbcDataInCbcMode) { - LoadLicense(TestLicenseBuilder::NoPadding()); + LoadLicense(TestLicenseBuilder::Padding::kNone); ASSERT_EQ( WB_License_Decrypt(whitebox_, WB_CIPHER_MODE_CBC, @@ -91,7 +92,7 @@ TEST_F(LicenseWhiteboxDecryptTest, CryptoKeyWithCbcDataInCbcMode) { } TEST_F(LicenseWhiteboxDecryptTest, CryptoKeyWithCtrDataInCtrMode) { - LoadLicense(TestLicenseBuilder::NoPadding()); + LoadLicense(TestLicenseBuilder::Padding::kNone); ASSERT_EQ( WB_License_Decrypt(whitebox_, WB_CIPHER_MODE_CTR, @@ -110,7 +111,7 @@ TEST_F(LicenseWhiteboxDecryptTest, CryptoKeyWithCtrDataInCtrMode) { // We try to decrypt CBC encrypted data in CTR mode. All operations should be // successful, but the resulting plaintext should not match. TEST_F(LicenseWhiteboxDecryptTest, CryptoKeyWithCbcDataInCtrMode) { - LoadLicense(TestLicenseBuilder::NoPadding()); + LoadLicense(TestLicenseBuilder::Padding::kNone); ASSERT_EQ( WB_License_Decrypt(whitebox_, WB_CIPHER_MODE_CTR, @@ -129,7 +130,7 @@ TEST_F(LicenseWhiteboxDecryptTest, CryptoKeyWithCbcDataInCtrMode) { // We try to decrypt CTR encrypted data in CBC mode. All operations should be // successful, but the resulting plaintext should not match. TEST_F(LicenseWhiteboxDecryptTest, CryptoKeyWithCtrDataInCbcMode) { - LoadLicense(TestLicenseBuilder::NoPadding()); + LoadLicense(TestLicenseBuilder::Padding::kNone); ASSERT_EQ( WB_License_Decrypt(whitebox_, WB_CIPHER_MODE_CBC, @@ -146,7 +147,7 @@ TEST_F(LicenseWhiteboxDecryptTest, CryptoKeyWithCtrDataInCbcMode) { } TEST_F(LicenseWhiteboxDecryptTest, CryptoKeyWithCbcDataAndPKCS8Padding) { - LoadLicense(TestLicenseBuilder::PKSC8Padding()); + LoadLicense(TestLicenseBuilder::Padding::kPKSC8); ASSERT_EQ( WB_License_Decrypt(whitebox_, WB_CIPHER_MODE_CBC, @@ -163,7 +164,7 @@ TEST_F(LicenseWhiteboxDecryptTest, CryptoKeyWithCbcDataAndPKCS8Padding) { } TEST_F(LicenseWhiteboxDecryptTest, CryptoKeyWithCtrDataAndPKCS8Padding) { - LoadLicense(TestLicenseBuilder::PKSC8Padding()); + LoadLicense(TestLicenseBuilder::Padding::kPKSC8); ASSERT_EQ( WB_License_Decrypt(whitebox_, WB_CIPHER_MODE_CTR, @@ -182,7 +183,7 @@ TEST_F(LicenseWhiteboxDecryptTest, CryptoKeyWithCtrDataAndPKCS8Padding) { // Try decrypting two different sets of content to make sure that two // different keys can be used at the same time. TEST_F(LicenseWhiteboxDecryptTest, SuccessWithMultipleKeys) { - LoadLicense(TestLicenseBuilder::NoPadding()); + LoadLicense(TestLicenseBuilder::Padding::kNone); ASSERT_EQ( WB_License_Decrypt(whitebox_, WB_CIPHER_MODE_CBC, @@ -218,7 +219,7 @@ TEST_F(LicenseWhiteboxDecryptTest, SuccessWithMultipleKeys) { } TEST_F(LicenseWhiteboxDecryptTest, InvalidParameterForNullWhitebox) { - LoadLicense(TestLicenseBuilder::NoPadding()); + LoadLicense(TestLicenseBuilder::Padding::kNone); ASSERT_EQ( WB_License_Decrypt(nullptr, WB_CIPHER_MODE_CBC, @@ -233,7 +234,7 @@ TEST_F(LicenseWhiteboxDecryptTest, InvalidParameterForNullWhitebox) { } TEST_F(LicenseWhiteboxDecryptTest, InvalidParameterForInvalidCipherMode) { - LoadLicense(TestLicenseBuilder::NoPadding()); + LoadLicense(TestLicenseBuilder::Padding::kNone); // In order to trick the compiler into letting us pass an invalid enum value // to WB__License_Decrypt(), we need to cast it. If we don't do this, the @@ -252,7 +253,7 @@ TEST_F(LicenseWhiteboxDecryptTest, InvalidParameterForInvalidCipherMode) { } TEST_F(LicenseWhiteboxDecryptTest, InvalidParameterForNullKeyId) { - LoadLicense(TestLicenseBuilder::NoPadding()); + LoadLicense(TestLicenseBuilder::Padding::kNone); ASSERT_EQ( WB_License_Decrypt(whitebox_, WB_CIPHER_MODE_CBC, nullptr, @@ -266,7 +267,7 @@ TEST_F(LicenseWhiteboxDecryptTest, InvalidParameterForNullKeyId) { } TEST_F(LicenseWhiteboxDecryptTest, InvalidParameterForZeroKeyIdSize) { - LoadLicense(TestLicenseBuilder::NoPadding()); + LoadLicense(TestLicenseBuilder::Padding::kNone); ASSERT_EQ( WB_License_Decrypt(whitebox_, WB_CIPHER_MODE_CBC, @@ -280,7 +281,7 @@ TEST_F(LicenseWhiteboxDecryptTest, InvalidParameterForZeroKeyIdSize) { } TEST_F(LicenseWhiteboxDecryptTest, InvalidParameterForNullInputData) { - LoadLicense(TestLicenseBuilder::NoPadding()); + LoadLicense(TestLicenseBuilder::Padding::kNone); ASSERT_EQ( WB_License_Decrypt(whitebox_, WB_CIPHER_MODE_CBC, @@ -296,7 +297,7 @@ TEST_F(LicenseWhiteboxDecryptTest, InvalidParameterForNullInputData) { // AES CBC requires that the input be block aligned (multiple of 16). CTR does // not care. TEST_F(LicenseWhiteboxDecryptTest, InvalidParameterForInvalidCBCInputDataSize) { - LoadLicense(TestLicenseBuilder::NoPadding()); + LoadLicense(TestLicenseBuilder::Padding::kNone); ASSERT_EQ( WB_License_Decrypt(whitebox_, WB_CIPHER_MODE_CBC, @@ -311,7 +312,7 @@ TEST_F(LicenseWhiteboxDecryptTest, InvalidParameterForInvalidCBCInputDataSize) { // The white-box (using any cipher mode) should reject input with size zero. TEST_F(LicenseWhiteboxDecryptTest, InvalidParameterForZeroInputDataSize) { - LoadLicense(TestLicenseBuilder::NoPadding()); + LoadLicense(TestLicenseBuilder::Padding::kNone); ASSERT_EQ( WB_License_Decrypt(whitebox_, WB_CIPHER_MODE_CBC, @@ -325,7 +326,7 @@ TEST_F(LicenseWhiteboxDecryptTest, InvalidParameterForZeroInputDataSize) { } TEST_F(LicenseWhiteboxDecryptTest, InvalidParameterForNullIV) { - LoadLicense(TestLicenseBuilder::NoPadding()); + LoadLicense(TestLicenseBuilder::Padding::kNone); ASSERT_EQ( WB_License_Decrypt( @@ -340,7 +341,7 @@ TEST_F(LicenseWhiteboxDecryptTest, InvalidParameterForNullIV) { // IV size should be 16. Any number other than 16 should fail. TEST_F(LicenseWhiteboxDecryptTest, InvalidParameterForInvalidIVSize) { - LoadLicense(TestLicenseBuilder::NoPadding()); + LoadLicense(TestLicenseBuilder::Padding::kNone); ASSERT_EQ( WB_License_Decrypt(whitebox_, WB_CIPHER_MODE_CBC, @@ -354,7 +355,7 @@ TEST_F(LicenseWhiteboxDecryptTest, InvalidParameterForInvalidIVSize) { } TEST_F(LicenseWhiteboxDecryptTest, InvalidParameterForNullOutput) { - LoadLicense(TestLicenseBuilder::NoPadding()); + LoadLicense(TestLicenseBuilder::Padding::kNone); ASSERT_EQ( WB_License_Decrypt(whitebox_, WB_CIPHER_MODE_CBC, @@ -369,7 +370,7 @@ TEST_F(LicenseWhiteboxDecryptTest, InvalidParameterForNullOutput) { } TEST_F(LicenseWhiteboxDecryptTest, InvalidParameterForNullOutputSize) { - LoadLicense(TestLicenseBuilder::NoPadding()); + LoadLicense(TestLicenseBuilder::Padding::kNone); ASSERT_EQ( WB_License_Decrypt(whitebox_, WB_CIPHER_MODE_CBC, @@ -387,7 +388,7 @@ TEST_F(LicenseWhiteboxDecryptTest, InvalidParameterForNullOutputSize) { // in the license to start with. This is different than "non content key" // and "dropped content key", as those keys were in the license but ignored. TEST_F(LicenseWhiteboxDecryptTest, KeyUnavailableForMissingKeyId) { - LoadLicense(TestLicenseBuilder::NoPadding()); + LoadLicense(TestLicenseBuilder::Padding::kNone); ASSERT_EQ( WB_License_Decrypt(whitebox_, WB_CIPHER_MODE_CBC, missing_key_id_.data(), @@ -401,7 +402,7 @@ TEST_F(LicenseWhiteboxDecryptTest, KeyUnavailableForMissingKeyId) { } TEST_F(LicenseWhiteboxDecryptTest, KeyUnavailableForNonContentKey) { - LoadLicense(TestLicenseBuilder::NoPadding()); + LoadLicense(TestLicenseBuilder::Padding::kNone); ASSERT_EQ( WB_License_Decrypt(whitebox_, WB_CIPHER_MODE_CBC, @@ -418,7 +419,7 @@ TEST_F(LicenseWhiteboxDecryptTest, KeyUnavailableForNonContentKey) { // to this rule is on ChromeOS with a special license. TEST_F(LicenseWhiteboxDecryptTest, InsufficientSecurityLevelForHardwareContentKey) { - LoadLicense(TestLicenseBuilder::NoPadding()); + LoadLicense(TestLicenseBuilder::Padding::kNone); ASSERT_EQ(WB_License_Decrypt( whitebox_, WB_CIPHER_MODE_CBC, @@ -433,7 +434,7 @@ TEST_F(LicenseWhiteboxDecryptTest, } TEST_F(LicenseWhiteboxDecryptTest, InsufficientSecurityLevelForDecodeKey) { - LoadLicense(TestLicenseBuilder::NoPadding()); + LoadLicense(TestLicenseBuilder::Padding::kNone); // Use the software decode key as they are limited to // WB_License_Decrypt(). @@ -450,7 +451,7 @@ TEST_F(LicenseWhiteboxDecryptTest, InsufficientSecurityLevelForDecodeKey) { } TEST_F(LicenseWhiteboxDecryptTest, BufferTooSmall) { - LoadLicense(TestLicenseBuilder::NoPadding()); + LoadLicense(TestLicenseBuilder::Padding::kNone); // Our ciphertext will be large enough that we should not need to worry about // using a constant here. diff --git a/whitebox/api/license_whitebox_get_secret_string_test.cc b/whitebox/api/license_whitebox_get_secret_string_test.cc index 08b81a6..5248985 100644 --- a/whitebox/api/license_whitebox_get_secret_string_test.cc +++ b/whitebox/api/license_whitebox_get_secret_string_test.cc @@ -23,8 +23,8 @@ class LicenseWhiteboxGetSecretStringTest : public LicenseWhiteboxTestBase { secret_string_size_ = 256; secret_string_.resize(secret_string_size_); - golden_data_.MakeKeyIdDifferent(&non_content_key_id_); - golden_data_.MakeKeyIdDifferent(&missing_key_id_); + non_content_key_id_ = golden_data_.GetFreeId(); + missing_key_id_ = golden_data_.GetFreeId(); } void LoadLicense() { @@ -32,18 +32,15 @@ class LicenseWhiteboxGetSecretStringTest : public LicenseWhiteboxTestBase { builder.AddContentKey(golden_data_.CBCCryptoKey().level, golden_data_.CBCCryptoKey().id, - golden_data_.CBCCryptoKey().content->key, - TestLicenseBuilder::NoPadding()); + golden_data_.CBCCryptoKey().content->key); builder.AddContentKey(golden_data_.CBCDecodeKey().level, golden_data_.CBCDecodeKey().id, - golden_data_.CBCDecodeKey().content->key, - TestLicenseBuilder::NoPadding()); + golden_data_.CBCDecodeKey().content->key); builder.AddContentKey(golden_data_.CBCHardwareKey().level, golden_data_.CBCHardwareKey().id, - golden_data_.CBCHardwareKey().content->key, - TestLicenseBuilder::NoPadding()); + golden_data_.CBCHardwareKey().content->key); builder.AddOperatorSessionKey(non_content_key_id_); @@ -62,8 +59,8 @@ class LicenseWhiteboxGetSecretStringTest : public LicenseWhiteboxTestBase { // We need two special keys for this test, one that will be used for a // non-content key and one that will never be in the license. - std::vector non_content_key_id_ = {0, 0, 0}; - std::vector missing_key_id_ = {1, 0, 0}; + KeyId non_content_key_id_; + KeyId missing_key_id_; size_t secret_string_size_; std::vector secret_string_; diff --git a/whitebox/api/license_whitebox_golden_data_test.cc b/whitebox/api/license_whitebox_golden_data_test.cc index 54daf09..436a260 100644 --- a/whitebox/api/license_whitebox_golden_data_test.cc +++ b/whitebox/api/license_whitebox_golden_data_test.cc @@ -8,11 +8,6 @@ #include "testing/gtest/include/gtest/gtest.h" namespace widevine { -// This must be defined by the implementation as they are implementation -// specific. They must be defined so that they work with the test key used to -// generate the license request. -extern const uint8_t kLicenseWhiteboxInitData[]; -extern const size_t kLicenseWhiteboxInitDataSize; namespace { diff --git a/whitebox/api/license_whitebox_masked_decrypt_test.cc b/whitebox/api/license_whitebox_masked_decrypt_test.cc index 692422e..1eb7bc9 100644 --- a/whitebox/api/license_whitebox_masked_decrypt_test.cc +++ b/whitebox/api/license_whitebox_masked_decrypt_test.cc @@ -32,37 +32,33 @@ class LicenseWhiteboxMaskedDecryptTest : public LicenseWhiteboxTestBase { secret_string_size_ = masked_text_size_; secret_string_.resize(secret_string_size_); - golden_data_.MakeKeyIdDifferent(&non_content_key_id_); - golden_data_.MakeKeyIdDifferent(&missing_key_id_); + non_content_key_id_ = golden_data_.GetFreeId(); + missing_key_id_ = golden_data_.GetFreeId(); } - void LoadLicense(const std::vector& padding) { + void LoadLicense(TestLicenseBuilder::Padding padding) { TestLicenseBuilder builder; + builder.GetSettings().padding = padding; builder.AddContentKey(golden_data_.CBCCryptoKey().level, golden_data_.CBCCryptoKey().id, - golden_data_.CBCCryptoKey().content->key, - TestLicenseBuilder::NoPadding()); + golden_data_.CBCCryptoKey().content->key); builder.AddContentKey(golden_data_.CTRCryptoKey().level, golden_data_.CTRCryptoKey().id, - golden_data_.CTRCryptoKey().content->key, - TestLicenseBuilder::NoPadding()); + golden_data_.CTRCryptoKey().content->key); builder.AddContentKey(golden_data_.CBCDecodeKey().level, golden_data_.CBCDecodeKey().id, - golden_data_.CBCDecodeKey().content->key, - TestLicenseBuilder::NoPadding()); + golden_data_.CBCDecodeKey().content->key); builder.AddContentKey(golden_data_.CTRDecodeKey().level, golden_data_.CTRDecodeKey().id, - golden_data_.CTRDecodeKey().content->key, - TestLicenseBuilder::NoPadding()); + golden_data_.CTRDecodeKey().content->key); builder.AddContentKey(golden_data_.CBCHardwareKey().level, golden_data_.CBCHardwareKey().id, - golden_data_.CBCHardwareKey().content->key, - TestLicenseBuilder::NoPadding()); + golden_data_.CBCHardwareKey().content->key); builder.AddOperatorSessionKey(non_content_key_id_); @@ -81,8 +77,8 @@ class LicenseWhiteboxMaskedDecryptTest : public LicenseWhiteboxTestBase { // We need two special keys for this test, one that will be used for a // non-content key and one that will never be in the license. - std::vector non_content_key_id_ = {0, 0, 0}; - std::vector missing_key_id_ = {1, 0, 0}; + KeyId non_content_key_id_; + KeyId missing_key_id_; size_t secret_string_size_; std::vector secret_string_; @@ -94,7 +90,7 @@ class LicenseWhiteboxMaskedDecryptTest : public LicenseWhiteboxTestBase { }; TEST_F(LicenseWhiteboxMaskedDecryptTest, DecodeKeyWithCbcDataInCbcMode) { - LoadLicense(TestLicenseBuilder::NoPadding()); + LoadLicense(TestLicenseBuilder::Padding::kNone); ASSERT_EQ( WB_License_MaskedDecrypt( @@ -132,7 +128,7 @@ TEST_F(LicenseWhiteboxMaskedDecryptTest, DecodeKeyWithCbcDataInCbcMode) { } TEST_F(LicenseWhiteboxMaskedDecryptTest, DecodeKeyWithCtrDataInCtrMode) { - LoadLicense(TestLicenseBuilder::NoPadding()); + LoadLicense(TestLicenseBuilder::Padding::kNone); ASSERT_EQ( WB_License_MaskedDecrypt( @@ -172,7 +168,7 @@ TEST_F(LicenseWhiteboxMaskedDecryptTest, DecodeKeyWithCtrDataInCtrMode) { // We try to decrypt CBC encrypted data in CTR mode. All operations should be // successful, but the resulting plaintext should not match. TEST_F(LicenseWhiteboxMaskedDecryptTest, DecodeKeyWithCbcDataInCtrMode) { - LoadLicense(TestLicenseBuilder::NoPadding()); + LoadLicense(TestLicenseBuilder::Padding::kNone); ASSERT_EQ( WB_License_MaskedDecrypt( @@ -211,7 +207,7 @@ TEST_F(LicenseWhiteboxMaskedDecryptTest, DecodeKeyWithCbcDataInCtrMode) { // We try to decrypt CTR encrypted data in CBC mode. All operations should be // successful, but the resulting plaintext should not match. TEST_F(LicenseWhiteboxMaskedDecryptTest, DecodeKeyWithCtrDataInCbcMode) { - LoadLicense(TestLicenseBuilder::NoPadding()); + LoadLicense(TestLicenseBuilder::Padding::kNone); ASSERT_EQ( WB_License_MaskedDecrypt( @@ -248,7 +244,7 @@ TEST_F(LicenseWhiteboxMaskedDecryptTest, DecodeKeyWithCtrDataInCbcMode) { } TEST_F(LicenseWhiteboxMaskedDecryptTest, CryptoKeyWithCbcDataInCbcMode) { - LoadLicense(TestLicenseBuilder::NoPadding()); + LoadLicense(TestLicenseBuilder::Padding::kNone); ASSERT_EQ( WB_License_MaskedDecrypt( @@ -286,7 +282,7 @@ TEST_F(LicenseWhiteboxMaskedDecryptTest, CryptoKeyWithCbcDataInCbcMode) { } TEST_F(LicenseWhiteboxMaskedDecryptTest, CryptoKeyWithCtrDataInCtrMode) { - LoadLicense(TestLicenseBuilder::NoPadding()); + LoadLicense(TestLicenseBuilder::Padding::kNone); ASSERT_EQ( WB_License_MaskedDecrypt( @@ -326,7 +322,7 @@ TEST_F(LicenseWhiteboxMaskedDecryptTest, CryptoKeyWithCtrDataInCtrMode) { // We try to decrypt CBC encrypted data in CTR mode. All operations should be // successful, but the resulting plaintext should not match. TEST_F(LicenseWhiteboxMaskedDecryptTest, CryptoKeyWithCbcDataInCtrMode) { - LoadLicense(TestLicenseBuilder::NoPadding()); + LoadLicense(TestLicenseBuilder::Padding::kNone); ASSERT_EQ( WB_License_MaskedDecrypt( @@ -365,7 +361,7 @@ TEST_F(LicenseWhiteboxMaskedDecryptTest, CryptoKeyWithCbcDataInCtrMode) { // We try to decrypt CTR encrypted data in CBC mode. All operations should be // successful, but the resulting plaintext should not match. TEST_F(LicenseWhiteboxMaskedDecryptTest, CryptoKeyWithCtrDataInCbcMode) { - LoadLicense(TestLicenseBuilder::NoPadding()); + LoadLicense(TestLicenseBuilder::Padding::kNone); ASSERT_EQ( WB_License_MaskedDecrypt( @@ -402,7 +398,7 @@ TEST_F(LicenseWhiteboxMaskedDecryptTest, CryptoKeyWithCtrDataInCbcMode) { } TEST_F(LicenseWhiteboxMaskedDecryptTest, CryptoKeyWithCbcDataAndPKCS8Padding) { - LoadLicense(TestLicenseBuilder::PKSC8Padding()); + LoadLicense(TestLicenseBuilder::Padding::kPKSC8); ASSERT_EQ( WB_License_MaskedDecrypt( @@ -440,7 +436,7 @@ TEST_F(LicenseWhiteboxMaskedDecryptTest, CryptoKeyWithCbcDataAndPKCS8Padding) { } TEST_F(LicenseWhiteboxMaskedDecryptTest, CryptoKeyWithCtrDataAndPKCS8Padding) { - LoadLicense(TestLicenseBuilder::PKSC8Padding()); + LoadLicense(TestLicenseBuilder::Padding::kPKSC8); ASSERT_EQ( WB_License_MaskedDecrypt( @@ -483,7 +479,7 @@ TEST_F(LicenseWhiteboxMaskedDecryptTest, CryptoKeyWithCtrDataAndPKCS8Padding) { // Since we have two CBC keys, try using the decode key and then the crypto // key. TEST_F(LicenseWhiteboxMaskedDecryptTest, SuccessWithMultipleKeys) { - LoadLicense(TestLicenseBuilder::NoPadding()); + LoadLicense(TestLicenseBuilder::Padding::kNone); ASSERT_EQ( WB_License_MaskedDecrypt( @@ -563,7 +559,7 @@ TEST_F(LicenseWhiteboxMaskedDecryptTest, SuccessWithMultipleKeys) { } TEST_F(LicenseWhiteboxMaskedDecryptTest, InvalidParameterForNullWhitebox) { - LoadLicense(TestLicenseBuilder::NoPadding()); + LoadLicense(TestLicenseBuilder::Padding::kNone); ASSERT_EQ( WB_License_MaskedDecrypt( @@ -578,7 +574,7 @@ TEST_F(LicenseWhiteboxMaskedDecryptTest, InvalidParameterForNullWhitebox) { } TEST_F(LicenseWhiteboxMaskedDecryptTest, InvalidParameterForInvalidCipherMode) { - LoadLicense(TestLicenseBuilder::NoPadding()); + LoadLicense(TestLicenseBuilder::Padding::kNone); // In order to trick the compiler into letting us pass an invalid enum value // to WB__License_MaskedDecrypt(), we need to cast it. If we don't do this, @@ -597,7 +593,7 @@ TEST_F(LicenseWhiteboxMaskedDecryptTest, InvalidParameterForInvalidCipherMode) { } TEST_F(LicenseWhiteboxMaskedDecryptTest, InvalidParameterForNullKeyId) { - LoadLicense(TestLicenseBuilder::NoPadding()); + LoadLicense(TestLicenseBuilder::Padding::kNone); ASSERT_EQ(WB_License_MaskedDecrypt( whitebox_, WB_CIPHER_MODE_CBC, nullptr, @@ -611,7 +607,7 @@ TEST_F(LicenseWhiteboxMaskedDecryptTest, InvalidParameterForNullKeyId) { } TEST_F(LicenseWhiteboxMaskedDecryptTest, InvalidParameterForNullZeroKeyIdSize) { - LoadLicense(TestLicenseBuilder::NoPadding()); + LoadLicense(TestLicenseBuilder::Padding::kNone); ASSERT_EQ( WB_License_MaskedDecrypt( @@ -625,7 +621,7 @@ TEST_F(LicenseWhiteboxMaskedDecryptTest, InvalidParameterForNullZeroKeyIdSize) { } TEST_F(LicenseWhiteboxMaskedDecryptTest, InvalidParameterForNullInputData) { - LoadLicense(TestLicenseBuilder::NoPadding()); + LoadLicense(TestLicenseBuilder::Padding::kNone); ASSERT_EQ( WB_License_MaskedDecrypt( @@ -642,7 +638,7 @@ TEST_F(LicenseWhiteboxMaskedDecryptTest, InvalidParameterForNullInputData) { // not care. TEST_F(LicenseWhiteboxMaskedDecryptTest, InvalidParameterForInvalidCBCInputDataSize) { - LoadLicense(TestLicenseBuilder::NoPadding()); + LoadLicense(TestLicenseBuilder::Padding::kNone); ASSERT_EQ( WB_License_MaskedDecrypt( @@ -657,7 +653,7 @@ TEST_F(LicenseWhiteboxMaskedDecryptTest, // The white-box (using any cipher mode) should reject input with size zero. TEST_F(LicenseWhiteboxMaskedDecryptTest, InvalidParameterForZeroInputDataSize) { - LoadLicense(TestLicenseBuilder::NoPadding()); + LoadLicense(TestLicenseBuilder::Padding::kNone); ASSERT_EQ( WB_License_MaskedDecrypt( @@ -671,7 +667,7 @@ TEST_F(LicenseWhiteboxMaskedDecryptTest, InvalidParameterForZeroInputDataSize) { } TEST_F(LicenseWhiteboxMaskedDecryptTest, InvalidParameterForNullIV) { - LoadLicense(TestLicenseBuilder::NoPadding()); + LoadLicense(TestLicenseBuilder::Padding::kNone); ASSERT_EQ( WB_License_MaskedDecrypt( @@ -686,7 +682,7 @@ TEST_F(LicenseWhiteboxMaskedDecryptTest, InvalidParameterForNullIV) { // IV size should be 16. Any number other than 16 should fail. TEST_F(LicenseWhiteboxMaskedDecryptTest, InvalidParameterForInvalidIVSize) { - LoadLicense(TestLicenseBuilder::NoPadding()); + LoadLicense(TestLicenseBuilder::Padding::kNone); ASSERT_EQ( WB_License_MaskedDecrypt( @@ -700,7 +696,7 @@ TEST_F(LicenseWhiteboxMaskedDecryptTest, InvalidParameterForInvalidIVSize) { } TEST_F(LicenseWhiteboxMaskedDecryptTest, InvalidParameterForNullOutput) { - LoadLicense(TestLicenseBuilder::NoPadding()); + LoadLicense(TestLicenseBuilder::Padding::kNone); ASSERT_EQ( WB_License_MaskedDecrypt( @@ -715,7 +711,7 @@ TEST_F(LicenseWhiteboxMaskedDecryptTest, InvalidParameterForNullOutput) { } TEST_F(LicenseWhiteboxMaskedDecryptTest, InvalidParameterForNullOutputSize) { - LoadLicense(TestLicenseBuilder::NoPadding()); + LoadLicense(TestLicenseBuilder::Padding::kNone); ASSERT_EQ( WB_License_MaskedDecrypt( @@ -733,7 +729,7 @@ TEST_F(LicenseWhiteboxMaskedDecryptTest, InvalidParameterForNullOutputSize) { // in the license to start with. This is different than "non content key" // and "dropped content key", as those keys were in the license but ignored. TEST_F(LicenseWhiteboxMaskedDecryptTest, KeyUnavailableForMissingKeyId) { - LoadLicense(TestLicenseBuilder::NoPadding()); + LoadLicense(TestLicenseBuilder::Padding::kNone); ASSERT_EQ(WB_License_MaskedDecrypt( whitebox_, WB_CIPHER_MODE_CBC, missing_key_id_.data(), @@ -747,7 +743,7 @@ TEST_F(LicenseWhiteboxMaskedDecryptTest, KeyUnavailableForMissingKeyId) { } TEST_F(LicenseWhiteboxMaskedDecryptTest, KeyUnavailableForNonContentKey) { - LoadLicense(TestLicenseBuilder::NoPadding()); + LoadLicense(TestLicenseBuilder::Padding::kNone); ASSERT_EQ(WB_License_MaskedDecrypt( whitebox_, WB_CIPHER_MODE_CBC, non_content_key_id_.data(), @@ -764,7 +760,7 @@ TEST_F(LicenseWhiteboxMaskedDecryptTest, KeyUnavailableForNonContentKey) { // to this rule is on ChromeOS with a special license. TEST_F(LicenseWhiteboxMaskedDecryptTest, InsufficientSecurityLevelForHardwareContentKey) { - LoadLicense(TestLicenseBuilder::NoPadding()); + LoadLicense(TestLicenseBuilder::Padding::kNone); ASSERT_EQ(WB_License_MaskedDecrypt( whitebox_, WB_CIPHER_MODE_CBC, @@ -795,7 +791,7 @@ TEST_F(LicenseWhiteboxMaskedDecryptTest, InvalidState) { } TEST_F(LicenseWhiteboxMaskedDecryptTest, BufferTooSmall) { - LoadLicense(TestLicenseBuilder::NoPadding()); + LoadLicense(TestLicenseBuilder::Padding::kNone); // Our ciphertext will be large enough that we should not need to worry about // using a constant here. @@ -821,7 +817,7 @@ TEST_F(LicenseWhiteboxMaskedDecryptTest, BufferTooSmall) { // Check that the result of unmasking only a small portion of the data is the // same as when we unmask the whole buffer. TEST_F(LicenseWhiteboxMaskedDecryptTest, SuccessForSubRangeUnmask) { - LoadLicense(TestLicenseBuilder::NoPadding()); + LoadLicense(TestLicenseBuilder::Padding::kNone); ASSERT_EQ( WB_License_MaskedDecrypt( diff --git a/whitebox/api/license_whitebox_process_license_response_core_message_test.cc b/whitebox/api/license_whitebox_process_license_response_core_message_test.cc index f33d89f..556e96b 100644 --- a/whitebox/api/license_whitebox_process_license_response_core_message_test.cc +++ b/whitebox/api/license_whitebox_process_license_response_core_message_test.cc @@ -9,22 +9,29 @@ #include "testing/gtest/include/gtest/gtest.h" namespace widevine { + +using OdkVersion = TestLicenseBuilder::OdkVersion; + class LicenseWhiteboxProcessLicenseResponseWithCoreMessageTest : public LicenseWhiteboxTestBase { protected: - void UseLicenseWithoutSigningKey(bool use_odk) { + void UseLicenseWithoutSigningKey(OdkVersion odk_version) { TestLicenseBuilder builder; + builder.GetSettings().odk_version = odk_version; + builder.AddStubbedContentKey(); - builder.SetUseODK(use_odk); builder.Build(*public_key_, &license_); } - void UseLicenseWithSigningKey(const std::vector& padding, - bool use_odk) { + void UseLicenseWithSigningKey(TestLicenseBuilder::Padding padding, + OdkVersion odk_version) { TestLicenseBuilder builder; - builder.AddSigningKey(TestLicenseBuilder::DefaultSigningKey(), padding); + builder.GetSettings().padding = padding; + builder.GetSettings().odk_version = odk_version; + + builder.AddSigningKey(TestLicenseBuilder::DefaultSigningKey()); builder.AddStubbedContentKey(); - builder.SetUseODK(use_odk); + builder.Build(*public_key_, &license_); } @@ -35,8 +42,7 @@ class LicenseWhiteboxProcessLicenseResponseWithCoreMessageTest TEST_F(LicenseWhiteboxProcessLicenseResponseWithCoreMessageTest, SuccessWithoutOdkAndWithoutSigningKey) { - UseLicenseWithoutSigningKey(/* use_odk = */ false); - + UseLicenseWithoutSigningKey(OdkVersion::kNone); ASSERT_EQ( WB_License_ProcessLicenseResponse( whitebox_, license_.core_message.data(), license_.core_message.size(), @@ -48,9 +54,21 @@ TEST_F(LicenseWhiteboxProcessLicenseResponseWithCoreMessageTest, } TEST_F(LicenseWhiteboxProcessLicenseResponseWithCoreMessageTest, - SuccessWithOdkAndWithoutSigningKey) { - UseLicenseWithoutSigningKey(/* use_odk = */ true); + SuccessWithOdk16_3AndWithoutSigningKey) { + UseLicenseWithoutSigningKey(OdkVersion::k16_3); + ASSERT_EQ( + WB_License_ProcessLicenseResponse( + whitebox_, license_.core_message.data(), license_.core_message.size(), + license_.message.data(), license_.message.size(), + license_.signature.data(), license_.signature.size(), + license_.session_key.data(), license_.session_key.size(), + license_.request.data(), license_.request.size()), + WB_RESULT_OK); +} +TEST_F(LicenseWhiteboxProcessLicenseResponseWithCoreMessageTest, + SuccessWithOdk16_5AndWithoutSigningKey) { + UseLicenseWithoutSigningKey(OdkVersion::k16_5); ASSERT_EQ( WB_License_ProcessLicenseResponse( whitebox_, license_.core_message.data(), license_.core_message.size(), @@ -63,9 +81,8 @@ TEST_F(LicenseWhiteboxProcessLicenseResponseWithCoreMessageTest, TEST_F(LicenseWhiteboxProcessLicenseResponseWithCoreMessageTest, SuccessWithoutOdkAndWithSigningKeyNoPadding) { - UseLicenseWithSigningKey(TestLicenseBuilder::NoPadding(), - /* use_odk = */ false); - + UseLicenseWithSigningKey(TestLicenseBuilder::Padding::kNone, + OdkVersion::kNone); ASSERT_EQ( WB_License_ProcessLicenseResponse( whitebox_, license_.core_message.data(), license_.core_message.size(), @@ -77,10 +94,23 @@ TEST_F(LicenseWhiteboxProcessLicenseResponseWithCoreMessageTest, } TEST_F(LicenseWhiteboxProcessLicenseResponseWithCoreMessageTest, - SuccessWithOdkAndWithSigningKeyNoPadding) { - UseLicenseWithSigningKey(TestLicenseBuilder::NoPadding(), - /* use_odk = */ true); + SuccessWithOdk16_3AndWithSigningKeyNoPadding) { + UseLicenseWithSigningKey(TestLicenseBuilder::Padding::kNone, + OdkVersion::k16_3); + ASSERT_EQ( + WB_License_ProcessLicenseResponse( + whitebox_, license_.core_message.data(), license_.core_message.size(), + license_.message.data(), license_.message.size(), + license_.signature.data(), license_.signature.size(), + license_.session_key.data(), license_.session_key.size(), + license_.request.data(), license_.request.size()), + WB_RESULT_OK); +} +TEST_F(LicenseWhiteboxProcessLicenseResponseWithCoreMessageTest, + SuccessWithOdk16_5AndWithSigningKeyNoPadding) { + UseLicenseWithSigningKey(TestLicenseBuilder::Padding::kNone, + OdkVersion::k16_5); ASSERT_EQ( WB_License_ProcessLicenseResponse( whitebox_, license_.core_message.data(), license_.core_message.size(), @@ -93,9 +123,8 @@ TEST_F(LicenseWhiteboxProcessLicenseResponseWithCoreMessageTest, TEST_F(LicenseWhiteboxProcessLicenseResponseWithCoreMessageTest, SuccessWithoutOdkAndWithSigningKeyPKSC8Padding) { - UseLicenseWithSigningKey(TestLicenseBuilder::PKSC8Padding(), - /* use_odk = */ false); - + UseLicenseWithSigningKey(TestLicenseBuilder::Padding::kPKSC8, + OdkVersion::kNone); ASSERT_EQ( WB_License_ProcessLicenseResponse( whitebox_, license_.core_message.data(), license_.core_message.size(), @@ -107,10 +136,23 @@ TEST_F(LicenseWhiteboxProcessLicenseResponseWithCoreMessageTest, } TEST_F(LicenseWhiteboxProcessLicenseResponseWithCoreMessageTest, - SuccessWithOdkAndWithSigningKeyPKSC8Padding) { - UseLicenseWithSigningKey(TestLicenseBuilder::PKSC8Padding(), - /* use_odk = */ true); + SuccessWithOdk16_3AndWithSigningKeyPKSC8Padding) { + UseLicenseWithSigningKey(TestLicenseBuilder::Padding::kPKSC8, + OdkVersion::k16_3); + ASSERT_EQ( + WB_License_ProcessLicenseResponse( + whitebox_, license_.core_message.data(), license_.core_message.size(), + license_.message.data(), license_.message.size(), + license_.signature.data(), license_.signature.size(), + license_.session_key.data(), license_.session_key.size(), + license_.request.data(), license_.request.size()), + WB_RESULT_OK); +} +TEST_F(LicenseWhiteboxProcessLicenseResponseWithCoreMessageTest, + SuccessWithOdk16_5AndWithSigningKeyPKSC8Padding) { + UseLicenseWithSigningKey(TestLicenseBuilder::Padding::kPKSC8, + OdkVersion::k16_5); ASSERT_EQ( WB_License_ProcessLicenseResponse( whitebox_, license_.core_message.data(), license_.core_message.size(), @@ -123,31 +165,33 @@ TEST_F(LicenseWhiteboxProcessLicenseResponseWithCoreMessageTest, TEST_F(LicenseWhiteboxProcessLicenseResponseWithCoreMessageTest, InvalidParameterForNullCoreMessage) { - UseLicenseWithoutSigningKey(/* use_odk = */ true); - ASSERT_EQ(WB_License_ProcessLicenseResponse( - whitebox_, nullptr, license_.core_message.size(), - license_.message.data(), license_.message.size(), - license_.signature.data(), license_.signature.size(), - license_.session_key.data(), license_.session_key.size(), - license_.request.data(), license_.request.size()), - WB_RESULT_INVALID_PARAMETER); + UseLicenseWithoutSigningKey(OdkVersion::k16_5); + ASSERT_EQ( + WB_License_ProcessLicenseResponse( + whitebox_, /*core_message=*/nullptr, license_.core_message.size(), + license_.message.data(), license_.message.size(), + license_.signature.data(), license_.signature.size(), + license_.session_key.data(), license_.session_key.size(), + license_.request.data(), license_.request.size()), + WB_RESULT_INVALID_PARAMETER); } TEST_F(LicenseWhiteboxProcessLicenseResponseWithCoreMessageTest, InvalidSignatureWithZeroCoreMessageSize) { - UseLicenseWithoutSigningKey(/* use_odk = */ true); + UseLicenseWithoutSigningKey(OdkVersion::k16_5); // |core_message_size| = 0 means the core message is not provided, so // no parameter check will be done. However, since the license was created // with one, signature checking will fail as there is no core message // to be included when checking the signature. - ASSERT_EQ(WB_License_ProcessLicenseResponse( - whitebox_, license_.core_message.data(), 0, - license_.message.data(), license_.message.size(), - license_.signature.data(), license_.signature.size(), - license_.session_key.data(), license_.session_key.size(), - license_.request.data(), license_.request.size()), - WB_RESULT_INVALID_SIGNATURE); + ASSERT_EQ( + WB_License_ProcessLicenseResponse( + whitebox_, license_.core_message.data(), /*core_message_size=*/0, + license_.message.data(), license_.message.size(), + license_.signature.data(), license_.signature.size(), + license_.session_key.data(), license_.session_key.size(), + license_.request.data(), license_.request.size()), + WB_RESULT_INVALID_SIGNATURE); } } // namespace widevine diff --git a/whitebox/api/license_whitebox_process_license_response_test.cc b/whitebox/api/license_whitebox_process_license_response_test.cc index 2c7c64b..02c87db 100644 --- a/whitebox/api/license_whitebox_process_license_response_test.cc +++ b/whitebox/api/license_whitebox_process_license_response_test.cc @@ -28,9 +28,11 @@ class LicenseWhiteboxProcessLicenseResponseTest builder.Build(*public_key_, &license_); } - void UseLicenseWithSigningKey(const std::vector& padding) { + void UseLicenseWithSigningKey(TestLicenseBuilder::Padding padding) { TestLicenseBuilder builder; - builder.AddSigningKey(TestLicenseBuilder::DefaultSigningKey(), padding); + builder.GetSettings().padding = padding; + + builder.AddSigningKey(TestLicenseBuilder::DefaultSigningKey()); builder.AddStubbedContentKey(); builder.Build(*public_key_, &license_); } @@ -53,7 +55,7 @@ TEST_F(LicenseWhiteboxProcessLicenseResponseTest, SuccessWithoutSigningKey) { TEST_F(LicenseWhiteboxProcessLicenseResponseTest, SuccessWithSigningKeyNoPadding) { - UseLicenseWithSigningKey(TestLicenseBuilder::NoPadding()); + UseLicenseWithSigningKey(TestLicenseBuilder::Padding::kNone); ASSERT_EQ( WB_License_ProcessLicenseResponse( @@ -67,7 +69,7 @@ TEST_F(LicenseWhiteboxProcessLicenseResponseTest, TEST_F(LicenseWhiteboxProcessLicenseResponseTest, SuccessWithSigningKeyPKSC8Padding) { - UseLicenseWithSigningKey(TestLicenseBuilder::PKSC8Padding()); + UseLicenseWithSigningKey(TestLicenseBuilder::Padding::kPKSC8); ASSERT_EQ( WB_License_ProcessLicenseResponse( @@ -82,10 +84,10 @@ TEST_F(LicenseWhiteboxProcessLicenseResponseTest, TEST_F(LicenseWhiteboxProcessLicenseResponseTest, InvalidParameterWithMultipleSigningKeys) { TestLicenseBuilder builder; - builder.AddSigningKey(TestLicenseBuilder::DefaultSigningKey(), - TestLicenseBuilder::PKSC8Padding()); - builder.AddSigningKey(TestLicenseBuilder::DefaultSigningKey(), - TestLicenseBuilder::PKSC8Padding()); + builder.GetSettings().padding = TestLicenseBuilder::Padding::kPKSC8; + + builder.AddSigningKey(TestLicenseBuilder::DefaultSigningKey()); + builder.AddSigningKey(TestLicenseBuilder::DefaultSigningKey()); builder.AddStubbedContentKey(); builder.Build(*public_key_, &license_); diff --git a/whitebox/api/license_whitebox_process_license_response_with_encrypted_key_control_block.cc b/whitebox/api/license_whitebox_process_license_response_with_encrypted_key_control_block.cc new file mode 100644 index 0000000..9fbb0d8 --- /dev/null +++ b/whitebox/api/license_whitebox_process_license_response_with_encrypted_key_control_block.cc @@ -0,0 +1,60 @@ +// Copyright 2020 Google LLC. All Rights Reserved. + +#include "api/license_whitebox.h" + +#include "api/license_whitebox_test_base.h" +#include "api/test_license_builder.h" +#include "base/logging.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace widevine { +class LicenseWhiteboxProcessLicenseResponseWithEncryptedKeyControlBlockTest + : public LicenseWhiteboxTestBase, + public testing::WithParamInterface< + std::tuple> { + protected: + void SetUp() override { + LicenseWhiteboxTestBase::SetUp(); + std::tie(kcb_, odk_) = GetParam(); + } + + TestLicenseBuilder::KeyControlBlock kcb_; + TestLicenseBuilder::OdkVersion odk_; +}; + +// Even though we are saying to use the ODK, since the key control block is +// encrypted, it should either decrypt the key control block or fallback to +// using protobuf parsing. It does not matter which method is used as long +// as it handles it. +TEST_P(LicenseWhiteboxProcessLicenseResponseWithEncryptedKeyControlBlockTest, + Success) { + TestLicenseBuilder builder; + builder.GetSettings().key_control_block = kcb_; + builder.GetSettings().odk_version = odk_; + + builder.AddStubbedContentKey(); + + License license; + builder.Build(*public_key_, &license); + + ASSERT_EQ( + WB_License_ProcessLicenseResponse( + whitebox_, license.core_message.data(), license.core_message.size(), + license.message.data(), license.message.size(), + license.signature.data(), license.signature.size(), + license.session_key.data(), license.session_key.size(), + license.request.data(), license.request.size()), + WB_RESULT_OK); +} + +INSTANTIATE_TEST_SUITE_P( + AllCombination, + LicenseWhiteboxProcessLicenseResponseWithEncryptedKeyControlBlockTest, + ::testing::Combine( + ::testing::Values(TestLicenseBuilder::KeyControlBlock::kClear, + TestLicenseBuilder::KeyControlBlock::kEncrypted), + ::testing::Values(TestLicenseBuilder::OdkVersion::kNone, + TestLicenseBuilder::OdkVersion::k16_3, + TestLicenseBuilder::OdkVersion::k16_5))); +} // namespace widevine diff --git a/whitebox/api/license_whitebox_sign_renewal_request_test.cc b/whitebox/api/license_whitebox_sign_renewal_request_test.cc index c4bef0d..124bd1e 100644 --- a/whitebox/api/license_whitebox_sign_renewal_request_test.cc +++ b/whitebox/api/license_whitebox_sign_renewal_request_test.cc @@ -25,9 +25,11 @@ class LicenseWhiteboxSignRenewalRequestTest : public LicenseWhiteboxTestBase { signature_.resize(signature_size_); } - void LoadLicense(const std::vector& padding) { + void LoadLicense(TestLicenseBuilder::Padding padding) { TestLicenseBuilder builder; - builder.AddSigningKey(signing_key_, padding); + builder.GetSettings().padding = padding; + + builder.AddSigningKey(signing_key_); // Add a throw away key. We just need a key in the license since a license // should always have a content key. builder.AddStubbedContentKey(); @@ -61,7 +63,7 @@ class LicenseWhiteboxSignRenewalRequestTest : public LicenseWhiteboxTestBase { const std::string session_key_ = "0123456789ABCDEF"; - const std::vector signing_key_ = + const std::array signing_key_ = TestLicenseBuilder::DefaultSigningKey(); size_t signature_size_; @@ -77,7 +79,7 @@ class LicenseWhiteboxSignRenewalRequestTest : public LicenseWhiteboxTestBase { }; TEST_F(LicenseWhiteboxSignRenewalRequestTest, SuccessWithInvalidRequest) { - LoadLicense(TestLicenseBuilder::NoPadding()); + LoadLicense(TestLicenseBuilder::Padding::kNone); ASSERT_EQ(WB_License_SignRenewalRequest(whitebox_, garbage_request_.data(), garbage_request_.size(), @@ -90,7 +92,7 @@ TEST_F(LicenseWhiteboxSignRenewalRequestTest, SuccessWithInvalidRequest) { TEST_F(LicenseWhiteboxSignRenewalRequestTest, SuccessWithSigningKeyPKSC8Padding) { - LoadLicense(TestLicenseBuilder::PKSC8Padding()); + LoadLicense(TestLicenseBuilder::Padding::kPKSC8); ASSERT_EQ(WB_License_SignRenewalRequest(whitebox_, garbage_request_.data(), garbage_request_.size(), @@ -102,7 +104,7 @@ TEST_F(LicenseWhiteboxSignRenewalRequestTest, } TEST_F(LicenseWhiteboxSignRenewalRequestTest, InvalidParameterForNullWhitebox) { - LoadLicense(TestLicenseBuilder::NoPadding()); + LoadLicense(TestLicenseBuilder::Padding::kNone); ASSERT_EQ(WB_License_SignRenewalRequest(nullptr, garbage_request_.data(), garbage_request_.size(), @@ -111,7 +113,7 @@ TEST_F(LicenseWhiteboxSignRenewalRequestTest, InvalidParameterForNullWhitebox) { } TEST_F(LicenseWhiteboxSignRenewalRequestTest, InvalidParameterForNullMessage) { - LoadLicense(TestLicenseBuilder::NoPadding()); + LoadLicense(TestLicenseBuilder::Padding::kNone); ASSERT_EQ( WB_License_SignRenewalRequest(whitebox_, nullptr, garbage_request_.size(), @@ -121,7 +123,7 @@ TEST_F(LicenseWhiteboxSignRenewalRequestTest, InvalidParameterForNullMessage) { TEST_F(LicenseWhiteboxSignRenewalRequestTest, InvalidParameterForZeroMessageSize) { - LoadLicense(TestLicenseBuilder::NoPadding()); + LoadLicense(TestLicenseBuilder::Padding::kNone); ASSERT_EQ(WB_License_SignRenewalRequest(whitebox_, garbage_request_.data(), 0, signature_.data(), &signature_size_), @@ -130,7 +132,7 @@ TEST_F(LicenseWhiteboxSignRenewalRequestTest, TEST_F(LicenseWhiteboxSignRenewalRequestTest, InvalidParameterForNullSignature) { - LoadLicense(TestLicenseBuilder::NoPadding()); + LoadLicense(TestLicenseBuilder::Padding::kNone); ASSERT_EQ(WB_License_SignRenewalRequest(whitebox_, garbage_request_.data(), garbage_request_.size(), nullptr, @@ -140,7 +142,7 @@ TEST_F(LicenseWhiteboxSignRenewalRequestTest, TEST_F(LicenseWhiteboxSignRenewalRequestTest, InvalidParameterForNullSignatureSize) { - LoadLicense(TestLicenseBuilder::NoPadding()); + LoadLicense(TestLicenseBuilder::Padding::kNone); ASSERT_EQ(WB_License_SignRenewalRequest(whitebox_, garbage_request_.data(), garbage_request_.size(), @@ -149,7 +151,7 @@ TEST_F(LicenseWhiteboxSignRenewalRequestTest, } TEST_F(LicenseWhiteboxSignRenewalRequestTest, BufferTooSmall) { - LoadLicense(TestLicenseBuilder::NoPadding()); + LoadLicense(TestLicenseBuilder::Padding::kNone); // We need the signature to be too small. While it would be possible to use // zero, using a non-zero value ensures that we are not combining "empty" and diff --git a/whitebox/api/license_whitebox_verify_renewal_response_test.cc b/whitebox/api/license_whitebox_verify_renewal_response_test.cc index 076e18f..db95065 100644 --- a/whitebox/api/license_whitebox_verify_renewal_response_test.cc +++ b/whitebox/api/license_whitebox_verify_renewal_response_test.cc @@ -22,14 +22,16 @@ class LicenseWhiteboxVerifyRenewalResponseTest garbage_renewal_signature_ = Sign(garbage_renewal_message_); } - void LoadLicense(const std::vector& padding) { + void LoadLicense(TestLicenseBuilder::Padding padding) { const auto signing_key = TestLicenseBuilder::DefaultSigningKey(); // We need a license so that we can always have a valid signature for our // message(s), but don't load the license as some test will need no // license loaded. TestLicenseBuilder builder; - builder.AddSigningKey(signing_key, padding); + builder.GetSettings().padding = padding; + + builder.AddSigningKey(signing_key); builder.AddStubbedContentKey(); License license; @@ -84,7 +86,7 @@ class LicenseWhiteboxVerifyRenewalResponseTest // SuccessForGarbageMessage - to use the real serialized response. TEST_F(LicenseWhiteboxVerifyRenewalResponseTest, SuccessForGarbageMessage) { - LoadLicense(TestLicenseBuilder::NoPadding()); + LoadLicense(TestLicenseBuilder::Padding::kNone); ASSERT_EQ(WB_License_VerifyRenewalResponse(whitebox_, garbage_renewal_message_.data(), @@ -96,7 +98,7 @@ TEST_F(LicenseWhiteboxVerifyRenewalResponseTest, SuccessForGarbageMessage) { TEST_F(LicenseWhiteboxVerifyRenewalResponseTest, SuccessWithSigningKeyPKSC8Padding) { - LoadLicense(TestLicenseBuilder::PKSC8Padding()); + LoadLicense(TestLicenseBuilder::Padding::kPKSC8); ASSERT_EQ(WB_License_VerifyRenewalResponse(whitebox_, garbage_renewal_message_.data(), @@ -108,7 +110,7 @@ TEST_F(LicenseWhiteboxVerifyRenewalResponseTest, TEST_F(LicenseWhiteboxVerifyRenewalResponseTest, InvalidParameterForNullWhitebox) { - LoadLicense(TestLicenseBuilder::NoPadding()); + LoadLicense(TestLicenseBuilder::Padding::kNone); ASSERT_EQ( WB_License_VerifyRenewalResponse(nullptr, garbage_renewal_message_.data(), @@ -120,7 +122,7 @@ TEST_F(LicenseWhiteboxVerifyRenewalResponseTest, TEST_F(LicenseWhiteboxVerifyRenewalResponseTest, InvalidParameterForNullMessage) { - LoadLicense(TestLicenseBuilder::NoPadding()); + LoadLicense(TestLicenseBuilder::Padding::kNone); ASSERT_EQ(WB_License_VerifyRenewalResponse(whitebox_, nullptr, garbage_renewal_message_.size(), @@ -131,7 +133,7 @@ TEST_F(LicenseWhiteboxVerifyRenewalResponseTest, TEST_F(LicenseWhiteboxVerifyRenewalResponseTest, InvalidParameterForZeroMessageSize) { - LoadLicense(TestLicenseBuilder::NoPadding()); + LoadLicense(TestLicenseBuilder::Padding::kNone); ASSERT_EQ(WB_License_VerifyRenewalResponse(whitebox_, garbage_renewal_message_.data(), 0, @@ -142,7 +144,7 @@ TEST_F(LicenseWhiteboxVerifyRenewalResponseTest, TEST_F(LicenseWhiteboxVerifyRenewalResponseTest, InvalidParameterForNullSignature) { - LoadLicense(TestLicenseBuilder::NoPadding()); + LoadLicense(TestLicenseBuilder::Padding::kNone); ASSERT_EQ(WB_License_VerifyRenewalResponse( whitebox_, garbage_renewal_message_.data(), @@ -153,7 +155,7 @@ TEST_F(LicenseWhiteboxVerifyRenewalResponseTest, TEST_F(LicenseWhiteboxVerifyRenewalResponseTest, InvalidParameterForInvalidSignatureSize) { - LoadLicense(TestLicenseBuilder::NoPadding()); + LoadLicense(TestLicenseBuilder::Padding::kNone); ASSERT_EQ(WB_License_VerifyRenewalResponse( whitebox_, garbage_renewal_message_.data(), @@ -164,7 +166,7 @@ TEST_F(LicenseWhiteboxVerifyRenewalResponseTest, TEST_F(LicenseWhiteboxVerifyRenewalResponseTest, InvalidSignatureForModifiedMessage) { - LoadLicense(TestLicenseBuilder::NoPadding()); + LoadLicense(TestLicenseBuilder::Padding::kNone); Modify(&garbage_renewal_message_); @@ -178,7 +180,7 @@ TEST_F(LicenseWhiteboxVerifyRenewalResponseTest, TEST_F(LicenseWhiteboxVerifyRenewalResponseTest, InvalidSignatureForModifiedSignature) { - LoadLicense(TestLicenseBuilder::NoPadding()); + LoadLicense(TestLicenseBuilder::Padding::kNone); Modify(&garbage_renewal_signature_); diff --git a/whitebox/api/test_key_types.h b/whitebox/api/test_key_types.h new file mode 100644 index 0000000..8ec1144 --- /dev/null +++ b/whitebox/api/test_key_types.h @@ -0,0 +1,23 @@ +// Copyright 2021 Google LLC. All Rights Reserved. + +#ifndef WHITEBOX_API_TEST_KEY_TYPES_H_ +#define WHITEBOX_API_TEST_KEY_TYPES_H_ + +#include +#include + +namespace widevine { + +// Key ids can be any non-zero length, normally they will be 16 bytes long, +// however, we use 4 in our tests since they are easier to set. +using KeyId = std::array; + +// AES keys can only be 16 bytes. +using AesKey = std::array; + +// AES IV can only be 16 bytes. +using AesIv = std::array; + +} // namespace widevine + +#endif // WHITEBOX_API_TEST_KEY_TYPES_H_ diff --git a/whitebox/api/test_license_builder.cc b/whitebox/api/test_license_builder.cc index f5c3c14..96b1d84 100644 --- a/whitebox/api/test_license_builder.cc +++ b/whitebox/api/test_license_builder.cc @@ -2,6 +2,7 @@ #include "api/test_license_builder.h" +#include #include #include "base/check.h" @@ -19,6 +20,8 @@ namespace widevine { namespace { +using KeyControlBlock = std::array; + void InitializeRequest(video_widevine::LicenseRequest* request) { request->set_request_time(std::time(nullptr)); // Use time=now. @@ -84,26 +87,29 @@ void InitializeResponse(const video_widevine::LicenseRequest& request, video_widevine::PlatformVerificationStatus::PLATFORM_UNVERIFIED); } -std::string EncryptKey(const std::string& key, - const std::string& iv, - const std::vector& plaintext) { +template +std::string Encrypt(const Key& key, const IV& iv, const Plaintext& plaintext) { + std::vector key_data(key.begin(), key.end()); + std::vector iv_data(iv.begin(), iv.end()); + std::vector plaintext_data(plaintext.begin(), plaintext.end()); + AesCbcEncryptor encryptor; - encryptor.SetKey(reinterpret_cast(key.data()), key.size()); + encryptor.SetKey(key_data.data(), key_data.size()); std::vector ciphertext(plaintext.size()); - CHECK(encryptor.Encrypt(reinterpret_cast(iv.data()), - iv.size(), plaintext.data(), plaintext.size(), - ciphertext.data())); + CHECK(encryptor.Encrypt(iv_data.data(), iv_data.size(), plaintext_data.data(), + plaintext_data.size(), ciphertext.data())); return std::string(ciphertext.begin(), ciphertext.end()); } -std::string DeriveIV(const std::vector& context) { +template +std::string DeriveIV(const In& context) { const std::string context_str(context.begin(), context.end()); return crypto_util::DeriveIv(context_str); } -void UpdateKeyControlBlock( +KeyControlBlock CreateKeyControlBlock( video_widevine::License_KeyContainer_SecurityLevel level, video_widevine::License_KeyContainer_KeyControl* key_control) { // The key control block is an 128 bit structure containing the following @@ -119,7 +125,7 @@ void UpdateKeyControlBlock( // 1 = SW_SECURE_DECODE // 2 = HW_SECURE_CRYPTO // 3 = HW_SECURE_DECODE or HW_SECURE_ALL - std::vector key_control_block = { + KeyControlBlock key_control_block = { 'k', 'c', 't', 'l', 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, }; @@ -138,18 +144,16 @@ void UpdateKeyControlBlock( break; } - // Key Control Block is no longer encrypted, so no need to set IV. - key_control->set_key_control_block(key_control_block.data(), - key_control_block.size()); + return key_control_block; } -std::string GenerateCoreMessage( - const std::string& serialized_request, - const std::string& serialized_license_response) { - constexpr uint16_t api_major_version = 16; - constexpr uint16_t api_minor_version = 5; - static_assert(api_major_version == ODK_MAJOR_VERSION, - "Verify ODK library is compatible."); +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) { + DCHECK_EQ(api_major_version, ODK_MAJOR_VERSION) + << "Verify ODK library is compatible."; constexpr uint32_t session_id = 0xcafebabe; constexpr uint32_t nonce = 0xdeadbeef; ODK_NonceValues nonce_values{api_minor_version, api_major_version, nonce, @@ -188,27 +192,109 @@ std::string GenerateCoreMessage( std::string oemcrypto_core_message; CHECK(oemcrypto_core_message::serialize::CreateCoreLicenseResponseFromProto( serialized_license_response, core_request, core_message_hash, - /* nonce_required= */ true, &oemcrypto_core_message)); + /* nonce_required= */ true, use_padding, &oemcrypto_core_message)); return oemcrypto_core_message; } +std::vector GetPadding(TestLicenseBuilder::Padding padding) { + if (padding == TestLicenseBuilder::Padding::kPKSC8) { + return { + 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, + 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, + }; + } + + return {}; +} + +void AddContentKeyToContainer(const TestLicenseBuilder::ContentKey& key_data, + const TestLicenseBuilder::Settings& settings, + const std::string& container_key, + video_widevine::License_KeyContainer* container) { + container->set_type(video_widevine::License_KeyContainer_KeyType_CONTENT); + container->set_id(key_data.id.data(), key_data.id.size()); + container->set_level(key_data.level); + + // To avoid having to define a key iv for each key, derive a key iv from the + // key. This will allows us to have a different IVs between keys but keep it + // deterministic. + const auto key_iv = DeriveIV(key_data.key); + container->set_iv(key_iv); + + std::vector key(key_data.key.begin(), key_data.key.end()); + + auto padding = GetPadding(settings.padding); + key.insert(key.end(), padding.begin(), padding.end()); + + container->set_key(Encrypt(container_key, key_iv, key)); + + const auto key_control_block = + CreateKeyControlBlock(key_data.level, container->mutable_key_control()); + + auto* key_control = container->mutable_key_control(); + + // It is only when the key control block is encrypted will the IV be set. + // The key control block is encrypted with the content key. This will no + // longer be the case in OEMCrypto 17. + if (settings.key_control_block == + TestLicenseBuilder::KeyControlBlock::kEncrypted) { + const auto key_control_block_iv = DeriveIV(key_control_block); + const auto encrypted_key_control_block = + Encrypt(key_data.key, key_control_block_iv, key_control_block); + + key_control->set_iv(key_control_block_iv); + key_control->set_key_control_block(encrypted_key_control_block.data(), + encrypted_key_control_block.size()); + } else { + key_control->set_key_control_block(key_control_block.data(), + key_control_block.size()); + } +} + +void AddSigningKeyToContainer(const TestLicenseBuilder::SigningKey& key_data, + const TestLicenseBuilder::Settings& settings, + const std::string& container_key, + video_widevine::License_KeyContainer* container) { + container->set_type(video_widevine::License_KeyContainer_KeyType_SIGNING); + + // To avoid having to define a key iv for each key, derive a key iv from the + // key. This will allows us to have a different IVs between keys but keep it + // deterministic. + const auto key_iv = DeriveIV(key_data); + container->set_iv(key_iv); + + std::vector key(key_data.begin(), key_data.end()); + + auto padding = GetPadding(settings.padding); + key.insert(key.end(), padding.begin(), padding.end()); + + container->set_key(Encrypt(container_key, key_iv, key)); +} + +void AddOperatorSessionKeyToContainer( + const KeyId& key_id, + video_widevine::License_KeyContainer* container) { + container->set_type( + video_widevine::License_KeyContainer_KeyType_OPERATOR_SESSION); + container->set_id(key_id.data(), key_id.size()); +} + +uint16_t GetOdkMinorVersion(TestLicenseBuilder::OdkVersion odk_version) { + switch (odk_version) { + case TestLicenseBuilder::OdkVersion::k16_3: + return 3; + case TestLicenseBuilder::OdkVersion::k16_5: + return 5; + case TestLicenseBuilder::OdkVersion::kNone: + DCHECK(false); + return 0; + } +} + } // namespace // static -std::vector TestLicenseBuilder::NoPadding() { - return {}; -} - -// static -std::vector TestLicenseBuilder::PKSC8Padding() { - return { - 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, - 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, - }; -} - -// static -std::vector TestLicenseBuilder::DefaultSigningKey() { +TestLicenseBuilder::SigningKey TestLicenseBuilder::DefaultSigningKey() { return { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, @@ -232,118 +318,83 @@ TestLicenseBuilder::TestLicenseBuilder() { crypto_util::kWrappingKeySizeBits); } -void TestLicenseBuilder::AddSigningKey(const std::vector& key, - const std::vector& padding) { - DCHECK_EQ(key.size(), 64u); // 512 bits - - auto* container = response_.add_key(); - container->set_type(video_widevine::License_KeyContainer_KeyType_SIGNING); - - // To avoid having to define a key iv for each key, derive a key iv from the - // key. This will allows us to have a different IVs between keys but keep it - // deterministic. - const auto key_iv = DeriveIV(key); - container->set_iv(key_iv); - - std::vector final_key = key; - final_key.insert(final_key.end(), padding.begin(), padding.end()); - container->set_key(EncryptKey(container_key_, key_iv, final_key)); +void TestLicenseBuilder::AddSigningKey(const SigningKey& key) { + signing_keys_.push_back(key); } void TestLicenseBuilder::AddStubbedContentKey() { - const video_widevine::License_KeyContainer_SecurityLevel kLevel = + content_keys_.emplace_back(); + auto& key_data = content_keys_.back(); + key_data.level = video_widevine::License_KeyContainer_SecurityLevel_SW_SECURE_CRYPTO; - auto* container = response_.add_key(); - - container->set_type(video_widevine::License_KeyContainer_KeyType_CONTENT); - container->set_id("stubbed-content-key"); - container->set_level(kLevel); - container->set_iv("0000000000000000"); - - // We don't bother encrypting the key, it should never be used and there is no - // way to verify it. Note that the ODK automatically strips padding, so - // this key needs to include 16 bytes of padding. - container->set_key("00000000000000000000000000000000"); - UpdateKeyControlBlock(kLevel, container->mutable_key_control()); + key_data.id = {0, 0, 0, 0}; + key_data.key = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; } void TestLicenseBuilder::AddContentKey( video_widevine::License_KeyContainer_SecurityLevel level, - const std::vector& key_id, - const std::vector& key, - const std::vector& padding) { - DCHECK_GT(key_id.size(), 0u); - DCHECK_EQ(key.size(), 16u); - - auto* container = response_.add_key(); - - container->set_type(video_widevine::License_KeyContainer_KeyType_CONTENT); - container->set_id(key_id.data(), key_id.size()); - container->set_level(level); - - // To avoid having to define a key iv for each key, derive a key iv from the - // key. This will allows us to have a different IVs between keys but keep it - // deterministic. - const auto key_iv = DeriveIV(key); - container->set_iv(key_iv); - - std::vector final_key = key; - final_key.insert(final_key.end(), padding.begin(), padding.end()); - container->set_key(EncryptKey(container_key_, key_iv, final_key)); - UpdateKeyControlBlock(level, container->mutable_key_control()); + const KeyId& id, + const AesKey& key) { + content_keys_.emplace_back(); + auto& key_data = content_keys_.back(); + key_data.level = level; + key_data.id = id; + key_data.key = key; } -void TestLicenseBuilder::AddOperatorSessionKey( - const std::vector& key_id) { - DCHECK_GT(key_id.size(), 0u); - - // We only set the type and id because the key should not actually be used. - auto* container = response_.add_key(); - container->set_type( - video_widevine::License_KeyContainer_KeyType_OPERATOR_SESSION); - container->set_id(key_id.data(), key_id.size()); -} - -void TestLicenseBuilder::SetRemoteAttestation(RemoteAttestation setting) { - switch (setting) { - case RemoteAttestation::kUnavailable: - response_.clear_remote_attestation_verified(); - break; - case RemoteAttestation::kVerified: - response_.set_remote_attestation_verified(true); - break; - case RemoteAttestation::kUnverified: - response_.set_remote_attestation_verified(false); - break; - } -} - -void TestLicenseBuilder::SetVerificationStatus(VerificationStatus setting) { - switch (setting) { - case VerificationStatus::kUnavailable: - response_.clear_platform_verification_status(); - break; - case VerificationStatus::kHardwareVerified: - response_.set_platform_verification_status( - video_widevine::PLATFORM_HARDWARE_VERIFIED); - break; - case VerificationStatus::kOther: - response_.set_platform_verification_status( - video_widevine::PLATFORM_UNVERIFIED); - break; - } -} - -void TestLicenseBuilder::SetUseODK(bool setting) { - use_odk_ = setting; +void TestLicenseBuilder::AddOperatorSessionKey(const KeyId& id) { + operator_session_keys_.push_back(id); } void TestLicenseBuilder::Build(const RsaPublicKey& public_key, License* license) const { DCHECK(license); - const std::string message_str = response_.SerializeAsString(); + // Make a copy to allow us to maintain the const-correctness of this + // function. + video_widevine::License response = response_; + switch (settings_.remote_attestation) { + case RemoteAttestation::kUnavailable: + response.clear_remote_attestation_verified(); + break; + case RemoteAttestation::kVerified: + response.set_remote_attestation_verified(true); + break; + case RemoteAttestation::kUnverified: + response.set_remote_attestation_verified(false); + break; + } + + switch (settings_.verification_status) { + case VerificationStatus::kUnavailable: + response.clear_platform_verification_status(); + break; + case VerificationStatus::kHardwareVerified: + response.set_platform_verification_status( + video_widevine::PLATFORM_HARDWARE_VERIFIED); + break; + case VerificationStatus::kOther: + response.set_platform_verification_status( + video_widevine::PLATFORM_UNVERIFIED); + break; + } + + for (const auto& key : signing_keys_) { + AddSigningKeyToContainer(key, settings_, container_key_, + response.add_key()); + } + + for (const auto& key : content_keys_) { + AddContentKeyToContainer(key, settings_, container_key_, + response.add_key()); + } + + for (const auto& key : operator_session_keys_) { + AddOperatorSessionKeyToContainer(key, response.add_key()); + } + + const std::string message_str = response.SerializeAsString(); std::string signing_key = crypto_util::DeriveKey( session_key_, crypto_util::kSigningKeyLabel, serialized_request_, crypto_util::kSigningKeySizeBits * 2); @@ -352,11 +403,16 @@ void TestLicenseBuilder::Build(const RsaPublicKey& public_key, std::string session_key_str; CHECK(public_key.Encrypt(session_key_, &session_key_str)); - const std::string oemcrypto_core_message = - use_odk_ ? GenerateCoreMessage(serialized_request_, message_str) - : std::string(); + std::string oemcrypto_core_message; + if (settings_.odk_version != OdkVersion::kNone) { + uint16_t api_major_version = 16; + 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); + } - // If |use_odk_| is false, |oemcrypto_core_message| will be empty. + // If |odk_version| is kNone, |oemcrypto_core_message| will be empty. const std::string signature_str = crypto_util::CreateSignatureHmacSha256( signing_key, oemcrypto_core_message + message_str); diff --git a/whitebox/api/test_license_builder.h b/whitebox/api/test_license_builder.h index 30e0107..f47ce3a 100644 --- a/whitebox/api/test_license_builder.h +++ b/whitebox/api/test_license_builder.h @@ -3,10 +3,11 @@ #ifndef WHITEBOX_API_LICENSE_BUILDER_H_ #define WHITEBOX_API_LICENSE_BUILDER_H_ -#include +#include #include #include +#include "api/test_key_types.h" #include "cdm/protos/license_protocol.pb.h" #include "crypto_utils/rsa_key.h" @@ -27,6 +28,23 @@ struct License { class TestLicenseBuilder { public: + struct ContentKey { + KeyId id; + + AesKey key; + + video_widevine::License_KeyContainer_SecurityLevel level = + video_widevine::License_KeyContainer_SecurityLevel_SW_SECURE_CRYPTO; + }; + + // Signing keys must be 512 bits (64 bytes). + using SigningKey = std::array; + + enum class Padding { + kNone, + kPKSC8, + }; + enum class RemoteAttestation { kUnavailable, kVerified, @@ -39,40 +57,48 @@ class TestLicenseBuilder { kOther, }; - // Returns padding data the can be used as |padding| when calling - // AddSigningKey() or AddContentKey(). - static std::vector NoPadding(); - static std::vector PKSC8Padding(); + // ODK version of the `core_message`. + enum class OdkVersion { + kNone, // No `core_message` + k16_3, // ODK version 16.3 + k16_5, // ODK version 16.5 + }; + + enum class KeyControlBlock { + kClear, + kEncrypted, + }; + + struct Settings { + Padding padding = Padding::kNone; + OdkVersion odk_version = OdkVersion::kNone; + KeyControlBlock key_control_block = KeyControlBlock::kClear; + RemoteAttestation remote_attestation = RemoteAttestation::kUnavailable; + VerificationStatus verification_status = VerificationStatus::kUnavailable; + }; // Returns a default signing key that can be used with AddSigningKey(). - static std::vector DefaultSigningKey(); + static SigningKey DefaultSigningKey(); TestLicenseBuilder(); - void AddSigningKey(const std::vector& key, - const std::vector& padding); + void AddSigningKey(const SigningKey& key); // Add a content key so that there is some key in the license. This should not // be used with AddContentKey(). void AddStubbedContentKey(); void AddContentKey(video_widevine::License_KeyContainer_SecurityLevel level, - const std::vector& key_id, - const std::vector& key, - const std::vector& padding); + const KeyId& id, + const AesKey& key); // The key id will matter as we will need to reference it, but the key won't // matter since we are only using it as a means to verify that a non-content // key can't be used as a content key. - void AddOperatorSessionKey(const std::vector& key_id); + void AddOperatorSessionKey(const KeyId& key_id); - void SetRemoteAttestation(RemoteAttestation setting); - - void SetVerificationStatus(VerificationStatus setting); - - // If set, then Build() will populate |core_message| in License with the - // matching ODK core message. If not set, then |core_message| will be empty. - void SetUseODK(bool setting); + Settings& GetSettings() { return settings_; } + const Settings& GetSettings() const { return settings_; } // Gets the serialized license request and response (in components) that would // have been used in the license exchange. @@ -85,7 +111,12 @@ class TestLicenseBuilder { video_widevine::License response_; std::string serialized_request_; std::string container_key_; - bool use_odk_ = false; + + Settings settings_; + + std::vector content_keys_; + std::vector signing_keys_; + std::vector operator_session_keys_; }; } // namespace widevine diff --git a/whitebox/benchmarking/data_source.cc b/whitebox/benchmarking/data_source.cc index 0d07448..21430bb 100644 --- a/whitebox/benchmarking/data_source.cc +++ b/whitebox/benchmarking/data_source.cc @@ -441,27 +441,19 @@ const size_t kGoldenDataSize = sizeof(kGoldenDataSize); } // namespace std::vector DataSource::Get(size_t size) { - std::vector buffer(size); + std::vector data(size); - size_t write_head = 0; - - while (write_head < size) { - const size_t remaining_read = kGoldenDataSize - read_head_; - const size_t remaining_write = size - write_head; - - // Figure out how much more we can write. If we are nearing the end of the - // read buffer, we have to cut short our read so that we can loop back to - // the start of the read buffer. - size_t next_write = std::min(remaining_read, remaining_write); - memcpy(buffer.data() + write_head, kGoldenData + read_head_, next_write); - - // Move both the read and write heads forward. Make sure to loop the read - // head back to the start so that we we can restart reading when necessary. - write_head += next_write; - read_head_ = (read_head_ + next_write) % kGoldenDataSize; + for (size_t i = 0; i < size; i++) { + data[i] = Get(); } - return buffer; + return data; +} + +uint8_t DataSource::Get() { + uint8_t x = kGoldenData[read_head_]; + read_head_ = (read_head_ + 1) % kGoldenDataSize; + return x; } } // namespace widevine diff --git a/whitebox/benchmarking/data_source.h b/whitebox/benchmarking/data_source.h index 5729a8f..130ea4c 100644 --- a/whitebox/benchmarking/data_source.h +++ b/whitebox/benchmarking/data_source.h @@ -3,18 +3,31 @@ #ifndef WHITEBOX_BENCHMARKING_DATA_SOURCE_H_ #define WHITEBOX_BENCHMARKING_DATA_SOURCE_H_ -#include -#include - +#include +#include +#include #include namespace widevine { class DataSource { public: + template + std::array Get() { + std::array data; + + for (size_t i = 0; i < size; i++) { + data[i] = Get(); + } + + return data; + } + std::vector Get(size_t size); private: + uint8_t Get(); + size_t read_head_ = 0; }; diff --git a/whitebox/external/odk.BUILD b/whitebox/external/odk.BUILD index 2f63bb8..c9ee630 100644 --- a/whitebox/external/odk.BUILD +++ b/whitebox/external/odk.BUILD @@ -23,6 +23,8 @@ cc_library( "oemcrypto/odk/src/odk_timer.c", "oemcrypto/odk/src/odk_util.c", "oemcrypto/odk/src/odk_util.h", + "oemcrypto/odk/src/odk_message_priv.h", + "oemcrypto/odk/src/odk_message.c", "oemcrypto/odk/src/serialization_base.c", ":odk_common_hdrs", ], @@ -31,6 +33,8 @@ cc_library( "oemcrypto/odk/include/odk.h", "oemcrypto/odk/include/odk_structs.h", "oemcrypto/odk/include/odk_target.h", + "oemcrypto/odk/include/odk_message.h", + "oemcrypto/odk/include/odk_attributes.h", ], copts = ["-std=c99"], includes = [ diff --git a/whitebox/impl/reference/BUILD b/whitebox/impl/reference/BUILD index fbbf041..6d979c1 100644 --- a/whitebox/impl/reference/BUILD +++ b/whitebox/impl/reference/BUILD @@ -19,6 +19,17 @@ cc_library( ], ) +cc_library( + name = "odk", + hdrs = ["odk.h",], + srcs = ["odk.cc",], + deps = [ + "//api:result", + "//external:odk", + "//chromium_deps/base:glog", + ], +) + cc_library( name = "license_private_key", hdrs = [ @@ -42,7 +53,7 @@ cc_library( "//crypto_utils:aes_ctr_encryptor", "//crypto_utils:crypto_util", "//crypto_utils:rsa_key", - "//external:odk", + ":odk", ], ) diff --git a/whitebox/impl/reference/license_whitebox_impl.cc b/whitebox/impl/reference/license_whitebox_impl.cc index a99937a..018658f 100644 --- a/whitebox/impl/reference/license_whitebox_impl.cc +++ b/whitebox/impl/reference/license_whitebox_impl.cc @@ -18,7 +18,9 @@ #include "crypto_utils/rsa_key.h" #include "impl/reference/license_private_key.h" #include "impl/reference/memory_util.h" +#include "impl/reference/odk.h" #include "oemcrypto/odk/include/odk.h" +#include "oemcrypto/odk/include/odk_message.h" #include "oemcrypto/odk/include/odk_structs.h" #include "oemcrypto/odk/src/odk_serialize.h" #include "oemcrypto/odk/src/serialization_base.h" @@ -54,7 +56,9 @@ bool Decrypt(AesCbcDecryptor& decryptor, const std::string& iv, const std::string& encrypted, std::string* decrypted) { + DCHECK_EQ(iv.size(), 16u); DCHECK_GE(decrypted->size(), encrypted.size()); + return decryptor.Decrypt( reinterpret_cast(iv.data()), iv.size(), reinterpret_cast(encrypted.data()), encrypted.size(), @@ -77,8 +81,9 @@ std::string ExtractItem(const OEMCrypto_Substring& item, return buffer.substr(item.offset, item.length); } -video_widevine::License_KeyContainer_SecurityLevel ExtractLevel( - const std::string& key_control_block) { +bool ExtractLevel( + const std::string& key_control_block, + video_widevine::License_KeyContainer_SecurityLevel* security_level) { // The key control block is an 128 bit structure containing the following // fields. The fields are defined to be in big-endian byte order. // @@ -93,29 +98,30 @@ video_widevine::License_KeyContainer_SecurityLevel ExtractLevel( // 2 = HW_SECURE_CRYPTO // 3 = HW_SECURE_DECODE or HW_SECURE_ALL - // Limited checks to verify that this is proper key control block. - // If not valid, assume it's the highest level. + // Make sure this is a valid key control block. Ideally the signature + // verification should have taken care of this. if ((key_control_block.size() != 16u) || (key_control_block[0] != 'k') || (key_control_block[1] != 'c')) { - return video_widevine::License_KeyContainer_SecurityLevel_HW_SECURE_DECODE; + return false; } // Extract bits 26..27 from Control Bits. - int security_level = (key_control_block[12] & 0x0C) >> 2; - switch (security_level) { + switch ((key_control_block[12] & 0x0C) >> 2) { case 0: - return video_widevine:: - License_KeyContainer_SecurityLevel_SW_SECURE_CRYPTO; + *security_level = + video_widevine::License_KeyContainer_SecurityLevel_SW_SECURE_CRYPTO; case 1: - return video_widevine:: - License_KeyContainer_SecurityLevel_SW_SECURE_DECODE; + *security_level = + video_widevine::License_KeyContainer_SecurityLevel_SW_SECURE_DECODE; case 2: - return video_widevine:: - License_KeyContainer_SecurityLevel_HW_SECURE_CRYPTO; + *security_level = + video_widevine::License_KeyContainer_SecurityLevel_HW_SECURE_CRYPTO; default: - return video_widevine:: - License_KeyContainer_SecurityLevel_HW_SECURE_DECODE; + *security_level = + video_widevine::License_KeyContainer_SecurityLevel_HW_SECURE_DECODE; } + + return true; } // Creates and returns a ContentKey based on the values provided. @@ -218,13 +224,13 @@ const uint8_t kCTRSecretStringPattern[] = { }; std::vector GetSecretStringFor(WB_CipherMode mode) { - return mode == WB_CIPHER_MODE_CBC ? - std::vector( - kCBCSecretStringPattern, - kCBCSecretStringPattern + sizeof(kCBCSecretStringPattern)) : - std::vector( - kCTRSecretStringPattern, - kCTRSecretStringPattern + sizeof(kCTRSecretStringPattern)); + return mode == WB_CIPHER_MODE_CBC + ? std::vector( + kCBCSecretStringPattern, + kCBCSecretStringPattern + sizeof(kCBCSecretStringPattern)) + : std::vector( + kCTRSecretStringPattern, + kCTRSecretStringPattern + sizeof(kCTRSecretStringPattern)); } const ContentKey* FindKey(const WB_License_Whitebox* whitebox, @@ -497,35 +503,14 @@ WB_Result WB_License_ProcessLicenseResponse(WB_License_Whitebox* whitebox, return WB_RESULT_INVALID_SIGNATURE; } - // If |core_message| is provided, parse it into |parsed_license| and validate - // that it's an appropriate version. - uint16_t odk_major_version = 0; - uint16_t odk_minor_version = 0; - ODK_ParsedLicense parsed_license; + ODKContext odk_context; + if (core_message_size > 0) { - // Decode |core_message|. - Message* msg = NULL; - AllocateMessage(&msg, message_block); - InitMessage(msg, - reinterpret_cast( - const_cast(combined_message_str.data())), - combined_message_str.size()); - // The core message is at the beginning of the buffer, and is the part to be - // parsed. - SetSize(msg, core_message_size); + auto result = + GetODKContext(combined_message_str, core_message_size, &odk_context); - ODK_LicenseResponse license_response = {{{0}}, &parsed_license, {0}}; - Unpack_ODK_LicenseResponse(msg, &license_response); - - odk_major_version = - license_response.request.core_message.nonce_values.api_major_version; - odk_minor_version = - license_response.request.core_message.nonce_values.api_minor_version; - if ((GetStatus(msg) != MESSAGE_STATUS_OK) || - (license_response.request.core_message.message_type != - ODK_License_Response_Type)) { - DVLOG(1) << "Failed to validate core message."; - return WB_RESULT_INVALID_SIGNATURE; + if (result != WB_RESULT_OK) { + return result; } } @@ -539,14 +524,16 @@ WB_Result WB_License_ProcessLicenseResponse(WB_License_Whitebox* whitebox, std::vector client_renewal_keys; // Even if |core_message| is provided, only ODK v16.5 and later support the - // fields needed. If an older API is used, ignore it and use the protobuf - // as if |core_message| was not provided. - if (IsOdkVersionSupported(odk_major_version, odk_minor_version)) { + // fields needed. If an older API is used, ignore it and use the protobuf as + // if |core_message| was not provided. + if (odk_context.is_valid && !HasEncryptedKeyControlBlock(odk_context) && + IsOdkVersionSupported(odk_context.major_version, + odk_context.minor_version)) { // Start by extracting the signing key. const std::string signing_key_encrypted = - ExtractItem(parsed_license.enc_mac_keys, message_str); + ExtractItem(odk_context.license.enc_mac_keys, message_str); const std::string signing_key_iv = - ExtractItem(parsed_license.enc_mac_keys_iv, message_str); + ExtractItem(odk_context.license.enc_mac_keys_iv, message_str); if (!signing_key_encrypted.empty() && !signing_key_iv.empty()) { std::string unwrapped_signing_key(signing_key_encrypted); @@ -568,14 +555,19 @@ WB_Result WB_License_ProcessLicenseResponse(WB_License_Whitebox* whitebox, } // Now extract all the content keys. - for (size_t i = 0; i < parsed_license.key_array_length; ++i) { - const OEMCrypto_KeyObject& key = parsed_license.key_array[i]; + for (size_t i = 0; i < odk_context.license.key_array_length; ++i) { + const OEMCrypto_KeyObject& key = odk_context.license.key_array[i]; + const std::string key_id = ExtractItem(key.key_id, message_str); + DCHECK_GT(key_id.size(), 0u); const std::string iv = ExtractItem(key.key_data_iv, message_str); - const std::string wrapped_key = ExtractItem(key.key_data, message_str); - std::string unwrapped_key(wrapped_key); + DCHECK_EQ(iv.size(), 16u); + const std::string wrapped_key = ExtractItem(key.key_data, message_str); + DCHECK_EQ(wrapped_key.size(), 16u); + + std::string unwrapped_key(wrapped_key); if (!Decrypt(decryptor, iv, wrapped_key, &unwrapped_key)) { DVLOG(1) << "Invalid parameter: Invalid key.key_data."; return WB_RESULT_INVALID_PARAMETER; @@ -595,9 +587,12 @@ WB_Result WB_License_ProcessLicenseResponse(WB_License_Whitebox* whitebox, // this. const std::string key_control_block = ExtractItem(key.key_control, message_str); - content_keys[key_id] = - CreateContentKey(ExtractLevel(key_control_block), - /* is_hw_verified */ false, unwrapped_key); + + video_widevine::License_KeyContainer_SecurityLevel security_level; + CHECK(ExtractLevel(key_control_block, &security_level)); + + content_keys[key_id] = CreateContentKey( + security_level, /* is_hw_verified */ false, unwrapped_key); } } else { // Core message not provided or an old version, so extract the keys from diff --git a/whitebox/impl/reference/odk.cc b/whitebox/impl/reference/odk.cc new file mode 100644 index 0000000..739a835 --- /dev/null +++ b/whitebox/impl/reference/odk.cc @@ -0,0 +1,73 @@ +// cOpyright 2021 Google LLC. All Rights Reserved. + +#include "impl/reference/odk.h" + +#include + +#include "base/check.h" +#include "base/check_op.h" +#include "base/logging.h" +#include "oemcrypto/odk/include/OEMCryptoCENCCommon.h" +#include "oemcrypto/odk/include/odk.h" +#include "oemcrypto/odk/include/odk_message.h" +#include "oemcrypto/odk/src/odk_serialize.h" +#include "oemcrypto/odk/src/serialization_base.h" + +// Most of the logic to parse the core message comes from `ODK_ParseResponse()`. +// We copy the logic since the function as a whole is not compatible with what +// we need to do here. +WB_Result GetODKContext(const std::string& combined_message, + size_t core_message_size, + ODKContext* context) { + // ODK_Message requires a mutable buffer and the buffer must exist as long as + // the message exists. + std::vector mutable_message(combined_message.begin(), + combined_message.end()); + + ODK_Message msg = + ODK_Message_Create(mutable_message.data(), mutable_message.size()); + ODK_Message_SetSize(&msg, core_message_size); + + const ODK_MessageStatus message_status = ODK_Message_GetStatus(&msg); + if (message_status != MESSAGE_STATUS_OK) { + DVLOG(1) << "Invalid core message status: " << message_status; + return WB_RESULT_INVALID_SIGNATURE; + } + + ODK_LicenseResponse license_response{{{0, 0, {}}}, NULL, {0}}; + license_response.parsed_license = &(context->license); + + Unpack_ODK_LicenseResponse(&msg, &license_response); + + const auto& core_message = license_response.request.core_message; + + if (core_message.message_type != ODK_License_Response_Type) { + DVLOG(1) << "Failed core message type: " << core_message.message_type; + return WB_RESULT_INVALID_SIGNATURE; + } + + context->major_version = core_message.nonce_values.api_major_version; + context->minor_version = core_message.nonce_values.api_minor_version; + + // Now that it is initialized, mark it as valid. + context->is_valid = true; + + return WB_RESULT_OK; +} + +bool HasEncryptedKeyControlBlock(const ODKContext& context) { + size_t encrypted_count = 0; + + for (uint32_t i = 0; i < context.license.key_array_length; i++) { + const OEMCrypto_KeyObject& key = context.license.key_array[i]; + + if (key.key_control_iv.length > 0) { + encrypted_count++; + } + } + + // Either no key control block should be encrypted or all of them should be. + // There is no case where only a subset of the keys should have encrypted key + // control blocks. + return encrypted_count > 0; +} diff --git a/whitebox/impl/reference/odk.h b/whitebox/impl/reference/odk.h new file mode 100644 index 0000000..2836860 --- /dev/null +++ b/whitebox/impl/reference/odk.h @@ -0,0 +1,25 @@ +// Copyright 2021 Google LLC. All Rights Reserved. + +#ifndef WHITEBOX_IMPL_REFERENCE_ODK_H_ +#define WHITEBOX_IMPL_REFERENCE_ODK_H_ + +#include +#include + +#include "api/result.h" +#include "oemcrypto/odk/include/odk_structs.h" + +struct ODKContext { + bool is_valid = false; + uint16_t major_version = 0; + uint16_t minor_version = 0; + ODK_ParsedLicense license; +}; + +WB_Result GetODKContext(const std::string& combined_message, + size_t core_message_size, + ODKContext* context); + +bool HasEncryptedKeyControlBlock(const ODKContext& context); + +#endif // WHITEBOX_IMPL_REFERENCE_ODK_H_