Add "Missing Security Level" Test
It is possible for the key security level to be omitted from the key container. When this happens, SW_SECURE_CRYPTO should be used as the key's security level (as per the protobuf definition). This only matters when reading the security level from the key container since the security level must appear in the key control block. This change adds a test that will purposely omit the key security level from the key container.
This commit is contained in:
@@ -138,6 +138,7 @@ cc_library(
|
||||
"license_whitebox_masked_decrypt_test.cc",
|
||||
"license_whitebox_process_license_response_core_message_test.cc",
|
||||
"license_whitebox_process_license_response_test.cc",
|
||||
"license_whitebox_security_level_test.cc",
|
||||
"license_whitebox_sign_license_request_test.cc",
|
||||
"license_whitebox_sign_renewal_request_test.cc",
|
||||
"license_whitebox_test_base.cc",
|
||||
|
||||
@@ -14,10 +14,6 @@ std::vector<uint8_t> GetValidAeadInitData();
|
||||
// Returns init data that the aead white-box will reject.
|
||||
std::vector<uint8_t> GetInvalidAeadInitData();
|
||||
|
||||
// Returns the matching public key for the data returned by
|
||||
// GetLicenseInitData(). The format is a DER encoded PKCS#1 RSAPublicKey.
|
||||
std::vector<uint8_t> GetMatchingLicensePublicKey();
|
||||
|
||||
} // namespace widevine
|
||||
|
||||
#endif // WHITEBOX_API_AEAD_TEST_DATA_H_
|
||||
|
||||
@@ -27,19 +27,19 @@ GoldenData::GoldenData() {
|
||||
};
|
||||
|
||||
cbc_crypto_key_ = {
|
||||
video_widevine::License_KeyContainer_SecurityLevel_SW_SECURE_CRYPTO,
|
||||
SecurityLevel::kSoftwareSecureCrypto,
|
||||
{0xFF, 0, 0, 0},
|
||||
&cbc_content_,
|
||||
};
|
||||
|
||||
cbc_decode_key_ = {
|
||||
video_widevine::License_KeyContainer_SecurityLevel_SW_SECURE_DECODE,
|
||||
SecurityLevel::kSoftwareSecureDecode,
|
||||
{0xFF, 1, 0, 0},
|
||||
&cbc_content_,
|
||||
};
|
||||
|
||||
cbc_hardware_key_ = {
|
||||
video_widevine::License_KeyContainer_SecurityLevel_HW_SECURE_CRYPTO,
|
||||
SecurityLevel::kHardwareSecureCrypto,
|
||||
{0xFF, 2, 0, 0},
|
||||
&cbc_content_,
|
||||
};
|
||||
@@ -65,19 +65,19 @@ GoldenData::GoldenData() {
|
||||
};
|
||||
|
||||
ctr_crypto_key_ = {
|
||||
video_widevine::License_KeyContainer_SecurityLevel_SW_SECURE_CRYPTO,
|
||||
SecurityLevel::kSoftwareSecureCrypto,
|
||||
{0xFF, 3, 0, 0},
|
||||
&ctr_content_,
|
||||
};
|
||||
|
||||
ctr_decode_key_ = {
|
||||
video_widevine::License_KeyContainer_SecurityLevel_SW_SECURE_DECODE,
|
||||
SecurityLevel::kSoftwareSecureDecode,
|
||||
{0xFF, 4, 0, 0},
|
||||
&ctr_content_,
|
||||
};
|
||||
|
||||
ctr_hardware_key_ = {
|
||||
video_widevine::License_KeyContainer_SecurityLevel_HW_SECURE_CRYPTO,
|
||||
SecurityLevel::kHardwareSecureCrypto,
|
||||
{0xFF, 5, 0, 0},
|
||||
&ctr_content_,
|
||||
};
|
||||
|
||||
@@ -22,7 +22,7 @@ class GoldenData {
|
||||
};
|
||||
|
||||
struct Key {
|
||||
video_widevine::License_KeyContainer_SecurityLevel level;
|
||||
SecurityLevel level;
|
||||
KeyId id;
|
||||
const Content* content;
|
||||
};
|
||||
|
||||
@@ -25,21 +25,20 @@ void LicenseWhiteboxBenchmark::SetUp() {
|
||||
iv_ = data_source_.Get<kBlockSize>();
|
||||
|
||||
const auto public_key_data = GetLicensePublicKey();
|
||||
public_key_.reset(widevine::RsaPublicKey::Create(
|
||||
public_key_.reset(RsaPublicKey::Create(
|
||||
std::string(public_key_data.begin(), public_key_data.end())));
|
||||
ASSERT_TRUE(public_key_);
|
||||
}
|
||||
|
||||
License LicenseWhiteboxBenchmark::CreateLicense() const {
|
||||
widevine::TestLicenseBuilder license_builder;
|
||||
TestLicenseBuilder license_builder;
|
||||
license_builder.AddSigningKey(TestLicenseBuilder::DefaultSigningKey());
|
||||
// Use secure crypto as it will work with both Decrypt() and
|
||||
// MaskedDecrypt().
|
||||
license_builder.AddContentKey(
|
||||
video_widevine::License_KeyContainer_SecurityLevel_SW_SECURE_CRYPTO,
|
||||
key_id_, key_);
|
||||
license_builder.AddContentKey(SecurityLevel::kSoftwareSecureCrypto, key_id_,
|
||||
key_);
|
||||
|
||||
widevine::License license;
|
||||
License license;
|
||||
license_builder.Build(*public_key_, &license);
|
||||
|
||||
return license;
|
||||
|
||||
100
whitebox/api/license_whitebox_security_level_test.cc
Normal file
100
whitebox/api/license_whitebox_security_level_test.cc
Normal file
@@ -0,0 +1,100 @@
|
||||
// Copyright 2020 Google LLC. All Rights Reserved.
|
||||
|
||||
#include "api/license_whitebox.h"
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "api/golden_data.h"
|
||||
#include "api/license_whitebox_test_base.h"
|
||||
#include "api/test_license_builder.h"
|
||||
#include "crypto_utils/rsa_key.h"
|
||||
#include "testing/gtest/include/gtest/gtest.h"
|
||||
|
||||
namespace widevine {
|
||||
|
||||
using KeyControlBlock = TestLicenseBuilder::KeyControlBlock;
|
||||
|
||||
class LicenseWhiteboxSecurityLevelTest
|
||||
: public LicenseWhiteboxTestBase,
|
||||
public testing::WithParamInterface<
|
||||
std::tuple<KeyControlBlock, SecurityLevel>> {
|
||||
protected:
|
||||
void SetUp() override {
|
||||
LicenseWhiteboxTestBase::SetUp();
|
||||
|
||||
std::tie(key_control_block_, security_level_) = GetParam();
|
||||
}
|
||||
|
||||
KeyControlBlock key_control_block_;
|
||||
SecurityLevel security_level_;
|
||||
|
||||
std::vector<uint8_t> plaintext_;
|
||||
size_t plaintext_size_;
|
||||
};
|
||||
|
||||
TEST_P(LicenseWhiteboxSecurityLevelTest, CanLoadAndUseKey) {
|
||||
TestLicenseBuilder builder;
|
||||
builder.GetSettings().key_control_block = key_control_block_;
|
||||
builder.AddContentKey(security_level_, golden_data_.CBCCryptoKey().id,
|
||||
golden_data_.CBCCryptoKey().content->key);
|
||||
|
||||
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);
|
||||
|
||||
plaintext_size_ = golden_data_.CBCCryptoKey().content->ciphertext.size();
|
||||
plaintext_.resize(plaintext_size_);
|
||||
|
||||
// Make sure that the key was loaded successfully even if the security level
|
||||
// was missing. When missing, SOFTWARE CRYPTO should be used.
|
||||
//
|
||||
// Use EXPECT instead of ASSERT so that we can see the result of both decrypt
|
||||
// calls before failing the test.
|
||||
|
||||
EXPECT_EQ(
|
||||
WB_License_Decrypt(whitebox_, WB_CIPHER_MODE_CBC,
|
||||
golden_data_.CBCCryptoKey().id.data(),
|
||||
golden_data_.CBCCryptoKey().id.size(),
|
||||
golden_data_.CBCCryptoKey().content->ciphertext.data(),
|
||||
golden_data_.CBCCryptoKey().content->ciphertext.size(),
|
||||
golden_data_.CBCCryptoKey().content->iv.data(),
|
||||
golden_data_.CBCCryptoKey().content->iv.size(),
|
||||
plaintext_.data(), &plaintext_size_),
|
||||
WB_RESULT_OK);
|
||||
|
||||
plaintext_size_ = golden_data_.CBCCryptoKey().content->ciphertext.size();
|
||||
plaintext_.resize(plaintext_size_);
|
||||
|
||||
EXPECT_EQ(
|
||||
WB_License_MaskedDecrypt(
|
||||
whitebox_, WB_CIPHER_MODE_CBC, golden_data_.CBCCryptoKey().id.data(),
|
||||
golden_data_.CBCCryptoKey().id.size(),
|
||||
golden_data_.CBCCryptoKey().content->ciphertext.data(),
|
||||
golden_data_.CBCCryptoKey().content->ciphertext.size(),
|
||||
golden_data_.CBCCryptoKey().content->iv.data(),
|
||||
golden_data_.CBCCryptoKey().content->iv.size(), plaintext_.data(),
|
||||
&plaintext_size_),
|
||||
WB_RESULT_OK);
|
||||
}
|
||||
|
||||
INSTANTIATE_TEST_SUITE_P(
|
||||
AllCombinations,
|
||||
LicenseWhiteboxSecurityLevelTest,
|
||||
::testing::Combine(
|
||||
::testing::Values(KeyControlBlock::kNone,
|
||||
KeyControlBlock::kClear,
|
||||
KeyControlBlock::kEncrypted),
|
||||
::testing::Values(SecurityLevel::kUndefined,
|
||||
SecurityLevel::kSoftwareSecureCrypto)));
|
||||
|
||||
} // namespace widevine
|
||||
@@ -18,6 +18,18 @@ using AesKey = std::array<uint8_t, 16>;
|
||||
// AES IV can only be 16 bytes.
|
||||
using AesIv = std::array<uint8_t, 16>;
|
||||
|
||||
// This enum mirrors the enum used in the protobuf, but allows us to add
|
||||
// value for "undefined" which signals that we don't want the security
|
||||
// level to appear in the license.
|
||||
enum class SecurityLevel {
|
||||
kSoftwareSecureCrypto,
|
||||
kSoftwareSecureDecode,
|
||||
kHardwareSecureCrypto,
|
||||
kHardwareSecureDecode,
|
||||
kHardwareSecureAll,
|
||||
kUndefined,
|
||||
};
|
||||
|
||||
} // namespace widevine
|
||||
|
||||
#endif // WHITEBOX_API_TEST_KEY_TYPES_H_
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
|
||||
#include <array>
|
||||
#include <ctime>
|
||||
#include <map>
|
||||
|
||||
#include "base/check.h"
|
||||
#include "base/check_op.h"
|
||||
@@ -109,6 +110,34 @@ std::string DeriveIV(const In& context) {
|
||||
return crypto_util::DeriveIv(context_str);
|
||||
}
|
||||
|
||||
video_widevine::License_KeyContainer_SecurityLevel SecurityLevelToProto(
|
||||
SecurityLevel level) {
|
||||
switch (level) {
|
||||
case SecurityLevel::kSoftwareSecureCrypto:
|
||||
return video_widevine::
|
||||
License_KeyContainer_SecurityLevel_SW_SECURE_CRYPTO;
|
||||
case SecurityLevel::kSoftwareSecureDecode:
|
||||
return video_widevine::
|
||||
License_KeyContainer_SecurityLevel_SW_SECURE_DECODE;
|
||||
case SecurityLevel::kHardwareSecureCrypto:
|
||||
return video_widevine::
|
||||
License_KeyContainer_SecurityLevel_HW_SECURE_CRYPTO;
|
||||
case SecurityLevel::kHardwareSecureDecode:
|
||||
return video_widevine::
|
||||
License_KeyContainer_SecurityLevel_HW_SECURE_DECODE;
|
||||
case SecurityLevel::kHardwareSecureAll:
|
||||
return video_widevine::License_KeyContainer_SecurityLevel_HW_SECURE_ALL;
|
||||
|
||||
case SecurityLevel::kUndefined:
|
||||
// If the security level is undefined (in the key container) it would
|
||||
// default to software secure crypto.
|
||||
return video_widevine::
|
||||
License_KeyContainer_SecurityLevel_SW_SECURE_CRYPTO;
|
||||
}
|
||||
|
||||
CHECK(false) << "Unsupported security level";
|
||||
}
|
||||
|
||||
KeyControlBlock CreateKeyControlBlock(
|
||||
video_widevine::License_KeyContainer_SecurityLevel level,
|
||||
video_widevine::License_KeyContainer_KeyControl* key_control) {
|
||||
@@ -213,7 +242,13 @@ void AddContentKeyToContainer(const TestLicenseBuilder::ContentKey& key_data,
|
||||
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);
|
||||
|
||||
// If the security level is undefined it means that no security level should
|
||||
// appear in the key container. When reading the key container, it will be
|
||||
// softare secure crypto as that is what the protobuf defaults to.
|
||||
if (key_data.level != SecurityLevel::kUndefined) {
|
||||
container->set_level(SecurityLevelToProto(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
|
||||
@@ -228,26 +263,50 @@ void AddContentKeyToContainer(const TestLicenseBuilder::ContentKey& key_data,
|
||||
|
||||
container->set_key(Encrypt(container_key, key_iv, key));
|
||||
|
||||
const auto key_control_block =
|
||||
CreateKeyControlBlock(key_data.level, container->mutable_key_control());
|
||||
// There are three different ways we can add a key control block. It is
|
||||
// possible to have no key control block (it is an optional field). This is
|
||||
// the only option if the security level for the key is missing (the security
|
||||
// level is non-optional in the key control block).
|
||||
//
|
||||
// The key control block can either be in the clear or encrypted. The latest
|
||||
// version of the license protocol has it in the clear.
|
||||
switch (settings.key_control_block) {
|
||||
case TestLicenseBuilder::KeyControlBlock::kNone:
|
||||
break;
|
||||
|
||||
auto* key_control = container->mutable_key_control();
|
||||
case TestLicenseBuilder::KeyControlBlock::kClear: {
|
||||
const auto key_control_block =
|
||||
CreateKeyControlBlock(SecurityLevelToProto(key_data.level),
|
||||
container->mutable_key_control());
|
||||
auto* key_control = container->mutable_key_control();
|
||||
key_control->set_key_control_block(key_control_block.data(),
|
||||
key_control_block.size());
|
||||
|
||||
// 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);
|
||||
break;
|
||||
}
|
||||
|
||||
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());
|
||||
case TestLicenseBuilder::KeyControlBlock::kEncrypted: {
|
||||
// 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.
|
||||
const auto key_control_block =
|
||||
CreateKeyControlBlock(SecurityLevelToProto(key_data.level),
|
||||
container->mutable_key_control());
|
||||
auto* key_control = container->mutable_key_control();
|
||||
|
||||
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());
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
CHECK(false) << "Unknown key control block settings";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -325,16 +384,14 @@ void TestLicenseBuilder::AddSigningKey(const SigningKey& key) {
|
||||
void TestLicenseBuilder::AddStubbedContentKey() {
|
||||
content_keys_.emplace_back();
|
||||
auto& key_data = content_keys_.back();
|
||||
key_data.level =
|
||||
video_widevine::License_KeyContainer_SecurityLevel_SW_SECURE_CRYPTO;
|
||||
key_data.level = SecurityLevel::kSoftwareSecureCrypto;
|
||||
key_data.id = {0, 0, 0, 0};
|
||||
key_data.key = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
|
||||
}
|
||||
|
||||
void TestLicenseBuilder::AddContentKey(
|
||||
video_widevine::License_KeyContainer_SecurityLevel level,
|
||||
const KeyId& id,
|
||||
const AesKey& key) {
|
||||
void TestLicenseBuilder::AddContentKey(SecurityLevel level,
|
||||
const KeyId& id,
|
||||
const AesKey& key) {
|
||||
content_keys_.emplace_back();
|
||||
auto& key_data = content_keys_.back();
|
||||
key_data.level = level;
|
||||
|
||||
@@ -33,8 +33,14 @@ class TestLicenseBuilder {
|
||||
|
||||
AesKey key;
|
||||
|
||||
video_widevine::License_KeyContainer_SecurityLevel level =
|
||||
video_widevine::License_KeyContainer_SecurityLevel_SW_SECURE_CRYPTO;
|
||||
// Default to the security level being undefined in the key container. When
|
||||
// undefined, the key should be treated as if it is "software secure
|
||||
// crypto".
|
||||
//
|
||||
// However, when we use the key control block, the security level in the key
|
||||
// container does not matter as the key control block will always have the
|
||||
// security level.
|
||||
SecurityLevel level = SecurityLevel::kUndefined;
|
||||
};
|
||||
|
||||
// Signing keys must be 512 bits (64 bytes).
|
||||
@@ -65,6 +71,7 @@ class TestLicenseBuilder {
|
||||
};
|
||||
|
||||
enum class KeyControlBlock {
|
||||
kNone,
|
||||
kClear,
|
||||
kEncrypted,
|
||||
};
|
||||
@@ -88,9 +95,7 @@ class TestLicenseBuilder {
|
||||
// be used with AddContentKey().
|
||||
void AddStubbedContentKey();
|
||||
|
||||
void AddContentKey(video_widevine::License_KeyContainer_SecurityLevel level,
|
||||
const KeyId& id,
|
||||
const AesKey& key);
|
||||
void AddContentKey(SecurityLevel level, const KeyId& id, const AesKey& key);
|
||||
|
||||
// The key id will matter as we will need to reference it, but the key won't
|
||||
// matter since we are only using it as a means to verify that a non-content
|
||||
|
||||
Reference in New Issue
Block a user