Encrypted Key Control Block
This change updates the external copy of the reference to match the internal copy at commit cda42fa07b533f8aad3183cd7eb99ce553949f88 which introduces the tests (and fix) to handle an encrypted key block.
This commit is contained in:
@@ -70,7 +70,7 @@ http_archive(
|
|||||||
new_git_repository(
|
new_git_repository(
|
||||||
name = "odk_repo",
|
name = "odk_repo",
|
||||||
build_file = "@whitebox_api_repo//external:odk.BUILD",
|
build_file = "@whitebox_api_repo//external:odk.BUILD",
|
||||||
commit = "bee799748752fac84d9c3ecd549aa54f72c88d02",
|
commit = "565237f8e6900e467eb236040374428387e90bd0",
|
||||||
remote = "https://widevine-partner.googlesource.com/oemcrypto_core_message.git",
|
remote = "https://widevine-partner.googlesource.com/oemcrypto_core_message.git",
|
||||||
repo_mapping = {"@whitebox" : "@whitebox_api_repo"}
|
repo_mapping = {"@whitebox" : "@whitebox_api_repo"}
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -70,7 +70,7 @@ http_archive(
|
|||||||
new_git_repository(
|
new_git_repository(
|
||||||
name = "odk_repo",
|
name = "odk_repo",
|
||||||
build_file = "@whitebox_api_repo//external:odk.BUILD",
|
build_file = "@whitebox_api_repo//external:odk.BUILD",
|
||||||
commit = "bee799748752fac84d9c3ecd549aa54f72c88d02",
|
commit = "565237f8e6900e467eb236040374428387e90bd0",
|
||||||
remote = "https://widevine-partner.googlesource.com/oemcrypto_core_message.git",
|
remote = "https://widevine-partner.googlesource.com/oemcrypto_core_message.git",
|
||||||
repo_mapping = {"@whitebox" : "@whitebox_api_repo"}
|
repo_mapping = {"@whitebox" : "@whitebox_api_repo"}
|
||||||
)
|
)
|
||||||
|
|||||||
1
whitebox/.gitignore
vendored
Normal file
1
whitebox/.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
bazel-*
|
||||||
@@ -65,7 +65,7 @@ http_archive(
|
|||||||
new_git_repository(
|
new_git_repository(
|
||||||
name = "odk_repo",
|
name = "odk_repo",
|
||||||
build_file = "//external:odk.BUILD",
|
build_file = "//external:odk.BUILD",
|
||||||
commit = "bee799748752fac84d9c3ecd549aa54f72c88d02",
|
commit = "565237f8e6900e467eb236040374428387e90bd0",
|
||||||
remote = "https://widevine-partner.googlesource.com/oemcrypto_core_message.git",
|
remote = "https://widevine-partner.googlesource.com/oemcrypto_core_message.git",
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -32,6 +32,14 @@ cc_library(
|
|||||||
],
|
],
|
||||||
)
|
)
|
||||||
|
|
||||||
|
cc_library(
|
||||||
|
name = "test_key_types",
|
||||||
|
testonly = True,
|
||||||
|
hdrs = [
|
||||||
|
"test_key_types.h",
|
||||||
|
],
|
||||||
|
)
|
||||||
|
|
||||||
cc_library(
|
cc_library(
|
||||||
name = "test_public_key",
|
name = "test_public_key",
|
||||||
testonly = True,
|
testonly = True,
|
||||||
@@ -60,6 +68,7 @@ cc_library(
|
|||||||
"golden_data.h",
|
"golden_data.h",
|
||||||
],
|
],
|
||||||
deps = [
|
deps = [
|
||||||
|
":test_key_types",
|
||||||
"//chromium_deps/cdm/protos:license_protocol_proto",
|
"//chromium_deps/cdm/protos:license_protocol_proto",
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
@@ -71,6 +80,7 @@ cc_library(
|
|||||||
hdrs = ["test_license_builder.h"],
|
hdrs = ["test_license_builder.h"],
|
||||||
visibility = ["//visibility:public"],
|
visibility = ["//visibility:public"],
|
||||||
deps = [
|
deps = [
|
||||||
|
":test_key_types",
|
||||||
"//chromium_deps/cdm/keys:dev_certs",
|
"//chromium_deps/cdm/keys:dev_certs",
|
||||||
"//chromium_deps/cdm/protos:license_protocol_proto",
|
"//chromium_deps/cdm/protos:license_protocol_proto",
|
||||||
"//crypto_utils:aes_cbc_encryptor",
|
"//crypto_utils:aes_cbc_encryptor",
|
||||||
@@ -126,6 +136,7 @@ cc_library(
|
|||||||
"license_whitebox_get_secret_string_test.cc",
|
"license_whitebox_get_secret_string_test.cc",
|
||||||
"license_whitebox_masked_decrypt_test.cc",
|
"license_whitebox_masked_decrypt_test.cc",
|
||||||
"license_whitebox_process_license_response_core_message_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_process_license_response_test.cc",
|
||||||
"license_whitebox_sign_license_request_test.cc",
|
"license_whitebox_sign_license_request_test.cc",
|
||||||
"license_whitebox_sign_renewal_request_test.cc",
|
"license_whitebox_sign_renewal_request_test.cc",
|
||||||
@@ -137,7 +148,6 @@ cc_library(
|
|||||||
],
|
],
|
||||||
visibility = ["//visibility:public"],
|
visibility = ["//visibility:public"],
|
||||||
deps = [
|
deps = [
|
||||||
":aead_test_data",
|
|
||||||
":golden_data",
|
":golden_data",
|
||||||
":license_whitebox",
|
":license_whitebox",
|
||||||
":test_license_builder",
|
":test_license_builder",
|
||||||
@@ -165,8 +175,8 @@ cc_library(
|
|||||||
],
|
],
|
||||||
visibility = ["//visibility:public"],
|
visibility = ["//visibility:public"],
|
||||||
deps = [
|
deps = [
|
||||||
":aead_test_data",
|
|
||||||
":license_whitebox",
|
":license_whitebox",
|
||||||
|
":test_key_types",
|
||||||
":test_license_builder",
|
":test_license_builder",
|
||||||
":test_public_key",
|
":test_public_key",
|
||||||
"//benchmarking:data_source",
|
"//benchmarking:data_source",
|
||||||
|
|||||||
@@ -83,10 +83,9 @@ GoldenData::GoldenData() {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
void GoldenData::MakeKeyIdDifferent(std::vector<uint8_t>* key_id) const {
|
KeyId GoldenData::GetFreeId() {
|
||||||
// All our internal key ids start with 0xFF, so pushing something that is not
|
// All our internal key ids start with 0xFF, so starting with 0xAB should
|
||||||
// 0xFF to the front will ensure that they don't collide.
|
// avoid conflicts.
|
||||||
key_id->insert(key_id->begin(), 0xAB);
|
return {0xAB, 0, 0, next_id_++};
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace widevine
|
} // namespace widevine
|
||||||
|
|||||||
@@ -3,10 +3,11 @@
|
|||||||
#ifndef WHITEBOX_API_GOLDEN_DATA_H_
|
#ifndef WHITEBOX_API_GOLDEN_DATA_H_
|
||||||
#define WHITEBOX_API_GOLDEN_DATA_H_
|
#define WHITEBOX_API_GOLDEN_DATA_H_
|
||||||
|
|
||||||
#include <stdint.h>
|
#include <cstdint>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
|
#include "api/test_key_types.h"
|
||||||
#include "cdm/protos/license_protocol.pb.h"
|
#include "cdm/protos/license_protocol.pb.h"
|
||||||
|
|
||||||
namespace widevine {
|
namespace widevine {
|
||||||
@@ -16,13 +17,13 @@ class GoldenData {
|
|||||||
struct Content {
|
struct Content {
|
||||||
std::vector<uint8_t> plaintext;
|
std::vector<uint8_t> plaintext;
|
||||||
std::vector<uint8_t> ciphertext;
|
std::vector<uint8_t> ciphertext;
|
||||||
std::vector<uint8_t> key;
|
AesKey key;
|
||||||
std::vector<uint8_t> iv;
|
AesIv iv;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct Key {
|
struct Key {
|
||||||
video_widevine::License_KeyContainer_SecurityLevel level;
|
video_widevine::License_KeyContainer_SecurityLevel level;
|
||||||
std::vector<uint8_t> id;
|
KeyId id;
|
||||||
const Content* content;
|
const Content* content;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -38,11 +39,7 @@ class GoldenData {
|
|||||||
const Key& CTRDecodeKey() const { return ctr_decode_key_; }
|
const Key& CTRDecodeKey() const { return ctr_decode_key_; }
|
||||||
const Key& CTRHardwareKey() const { return ctr_hardware_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
|
KeyId GetFreeId();
|
||||||
// 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<uint8_t>* key_id) const;
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Content cbc_content_;
|
Content cbc_content_;
|
||||||
@@ -54,6 +51,8 @@ class GoldenData {
|
|||||||
Key ctr_crypto_key_;
|
Key ctr_crypto_key_;
|
||||||
Key ctr_decode_key_;
|
Key ctr_decode_key_;
|
||||||
Key ctr_hardware_key_;
|
Key ctr_hardware_key_;
|
||||||
|
|
||||||
|
uint8_t next_id_ = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace widevine
|
} // namespace widevine
|
||||||
|
|||||||
@@ -2,9 +2,8 @@
|
|||||||
|
|
||||||
#include "api/license_whitebox_benchmark.h"
|
#include "api/license_whitebox_benchmark.h"
|
||||||
|
|
||||||
#include <stddef.h>
|
#include <cstddef>
|
||||||
#include <stdint.h>
|
#include <cstdint>
|
||||||
|
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
@@ -21,9 +20,9 @@ constexpr size_t kBlockSize = 16; // This must align with the AES block size.
|
|||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
void LicenseWhiteboxBenchmark::SetUp() {
|
void LicenseWhiteboxBenchmark::SetUp() {
|
||||||
key_id_ = data_source_.Get(8); // The id size is not meaningful.
|
key_id_ = data_source_.Get<4>(); // We use size=4 for all our test key ids.
|
||||||
key_ = data_source_.Get(kBlockSize);
|
key_ = data_source_.Get<kBlockSize>();
|
||||||
iv_ = data_source_.Get(kBlockSize);
|
iv_ = data_source_.Get<kBlockSize>();
|
||||||
|
|
||||||
const auto public_key_data = GetLicensePublicKey();
|
const auto public_key_data = GetLicensePublicKey();
|
||||||
public_key_.reset(widevine::RsaPublicKey::Create(
|
public_key_.reset(widevine::RsaPublicKey::Create(
|
||||||
@@ -33,13 +32,12 @@ void LicenseWhiteboxBenchmark::SetUp() {
|
|||||||
|
|
||||||
License LicenseWhiteboxBenchmark::CreateLicense() const {
|
License LicenseWhiteboxBenchmark::CreateLicense() const {
|
||||||
widevine::TestLicenseBuilder license_builder;
|
widevine::TestLicenseBuilder license_builder;
|
||||||
license_builder.AddSigningKey(TestLicenseBuilder::DefaultSigningKey(),
|
license_builder.AddSigningKey(TestLicenseBuilder::DefaultSigningKey());
|
||||||
TestLicenseBuilder::NoPadding());
|
|
||||||
// Use secure crypto as it will work with both Decrypt() and
|
// Use secure crypto as it will work with both Decrypt() and
|
||||||
// MaskedDecrypt().
|
// MaskedDecrypt().
|
||||||
license_builder.AddContentKey(
|
license_builder.AddContentKey(
|
||||||
video_widevine::License_KeyContainer_SecurityLevel_SW_SECURE_CRYPTO,
|
video_widevine::License_KeyContainer_SecurityLevel_SW_SECURE_CRYPTO,
|
||||||
key_id_, key_, TestLicenseBuilder::NoPadding());
|
key_id_, key_);
|
||||||
|
|
||||||
widevine::License license;
|
widevine::License license;
|
||||||
license_builder.Build(*public_key_, &license);
|
license_builder.Build(*public_key_, &license);
|
||||||
|
|||||||
@@ -9,6 +9,7 @@
|
|||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
#include "api/license_whitebox.h"
|
#include "api/license_whitebox.h"
|
||||||
|
#include "api/test_key_types.h"
|
||||||
#include "api/test_license_builder.h"
|
#include "api/test_license_builder.h"
|
||||||
#include "benchmarking/data_source.h"
|
#include "benchmarking/data_source.h"
|
||||||
#include "crypto_utils/rsa_key.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 RsaPublicKey* PublicKey() const { return public_key_.get(); }
|
||||||
|
|
||||||
const std::vector<uint8_t>& ContentKeyId() const { return key_id_; }
|
const KeyId& ContentKeyId() const { return key_id_; }
|
||||||
|
|
||||||
const std::vector<uint8_t>& ContentKey() const { return key_; }
|
const AesKey& ContentKey() const { return key_; }
|
||||||
|
|
||||||
const std::vector<uint8_t>& ContentIV() const { return iv_; }
|
const AesIv& ContentIV() const { return iv_; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
DataSource data_source_;
|
DataSource data_source_;
|
||||||
std::unique_ptr<RsaPublicKey> public_key_;
|
std::unique_ptr<RsaPublicKey> public_key_;
|
||||||
|
|
||||||
std::vector<uint8_t> key_id_;
|
KeyId key_id_;
|
||||||
std::vector<uint8_t> key_;
|
AesKey key_;
|
||||||
std::vector<uint8_t> iv_;
|
AesIv iv_;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace widevine
|
} // namespace widevine
|
||||||
|
|||||||
@@ -56,21 +56,18 @@ class LicenseWhiteboxChromeOSTest
|
|||||||
// Only use CBC keys so that we can always use the CBC content.
|
// Only use CBC keys so that we can always use the CBC content.
|
||||||
builder.AddContentKey(golden_data_.CBCCryptoKey().level,
|
builder.AddContentKey(golden_data_.CBCCryptoKey().level,
|
||||||
golden_data_.CBCCryptoKey().id,
|
golden_data_.CBCCryptoKey().id,
|
||||||
golden_data_.CBCCryptoKey().content->key,
|
golden_data_.CBCCryptoKey().content->key);
|
||||||
TestLicenseBuilder::NoPadding());
|
|
||||||
|
|
||||||
builder.AddContentKey(golden_data_.CBCDecodeKey().level,
|
builder.AddContentKey(golden_data_.CBCDecodeKey().level,
|
||||||
golden_data_.CBCDecodeKey().id,
|
golden_data_.CBCDecodeKey().id,
|
||||||
golden_data_.CBCDecodeKey().content->key,
|
golden_data_.CBCDecodeKey().content->key);
|
||||||
TestLicenseBuilder::NoPadding());
|
|
||||||
|
|
||||||
builder.AddContentKey(golden_data_.CBCHardwareKey().level,
|
builder.AddContentKey(golden_data_.CBCHardwareKey().level,
|
||||||
golden_data_.CBCHardwareKey().id,
|
golden_data_.CBCHardwareKey().id,
|
||||||
golden_data_.CBCHardwareKey().content->key,
|
golden_data_.CBCHardwareKey().content->key);
|
||||||
TestLicenseBuilder::NoPadding());
|
|
||||||
|
|
||||||
builder.SetRemoteAttestation(remote_attestation_);
|
builder.GetSettings().remote_attestation = remote_attestation_;
|
||||||
builder.SetVerificationStatus(verification_status_);
|
builder.GetSettings().verification_status = verification_status_;
|
||||||
|
|
||||||
License license;
|
License license;
|
||||||
builder.Build(*public_key_, &license);
|
builder.Build(*public_key_, &license);
|
||||||
|
|||||||
@@ -25,28 +25,29 @@ class LicenseWhiteboxDecryptTest : public LicenseWhiteboxTestBase {
|
|||||||
golden_data_.CTRContent().ciphertext.size());
|
golden_data_.CTRContent().ciphertext.size());
|
||||||
plaintext_.resize(plaintext_size_);
|
plaintext_.resize(plaintext_size_);
|
||||||
|
|
||||||
golden_data_.MakeKeyIdDifferent(&non_content_key_id_);
|
non_content_key_id_ = golden_data_.GetFreeId();
|
||||||
golden_data_.MakeKeyIdDifferent(&missing_key_id_);
|
missing_key_id_ = golden_data_.GetFreeId();
|
||||||
}
|
}
|
||||||
|
|
||||||
void LoadLicense(const std::vector<uint8_t>& padding) {
|
void LoadLicense(TestLicenseBuilder::Padding padding) {
|
||||||
TestLicenseBuilder builder;
|
TestLicenseBuilder builder;
|
||||||
|
builder.GetSettings().padding = padding;
|
||||||
|
|
||||||
builder.AddContentKey(golden_data_.CBCCryptoKey().level,
|
builder.AddContentKey(golden_data_.CBCCryptoKey().level,
|
||||||
golden_data_.CBCCryptoKey().id,
|
golden_data_.CBCCryptoKey().id,
|
||||||
golden_data_.CBCCryptoKey().content->key, padding);
|
golden_data_.CBCCryptoKey().content->key);
|
||||||
|
|
||||||
builder.AddContentKey(golden_data_.CTRCryptoKey().level,
|
builder.AddContentKey(golden_data_.CTRCryptoKey().level,
|
||||||
golden_data_.CTRCryptoKey().id,
|
golden_data_.CTRCryptoKey().id,
|
||||||
golden_data_.CTRCryptoKey().content->key, padding);
|
golden_data_.CTRCryptoKey().content->key);
|
||||||
|
|
||||||
builder.AddContentKey(golden_data_.CBCDecodeKey().level,
|
builder.AddContentKey(golden_data_.CBCDecodeKey().level,
|
||||||
golden_data_.CBCDecodeKey().id,
|
golden_data_.CBCDecodeKey().id,
|
||||||
golden_data_.CBCDecodeKey().content->key, padding);
|
golden_data_.CBCDecodeKey().content->key);
|
||||||
|
|
||||||
builder.AddContentKey(golden_data_.CBCHardwareKey().level,
|
builder.AddContentKey(golden_data_.CBCHardwareKey().level,
|
||||||
golden_data_.CBCHardwareKey().id,
|
golden_data_.CBCHardwareKey().id,
|
||||||
golden_data_.CBCHardwareKey().content->key, padding);
|
golden_data_.CBCHardwareKey().content->key);
|
||||||
|
|
||||||
builder.AddOperatorSessionKey(non_content_key_id_);
|
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
|
// 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.
|
// non-content key and one that will never be in the license.
|
||||||
std::vector<uint8_t> non_content_key_id_ = {0, 0, 0};
|
KeyId non_content_key_id_;
|
||||||
std::vector<uint8_t> missing_key_id_ = {1, 0, 0};
|
KeyId missing_key_id_;
|
||||||
|
|
||||||
// This is the buffer used to store the output of each decrypt call.
|
// This is the buffer used to store the output of each decrypt call.
|
||||||
size_t plaintext_size_;
|
size_t plaintext_size_;
|
||||||
@@ -74,7 +75,7 @@ class LicenseWhiteboxDecryptTest : public LicenseWhiteboxTestBase {
|
|||||||
};
|
};
|
||||||
|
|
||||||
TEST_F(LicenseWhiteboxDecryptTest, CryptoKeyWithCbcDataInCbcMode) {
|
TEST_F(LicenseWhiteboxDecryptTest, CryptoKeyWithCbcDataInCbcMode) {
|
||||||
LoadLicense(TestLicenseBuilder::NoPadding());
|
LoadLicense(TestLicenseBuilder::Padding::kNone);
|
||||||
|
|
||||||
ASSERT_EQ(
|
ASSERT_EQ(
|
||||||
WB_License_Decrypt(whitebox_, WB_CIPHER_MODE_CBC,
|
WB_License_Decrypt(whitebox_, WB_CIPHER_MODE_CBC,
|
||||||
@@ -91,7 +92,7 @@ TEST_F(LicenseWhiteboxDecryptTest, CryptoKeyWithCbcDataInCbcMode) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(LicenseWhiteboxDecryptTest, CryptoKeyWithCtrDataInCtrMode) {
|
TEST_F(LicenseWhiteboxDecryptTest, CryptoKeyWithCtrDataInCtrMode) {
|
||||||
LoadLicense(TestLicenseBuilder::NoPadding());
|
LoadLicense(TestLicenseBuilder::Padding::kNone);
|
||||||
|
|
||||||
ASSERT_EQ(
|
ASSERT_EQ(
|
||||||
WB_License_Decrypt(whitebox_, WB_CIPHER_MODE_CTR,
|
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
|
// We try to decrypt CBC encrypted data in CTR mode. All operations should be
|
||||||
// successful, but the resulting plaintext should not match.
|
// successful, but the resulting plaintext should not match.
|
||||||
TEST_F(LicenseWhiteboxDecryptTest, CryptoKeyWithCbcDataInCtrMode) {
|
TEST_F(LicenseWhiteboxDecryptTest, CryptoKeyWithCbcDataInCtrMode) {
|
||||||
LoadLicense(TestLicenseBuilder::NoPadding());
|
LoadLicense(TestLicenseBuilder::Padding::kNone);
|
||||||
|
|
||||||
ASSERT_EQ(
|
ASSERT_EQ(
|
||||||
WB_License_Decrypt(whitebox_, WB_CIPHER_MODE_CTR,
|
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
|
// We try to decrypt CTR encrypted data in CBC mode. All operations should be
|
||||||
// successful, but the resulting plaintext should not match.
|
// successful, but the resulting plaintext should not match.
|
||||||
TEST_F(LicenseWhiteboxDecryptTest, CryptoKeyWithCtrDataInCbcMode) {
|
TEST_F(LicenseWhiteboxDecryptTest, CryptoKeyWithCtrDataInCbcMode) {
|
||||||
LoadLicense(TestLicenseBuilder::NoPadding());
|
LoadLicense(TestLicenseBuilder::Padding::kNone);
|
||||||
|
|
||||||
ASSERT_EQ(
|
ASSERT_EQ(
|
||||||
WB_License_Decrypt(whitebox_, WB_CIPHER_MODE_CBC,
|
WB_License_Decrypt(whitebox_, WB_CIPHER_MODE_CBC,
|
||||||
@@ -146,7 +147,7 @@ TEST_F(LicenseWhiteboxDecryptTest, CryptoKeyWithCtrDataInCbcMode) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(LicenseWhiteboxDecryptTest, CryptoKeyWithCbcDataAndPKCS8Padding) {
|
TEST_F(LicenseWhiteboxDecryptTest, CryptoKeyWithCbcDataAndPKCS8Padding) {
|
||||||
LoadLicense(TestLicenseBuilder::PKSC8Padding());
|
LoadLicense(TestLicenseBuilder::Padding::kPKSC8);
|
||||||
|
|
||||||
ASSERT_EQ(
|
ASSERT_EQ(
|
||||||
WB_License_Decrypt(whitebox_, WB_CIPHER_MODE_CBC,
|
WB_License_Decrypt(whitebox_, WB_CIPHER_MODE_CBC,
|
||||||
@@ -163,7 +164,7 @@ TEST_F(LicenseWhiteboxDecryptTest, CryptoKeyWithCbcDataAndPKCS8Padding) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(LicenseWhiteboxDecryptTest, CryptoKeyWithCtrDataAndPKCS8Padding) {
|
TEST_F(LicenseWhiteboxDecryptTest, CryptoKeyWithCtrDataAndPKCS8Padding) {
|
||||||
LoadLicense(TestLicenseBuilder::PKSC8Padding());
|
LoadLicense(TestLicenseBuilder::Padding::kPKSC8);
|
||||||
|
|
||||||
ASSERT_EQ(
|
ASSERT_EQ(
|
||||||
WB_License_Decrypt(whitebox_, WB_CIPHER_MODE_CTR,
|
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
|
// Try decrypting two different sets of content to make sure that two
|
||||||
// different keys can be used at the same time.
|
// different keys can be used at the same time.
|
||||||
TEST_F(LicenseWhiteboxDecryptTest, SuccessWithMultipleKeys) {
|
TEST_F(LicenseWhiteboxDecryptTest, SuccessWithMultipleKeys) {
|
||||||
LoadLicense(TestLicenseBuilder::NoPadding());
|
LoadLicense(TestLicenseBuilder::Padding::kNone);
|
||||||
|
|
||||||
ASSERT_EQ(
|
ASSERT_EQ(
|
||||||
WB_License_Decrypt(whitebox_, WB_CIPHER_MODE_CBC,
|
WB_License_Decrypt(whitebox_, WB_CIPHER_MODE_CBC,
|
||||||
@@ -218,7 +219,7 @@ TEST_F(LicenseWhiteboxDecryptTest, SuccessWithMultipleKeys) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(LicenseWhiteboxDecryptTest, InvalidParameterForNullWhitebox) {
|
TEST_F(LicenseWhiteboxDecryptTest, InvalidParameterForNullWhitebox) {
|
||||||
LoadLicense(TestLicenseBuilder::NoPadding());
|
LoadLicense(TestLicenseBuilder::Padding::kNone);
|
||||||
|
|
||||||
ASSERT_EQ(
|
ASSERT_EQ(
|
||||||
WB_License_Decrypt(nullptr, WB_CIPHER_MODE_CBC,
|
WB_License_Decrypt(nullptr, WB_CIPHER_MODE_CBC,
|
||||||
@@ -233,7 +234,7 @@ TEST_F(LicenseWhiteboxDecryptTest, InvalidParameterForNullWhitebox) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(LicenseWhiteboxDecryptTest, InvalidParameterForInvalidCipherMode) {
|
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
|
// 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
|
// 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) {
|
TEST_F(LicenseWhiteboxDecryptTest, InvalidParameterForNullKeyId) {
|
||||||
LoadLicense(TestLicenseBuilder::NoPadding());
|
LoadLicense(TestLicenseBuilder::Padding::kNone);
|
||||||
|
|
||||||
ASSERT_EQ(
|
ASSERT_EQ(
|
||||||
WB_License_Decrypt(whitebox_, WB_CIPHER_MODE_CBC, nullptr,
|
WB_License_Decrypt(whitebox_, WB_CIPHER_MODE_CBC, nullptr,
|
||||||
@@ -266,7 +267,7 @@ TEST_F(LicenseWhiteboxDecryptTest, InvalidParameterForNullKeyId) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(LicenseWhiteboxDecryptTest, InvalidParameterForZeroKeyIdSize) {
|
TEST_F(LicenseWhiteboxDecryptTest, InvalidParameterForZeroKeyIdSize) {
|
||||||
LoadLicense(TestLicenseBuilder::NoPadding());
|
LoadLicense(TestLicenseBuilder::Padding::kNone);
|
||||||
|
|
||||||
ASSERT_EQ(
|
ASSERT_EQ(
|
||||||
WB_License_Decrypt(whitebox_, WB_CIPHER_MODE_CBC,
|
WB_License_Decrypt(whitebox_, WB_CIPHER_MODE_CBC,
|
||||||
@@ -280,7 +281,7 @@ TEST_F(LicenseWhiteboxDecryptTest, InvalidParameterForZeroKeyIdSize) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(LicenseWhiteboxDecryptTest, InvalidParameterForNullInputData) {
|
TEST_F(LicenseWhiteboxDecryptTest, InvalidParameterForNullInputData) {
|
||||||
LoadLicense(TestLicenseBuilder::NoPadding());
|
LoadLicense(TestLicenseBuilder::Padding::kNone);
|
||||||
|
|
||||||
ASSERT_EQ(
|
ASSERT_EQ(
|
||||||
WB_License_Decrypt(whitebox_, WB_CIPHER_MODE_CBC,
|
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
|
// AES CBC requires that the input be block aligned (multiple of 16). CTR does
|
||||||
// not care.
|
// not care.
|
||||||
TEST_F(LicenseWhiteboxDecryptTest, InvalidParameterForInvalidCBCInputDataSize) {
|
TEST_F(LicenseWhiteboxDecryptTest, InvalidParameterForInvalidCBCInputDataSize) {
|
||||||
LoadLicense(TestLicenseBuilder::NoPadding());
|
LoadLicense(TestLicenseBuilder::Padding::kNone);
|
||||||
|
|
||||||
ASSERT_EQ(
|
ASSERT_EQ(
|
||||||
WB_License_Decrypt(whitebox_, WB_CIPHER_MODE_CBC,
|
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.
|
// The white-box (using any cipher mode) should reject input with size zero.
|
||||||
TEST_F(LicenseWhiteboxDecryptTest, InvalidParameterForZeroInputDataSize) {
|
TEST_F(LicenseWhiteboxDecryptTest, InvalidParameterForZeroInputDataSize) {
|
||||||
LoadLicense(TestLicenseBuilder::NoPadding());
|
LoadLicense(TestLicenseBuilder::Padding::kNone);
|
||||||
|
|
||||||
ASSERT_EQ(
|
ASSERT_EQ(
|
||||||
WB_License_Decrypt(whitebox_, WB_CIPHER_MODE_CBC,
|
WB_License_Decrypt(whitebox_, WB_CIPHER_MODE_CBC,
|
||||||
@@ -325,7 +326,7 @@ TEST_F(LicenseWhiteboxDecryptTest, InvalidParameterForZeroInputDataSize) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(LicenseWhiteboxDecryptTest, InvalidParameterForNullIV) {
|
TEST_F(LicenseWhiteboxDecryptTest, InvalidParameterForNullIV) {
|
||||||
LoadLicense(TestLicenseBuilder::NoPadding());
|
LoadLicense(TestLicenseBuilder::Padding::kNone);
|
||||||
|
|
||||||
ASSERT_EQ(
|
ASSERT_EQ(
|
||||||
WB_License_Decrypt(
|
WB_License_Decrypt(
|
||||||
@@ -340,7 +341,7 @@ TEST_F(LicenseWhiteboxDecryptTest, InvalidParameterForNullIV) {
|
|||||||
|
|
||||||
// IV size should be 16. Any number other than 16 should fail.
|
// IV size should be 16. Any number other than 16 should fail.
|
||||||
TEST_F(LicenseWhiteboxDecryptTest, InvalidParameterForInvalidIVSize) {
|
TEST_F(LicenseWhiteboxDecryptTest, InvalidParameterForInvalidIVSize) {
|
||||||
LoadLicense(TestLicenseBuilder::NoPadding());
|
LoadLicense(TestLicenseBuilder::Padding::kNone);
|
||||||
|
|
||||||
ASSERT_EQ(
|
ASSERT_EQ(
|
||||||
WB_License_Decrypt(whitebox_, WB_CIPHER_MODE_CBC,
|
WB_License_Decrypt(whitebox_, WB_CIPHER_MODE_CBC,
|
||||||
@@ -354,7 +355,7 @@ TEST_F(LicenseWhiteboxDecryptTest, InvalidParameterForInvalidIVSize) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(LicenseWhiteboxDecryptTest, InvalidParameterForNullOutput) {
|
TEST_F(LicenseWhiteboxDecryptTest, InvalidParameterForNullOutput) {
|
||||||
LoadLicense(TestLicenseBuilder::NoPadding());
|
LoadLicense(TestLicenseBuilder::Padding::kNone);
|
||||||
|
|
||||||
ASSERT_EQ(
|
ASSERT_EQ(
|
||||||
WB_License_Decrypt(whitebox_, WB_CIPHER_MODE_CBC,
|
WB_License_Decrypt(whitebox_, WB_CIPHER_MODE_CBC,
|
||||||
@@ -369,7 +370,7 @@ TEST_F(LicenseWhiteboxDecryptTest, InvalidParameterForNullOutput) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(LicenseWhiteboxDecryptTest, InvalidParameterForNullOutputSize) {
|
TEST_F(LicenseWhiteboxDecryptTest, InvalidParameterForNullOutputSize) {
|
||||||
LoadLicense(TestLicenseBuilder::NoPadding());
|
LoadLicense(TestLicenseBuilder::Padding::kNone);
|
||||||
|
|
||||||
ASSERT_EQ(
|
ASSERT_EQ(
|
||||||
WB_License_Decrypt(whitebox_, WB_CIPHER_MODE_CBC,
|
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"
|
// 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.
|
// and "dropped content key", as those keys were in the license but ignored.
|
||||||
TEST_F(LicenseWhiteboxDecryptTest, KeyUnavailableForMissingKeyId) {
|
TEST_F(LicenseWhiteboxDecryptTest, KeyUnavailableForMissingKeyId) {
|
||||||
LoadLicense(TestLicenseBuilder::NoPadding());
|
LoadLicense(TestLicenseBuilder::Padding::kNone);
|
||||||
|
|
||||||
ASSERT_EQ(
|
ASSERT_EQ(
|
||||||
WB_License_Decrypt(whitebox_, WB_CIPHER_MODE_CBC, missing_key_id_.data(),
|
WB_License_Decrypt(whitebox_, WB_CIPHER_MODE_CBC, missing_key_id_.data(),
|
||||||
@@ -401,7 +402,7 @@ TEST_F(LicenseWhiteboxDecryptTest, KeyUnavailableForMissingKeyId) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(LicenseWhiteboxDecryptTest, KeyUnavailableForNonContentKey) {
|
TEST_F(LicenseWhiteboxDecryptTest, KeyUnavailableForNonContentKey) {
|
||||||
LoadLicense(TestLicenseBuilder::NoPadding());
|
LoadLicense(TestLicenseBuilder::Padding::kNone);
|
||||||
|
|
||||||
ASSERT_EQ(
|
ASSERT_EQ(
|
||||||
WB_License_Decrypt(whitebox_, WB_CIPHER_MODE_CBC,
|
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.
|
// to this rule is on ChromeOS with a special license.
|
||||||
TEST_F(LicenseWhiteboxDecryptTest,
|
TEST_F(LicenseWhiteboxDecryptTest,
|
||||||
InsufficientSecurityLevelForHardwareContentKey) {
|
InsufficientSecurityLevelForHardwareContentKey) {
|
||||||
LoadLicense(TestLicenseBuilder::NoPadding());
|
LoadLicense(TestLicenseBuilder::Padding::kNone);
|
||||||
|
|
||||||
ASSERT_EQ(WB_License_Decrypt(
|
ASSERT_EQ(WB_License_Decrypt(
|
||||||
whitebox_, WB_CIPHER_MODE_CBC,
|
whitebox_, WB_CIPHER_MODE_CBC,
|
||||||
@@ -433,7 +434,7 @@ TEST_F(LicenseWhiteboxDecryptTest,
|
|||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(LicenseWhiteboxDecryptTest, InsufficientSecurityLevelForDecodeKey) {
|
TEST_F(LicenseWhiteboxDecryptTest, InsufficientSecurityLevelForDecodeKey) {
|
||||||
LoadLicense(TestLicenseBuilder::NoPadding());
|
LoadLicense(TestLicenseBuilder::Padding::kNone);
|
||||||
|
|
||||||
// Use the software decode key as they are limited to
|
// Use the software decode key as they are limited to
|
||||||
// WB_License_Decrypt().
|
// WB_License_Decrypt().
|
||||||
@@ -450,7 +451,7 @@ TEST_F(LicenseWhiteboxDecryptTest, InsufficientSecurityLevelForDecodeKey) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(LicenseWhiteboxDecryptTest, BufferTooSmall) {
|
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
|
// Our ciphertext will be large enough that we should not need to worry about
|
||||||
// using a constant here.
|
// using a constant here.
|
||||||
|
|||||||
@@ -23,8 +23,8 @@ class LicenseWhiteboxGetSecretStringTest : public LicenseWhiteboxTestBase {
|
|||||||
secret_string_size_ = 256;
|
secret_string_size_ = 256;
|
||||||
secret_string_.resize(secret_string_size_);
|
secret_string_.resize(secret_string_size_);
|
||||||
|
|
||||||
golden_data_.MakeKeyIdDifferent(&non_content_key_id_);
|
non_content_key_id_ = golden_data_.GetFreeId();
|
||||||
golden_data_.MakeKeyIdDifferent(&missing_key_id_);
|
missing_key_id_ = golden_data_.GetFreeId();
|
||||||
}
|
}
|
||||||
|
|
||||||
void LoadLicense() {
|
void LoadLicense() {
|
||||||
@@ -32,18 +32,15 @@ class LicenseWhiteboxGetSecretStringTest : public LicenseWhiteboxTestBase {
|
|||||||
|
|
||||||
builder.AddContentKey(golden_data_.CBCCryptoKey().level,
|
builder.AddContentKey(golden_data_.CBCCryptoKey().level,
|
||||||
golden_data_.CBCCryptoKey().id,
|
golden_data_.CBCCryptoKey().id,
|
||||||
golden_data_.CBCCryptoKey().content->key,
|
golden_data_.CBCCryptoKey().content->key);
|
||||||
TestLicenseBuilder::NoPadding());
|
|
||||||
|
|
||||||
builder.AddContentKey(golden_data_.CBCDecodeKey().level,
|
builder.AddContentKey(golden_data_.CBCDecodeKey().level,
|
||||||
golden_data_.CBCDecodeKey().id,
|
golden_data_.CBCDecodeKey().id,
|
||||||
golden_data_.CBCDecodeKey().content->key,
|
golden_data_.CBCDecodeKey().content->key);
|
||||||
TestLicenseBuilder::NoPadding());
|
|
||||||
|
|
||||||
builder.AddContentKey(golden_data_.CBCHardwareKey().level,
|
builder.AddContentKey(golden_data_.CBCHardwareKey().level,
|
||||||
golden_data_.CBCHardwareKey().id,
|
golden_data_.CBCHardwareKey().id,
|
||||||
golden_data_.CBCHardwareKey().content->key,
|
golden_data_.CBCHardwareKey().content->key);
|
||||||
TestLicenseBuilder::NoPadding());
|
|
||||||
|
|
||||||
builder.AddOperatorSessionKey(non_content_key_id_);
|
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
|
// 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.
|
// non-content key and one that will never be in the license.
|
||||||
std::vector<uint8_t> non_content_key_id_ = {0, 0, 0};
|
KeyId non_content_key_id_;
|
||||||
std::vector<uint8_t> missing_key_id_ = {1, 0, 0};
|
KeyId missing_key_id_;
|
||||||
|
|
||||||
size_t secret_string_size_;
|
size_t secret_string_size_;
|
||||||
std::vector<uint8_t> secret_string_;
|
std::vector<uint8_t> secret_string_;
|
||||||
|
|||||||
@@ -8,11 +8,6 @@
|
|||||||
#include "testing/gtest/include/gtest/gtest.h"
|
#include "testing/gtest/include/gtest/gtest.h"
|
||||||
|
|
||||||
namespace widevine {
|
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 {
|
namespace {
|
||||||
|
|
||||||
|
|||||||
@@ -32,37 +32,33 @@ class LicenseWhiteboxMaskedDecryptTest : public LicenseWhiteboxTestBase {
|
|||||||
secret_string_size_ = masked_text_size_;
|
secret_string_size_ = masked_text_size_;
|
||||||
secret_string_.resize(secret_string_size_);
|
secret_string_.resize(secret_string_size_);
|
||||||
|
|
||||||
golden_data_.MakeKeyIdDifferent(&non_content_key_id_);
|
non_content_key_id_ = golden_data_.GetFreeId();
|
||||||
golden_data_.MakeKeyIdDifferent(&missing_key_id_);
|
missing_key_id_ = golden_data_.GetFreeId();
|
||||||
}
|
}
|
||||||
|
|
||||||
void LoadLicense(const std::vector<uint8_t>& padding) {
|
void LoadLicense(TestLicenseBuilder::Padding padding) {
|
||||||
TestLicenseBuilder builder;
|
TestLicenseBuilder builder;
|
||||||
|
builder.GetSettings().padding = padding;
|
||||||
|
|
||||||
builder.AddContentKey(golden_data_.CBCCryptoKey().level,
|
builder.AddContentKey(golden_data_.CBCCryptoKey().level,
|
||||||
golden_data_.CBCCryptoKey().id,
|
golden_data_.CBCCryptoKey().id,
|
||||||
golden_data_.CBCCryptoKey().content->key,
|
golden_data_.CBCCryptoKey().content->key);
|
||||||
TestLicenseBuilder::NoPadding());
|
|
||||||
|
|
||||||
builder.AddContentKey(golden_data_.CTRCryptoKey().level,
|
builder.AddContentKey(golden_data_.CTRCryptoKey().level,
|
||||||
golden_data_.CTRCryptoKey().id,
|
golden_data_.CTRCryptoKey().id,
|
||||||
golden_data_.CTRCryptoKey().content->key,
|
golden_data_.CTRCryptoKey().content->key);
|
||||||
TestLicenseBuilder::NoPadding());
|
|
||||||
|
|
||||||
builder.AddContentKey(golden_data_.CBCDecodeKey().level,
|
builder.AddContentKey(golden_data_.CBCDecodeKey().level,
|
||||||
golden_data_.CBCDecodeKey().id,
|
golden_data_.CBCDecodeKey().id,
|
||||||
golden_data_.CBCDecodeKey().content->key,
|
golden_data_.CBCDecodeKey().content->key);
|
||||||
TestLicenseBuilder::NoPadding());
|
|
||||||
|
|
||||||
builder.AddContentKey(golden_data_.CTRDecodeKey().level,
|
builder.AddContentKey(golden_data_.CTRDecodeKey().level,
|
||||||
golden_data_.CTRDecodeKey().id,
|
golden_data_.CTRDecodeKey().id,
|
||||||
golden_data_.CTRDecodeKey().content->key,
|
golden_data_.CTRDecodeKey().content->key);
|
||||||
TestLicenseBuilder::NoPadding());
|
|
||||||
|
|
||||||
builder.AddContentKey(golden_data_.CBCHardwareKey().level,
|
builder.AddContentKey(golden_data_.CBCHardwareKey().level,
|
||||||
golden_data_.CBCHardwareKey().id,
|
golden_data_.CBCHardwareKey().id,
|
||||||
golden_data_.CBCHardwareKey().content->key,
|
golden_data_.CBCHardwareKey().content->key);
|
||||||
TestLicenseBuilder::NoPadding());
|
|
||||||
|
|
||||||
builder.AddOperatorSessionKey(non_content_key_id_);
|
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
|
// 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.
|
// non-content key and one that will never be in the license.
|
||||||
std::vector<uint8_t> non_content_key_id_ = {0, 0, 0};
|
KeyId non_content_key_id_;
|
||||||
std::vector<uint8_t> missing_key_id_ = {1, 0, 0};
|
KeyId missing_key_id_;
|
||||||
|
|
||||||
size_t secret_string_size_;
|
size_t secret_string_size_;
|
||||||
std::vector<uint8_t> secret_string_;
|
std::vector<uint8_t> secret_string_;
|
||||||
@@ -94,7 +90,7 @@ class LicenseWhiteboxMaskedDecryptTest : public LicenseWhiteboxTestBase {
|
|||||||
};
|
};
|
||||||
|
|
||||||
TEST_F(LicenseWhiteboxMaskedDecryptTest, DecodeKeyWithCbcDataInCbcMode) {
|
TEST_F(LicenseWhiteboxMaskedDecryptTest, DecodeKeyWithCbcDataInCbcMode) {
|
||||||
LoadLicense(TestLicenseBuilder::NoPadding());
|
LoadLicense(TestLicenseBuilder::Padding::kNone);
|
||||||
|
|
||||||
ASSERT_EQ(
|
ASSERT_EQ(
|
||||||
WB_License_MaskedDecrypt(
|
WB_License_MaskedDecrypt(
|
||||||
@@ -132,7 +128,7 @@ TEST_F(LicenseWhiteboxMaskedDecryptTest, DecodeKeyWithCbcDataInCbcMode) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(LicenseWhiteboxMaskedDecryptTest, DecodeKeyWithCtrDataInCtrMode) {
|
TEST_F(LicenseWhiteboxMaskedDecryptTest, DecodeKeyWithCtrDataInCtrMode) {
|
||||||
LoadLicense(TestLicenseBuilder::NoPadding());
|
LoadLicense(TestLicenseBuilder::Padding::kNone);
|
||||||
|
|
||||||
ASSERT_EQ(
|
ASSERT_EQ(
|
||||||
WB_License_MaskedDecrypt(
|
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
|
// We try to decrypt CBC encrypted data in CTR mode. All operations should be
|
||||||
// successful, but the resulting plaintext should not match.
|
// successful, but the resulting plaintext should not match.
|
||||||
TEST_F(LicenseWhiteboxMaskedDecryptTest, DecodeKeyWithCbcDataInCtrMode) {
|
TEST_F(LicenseWhiteboxMaskedDecryptTest, DecodeKeyWithCbcDataInCtrMode) {
|
||||||
LoadLicense(TestLicenseBuilder::NoPadding());
|
LoadLicense(TestLicenseBuilder::Padding::kNone);
|
||||||
|
|
||||||
ASSERT_EQ(
|
ASSERT_EQ(
|
||||||
WB_License_MaskedDecrypt(
|
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
|
// We try to decrypt CTR encrypted data in CBC mode. All operations should be
|
||||||
// successful, but the resulting plaintext should not match.
|
// successful, but the resulting plaintext should not match.
|
||||||
TEST_F(LicenseWhiteboxMaskedDecryptTest, DecodeKeyWithCtrDataInCbcMode) {
|
TEST_F(LicenseWhiteboxMaskedDecryptTest, DecodeKeyWithCtrDataInCbcMode) {
|
||||||
LoadLicense(TestLicenseBuilder::NoPadding());
|
LoadLicense(TestLicenseBuilder::Padding::kNone);
|
||||||
|
|
||||||
ASSERT_EQ(
|
ASSERT_EQ(
|
||||||
WB_License_MaskedDecrypt(
|
WB_License_MaskedDecrypt(
|
||||||
@@ -248,7 +244,7 @@ TEST_F(LicenseWhiteboxMaskedDecryptTest, DecodeKeyWithCtrDataInCbcMode) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(LicenseWhiteboxMaskedDecryptTest, CryptoKeyWithCbcDataInCbcMode) {
|
TEST_F(LicenseWhiteboxMaskedDecryptTest, CryptoKeyWithCbcDataInCbcMode) {
|
||||||
LoadLicense(TestLicenseBuilder::NoPadding());
|
LoadLicense(TestLicenseBuilder::Padding::kNone);
|
||||||
|
|
||||||
ASSERT_EQ(
|
ASSERT_EQ(
|
||||||
WB_License_MaskedDecrypt(
|
WB_License_MaskedDecrypt(
|
||||||
@@ -286,7 +282,7 @@ TEST_F(LicenseWhiteboxMaskedDecryptTest, CryptoKeyWithCbcDataInCbcMode) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(LicenseWhiteboxMaskedDecryptTest, CryptoKeyWithCtrDataInCtrMode) {
|
TEST_F(LicenseWhiteboxMaskedDecryptTest, CryptoKeyWithCtrDataInCtrMode) {
|
||||||
LoadLicense(TestLicenseBuilder::NoPadding());
|
LoadLicense(TestLicenseBuilder::Padding::kNone);
|
||||||
|
|
||||||
ASSERT_EQ(
|
ASSERT_EQ(
|
||||||
WB_License_MaskedDecrypt(
|
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
|
// We try to decrypt CBC encrypted data in CTR mode. All operations should be
|
||||||
// successful, but the resulting plaintext should not match.
|
// successful, but the resulting plaintext should not match.
|
||||||
TEST_F(LicenseWhiteboxMaskedDecryptTest, CryptoKeyWithCbcDataInCtrMode) {
|
TEST_F(LicenseWhiteboxMaskedDecryptTest, CryptoKeyWithCbcDataInCtrMode) {
|
||||||
LoadLicense(TestLicenseBuilder::NoPadding());
|
LoadLicense(TestLicenseBuilder::Padding::kNone);
|
||||||
|
|
||||||
ASSERT_EQ(
|
ASSERT_EQ(
|
||||||
WB_License_MaskedDecrypt(
|
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
|
// We try to decrypt CTR encrypted data in CBC mode. All operations should be
|
||||||
// successful, but the resulting plaintext should not match.
|
// successful, but the resulting plaintext should not match.
|
||||||
TEST_F(LicenseWhiteboxMaskedDecryptTest, CryptoKeyWithCtrDataInCbcMode) {
|
TEST_F(LicenseWhiteboxMaskedDecryptTest, CryptoKeyWithCtrDataInCbcMode) {
|
||||||
LoadLicense(TestLicenseBuilder::NoPadding());
|
LoadLicense(TestLicenseBuilder::Padding::kNone);
|
||||||
|
|
||||||
ASSERT_EQ(
|
ASSERT_EQ(
|
||||||
WB_License_MaskedDecrypt(
|
WB_License_MaskedDecrypt(
|
||||||
@@ -402,7 +398,7 @@ TEST_F(LicenseWhiteboxMaskedDecryptTest, CryptoKeyWithCtrDataInCbcMode) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(LicenseWhiteboxMaskedDecryptTest, CryptoKeyWithCbcDataAndPKCS8Padding) {
|
TEST_F(LicenseWhiteboxMaskedDecryptTest, CryptoKeyWithCbcDataAndPKCS8Padding) {
|
||||||
LoadLicense(TestLicenseBuilder::PKSC8Padding());
|
LoadLicense(TestLicenseBuilder::Padding::kPKSC8);
|
||||||
|
|
||||||
ASSERT_EQ(
|
ASSERT_EQ(
|
||||||
WB_License_MaskedDecrypt(
|
WB_License_MaskedDecrypt(
|
||||||
@@ -440,7 +436,7 @@ TEST_F(LicenseWhiteboxMaskedDecryptTest, CryptoKeyWithCbcDataAndPKCS8Padding) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(LicenseWhiteboxMaskedDecryptTest, CryptoKeyWithCtrDataAndPKCS8Padding) {
|
TEST_F(LicenseWhiteboxMaskedDecryptTest, CryptoKeyWithCtrDataAndPKCS8Padding) {
|
||||||
LoadLicense(TestLicenseBuilder::PKSC8Padding());
|
LoadLicense(TestLicenseBuilder::Padding::kPKSC8);
|
||||||
|
|
||||||
ASSERT_EQ(
|
ASSERT_EQ(
|
||||||
WB_License_MaskedDecrypt(
|
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
|
// Since we have two CBC keys, try using the decode key and then the crypto
|
||||||
// key.
|
// key.
|
||||||
TEST_F(LicenseWhiteboxMaskedDecryptTest, SuccessWithMultipleKeys) {
|
TEST_F(LicenseWhiteboxMaskedDecryptTest, SuccessWithMultipleKeys) {
|
||||||
LoadLicense(TestLicenseBuilder::NoPadding());
|
LoadLicense(TestLicenseBuilder::Padding::kNone);
|
||||||
|
|
||||||
ASSERT_EQ(
|
ASSERT_EQ(
|
||||||
WB_License_MaskedDecrypt(
|
WB_License_MaskedDecrypt(
|
||||||
@@ -563,7 +559,7 @@ TEST_F(LicenseWhiteboxMaskedDecryptTest, SuccessWithMultipleKeys) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(LicenseWhiteboxMaskedDecryptTest, InvalidParameterForNullWhitebox) {
|
TEST_F(LicenseWhiteboxMaskedDecryptTest, InvalidParameterForNullWhitebox) {
|
||||||
LoadLicense(TestLicenseBuilder::NoPadding());
|
LoadLicense(TestLicenseBuilder::Padding::kNone);
|
||||||
|
|
||||||
ASSERT_EQ(
|
ASSERT_EQ(
|
||||||
WB_License_MaskedDecrypt(
|
WB_License_MaskedDecrypt(
|
||||||
@@ -578,7 +574,7 @@ TEST_F(LicenseWhiteboxMaskedDecryptTest, InvalidParameterForNullWhitebox) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(LicenseWhiteboxMaskedDecryptTest, InvalidParameterForInvalidCipherMode) {
|
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
|
// 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,
|
// 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) {
|
TEST_F(LicenseWhiteboxMaskedDecryptTest, InvalidParameterForNullKeyId) {
|
||||||
LoadLicense(TestLicenseBuilder::NoPadding());
|
LoadLicense(TestLicenseBuilder::Padding::kNone);
|
||||||
|
|
||||||
ASSERT_EQ(WB_License_MaskedDecrypt(
|
ASSERT_EQ(WB_License_MaskedDecrypt(
|
||||||
whitebox_, WB_CIPHER_MODE_CBC, nullptr,
|
whitebox_, WB_CIPHER_MODE_CBC, nullptr,
|
||||||
@@ -611,7 +607,7 @@ TEST_F(LicenseWhiteboxMaskedDecryptTest, InvalidParameterForNullKeyId) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(LicenseWhiteboxMaskedDecryptTest, InvalidParameterForNullZeroKeyIdSize) {
|
TEST_F(LicenseWhiteboxMaskedDecryptTest, InvalidParameterForNullZeroKeyIdSize) {
|
||||||
LoadLicense(TestLicenseBuilder::NoPadding());
|
LoadLicense(TestLicenseBuilder::Padding::kNone);
|
||||||
|
|
||||||
ASSERT_EQ(
|
ASSERT_EQ(
|
||||||
WB_License_MaskedDecrypt(
|
WB_License_MaskedDecrypt(
|
||||||
@@ -625,7 +621,7 @@ TEST_F(LicenseWhiteboxMaskedDecryptTest, InvalidParameterForNullZeroKeyIdSize) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(LicenseWhiteboxMaskedDecryptTest, InvalidParameterForNullInputData) {
|
TEST_F(LicenseWhiteboxMaskedDecryptTest, InvalidParameterForNullInputData) {
|
||||||
LoadLicense(TestLicenseBuilder::NoPadding());
|
LoadLicense(TestLicenseBuilder::Padding::kNone);
|
||||||
|
|
||||||
ASSERT_EQ(
|
ASSERT_EQ(
|
||||||
WB_License_MaskedDecrypt(
|
WB_License_MaskedDecrypt(
|
||||||
@@ -642,7 +638,7 @@ TEST_F(LicenseWhiteboxMaskedDecryptTest, InvalidParameterForNullInputData) {
|
|||||||
// not care.
|
// not care.
|
||||||
TEST_F(LicenseWhiteboxMaskedDecryptTest,
|
TEST_F(LicenseWhiteboxMaskedDecryptTest,
|
||||||
InvalidParameterForInvalidCBCInputDataSize) {
|
InvalidParameterForInvalidCBCInputDataSize) {
|
||||||
LoadLicense(TestLicenseBuilder::NoPadding());
|
LoadLicense(TestLicenseBuilder::Padding::kNone);
|
||||||
|
|
||||||
ASSERT_EQ(
|
ASSERT_EQ(
|
||||||
WB_License_MaskedDecrypt(
|
WB_License_MaskedDecrypt(
|
||||||
@@ -657,7 +653,7 @@ TEST_F(LicenseWhiteboxMaskedDecryptTest,
|
|||||||
|
|
||||||
// The white-box (using any cipher mode) should reject input with size zero.
|
// The white-box (using any cipher mode) should reject input with size zero.
|
||||||
TEST_F(LicenseWhiteboxMaskedDecryptTest, InvalidParameterForZeroInputDataSize) {
|
TEST_F(LicenseWhiteboxMaskedDecryptTest, InvalidParameterForZeroInputDataSize) {
|
||||||
LoadLicense(TestLicenseBuilder::NoPadding());
|
LoadLicense(TestLicenseBuilder::Padding::kNone);
|
||||||
|
|
||||||
ASSERT_EQ(
|
ASSERT_EQ(
|
||||||
WB_License_MaskedDecrypt(
|
WB_License_MaskedDecrypt(
|
||||||
@@ -671,7 +667,7 @@ TEST_F(LicenseWhiteboxMaskedDecryptTest, InvalidParameterForZeroInputDataSize) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(LicenseWhiteboxMaskedDecryptTest, InvalidParameterForNullIV) {
|
TEST_F(LicenseWhiteboxMaskedDecryptTest, InvalidParameterForNullIV) {
|
||||||
LoadLicense(TestLicenseBuilder::NoPadding());
|
LoadLicense(TestLicenseBuilder::Padding::kNone);
|
||||||
|
|
||||||
ASSERT_EQ(
|
ASSERT_EQ(
|
||||||
WB_License_MaskedDecrypt(
|
WB_License_MaskedDecrypt(
|
||||||
@@ -686,7 +682,7 @@ TEST_F(LicenseWhiteboxMaskedDecryptTest, InvalidParameterForNullIV) {
|
|||||||
|
|
||||||
// IV size should be 16. Any number other than 16 should fail.
|
// IV size should be 16. Any number other than 16 should fail.
|
||||||
TEST_F(LicenseWhiteboxMaskedDecryptTest, InvalidParameterForInvalidIVSize) {
|
TEST_F(LicenseWhiteboxMaskedDecryptTest, InvalidParameterForInvalidIVSize) {
|
||||||
LoadLicense(TestLicenseBuilder::NoPadding());
|
LoadLicense(TestLicenseBuilder::Padding::kNone);
|
||||||
|
|
||||||
ASSERT_EQ(
|
ASSERT_EQ(
|
||||||
WB_License_MaskedDecrypt(
|
WB_License_MaskedDecrypt(
|
||||||
@@ -700,7 +696,7 @@ TEST_F(LicenseWhiteboxMaskedDecryptTest, InvalidParameterForInvalidIVSize) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(LicenseWhiteboxMaskedDecryptTest, InvalidParameterForNullOutput) {
|
TEST_F(LicenseWhiteboxMaskedDecryptTest, InvalidParameterForNullOutput) {
|
||||||
LoadLicense(TestLicenseBuilder::NoPadding());
|
LoadLicense(TestLicenseBuilder::Padding::kNone);
|
||||||
|
|
||||||
ASSERT_EQ(
|
ASSERT_EQ(
|
||||||
WB_License_MaskedDecrypt(
|
WB_License_MaskedDecrypt(
|
||||||
@@ -715,7 +711,7 @@ TEST_F(LicenseWhiteboxMaskedDecryptTest, InvalidParameterForNullOutput) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(LicenseWhiteboxMaskedDecryptTest, InvalidParameterForNullOutputSize) {
|
TEST_F(LicenseWhiteboxMaskedDecryptTest, InvalidParameterForNullOutputSize) {
|
||||||
LoadLicense(TestLicenseBuilder::NoPadding());
|
LoadLicense(TestLicenseBuilder::Padding::kNone);
|
||||||
|
|
||||||
ASSERT_EQ(
|
ASSERT_EQ(
|
||||||
WB_License_MaskedDecrypt(
|
WB_License_MaskedDecrypt(
|
||||||
@@ -733,7 +729,7 @@ TEST_F(LicenseWhiteboxMaskedDecryptTest, InvalidParameterForNullOutputSize) {
|
|||||||
// in the license to start with. This is different than "non content key"
|
// 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.
|
// and "dropped content key", as those keys were in the license but ignored.
|
||||||
TEST_F(LicenseWhiteboxMaskedDecryptTest, KeyUnavailableForMissingKeyId) {
|
TEST_F(LicenseWhiteboxMaskedDecryptTest, KeyUnavailableForMissingKeyId) {
|
||||||
LoadLicense(TestLicenseBuilder::NoPadding());
|
LoadLicense(TestLicenseBuilder::Padding::kNone);
|
||||||
|
|
||||||
ASSERT_EQ(WB_License_MaskedDecrypt(
|
ASSERT_EQ(WB_License_MaskedDecrypt(
|
||||||
whitebox_, WB_CIPHER_MODE_CBC, missing_key_id_.data(),
|
whitebox_, WB_CIPHER_MODE_CBC, missing_key_id_.data(),
|
||||||
@@ -747,7 +743,7 @@ TEST_F(LicenseWhiteboxMaskedDecryptTest, KeyUnavailableForMissingKeyId) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(LicenseWhiteboxMaskedDecryptTest, KeyUnavailableForNonContentKey) {
|
TEST_F(LicenseWhiteboxMaskedDecryptTest, KeyUnavailableForNonContentKey) {
|
||||||
LoadLicense(TestLicenseBuilder::NoPadding());
|
LoadLicense(TestLicenseBuilder::Padding::kNone);
|
||||||
|
|
||||||
ASSERT_EQ(WB_License_MaskedDecrypt(
|
ASSERT_EQ(WB_License_MaskedDecrypt(
|
||||||
whitebox_, WB_CIPHER_MODE_CBC, non_content_key_id_.data(),
|
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.
|
// to this rule is on ChromeOS with a special license.
|
||||||
TEST_F(LicenseWhiteboxMaskedDecryptTest,
|
TEST_F(LicenseWhiteboxMaskedDecryptTest,
|
||||||
InsufficientSecurityLevelForHardwareContentKey) {
|
InsufficientSecurityLevelForHardwareContentKey) {
|
||||||
LoadLicense(TestLicenseBuilder::NoPadding());
|
LoadLicense(TestLicenseBuilder::Padding::kNone);
|
||||||
|
|
||||||
ASSERT_EQ(WB_License_MaskedDecrypt(
|
ASSERT_EQ(WB_License_MaskedDecrypt(
|
||||||
whitebox_, WB_CIPHER_MODE_CBC,
|
whitebox_, WB_CIPHER_MODE_CBC,
|
||||||
@@ -795,7 +791,7 @@ TEST_F(LicenseWhiteboxMaskedDecryptTest, InvalidState) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(LicenseWhiteboxMaskedDecryptTest, BufferTooSmall) {
|
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
|
// Our ciphertext will be large enough that we should not need to worry about
|
||||||
// using a constant here.
|
// 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
|
// Check that the result of unmasking only a small portion of the data is the
|
||||||
// same as when we unmask the whole buffer.
|
// same as when we unmask the whole buffer.
|
||||||
TEST_F(LicenseWhiteboxMaskedDecryptTest, SuccessForSubRangeUnmask) {
|
TEST_F(LicenseWhiteboxMaskedDecryptTest, SuccessForSubRangeUnmask) {
|
||||||
LoadLicense(TestLicenseBuilder::NoPadding());
|
LoadLicense(TestLicenseBuilder::Padding::kNone);
|
||||||
|
|
||||||
ASSERT_EQ(
|
ASSERT_EQ(
|
||||||
WB_License_MaskedDecrypt(
|
WB_License_MaskedDecrypt(
|
||||||
|
|||||||
@@ -9,22 +9,29 @@
|
|||||||
#include "testing/gtest/include/gtest/gtest.h"
|
#include "testing/gtest/include/gtest/gtest.h"
|
||||||
|
|
||||||
namespace widevine {
|
namespace widevine {
|
||||||
|
|
||||||
|
using OdkVersion = TestLicenseBuilder::OdkVersion;
|
||||||
|
|
||||||
class LicenseWhiteboxProcessLicenseResponseWithCoreMessageTest
|
class LicenseWhiteboxProcessLicenseResponseWithCoreMessageTest
|
||||||
: public LicenseWhiteboxTestBase {
|
: public LicenseWhiteboxTestBase {
|
||||||
protected:
|
protected:
|
||||||
void UseLicenseWithoutSigningKey(bool use_odk) {
|
void UseLicenseWithoutSigningKey(OdkVersion odk_version) {
|
||||||
TestLicenseBuilder builder;
|
TestLicenseBuilder builder;
|
||||||
|
builder.GetSettings().odk_version = odk_version;
|
||||||
|
|
||||||
builder.AddStubbedContentKey();
|
builder.AddStubbedContentKey();
|
||||||
builder.SetUseODK(use_odk);
|
|
||||||
builder.Build(*public_key_, &license_);
|
builder.Build(*public_key_, &license_);
|
||||||
}
|
}
|
||||||
|
|
||||||
void UseLicenseWithSigningKey(const std::vector<uint8_t>& padding,
|
void UseLicenseWithSigningKey(TestLicenseBuilder::Padding padding,
|
||||||
bool use_odk) {
|
OdkVersion odk_version) {
|
||||||
TestLicenseBuilder builder;
|
TestLicenseBuilder builder;
|
||||||
builder.AddSigningKey(TestLicenseBuilder::DefaultSigningKey(), padding);
|
builder.GetSettings().padding = padding;
|
||||||
|
builder.GetSettings().odk_version = odk_version;
|
||||||
|
|
||||||
|
builder.AddSigningKey(TestLicenseBuilder::DefaultSigningKey());
|
||||||
builder.AddStubbedContentKey();
|
builder.AddStubbedContentKey();
|
||||||
builder.SetUseODK(use_odk);
|
|
||||||
builder.Build(*public_key_, &license_);
|
builder.Build(*public_key_, &license_);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -35,8 +42,7 @@ class LicenseWhiteboxProcessLicenseResponseWithCoreMessageTest
|
|||||||
|
|
||||||
TEST_F(LicenseWhiteboxProcessLicenseResponseWithCoreMessageTest,
|
TEST_F(LicenseWhiteboxProcessLicenseResponseWithCoreMessageTest,
|
||||||
SuccessWithoutOdkAndWithoutSigningKey) {
|
SuccessWithoutOdkAndWithoutSigningKey) {
|
||||||
UseLicenseWithoutSigningKey(/* use_odk = */ false);
|
UseLicenseWithoutSigningKey(OdkVersion::kNone);
|
||||||
|
|
||||||
ASSERT_EQ(
|
ASSERT_EQ(
|
||||||
WB_License_ProcessLicenseResponse(
|
WB_License_ProcessLicenseResponse(
|
||||||
whitebox_, license_.core_message.data(), license_.core_message.size(),
|
whitebox_, license_.core_message.data(), license_.core_message.size(),
|
||||||
@@ -48,9 +54,21 @@ TEST_F(LicenseWhiteboxProcessLicenseResponseWithCoreMessageTest,
|
|||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(LicenseWhiteboxProcessLicenseResponseWithCoreMessageTest,
|
TEST_F(LicenseWhiteboxProcessLicenseResponseWithCoreMessageTest,
|
||||||
SuccessWithOdkAndWithoutSigningKey) {
|
SuccessWithOdk16_3AndWithoutSigningKey) {
|
||||||
UseLicenseWithoutSigningKey(/* use_odk = */ true);
|
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(
|
ASSERT_EQ(
|
||||||
WB_License_ProcessLicenseResponse(
|
WB_License_ProcessLicenseResponse(
|
||||||
whitebox_, license_.core_message.data(), license_.core_message.size(),
|
whitebox_, license_.core_message.data(), license_.core_message.size(),
|
||||||
@@ -63,9 +81,8 @@ TEST_F(LicenseWhiteboxProcessLicenseResponseWithCoreMessageTest,
|
|||||||
|
|
||||||
TEST_F(LicenseWhiteboxProcessLicenseResponseWithCoreMessageTest,
|
TEST_F(LicenseWhiteboxProcessLicenseResponseWithCoreMessageTest,
|
||||||
SuccessWithoutOdkAndWithSigningKeyNoPadding) {
|
SuccessWithoutOdkAndWithSigningKeyNoPadding) {
|
||||||
UseLicenseWithSigningKey(TestLicenseBuilder::NoPadding(),
|
UseLicenseWithSigningKey(TestLicenseBuilder::Padding::kNone,
|
||||||
/* use_odk = */ false);
|
OdkVersion::kNone);
|
||||||
|
|
||||||
ASSERT_EQ(
|
ASSERT_EQ(
|
||||||
WB_License_ProcessLicenseResponse(
|
WB_License_ProcessLicenseResponse(
|
||||||
whitebox_, license_.core_message.data(), license_.core_message.size(),
|
whitebox_, license_.core_message.data(), license_.core_message.size(),
|
||||||
@@ -77,10 +94,23 @@ TEST_F(LicenseWhiteboxProcessLicenseResponseWithCoreMessageTest,
|
|||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(LicenseWhiteboxProcessLicenseResponseWithCoreMessageTest,
|
TEST_F(LicenseWhiteboxProcessLicenseResponseWithCoreMessageTest,
|
||||||
SuccessWithOdkAndWithSigningKeyNoPadding) {
|
SuccessWithOdk16_3AndWithSigningKeyNoPadding) {
|
||||||
UseLicenseWithSigningKey(TestLicenseBuilder::NoPadding(),
|
UseLicenseWithSigningKey(TestLicenseBuilder::Padding::kNone,
|
||||||
/* use_odk = */ true);
|
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(
|
ASSERT_EQ(
|
||||||
WB_License_ProcessLicenseResponse(
|
WB_License_ProcessLicenseResponse(
|
||||||
whitebox_, license_.core_message.data(), license_.core_message.size(),
|
whitebox_, license_.core_message.data(), license_.core_message.size(),
|
||||||
@@ -93,9 +123,8 @@ TEST_F(LicenseWhiteboxProcessLicenseResponseWithCoreMessageTest,
|
|||||||
|
|
||||||
TEST_F(LicenseWhiteboxProcessLicenseResponseWithCoreMessageTest,
|
TEST_F(LicenseWhiteboxProcessLicenseResponseWithCoreMessageTest,
|
||||||
SuccessWithoutOdkAndWithSigningKeyPKSC8Padding) {
|
SuccessWithoutOdkAndWithSigningKeyPKSC8Padding) {
|
||||||
UseLicenseWithSigningKey(TestLicenseBuilder::PKSC8Padding(),
|
UseLicenseWithSigningKey(TestLicenseBuilder::Padding::kPKSC8,
|
||||||
/* use_odk = */ false);
|
OdkVersion::kNone);
|
||||||
|
|
||||||
ASSERT_EQ(
|
ASSERT_EQ(
|
||||||
WB_License_ProcessLicenseResponse(
|
WB_License_ProcessLicenseResponse(
|
||||||
whitebox_, license_.core_message.data(), license_.core_message.size(),
|
whitebox_, license_.core_message.data(), license_.core_message.size(),
|
||||||
@@ -107,10 +136,23 @@ TEST_F(LicenseWhiteboxProcessLicenseResponseWithCoreMessageTest,
|
|||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(LicenseWhiteboxProcessLicenseResponseWithCoreMessageTest,
|
TEST_F(LicenseWhiteboxProcessLicenseResponseWithCoreMessageTest,
|
||||||
SuccessWithOdkAndWithSigningKeyPKSC8Padding) {
|
SuccessWithOdk16_3AndWithSigningKeyPKSC8Padding) {
|
||||||
UseLicenseWithSigningKey(TestLicenseBuilder::PKSC8Padding(),
|
UseLicenseWithSigningKey(TestLicenseBuilder::Padding::kPKSC8,
|
||||||
/* use_odk = */ true);
|
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(
|
ASSERT_EQ(
|
||||||
WB_License_ProcessLicenseResponse(
|
WB_License_ProcessLicenseResponse(
|
||||||
whitebox_, license_.core_message.data(), license_.core_message.size(),
|
whitebox_, license_.core_message.data(), license_.core_message.size(),
|
||||||
@@ -123,9 +165,10 @@ TEST_F(LicenseWhiteboxProcessLicenseResponseWithCoreMessageTest,
|
|||||||
|
|
||||||
TEST_F(LicenseWhiteboxProcessLicenseResponseWithCoreMessageTest,
|
TEST_F(LicenseWhiteboxProcessLicenseResponseWithCoreMessageTest,
|
||||||
InvalidParameterForNullCoreMessage) {
|
InvalidParameterForNullCoreMessage) {
|
||||||
UseLicenseWithoutSigningKey(/* use_odk = */ true);
|
UseLicenseWithoutSigningKey(OdkVersion::k16_5);
|
||||||
ASSERT_EQ(WB_License_ProcessLicenseResponse(
|
ASSERT_EQ(
|
||||||
whitebox_, nullptr, license_.core_message.size(),
|
WB_License_ProcessLicenseResponse(
|
||||||
|
whitebox_, /*core_message=*/nullptr, license_.core_message.size(),
|
||||||
license_.message.data(), license_.message.size(),
|
license_.message.data(), license_.message.size(),
|
||||||
license_.signature.data(), license_.signature.size(),
|
license_.signature.data(), license_.signature.size(),
|
||||||
license_.session_key.data(), license_.session_key.size(),
|
license_.session_key.data(), license_.session_key.size(),
|
||||||
@@ -135,14 +178,15 @@ TEST_F(LicenseWhiteboxProcessLicenseResponseWithCoreMessageTest,
|
|||||||
|
|
||||||
TEST_F(LicenseWhiteboxProcessLicenseResponseWithCoreMessageTest,
|
TEST_F(LicenseWhiteboxProcessLicenseResponseWithCoreMessageTest,
|
||||||
InvalidSignatureWithZeroCoreMessageSize) {
|
InvalidSignatureWithZeroCoreMessageSize) {
|
||||||
UseLicenseWithoutSigningKey(/* use_odk = */ true);
|
UseLicenseWithoutSigningKey(OdkVersion::k16_5);
|
||||||
|
|
||||||
// |core_message_size| = 0 means the core message is not provided, so
|
// |core_message_size| = 0 means the core message is not provided, so
|
||||||
// no parameter check will be done. However, since the license was created
|
// no parameter check will be done. However, since the license was created
|
||||||
// with one, signature checking will fail as there is no core message
|
// with one, signature checking will fail as there is no core message
|
||||||
// to be included when checking the signature.
|
// to be included when checking the signature.
|
||||||
ASSERT_EQ(WB_License_ProcessLicenseResponse(
|
ASSERT_EQ(
|
||||||
whitebox_, license_.core_message.data(), 0,
|
WB_License_ProcessLicenseResponse(
|
||||||
|
whitebox_, license_.core_message.data(), /*core_message_size=*/0,
|
||||||
license_.message.data(), license_.message.size(),
|
license_.message.data(), license_.message.size(),
|
||||||
license_.signature.data(), license_.signature.size(),
|
license_.signature.data(), license_.signature.size(),
|
||||||
license_.session_key.data(), license_.session_key.size(),
|
license_.session_key.data(), license_.session_key.size(),
|
||||||
|
|||||||
@@ -28,9 +28,11 @@ class LicenseWhiteboxProcessLicenseResponseTest
|
|||||||
builder.Build(*public_key_, &license_);
|
builder.Build(*public_key_, &license_);
|
||||||
}
|
}
|
||||||
|
|
||||||
void UseLicenseWithSigningKey(const std::vector<uint8_t>& padding) {
|
void UseLicenseWithSigningKey(TestLicenseBuilder::Padding padding) {
|
||||||
TestLicenseBuilder builder;
|
TestLicenseBuilder builder;
|
||||||
builder.AddSigningKey(TestLicenseBuilder::DefaultSigningKey(), padding);
|
builder.GetSettings().padding = padding;
|
||||||
|
|
||||||
|
builder.AddSigningKey(TestLicenseBuilder::DefaultSigningKey());
|
||||||
builder.AddStubbedContentKey();
|
builder.AddStubbedContentKey();
|
||||||
builder.Build(*public_key_, &license_);
|
builder.Build(*public_key_, &license_);
|
||||||
}
|
}
|
||||||
@@ -53,7 +55,7 @@ TEST_F(LicenseWhiteboxProcessLicenseResponseTest, SuccessWithoutSigningKey) {
|
|||||||
|
|
||||||
TEST_F(LicenseWhiteboxProcessLicenseResponseTest,
|
TEST_F(LicenseWhiteboxProcessLicenseResponseTest,
|
||||||
SuccessWithSigningKeyNoPadding) {
|
SuccessWithSigningKeyNoPadding) {
|
||||||
UseLicenseWithSigningKey(TestLicenseBuilder::NoPadding());
|
UseLicenseWithSigningKey(TestLicenseBuilder::Padding::kNone);
|
||||||
|
|
||||||
ASSERT_EQ(
|
ASSERT_EQ(
|
||||||
WB_License_ProcessLicenseResponse(
|
WB_License_ProcessLicenseResponse(
|
||||||
@@ -67,7 +69,7 @@ TEST_F(LicenseWhiteboxProcessLicenseResponseTest,
|
|||||||
|
|
||||||
TEST_F(LicenseWhiteboxProcessLicenseResponseTest,
|
TEST_F(LicenseWhiteboxProcessLicenseResponseTest,
|
||||||
SuccessWithSigningKeyPKSC8Padding) {
|
SuccessWithSigningKeyPKSC8Padding) {
|
||||||
UseLicenseWithSigningKey(TestLicenseBuilder::PKSC8Padding());
|
UseLicenseWithSigningKey(TestLicenseBuilder::Padding::kPKSC8);
|
||||||
|
|
||||||
ASSERT_EQ(
|
ASSERT_EQ(
|
||||||
WB_License_ProcessLicenseResponse(
|
WB_License_ProcessLicenseResponse(
|
||||||
@@ -82,10 +84,10 @@ TEST_F(LicenseWhiteboxProcessLicenseResponseTest,
|
|||||||
TEST_F(LicenseWhiteboxProcessLicenseResponseTest,
|
TEST_F(LicenseWhiteboxProcessLicenseResponseTest,
|
||||||
InvalidParameterWithMultipleSigningKeys) {
|
InvalidParameterWithMultipleSigningKeys) {
|
||||||
TestLicenseBuilder builder;
|
TestLicenseBuilder builder;
|
||||||
builder.AddSigningKey(TestLicenseBuilder::DefaultSigningKey(),
|
builder.GetSettings().padding = TestLicenseBuilder::Padding::kPKSC8;
|
||||||
TestLicenseBuilder::PKSC8Padding());
|
|
||||||
builder.AddSigningKey(TestLicenseBuilder::DefaultSigningKey(),
|
builder.AddSigningKey(TestLicenseBuilder::DefaultSigningKey());
|
||||||
TestLicenseBuilder::PKSC8Padding());
|
builder.AddSigningKey(TestLicenseBuilder::DefaultSigningKey());
|
||||||
builder.AddStubbedContentKey();
|
builder.AddStubbedContentKey();
|
||||||
builder.Build(*public_key_, &license_);
|
builder.Build(*public_key_, &license_);
|
||||||
|
|
||||||
|
|||||||
@@ -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<TestLicenseBuilder::KeyControlBlock,
|
||||||
|
TestLicenseBuilder::OdkVersion>> {
|
||||||
|
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
|
||||||
@@ -25,9 +25,11 @@ class LicenseWhiteboxSignRenewalRequestTest : public LicenseWhiteboxTestBase {
|
|||||||
signature_.resize(signature_size_);
|
signature_.resize(signature_size_);
|
||||||
}
|
}
|
||||||
|
|
||||||
void LoadLicense(const std::vector<uint8_t>& padding) {
|
void LoadLicense(TestLicenseBuilder::Padding padding) {
|
||||||
TestLicenseBuilder builder;
|
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
|
// Add a throw away key. We just need a key in the license since a license
|
||||||
// should always have a content key.
|
// should always have a content key.
|
||||||
builder.AddStubbedContentKey();
|
builder.AddStubbedContentKey();
|
||||||
@@ -61,7 +63,7 @@ class LicenseWhiteboxSignRenewalRequestTest : public LicenseWhiteboxTestBase {
|
|||||||
|
|
||||||
const std::string session_key_ = "0123456789ABCDEF";
|
const std::string session_key_ = "0123456789ABCDEF";
|
||||||
|
|
||||||
const std::vector<uint8_t> signing_key_ =
|
const std::array<uint8_t, 64> signing_key_ =
|
||||||
TestLicenseBuilder::DefaultSigningKey();
|
TestLicenseBuilder::DefaultSigningKey();
|
||||||
|
|
||||||
size_t signature_size_;
|
size_t signature_size_;
|
||||||
@@ -77,7 +79,7 @@ class LicenseWhiteboxSignRenewalRequestTest : public LicenseWhiteboxTestBase {
|
|||||||
};
|
};
|
||||||
|
|
||||||
TEST_F(LicenseWhiteboxSignRenewalRequestTest, SuccessWithInvalidRequest) {
|
TEST_F(LicenseWhiteboxSignRenewalRequestTest, SuccessWithInvalidRequest) {
|
||||||
LoadLicense(TestLicenseBuilder::NoPadding());
|
LoadLicense(TestLicenseBuilder::Padding::kNone);
|
||||||
|
|
||||||
ASSERT_EQ(WB_License_SignRenewalRequest(whitebox_, garbage_request_.data(),
|
ASSERT_EQ(WB_License_SignRenewalRequest(whitebox_, garbage_request_.data(),
|
||||||
garbage_request_.size(),
|
garbage_request_.size(),
|
||||||
@@ -90,7 +92,7 @@ TEST_F(LicenseWhiteboxSignRenewalRequestTest, SuccessWithInvalidRequest) {
|
|||||||
|
|
||||||
TEST_F(LicenseWhiteboxSignRenewalRequestTest,
|
TEST_F(LicenseWhiteboxSignRenewalRequestTest,
|
||||||
SuccessWithSigningKeyPKSC8Padding) {
|
SuccessWithSigningKeyPKSC8Padding) {
|
||||||
LoadLicense(TestLicenseBuilder::PKSC8Padding());
|
LoadLicense(TestLicenseBuilder::Padding::kPKSC8);
|
||||||
|
|
||||||
ASSERT_EQ(WB_License_SignRenewalRequest(whitebox_, garbage_request_.data(),
|
ASSERT_EQ(WB_License_SignRenewalRequest(whitebox_, garbage_request_.data(),
|
||||||
garbage_request_.size(),
|
garbage_request_.size(),
|
||||||
@@ -102,7 +104,7 @@ TEST_F(LicenseWhiteboxSignRenewalRequestTest,
|
|||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(LicenseWhiteboxSignRenewalRequestTest, InvalidParameterForNullWhitebox) {
|
TEST_F(LicenseWhiteboxSignRenewalRequestTest, InvalidParameterForNullWhitebox) {
|
||||||
LoadLicense(TestLicenseBuilder::NoPadding());
|
LoadLicense(TestLicenseBuilder::Padding::kNone);
|
||||||
|
|
||||||
ASSERT_EQ(WB_License_SignRenewalRequest(nullptr, garbage_request_.data(),
|
ASSERT_EQ(WB_License_SignRenewalRequest(nullptr, garbage_request_.data(),
|
||||||
garbage_request_.size(),
|
garbage_request_.size(),
|
||||||
@@ -111,7 +113,7 @@ TEST_F(LicenseWhiteboxSignRenewalRequestTest, InvalidParameterForNullWhitebox) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(LicenseWhiteboxSignRenewalRequestTest, InvalidParameterForNullMessage) {
|
TEST_F(LicenseWhiteboxSignRenewalRequestTest, InvalidParameterForNullMessage) {
|
||||||
LoadLicense(TestLicenseBuilder::NoPadding());
|
LoadLicense(TestLicenseBuilder::Padding::kNone);
|
||||||
|
|
||||||
ASSERT_EQ(
|
ASSERT_EQ(
|
||||||
WB_License_SignRenewalRequest(whitebox_, nullptr, garbage_request_.size(),
|
WB_License_SignRenewalRequest(whitebox_, nullptr, garbage_request_.size(),
|
||||||
@@ -121,7 +123,7 @@ TEST_F(LicenseWhiteboxSignRenewalRequestTest, InvalidParameterForNullMessage) {
|
|||||||
|
|
||||||
TEST_F(LicenseWhiteboxSignRenewalRequestTest,
|
TEST_F(LicenseWhiteboxSignRenewalRequestTest,
|
||||||
InvalidParameterForZeroMessageSize) {
|
InvalidParameterForZeroMessageSize) {
|
||||||
LoadLicense(TestLicenseBuilder::NoPadding());
|
LoadLicense(TestLicenseBuilder::Padding::kNone);
|
||||||
|
|
||||||
ASSERT_EQ(WB_License_SignRenewalRequest(whitebox_, garbage_request_.data(), 0,
|
ASSERT_EQ(WB_License_SignRenewalRequest(whitebox_, garbage_request_.data(), 0,
|
||||||
signature_.data(), &signature_size_),
|
signature_.data(), &signature_size_),
|
||||||
@@ -130,7 +132,7 @@ TEST_F(LicenseWhiteboxSignRenewalRequestTest,
|
|||||||
|
|
||||||
TEST_F(LicenseWhiteboxSignRenewalRequestTest,
|
TEST_F(LicenseWhiteboxSignRenewalRequestTest,
|
||||||
InvalidParameterForNullSignature) {
|
InvalidParameterForNullSignature) {
|
||||||
LoadLicense(TestLicenseBuilder::NoPadding());
|
LoadLicense(TestLicenseBuilder::Padding::kNone);
|
||||||
|
|
||||||
ASSERT_EQ(WB_License_SignRenewalRequest(whitebox_, garbage_request_.data(),
|
ASSERT_EQ(WB_License_SignRenewalRequest(whitebox_, garbage_request_.data(),
|
||||||
garbage_request_.size(), nullptr,
|
garbage_request_.size(), nullptr,
|
||||||
@@ -140,7 +142,7 @@ TEST_F(LicenseWhiteboxSignRenewalRequestTest,
|
|||||||
|
|
||||||
TEST_F(LicenseWhiteboxSignRenewalRequestTest,
|
TEST_F(LicenseWhiteboxSignRenewalRequestTest,
|
||||||
InvalidParameterForNullSignatureSize) {
|
InvalidParameterForNullSignatureSize) {
|
||||||
LoadLicense(TestLicenseBuilder::NoPadding());
|
LoadLicense(TestLicenseBuilder::Padding::kNone);
|
||||||
|
|
||||||
ASSERT_EQ(WB_License_SignRenewalRequest(whitebox_, garbage_request_.data(),
|
ASSERT_EQ(WB_License_SignRenewalRequest(whitebox_, garbage_request_.data(),
|
||||||
garbage_request_.size(),
|
garbage_request_.size(),
|
||||||
@@ -149,7 +151,7 @@ TEST_F(LicenseWhiteboxSignRenewalRequestTest,
|
|||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(LicenseWhiteboxSignRenewalRequestTest, BufferTooSmall) {
|
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
|
// 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
|
// zero, using a non-zero value ensures that we are not combining "empty" and
|
||||||
|
|||||||
@@ -22,14 +22,16 @@ class LicenseWhiteboxVerifyRenewalResponseTest
|
|||||||
garbage_renewal_signature_ = Sign(garbage_renewal_message_);
|
garbage_renewal_signature_ = Sign(garbage_renewal_message_);
|
||||||
}
|
}
|
||||||
|
|
||||||
void LoadLicense(const std::vector<uint8_t>& padding) {
|
void LoadLicense(TestLicenseBuilder::Padding padding) {
|
||||||
const auto signing_key = TestLicenseBuilder::DefaultSigningKey();
|
const auto signing_key = TestLicenseBuilder::DefaultSigningKey();
|
||||||
|
|
||||||
// We need a license so that we can always have a valid signature for our
|
// 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
|
// message(s), but don't load the license as some test will need no
|
||||||
// license loaded.
|
// license loaded.
|
||||||
TestLicenseBuilder builder;
|
TestLicenseBuilder builder;
|
||||||
builder.AddSigningKey(signing_key, padding);
|
builder.GetSettings().padding = padding;
|
||||||
|
|
||||||
|
builder.AddSigningKey(signing_key);
|
||||||
builder.AddStubbedContentKey();
|
builder.AddStubbedContentKey();
|
||||||
|
|
||||||
License license;
|
License license;
|
||||||
@@ -84,7 +86,7 @@ class LicenseWhiteboxVerifyRenewalResponseTest
|
|||||||
// SuccessForGarbageMessage - to use the real serialized response.
|
// SuccessForGarbageMessage - to use the real serialized response.
|
||||||
|
|
||||||
TEST_F(LicenseWhiteboxVerifyRenewalResponseTest, SuccessForGarbageMessage) {
|
TEST_F(LicenseWhiteboxVerifyRenewalResponseTest, SuccessForGarbageMessage) {
|
||||||
LoadLicense(TestLicenseBuilder::NoPadding());
|
LoadLicense(TestLicenseBuilder::Padding::kNone);
|
||||||
|
|
||||||
ASSERT_EQ(WB_License_VerifyRenewalResponse(whitebox_,
|
ASSERT_EQ(WB_License_VerifyRenewalResponse(whitebox_,
|
||||||
garbage_renewal_message_.data(),
|
garbage_renewal_message_.data(),
|
||||||
@@ -96,7 +98,7 @@ TEST_F(LicenseWhiteboxVerifyRenewalResponseTest, SuccessForGarbageMessage) {
|
|||||||
|
|
||||||
TEST_F(LicenseWhiteboxVerifyRenewalResponseTest,
|
TEST_F(LicenseWhiteboxVerifyRenewalResponseTest,
|
||||||
SuccessWithSigningKeyPKSC8Padding) {
|
SuccessWithSigningKeyPKSC8Padding) {
|
||||||
LoadLicense(TestLicenseBuilder::PKSC8Padding());
|
LoadLicense(TestLicenseBuilder::Padding::kPKSC8);
|
||||||
|
|
||||||
ASSERT_EQ(WB_License_VerifyRenewalResponse(whitebox_,
|
ASSERT_EQ(WB_License_VerifyRenewalResponse(whitebox_,
|
||||||
garbage_renewal_message_.data(),
|
garbage_renewal_message_.data(),
|
||||||
@@ -108,7 +110,7 @@ TEST_F(LicenseWhiteboxVerifyRenewalResponseTest,
|
|||||||
|
|
||||||
TEST_F(LicenseWhiteboxVerifyRenewalResponseTest,
|
TEST_F(LicenseWhiteboxVerifyRenewalResponseTest,
|
||||||
InvalidParameterForNullWhitebox) {
|
InvalidParameterForNullWhitebox) {
|
||||||
LoadLicense(TestLicenseBuilder::NoPadding());
|
LoadLicense(TestLicenseBuilder::Padding::kNone);
|
||||||
|
|
||||||
ASSERT_EQ(
|
ASSERT_EQ(
|
||||||
WB_License_VerifyRenewalResponse(nullptr, garbage_renewal_message_.data(),
|
WB_License_VerifyRenewalResponse(nullptr, garbage_renewal_message_.data(),
|
||||||
@@ -120,7 +122,7 @@ TEST_F(LicenseWhiteboxVerifyRenewalResponseTest,
|
|||||||
|
|
||||||
TEST_F(LicenseWhiteboxVerifyRenewalResponseTest,
|
TEST_F(LicenseWhiteboxVerifyRenewalResponseTest,
|
||||||
InvalidParameterForNullMessage) {
|
InvalidParameterForNullMessage) {
|
||||||
LoadLicense(TestLicenseBuilder::NoPadding());
|
LoadLicense(TestLicenseBuilder::Padding::kNone);
|
||||||
|
|
||||||
ASSERT_EQ(WB_License_VerifyRenewalResponse(whitebox_, nullptr,
|
ASSERT_EQ(WB_License_VerifyRenewalResponse(whitebox_, nullptr,
|
||||||
garbage_renewal_message_.size(),
|
garbage_renewal_message_.size(),
|
||||||
@@ -131,7 +133,7 @@ TEST_F(LicenseWhiteboxVerifyRenewalResponseTest,
|
|||||||
|
|
||||||
TEST_F(LicenseWhiteboxVerifyRenewalResponseTest,
|
TEST_F(LicenseWhiteboxVerifyRenewalResponseTest,
|
||||||
InvalidParameterForZeroMessageSize) {
|
InvalidParameterForZeroMessageSize) {
|
||||||
LoadLicense(TestLicenseBuilder::NoPadding());
|
LoadLicense(TestLicenseBuilder::Padding::kNone);
|
||||||
|
|
||||||
ASSERT_EQ(WB_License_VerifyRenewalResponse(whitebox_,
|
ASSERT_EQ(WB_License_VerifyRenewalResponse(whitebox_,
|
||||||
garbage_renewal_message_.data(), 0,
|
garbage_renewal_message_.data(), 0,
|
||||||
@@ -142,7 +144,7 @@ TEST_F(LicenseWhiteboxVerifyRenewalResponseTest,
|
|||||||
|
|
||||||
TEST_F(LicenseWhiteboxVerifyRenewalResponseTest,
|
TEST_F(LicenseWhiteboxVerifyRenewalResponseTest,
|
||||||
InvalidParameterForNullSignature) {
|
InvalidParameterForNullSignature) {
|
||||||
LoadLicense(TestLicenseBuilder::NoPadding());
|
LoadLicense(TestLicenseBuilder::Padding::kNone);
|
||||||
|
|
||||||
ASSERT_EQ(WB_License_VerifyRenewalResponse(
|
ASSERT_EQ(WB_License_VerifyRenewalResponse(
|
||||||
whitebox_, garbage_renewal_message_.data(),
|
whitebox_, garbage_renewal_message_.data(),
|
||||||
@@ -153,7 +155,7 @@ TEST_F(LicenseWhiteboxVerifyRenewalResponseTest,
|
|||||||
|
|
||||||
TEST_F(LicenseWhiteboxVerifyRenewalResponseTest,
|
TEST_F(LicenseWhiteboxVerifyRenewalResponseTest,
|
||||||
InvalidParameterForInvalidSignatureSize) {
|
InvalidParameterForInvalidSignatureSize) {
|
||||||
LoadLicense(TestLicenseBuilder::NoPadding());
|
LoadLicense(TestLicenseBuilder::Padding::kNone);
|
||||||
|
|
||||||
ASSERT_EQ(WB_License_VerifyRenewalResponse(
|
ASSERT_EQ(WB_License_VerifyRenewalResponse(
|
||||||
whitebox_, garbage_renewal_message_.data(),
|
whitebox_, garbage_renewal_message_.data(),
|
||||||
@@ -164,7 +166,7 @@ TEST_F(LicenseWhiteboxVerifyRenewalResponseTest,
|
|||||||
|
|
||||||
TEST_F(LicenseWhiteboxVerifyRenewalResponseTest,
|
TEST_F(LicenseWhiteboxVerifyRenewalResponseTest,
|
||||||
InvalidSignatureForModifiedMessage) {
|
InvalidSignatureForModifiedMessage) {
|
||||||
LoadLicense(TestLicenseBuilder::NoPadding());
|
LoadLicense(TestLicenseBuilder::Padding::kNone);
|
||||||
|
|
||||||
Modify(&garbage_renewal_message_);
|
Modify(&garbage_renewal_message_);
|
||||||
|
|
||||||
@@ -178,7 +180,7 @@ TEST_F(LicenseWhiteboxVerifyRenewalResponseTest,
|
|||||||
|
|
||||||
TEST_F(LicenseWhiteboxVerifyRenewalResponseTest,
|
TEST_F(LicenseWhiteboxVerifyRenewalResponseTest,
|
||||||
InvalidSignatureForModifiedSignature) {
|
InvalidSignatureForModifiedSignature) {
|
||||||
LoadLicense(TestLicenseBuilder::NoPadding());
|
LoadLicense(TestLicenseBuilder::Padding::kNone);
|
||||||
|
|
||||||
Modify(&garbage_renewal_signature_);
|
Modify(&garbage_renewal_signature_);
|
||||||
|
|
||||||
|
|||||||
23
whitebox/api/test_key_types.h
Normal file
23
whitebox/api/test_key_types.h
Normal file
@@ -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 <array>
|
||||||
|
#include <cstdint>
|
||||||
|
|
||||||
|
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<uint8_t, 4>;
|
||||||
|
|
||||||
|
// AES keys can only be 16 bytes.
|
||||||
|
using AesKey = std::array<uint8_t, 16>;
|
||||||
|
|
||||||
|
// AES IV can only be 16 bytes.
|
||||||
|
using AesIv = std::array<uint8_t, 16>;
|
||||||
|
|
||||||
|
} // namespace widevine
|
||||||
|
|
||||||
|
#endif // WHITEBOX_API_TEST_KEY_TYPES_H_
|
||||||
@@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
#include "api/test_license_builder.h"
|
#include "api/test_license_builder.h"
|
||||||
|
|
||||||
|
#include <array>
|
||||||
#include <ctime>
|
#include <ctime>
|
||||||
|
|
||||||
#include "base/check.h"
|
#include "base/check.h"
|
||||||
@@ -19,6 +20,8 @@ namespace widevine {
|
|||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
|
using KeyControlBlock = std::array<uint8_t, 16>;
|
||||||
|
|
||||||
void InitializeRequest(video_widevine::LicenseRequest* request) {
|
void InitializeRequest(video_widevine::LicenseRequest* request) {
|
||||||
request->set_request_time(std::time(nullptr)); // Use time=now.
|
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);
|
video_widevine::PlatformVerificationStatus::PLATFORM_UNVERIFIED);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string EncryptKey(const std::string& key,
|
template <typename Key, typename IV, typename Plaintext>
|
||||||
const std::string& iv,
|
std::string Encrypt(const Key& key, const IV& iv, const Plaintext& plaintext) {
|
||||||
const std::vector<uint8_t>& plaintext) {
|
std::vector<uint8_t> key_data(key.begin(), key.end());
|
||||||
|
std::vector<uint8_t> iv_data(iv.begin(), iv.end());
|
||||||
|
std::vector<uint8_t> plaintext_data(plaintext.begin(), plaintext.end());
|
||||||
|
|
||||||
AesCbcEncryptor encryptor;
|
AesCbcEncryptor encryptor;
|
||||||
encryptor.SetKey(reinterpret_cast<const uint8_t*>(key.data()), key.size());
|
encryptor.SetKey(key_data.data(), key_data.size());
|
||||||
|
|
||||||
std::vector<uint8_t> ciphertext(plaintext.size());
|
std::vector<uint8_t> ciphertext(plaintext.size());
|
||||||
CHECK(encryptor.Encrypt(reinterpret_cast<const uint8_t*>(iv.data()),
|
CHECK(encryptor.Encrypt(iv_data.data(), iv_data.size(), plaintext_data.data(),
|
||||||
iv.size(), plaintext.data(), plaintext.size(),
|
plaintext_data.size(), ciphertext.data()));
|
||||||
ciphertext.data()));
|
|
||||||
|
|
||||||
return std::string(ciphertext.begin(), ciphertext.end());
|
return std::string(ciphertext.begin(), ciphertext.end());
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string DeriveIV(const std::vector<uint8_t>& context) {
|
template <typename In>
|
||||||
|
std::string DeriveIV(const In& context) {
|
||||||
const std::string context_str(context.begin(), context.end());
|
const std::string context_str(context.begin(), context.end());
|
||||||
return crypto_util::DeriveIv(context_str);
|
return crypto_util::DeriveIv(context_str);
|
||||||
}
|
}
|
||||||
|
|
||||||
void UpdateKeyControlBlock(
|
KeyControlBlock CreateKeyControlBlock(
|
||||||
video_widevine::License_KeyContainer_SecurityLevel level,
|
video_widevine::License_KeyContainer_SecurityLevel level,
|
||||||
video_widevine::License_KeyContainer_KeyControl* key_control) {
|
video_widevine::License_KeyContainer_KeyControl* key_control) {
|
||||||
// The key control block is an 128 bit structure containing the following
|
// The key control block is an 128 bit structure containing the following
|
||||||
@@ -119,7 +125,7 @@ void UpdateKeyControlBlock(
|
|||||||
// 1 = SW_SECURE_DECODE
|
// 1 = SW_SECURE_DECODE
|
||||||
// 2 = HW_SECURE_CRYPTO
|
// 2 = HW_SECURE_CRYPTO
|
||||||
// 3 = HW_SECURE_DECODE or HW_SECURE_ALL
|
// 3 = HW_SECURE_DECODE or HW_SECURE_ALL
|
||||||
std::vector<uint8_t> key_control_block = {
|
KeyControlBlock key_control_block = {
|
||||||
'k', 'c', 't', 'l', 0x00, 0x00, 0x00, 0x00,
|
'k', 'c', 't', 'l', 0x00, 0x00, 0x00, 0x00,
|
||||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
};
|
};
|
||||||
@@ -138,18 +144,16 @@ void UpdateKeyControlBlock(
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Key Control Block is no longer encrypted, so no need to set IV.
|
return key_control_block;
|
||||||
key_control->set_key_control_block(key_control_block.data(),
|
|
||||||
key_control_block.size());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string GenerateCoreMessage(
|
std::string GenerateCoreMessage(const std::string& serialized_request,
|
||||||
const std::string& serialized_request,
|
const std::string& serialized_license_response,
|
||||||
const std::string& serialized_license_response) {
|
uint16_t api_major_version,
|
||||||
constexpr uint16_t api_major_version = 16;
|
uint16_t api_minor_version,
|
||||||
constexpr uint16_t api_minor_version = 5;
|
bool use_padding) {
|
||||||
static_assert(api_major_version == ODK_MAJOR_VERSION,
|
DCHECK_EQ(api_major_version, ODK_MAJOR_VERSION)
|
||||||
"Verify ODK library is compatible.");
|
<< "Verify ODK library is compatible.";
|
||||||
constexpr uint32_t session_id = 0xcafebabe;
|
constexpr uint32_t session_id = 0xcafebabe;
|
||||||
constexpr uint32_t nonce = 0xdeadbeef;
|
constexpr uint32_t nonce = 0xdeadbeef;
|
||||||
ODK_NonceValues nonce_values{api_minor_version, api_major_version, nonce,
|
ODK_NonceValues nonce_values{api_minor_version, api_major_version, nonce,
|
||||||
@@ -188,27 +192,109 @@ std::string GenerateCoreMessage(
|
|||||||
std::string oemcrypto_core_message;
|
std::string oemcrypto_core_message;
|
||||||
CHECK(oemcrypto_core_message::serialize::CreateCoreLicenseResponseFromProto(
|
CHECK(oemcrypto_core_message::serialize::CreateCoreLicenseResponseFromProto(
|
||||||
serialized_license_response, core_request, core_message_hash,
|
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;
|
return oemcrypto_core_message;
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace
|
std::vector<uint8_t> GetPadding(TestLicenseBuilder::Padding padding) {
|
||||||
|
if (padding == TestLicenseBuilder::Padding::kPKSC8) {
|
||||||
// static
|
|
||||||
std::vector<uint8_t> TestLicenseBuilder::NoPadding() {
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
|
|
||||||
// static
|
|
||||||
std::vector<uint8_t> TestLicenseBuilder::PKSC8Padding() {
|
|
||||||
return {
|
return {
|
||||||
0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10,
|
0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10,
|
||||||
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<uint8_t> 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<uint8_t> 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
|
// static
|
||||||
std::vector<uint8_t> TestLicenseBuilder::DefaultSigningKey() {
|
TestLicenseBuilder::SigningKey TestLicenseBuilder::DefaultSigningKey() {
|
||||||
return {
|
return {
|
||||||
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
|
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,
|
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);
|
crypto_util::kWrappingKeySizeBits);
|
||||||
}
|
}
|
||||||
|
|
||||||
void TestLicenseBuilder::AddSigningKey(const std::vector<uint8_t>& key,
|
void TestLicenseBuilder::AddSigningKey(const SigningKey& key) {
|
||||||
const std::vector<uint8_t>& padding) {
|
signing_keys_.push_back(key);
|
||||||
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<uint8_t> 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::AddStubbedContentKey() {
|
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;
|
video_widevine::License_KeyContainer_SecurityLevel_SW_SECURE_CRYPTO;
|
||||||
auto* container = response_.add_key();
|
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};
|
||||||
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());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void TestLicenseBuilder::AddContentKey(
|
void TestLicenseBuilder::AddContentKey(
|
||||||
video_widevine::License_KeyContainer_SecurityLevel level,
|
video_widevine::License_KeyContainer_SecurityLevel level,
|
||||||
const std::vector<uint8_t>& key_id,
|
const KeyId& id,
|
||||||
const std::vector<uint8_t>& key,
|
const AesKey& key) {
|
||||||
const std::vector<uint8_t>& padding) {
|
content_keys_.emplace_back();
|
||||||
DCHECK_GT(key_id.size(), 0u);
|
auto& key_data = content_keys_.back();
|
||||||
DCHECK_EQ(key.size(), 16u);
|
key_data.level = level;
|
||||||
|
key_data.id = id;
|
||||||
auto* container = response_.add_key();
|
key_data.key = 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<uint8_t> 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());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void TestLicenseBuilder::AddOperatorSessionKey(
|
void TestLicenseBuilder::AddOperatorSessionKey(const KeyId& id) {
|
||||||
const std::vector<uint8_t>& key_id) {
|
operator_session_keys_.push_back(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::Build(const RsaPublicKey& public_key,
|
void TestLicenseBuilder::Build(const RsaPublicKey& public_key,
|
||||||
License* license) const {
|
License* license) const {
|
||||||
DCHECK(license);
|
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(
|
std::string signing_key = crypto_util::DeriveKey(
|
||||||
session_key_, crypto_util::kSigningKeyLabel, serialized_request_,
|
session_key_, crypto_util::kSigningKeyLabel, serialized_request_,
|
||||||
crypto_util::kSigningKeySizeBits * 2);
|
crypto_util::kSigningKeySizeBits * 2);
|
||||||
@@ -352,11 +403,16 @@ void TestLicenseBuilder::Build(const RsaPublicKey& public_key,
|
|||||||
std::string session_key_str;
|
std::string session_key_str;
|
||||||
CHECK(public_key.Encrypt(session_key_, &session_key_str));
|
CHECK(public_key.Encrypt(session_key_, &session_key_str));
|
||||||
|
|
||||||
const std::string oemcrypto_core_message =
|
std::string oemcrypto_core_message;
|
||||||
use_odk_ ? GenerateCoreMessage(serialized_request_, message_str)
|
if (settings_.odk_version != OdkVersion::kNone) {
|
||||||
: std::string();
|
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(
|
const std::string signature_str = crypto_util::CreateSignatureHmacSha256(
|
||||||
signing_key, oemcrypto_core_message + message_str);
|
signing_key, oemcrypto_core_message + message_str);
|
||||||
|
|
||||||
|
|||||||
@@ -3,10 +3,11 @@
|
|||||||
#ifndef WHITEBOX_API_LICENSE_BUILDER_H_
|
#ifndef WHITEBOX_API_LICENSE_BUILDER_H_
|
||||||
#define WHITEBOX_API_LICENSE_BUILDER_H_
|
#define WHITEBOX_API_LICENSE_BUILDER_H_
|
||||||
|
|
||||||
#include <stdint.h>
|
#include <cstdint>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
|
#include "api/test_key_types.h"
|
||||||
#include "cdm/protos/license_protocol.pb.h"
|
#include "cdm/protos/license_protocol.pb.h"
|
||||||
#include "crypto_utils/rsa_key.h"
|
#include "crypto_utils/rsa_key.h"
|
||||||
|
|
||||||
@@ -27,6 +28,23 @@ struct License {
|
|||||||
|
|
||||||
class TestLicenseBuilder {
|
class TestLicenseBuilder {
|
||||||
public:
|
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<uint8_t, 64>;
|
||||||
|
|
||||||
|
enum class Padding {
|
||||||
|
kNone,
|
||||||
|
kPKSC8,
|
||||||
|
};
|
||||||
|
|
||||||
enum class RemoteAttestation {
|
enum class RemoteAttestation {
|
||||||
kUnavailable,
|
kUnavailable,
|
||||||
kVerified,
|
kVerified,
|
||||||
@@ -39,40 +57,48 @@ class TestLicenseBuilder {
|
|||||||
kOther,
|
kOther,
|
||||||
};
|
};
|
||||||
|
|
||||||
// Returns padding data the can be used as |padding| when calling
|
// ODK version of the `core_message`.
|
||||||
// AddSigningKey() or AddContentKey().
|
enum class OdkVersion {
|
||||||
static std::vector<uint8_t> NoPadding();
|
kNone, // No `core_message`
|
||||||
static std::vector<uint8_t> PKSC8Padding();
|
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().
|
// Returns a default signing key that can be used with AddSigningKey().
|
||||||
static std::vector<uint8_t> DefaultSigningKey();
|
static SigningKey DefaultSigningKey();
|
||||||
|
|
||||||
TestLicenseBuilder();
|
TestLicenseBuilder();
|
||||||
|
|
||||||
void AddSigningKey(const std::vector<uint8_t>& key,
|
void AddSigningKey(const SigningKey& key);
|
||||||
const std::vector<uint8_t>& padding);
|
|
||||||
|
|
||||||
// Add a content key so that there is some key in the license. This should not
|
// Add a content key so that there is some key in the license. This should not
|
||||||
// be used with AddContentKey().
|
// be used with AddContentKey().
|
||||||
void AddStubbedContentKey();
|
void AddStubbedContentKey();
|
||||||
|
|
||||||
void AddContentKey(video_widevine::License_KeyContainer_SecurityLevel level,
|
void AddContentKey(video_widevine::License_KeyContainer_SecurityLevel level,
|
||||||
const std::vector<uint8_t>& key_id,
|
const KeyId& id,
|
||||||
const std::vector<uint8_t>& key,
|
const AesKey& key);
|
||||||
const std::vector<uint8_t>& padding);
|
|
||||||
|
|
||||||
// The key id will matter as we will need to reference it, but the key won't
|
// 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
|
// 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.
|
// key can't be used as a content key.
|
||||||
void AddOperatorSessionKey(const std::vector<uint8_t>& key_id);
|
void AddOperatorSessionKey(const KeyId& key_id);
|
||||||
|
|
||||||
void SetRemoteAttestation(RemoteAttestation setting);
|
Settings& GetSettings() { return settings_; }
|
||||||
|
const Settings& GetSettings() const { return settings_; }
|
||||||
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);
|
|
||||||
|
|
||||||
// Gets the serialized license request and response (in components) that would
|
// Gets the serialized license request and response (in components) that would
|
||||||
// have been used in the license exchange.
|
// have been used in the license exchange.
|
||||||
@@ -85,7 +111,12 @@ class TestLicenseBuilder {
|
|||||||
video_widevine::License response_;
|
video_widevine::License response_;
|
||||||
std::string serialized_request_;
|
std::string serialized_request_;
|
||||||
std::string container_key_;
|
std::string container_key_;
|
||||||
bool use_odk_ = false;
|
|
||||||
|
Settings settings_;
|
||||||
|
|
||||||
|
std::vector<ContentKey> content_keys_;
|
||||||
|
std::vector<SigningKey> signing_keys_;
|
||||||
|
std::vector<KeyId> operator_session_keys_;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace widevine
|
} // namespace widevine
|
||||||
|
|||||||
@@ -441,27 +441,19 @@ const size_t kGoldenDataSize = sizeof(kGoldenDataSize);
|
|||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
std::vector<uint8_t> DataSource::Get(size_t size) {
|
std::vector<uint8_t> DataSource::Get(size_t size) {
|
||||||
std::vector<uint8_t> buffer(size);
|
std::vector<uint8_t> data(size);
|
||||||
|
|
||||||
size_t write_head = 0;
|
for (size_t i = 0; i < size; i++) {
|
||||||
|
data[i] = Get();
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return buffer;
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t DataSource::Get() {
|
||||||
|
uint8_t x = kGoldenData[read_head_];
|
||||||
|
read_head_ = (read_head_ + 1) % kGoldenDataSize;
|
||||||
|
return x;
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace widevine
|
} // namespace widevine
|
||||||
|
|||||||
@@ -3,18 +3,31 @@
|
|||||||
#ifndef WHITEBOX_BENCHMARKING_DATA_SOURCE_H_
|
#ifndef WHITEBOX_BENCHMARKING_DATA_SOURCE_H_
|
||||||
#define WHITEBOX_BENCHMARKING_DATA_SOURCE_H_
|
#define WHITEBOX_BENCHMARKING_DATA_SOURCE_H_
|
||||||
|
|
||||||
#include <stddef.h>
|
#include <array>
|
||||||
#include <stdint.h>
|
#include <cstddef>
|
||||||
|
#include <cstdint>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
namespace widevine {
|
namespace widevine {
|
||||||
|
|
||||||
class DataSource {
|
class DataSource {
|
||||||
public:
|
public:
|
||||||
|
template <size_t size>
|
||||||
|
std::array<uint8_t, size> Get() {
|
||||||
|
std::array<uint8_t, size> data;
|
||||||
|
|
||||||
|
for (size_t i = 0; i < size; i++) {
|
||||||
|
data[i] = Get();
|
||||||
|
}
|
||||||
|
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
std::vector<uint8_t> Get(size_t size);
|
std::vector<uint8_t> Get(size_t size);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
uint8_t Get();
|
||||||
|
|
||||||
size_t read_head_ = 0;
|
size_t read_head_ = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
4
whitebox/external/odk.BUILD
vendored
4
whitebox/external/odk.BUILD
vendored
@@ -23,6 +23,8 @@ cc_library(
|
|||||||
"oemcrypto/odk/src/odk_timer.c",
|
"oemcrypto/odk/src/odk_timer.c",
|
||||||
"oemcrypto/odk/src/odk_util.c",
|
"oemcrypto/odk/src/odk_util.c",
|
||||||
"oemcrypto/odk/src/odk_util.h",
|
"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",
|
"oemcrypto/odk/src/serialization_base.c",
|
||||||
":odk_common_hdrs",
|
":odk_common_hdrs",
|
||||||
],
|
],
|
||||||
@@ -31,6 +33,8 @@ cc_library(
|
|||||||
"oemcrypto/odk/include/odk.h",
|
"oemcrypto/odk/include/odk.h",
|
||||||
"oemcrypto/odk/include/odk_structs.h",
|
"oemcrypto/odk/include/odk_structs.h",
|
||||||
"oemcrypto/odk/include/odk_target.h",
|
"oemcrypto/odk/include/odk_target.h",
|
||||||
|
"oemcrypto/odk/include/odk_message.h",
|
||||||
|
"oemcrypto/odk/include/odk_attributes.h",
|
||||||
],
|
],
|
||||||
copts = ["-std=c99"],
|
copts = ["-std=c99"],
|
||||||
includes = [
|
includes = [
|
||||||
|
|||||||
@@ -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(
|
cc_library(
|
||||||
name = "license_private_key",
|
name = "license_private_key",
|
||||||
hdrs = [
|
hdrs = [
|
||||||
@@ -42,7 +53,7 @@ cc_library(
|
|||||||
"//crypto_utils:aes_ctr_encryptor",
|
"//crypto_utils:aes_ctr_encryptor",
|
||||||
"//crypto_utils:crypto_util",
|
"//crypto_utils:crypto_util",
|
||||||
"//crypto_utils:rsa_key",
|
"//crypto_utils:rsa_key",
|
||||||
"//external:odk",
|
":odk",
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -18,7 +18,9 @@
|
|||||||
#include "crypto_utils/rsa_key.h"
|
#include "crypto_utils/rsa_key.h"
|
||||||
#include "impl/reference/license_private_key.h"
|
#include "impl/reference/license_private_key.h"
|
||||||
#include "impl/reference/memory_util.h"
|
#include "impl/reference/memory_util.h"
|
||||||
|
#include "impl/reference/odk.h"
|
||||||
#include "oemcrypto/odk/include/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/include/odk_structs.h"
|
||||||
#include "oemcrypto/odk/src/odk_serialize.h"
|
#include "oemcrypto/odk/src/odk_serialize.h"
|
||||||
#include "oemcrypto/odk/src/serialization_base.h"
|
#include "oemcrypto/odk/src/serialization_base.h"
|
||||||
@@ -54,7 +56,9 @@ bool Decrypt(AesCbcDecryptor& decryptor,
|
|||||||
const std::string& iv,
|
const std::string& iv,
|
||||||
const std::string& encrypted,
|
const std::string& encrypted,
|
||||||
std::string* decrypted) {
|
std::string* decrypted) {
|
||||||
|
DCHECK_EQ(iv.size(), 16u);
|
||||||
DCHECK_GE(decrypted->size(), encrypted.size());
|
DCHECK_GE(decrypted->size(), encrypted.size());
|
||||||
|
|
||||||
return decryptor.Decrypt(
|
return decryptor.Decrypt(
|
||||||
reinterpret_cast<const uint8_t*>(iv.data()), iv.size(),
|
reinterpret_cast<const uint8_t*>(iv.data()), iv.size(),
|
||||||
reinterpret_cast<const uint8_t*>(encrypted.data()), encrypted.size(),
|
reinterpret_cast<const uint8_t*>(encrypted.data()), encrypted.size(),
|
||||||
@@ -77,8 +81,9 @@ std::string ExtractItem(const OEMCrypto_Substring& item,
|
|||||||
return buffer.substr(item.offset, item.length);
|
return buffer.substr(item.offset, item.length);
|
||||||
}
|
}
|
||||||
|
|
||||||
video_widevine::License_KeyContainer_SecurityLevel ExtractLevel(
|
bool ExtractLevel(
|
||||||
const std::string& key_control_block) {
|
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
|
// The key control block is an 128 bit structure containing the following
|
||||||
// fields. The fields are defined to be in big-endian byte order.
|
// 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
|
// 2 = HW_SECURE_CRYPTO
|
||||||
// 3 = HW_SECURE_DECODE or HW_SECURE_ALL
|
// 3 = HW_SECURE_DECODE or HW_SECURE_ALL
|
||||||
|
|
||||||
// Limited checks to verify that this is proper key control block.
|
// Make sure this is a valid key control block. Ideally the signature
|
||||||
// If not valid, assume it's the highest level.
|
// verification should have taken care of this.
|
||||||
if ((key_control_block.size() != 16u) || (key_control_block[0] != 'k') ||
|
if ((key_control_block.size() != 16u) || (key_control_block[0] != 'k') ||
|
||||||
(key_control_block[1] != 'c')) {
|
(key_control_block[1] != 'c')) {
|
||||||
return video_widevine::License_KeyContainer_SecurityLevel_HW_SECURE_DECODE;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Extract bits 26..27 from Control Bits.
|
// Extract bits 26..27 from Control Bits.
|
||||||
int security_level = (key_control_block[12] & 0x0C) >> 2;
|
switch ((key_control_block[12] & 0x0C) >> 2) {
|
||||||
switch (security_level) {
|
|
||||||
case 0:
|
case 0:
|
||||||
return video_widevine::
|
*security_level =
|
||||||
License_KeyContainer_SecurityLevel_SW_SECURE_CRYPTO;
|
video_widevine::License_KeyContainer_SecurityLevel_SW_SECURE_CRYPTO;
|
||||||
case 1:
|
case 1:
|
||||||
return video_widevine::
|
*security_level =
|
||||||
License_KeyContainer_SecurityLevel_SW_SECURE_DECODE;
|
video_widevine::License_KeyContainer_SecurityLevel_SW_SECURE_DECODE;
|
||||||
case 2:
|
case 2:
|
||||||
return video_widevine::
|
*security_level =
|
||||||
License_KeyContainer_SecurityLevel_HW_SECURE_CRYPTO;
|
video_widevine::License_KeyContainer_SecurityLevel_HW_SECURE_CRYPTO;
|
||||||
default:
|
default:
|
||||||
return video_widevine::
|
*security_level =
|
||||||
License_KeyContainer_SecurityLevel_HW_SECURE_DECODE;
|
video_widevine::License_KeyContainer_SecurityLevel_HW_SECURE_DECODE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Creates and returns a ContentKey based on the values provided.
|
// Creates and returns a ContentKey based on the values provided.
|
||||||
@@ -218,11 +224,11 @@ const uint8_t kCTRSecretStringPattern[] = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
std::vector<uint8_t> GetSecretStringFor(WB_CipherMode mode) {
|
std::vector<uint8_t> GetSecretStringFor(WB_CipherMode mode) {
|
||||||
return mode == WB_CIPHER_MODE_CBC ?
|
return mode == WB_CIPHER_MODE_CBC
|
||||||
std::vector<uint8_t>(
|
? std::vector<uint8_t>(
|
||||||
kCBCSecretStringPattern,
|
kCBCSecretStringPattern,
|
||||||
kCBCSecretStringPattern + sizeof(kCBCSecretStringPattern)) :
|
kCBCSecretStringPattern + sizeof(kCBCSecretStringPattern))
|
||||||
std::vector<uint8_t>(
|
: std::vector<uint8_t>(
|
||||||
kCTRSecretStringPattern,
|
kCTRSecretStringPattern,
|
||||||
kCTRSecretStringPattern + sizeof(kCTRSecretStringPattern));
|
kCTRSecretStringPattern + sizeof(kCTRSecretStringPattern));
|
||||||
}
|
}
|
||||||
@@ -497,35 +503,14 @@ WB_Result WB_License_ProcessLicenseResponse(WB_License_Whitebox* whitebox,
|
|||||||
return WB_RESULT_INVALID_SIGNATURE;
|
return WB_RESULT_INVALID_SIGNATURE;
|
||||||
}
|
}
|
||||||
|
|
||||||
// If |core_message| is provided, parse it into |parsed_license| and validate
|
ODKContext odk_context;
|
||||||
// that it's an appropriate version.
|
|
||||||
uint16_t odk_major_version = 0;
|
|
||||||
uint16_t odk_minor_version = 0;
|
|
||||||
ODK_ParsedLicense parsed_license;
|
|
||||||
if (core_message_size > 0) {
|
if (core_message_size > 0) {
|
||||||
// Decode |core_message|.
|
auto result =
|
||||||
Message* msg = NULL;
|
GetODKContext(combined_message_str, core_message_size, &odk_context);
|
||||||
AllocateMessage(&msg, message_block);
|
|
||||||
InitMessage(msg,
|
|
||||||
reinterpret_cast<uint8_t*>(
|
|
||||||
const_cast<char*>(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);
|
|
||||||
|
|
||||||
ODK_LicenseResponse license_response = {{{0}}, &parsed_license, {0}};
|
if (result != WB_RESULT_OK) {
|
||||||
Unpack_ODK_LicenseResponse(msg, &license_response);
|
return result;
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -539,14 +524,16 @@ WB_Result WB_License_ProcessLicenseResponse(WB_License_Whitebox* whitebox,
|
|||||||
std::vector<std::string> client_renewal_keys;
|
std::vector<std::string> client_renewal_keys;
|
||||||
|
|
||||||
// Even if |core_message| is provided, only ODK v16.5 and later support the
|
// 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
|
// fields needed. If an older API is used, ignore it and use the protobuf as
|
||||||
// as if |core_message| was not provided.
|
// if |core_message| was not provided.
|
||||||
if (IsOdkVersionSupported(odk_major_version, odk_minor_version)) {
|
if (odk_context.is_valid && !HasEncryptedKeyControlBlock(odk_context) &&
|
||||||
|
IsOdkVersionSupported(odk_context.major_version,
|
||||||
|
odk_context.minor_version)) {
|
||||||
// Start by extracting the signing key.
|
// Start by extracting the signing key.
|
||||||
const std::string signing_key_encrypted =
|
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 =
|
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()) {
|
if (!signing_key_encrypted.empty() && !signing_key_iv.empty()) {
|
||||||
std::string unwrapped_signing_key(signing_key_encrypted);
|
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.
|
// Now extract all the content keys.
|
||||||
for (size_t i = 0; i < parsed_license.key_array_length; ++i) {
|
for (size_t i = 0; i < odk_context.license.key_array_length; ++i) {
|
||||||
const OEMCrypto_KeyObject& key = parsed_license.key_array[i];
|
const OEMCrypto_KeyObject& key = odk_context.license.key_array[i];
|
||||||
|
|
||||||
const std::string key_id = ExtractItem(key.key_id, message_str);
|
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 iv = ExtractItem(key.key_data_iv, message_str);
|
||||||
const std::string wrapped_key = ExtractItem(key.key_data, message_str);
|
DCHECK_EQ(iv.size(), 16u);
|
||||||
std::string unwrapped_key(wrapped_key);
|
|
||||||
|
|
||||||
|
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)) {
|
if (!Decrypt(decryptor, iv, wrapped_key, &unwrapped_key)) {
|
||||||
DVLOG(1) << "Invalid parameter: Invalid key.key_data.";
|
DVLOG(1) << "Invalid parameter: Invalid key.key_data.";
|
||||||
return WB_RESULT_INVALID_PARAMETER;
|
return WB_RESULT_INVALID_PARAMETER;
|
||||||
@@ -595,9 +587,12 @@ WB_Result WB_License_ProcessLicenseResponse(WB_License_Whitebox* whitebox,
|
|||||||
// this.
|
// this.
|
||||||
const std::string key_control_block =
|
const std::string key_control_block =
|
||||||
ExtractItem(key.key_control, message_str);
|
ExtractItem(key.key_control, message_str);
|
||||||
content_keys[key_id] =
|
|
||||||
CreateContentKey(ExtractLevel(key_control_block),
|
video_widevine::License_KeyContainer_SecurityLevel security_level;
|
||||||
/* is_hw_verified */ false, unwrapped_key);
|
CHECK(ExtractLevel(key_control_block, &security_level));
|
||||||
|
|
||||||
|
content_keys[key_id] = CreateContentKey(
|
||||||
|
security_level, /* is_hw_verified */ false, unwrapped_key);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// Core message not provided or an old version, so extract the keys from
|
// Core message not provided or an old version, so extract the keys from
|
||||||
|
|||||||
73
whitebox/impl/reference/odk.cc
Normal file
73
whitebox/impl/reference/odk.cc
Normal file
@@ -0,0 +1,73 @@
|
|||||||
|
// cOpyright 2021 Google LLC. All Rights Reserved.
|
||||||
|
|
||||||
|
#include "impl/reference/odk.h"
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#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<uint8_t> 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;
|
||||||
|
}
|
||||||
25
whitebox/impl/reference/odk.h
Normal file
25
whitebox/impl/reference/odk.h
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
// Copyright 2021 Google LLC. All Rights Reserved.
|
||||||
|
|
||||||
|
#ifndef WHITEBOX_IMPL_REFERENCE_ODK_H_
|
||||||
|
#define WHITEBOX_IMPL_REFERENCE_ODK_H_
|
||||||
|
|
||||||
|
#include <cstdint>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
#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_
|
||||||
Reference in New Issue
Block a user