Update the test to support license protocol 2.2

- Add a flag ENABLE_LICENSE_PROTOCOL_2_2, when the flag is enabled
  - Hash the license request in WB_License_ProcessLicenseResponse, i.e.
    the request used for the key derivation, which ensures the key
    derivation message to be a string of constant size 64 bytes.
  - Hash the license request in WB_License_SignLicenseRequest. Note that
    the function takes license request (or hashed) + odk message as
    parameter for odk v17 or above.
- Enable the flag just for Chrome and ChromeOS for now.

We may change the implementation to hash inside the white-box in the
future.

Also included a few other misc changes, e.g. updating the DEPS of
boringssl and googletest which are already in the white-box directory,
adding a test main etc.
This commit is contained in:
KongQun Yang
2023-03-29 19:19:52 +00:00
parent 317916385a
commit d6ef4e1133
13 changed files with 185 additions and 17 deletions

View File

@@ -32,7 +32,7 @@ git_repository(
git_repository(
name = "googletest_repo",
commit = "b6cd405286ed8635ece71c72f118e659f4ade3fb", # 2019-01-04
commit = "b796f7d44681514f58a683a3a71ff17c94edb0c1", # 2023-01-17
remote = "https://github.com/google/googletest.git",
)

View File

@@ -17,8 +17,8 @@ cc_library(
"//:is_old_api": [],
"//:is_old_vmpra": [],
"//conditions:default": [ # Chrome
# Comment out HAS_PROVIDER_KEYS temporarily
# "HAS_PROVIDER_KEYS",
"HAS_PROVIDER_KEYS",
"ENABLE_LICENSE_PROTOCOL_2_2",
],
}) + select({
"//:is_chromeos": ["WV_ENABLE_HW_VERIFICATION=1"],
@@ -346,3 +346,19 @@ cc_library(
],
alwayslink = True,
)
cc_library(
name = "license_whitebox_main",
srcs = [
"license_whitebox_main.cc",
],
visibility = ["//visibility:public"],
deps = [
":golden_data",
":license_whitebox_provider_keys_test_data",
":test_license_builder",
"//chromium_deps/base:glog",
"//external:gflags",
],
alwayslink = True,
)

View File

@@ -154,7 +154,8 @@ TEST_F(LicenseWhiteboxKeyControlBlockSingleTest, KcbHeaderError) {
content.ciphertext.data(), content.ciphertext.size(), content.iv.data(),
content.iv.size(), plaintext.data(), &plaintext_size);
// Either it works or the key should be skipped.
if (result != WB_RESULT_OK)
if (result != WB_RESULT_OK) {
EXPECT_EQ(result, WB_RESULT_KEY_UNAVAILABLE);
}
}
} // namespace widevine

View File

@@ -0,0 +1,109 @@
// Copyright 2023 Google LLC. All Rights Reserved.
#include <gflags/gflags.h>
#include <memory>
#include <string>
#include <vector>
#include "api/golden_data.h"
#include "api/license_whitebox.h"
#include "api/license_whitebox_provider_keys_test_data.h"
#include "api/test_license_builder.h"
#include "base/logging.h"
DEFINE_int32(
provider_key_id,
0,
"Specifies the provider key id (1 and 2 are valid provider keys).");
namespace widevine {
class LicenseWhitebox {
public:
LicenseWhitebox() {
auto init_data = GetLicenseWhiteboxProviderKeysInitData();
const auto result =
WB_License_Create(init_data.data(), init_data.size(), &whitebox_);
if (result != WB_RESULT_OK)
LOG(ERROR) << "Failed to create license whitebox " << result;
}
~LicenseWhitebox() {
if (whitebox_)
WB_License_Delete(whitebox_);
}
bool LoadLicense(const ContentKeyData& content_key) {
if (!whitebox_)
return false;
TestLicenseBuilder::Settings settings;
settings.padding = TestLicenseBuilder::Padding::kNone;
settings.provider_key_id = FLAGS_provider_key_id;
TestLicenseBuilder builder;
builder.SetSettings(settings);
builder.AddContentKey(content_key);
auto server = TestServer::CreateDualKey();
License license;
builder.Build(*server, &license);
const auto result = WB_License_ProcessLicenseResponse(
whitebox_, WB_LICENSE_KEY_MODE_DUAL_KEY, 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(), builder.GetSettings().provider_key_id,
license.request.data(), license.request.size());
if (result != WB_RESULT_OK) {
LOG(ERROR) << "WB_License_ProcessLicenseResponse failed: " << result;
return false;
}
return true;
}
WB_License_Whitebox* whitebox() { return whitebox_; }
private:
WB_License_Whitebox* whitebox_ = nullptr;
};
} // namespace widevine
int main(int argc, char** argv) {
google::ParseCommandLineFlags(&argc, &argv, true);
google::InitGoogleLogging(argv[0]);
widevine::LicenseWhitebox whitebox;
widevine::GoldenData golden_data;
if (!whitebox.LoadLicense(golden_data.CTRContent().software_crypto_key)) {
LOG(ERROR) << "Failed to load license.";
return 1;
}
// Make sure the buffer is large enough for either CTR or CBC.
size_t plaintext_size = std::max(golden_data.CBCContent().ciphertext.size(),
golden_data.CTRContent().ciphertext.size());
std::vector<uint8_t> plaintext(plaintext_size);
const auto result = WB_License_Decrypt(
whitebox.whitebox(), WB_CIPHER_MODE_CTR,
golden_data.CTRContent().software_crypto_key.id.data(),
golden_data.CTRContent().software_crypto_key.id.size(),
golden_data.CTRContent().ciphertext.data(),
golden_data.CTRContent().ciphertext.size(),
golden_data.CTRContent().iv.data(), golden_data.CTRContent().iv.size(),
plaintext.data(), &plaintext_size);
if (result != WB_RESULT_OK) {
LOG(ERROR) << "WB_License_Decrypt failed " << result;
return 1;
}
plaintext.resize(plaintext_size);
if (plaintext != golden_data.CTRContent().plaintext) {
LOG(ERROR) << "Decrypted data does not match.";
return 1;
}
LOG(INFO) << "Decrypted successfully.";
return 0;
}

View File

@@ -18,7 +18,7 @@
namespace widevine {
namespace {
constexpr size_t kMessageSize = 4 * 1024;
constexpr size_t kMessageSize = 1024;
constexpr size_t kSignatureSize = 256;
constexpr size_t kIterations = 10;

View File

@@ -355,7 +355,11 @@ class LicenseWhiteboxDecryptUatTest : public ::testing::Test {
WB_License_Whitebox* whitebox_ = nullptr;
};
TEST_F(LicenseWhiteboxDecryptUatTest, CryptoKeyWithCbcDataInCbcMode) {
// Disabled the test as it needs to be updated for license protocol v2.2. On the
// other hand, we don't really need it as there are already Alcatraz unit-tests
// and CE CDM unit-tests against uat license servers.
// Still keep the test as it may still be helpful for debugging in the future.
TEST_F(LicenseWhiteboxDecryptUatTest, DISABLED_CryptoKeyWithCbcDataInCbcMode) {
auto init_data = GetLicenseWhiteboxProviderKeysInitData();
ASSERT_EQ(WB_License_Create(init_data.data(), init_data.size(), &whitebox_),
WB_RESULT_OK);

View File

@@ -529,10 +529,15 @@ TestLicenseBuilder::TestLicenseBuilder() {
InitializeResponse(request_, &response_);
serialized_request_ = request_.SerializeAsString();
#ifdef ENABLE_LICENSE_PROTOCOL_2_2
request_for_key_derivation_ = Sha512_Hash(serialized_request_);
#else
request_for_key_derivation_ = serialized_request_;
#endif
container_key_ = crypto_util::DeriveKey(
std::string(session_key_.begin(), session_key_.end()),
crypto_util::kWrappingKeyLabel, serialized_request_,
crypto_util::kWrappingKeyLabel, request_for_key_derivation_,
crypto_util::kWrappingKeySizeBits);
}
@@ -624,7 +629,7 @@ void TestLicenseBuilder::Build(const TestServer& server,
const std::string message_str = response.SerializeAsString();
std::string signing_key = crypto_util::DeriveKey(
std::string(session_key_.begin(), session_key_.end()),
crypto_util::kSigningKeyLabel, serialized_request_,
crypto_util::kSigningKeyLabel, request_for_key_derivation_,
crypto_util::kSigningKeySizeBits * 2);
signing_key.resize(crypto_util::kSigningKeySizeBytes);
@@ -651,8 +656,8 @@ void TestLicenseBuilder::Build(const TestServer& server,
const std::string signature_str = crypto_util::CreateSignatureHmacSha256(
signing_key, oemcrypto_core_message + message_str);
license->request.assign(serialized_request_.begin(),
serialized_request_.end());
license->request.assign(request_for_key_derivation_.begin(),
request_for_key_derivation_.end());
if (!oemcrypto_core_message.empty()) {
license->core_message.assign(oemcrypto_core_message.begin(),
oemcrypto_core_message.end());

View File

@@ -151,6 +151,9 @@ class TestLicenseBuilder {
video_widevine::LicenseRequest request_;
video_widevine::License response_;
std::string serialized_request_;
// It is `serialized_request_` for license protocol 2.1, and
// `sha512(serialized_request_)` for license protocol 2.2.
std::string request_for_key_derivation_;
std::string container_key_;
Settings settings_;

View File

@@ -65,6 +65,7 @@ uint8_t MaskingFunction1(uint8_t input) {
return y % 257;
}
#ifndef ALWAYS_DECRYPT_TO_CLEAR
// This function performs the reverse of MaskingFunction1().
uint8_t InverseMaskingFunction1(uint8_t input) {
// Rather than compute the byte each time, generate a lookup table for all
@@ -82,6 +83,7 @@ uint8_t InverseMaskingFunction1(uint8_t input) {
return mapping[input];
}
#endif
bool CheckAndUpdateSize(size_t min_size, size_t* out_size) {
const bool good = *out_size >= min_size;
@@ -128,6 +130,7 @@ namespace {
// Use a different pattern for CBC than for CTR to ensure that mixing them will
// fail. An implementation can use a different string per key and per cipher
// mode.
#ifndef ALWAYS_DECRYPT_TO_CLEAR
const uint8_t kCBCSecretStringPattern[] = {
0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F,
@@ -147,6 +150,7 @@ std::vector<uint8_t> GetSecretStringFor(WB_CipherMode mode) {
kCTRSecretStringPattern,
kCTRSecretStringPattern + sizeof(kCTRSecretStringPattern));
}
#endif
const widevine::InternalKey* FindKey(const WB_License_Whitebox* whitebox,
const uint8_t* id,
@@ -375,6 +379,14 @@ WB_Result WB_License_ProcessLicenseResponse(WB_License_Whitebox* whitebox,
return WB_RESULT_INVALID_PARAMETER;
}
#ifdef ENABLE_LICENSE_PROTOCOL_2_2
const size_t kSha512Size = 64;
if (license_request_size != kSha512Size) {
DVLOG(1) << "Invalid parameter: invalid request size.";
return WB_RESULT_INVALID_PARAMETER;
}
#endif
// Because we use SHA256, the hash will be 32 bytes (256 bits).
if (signature_size != 32) {
DVLOG(1) << "Invalid parameter: invalid signature size.";