Multiple Renewal Keys and Logging
In this code update we add a test to ensure that the White-box API implementation handle seeing multiple renewal keys correctly. Since there should be no more than one renewal key in a license response, upon seeing a second renewal key, the implementation should return a WB_RESULT_INVALID_PARAMETER code. Due to changes in how Chrome manages CHECKS and DCHECKS, this code has been updated to use the new headers.
This commit is contained in:
@@ -7,7 +7,6 @@
|
|||||||
|
|
||||||
#include "api/aead_whitebox.h"
|
#include "api/aead_whitebox.h"
|
||||||
#include "api/test_data.h"
|
#include "api/test_data.h"
|
||||||
#include "base/logging.h"
|
|
||||||
#include "benchmarking/data_source.h"
|
#include "benchmarking/data_source.h"
|
||||||
#include "benchmarking/measurements.h"
|
#include "benchmarking/measurements.h"
|
||||||
#include "testing/gtest/include/gtest/gtest.h"
|
#include "testing/gtest/include/gtest/gtest.h"
|
||||||
|
|||||||
@@ -11,7 +11,6 @@
|
|||||||
#include "api/result.h"
|
#include "api/result.h"
|
||||||
#include "api/test_data.h"
|
#include "api/test_data.h"
|
||||||
#include "api/test_license_builder.h"
|
#include "api/test_license_builder.h"
|
||||||
#include "base/logging.h"
|
|
||||||
#include "benchmarking/data_source.h"
|
#include "benchmarking/data_source.h"
|
||||||
#include "benchmarking/measurements.h"
|
#include "benchmarking/measurements.h"
|
||||||
#include "testing/gtest/include/gtest/gtest.h"
|
#include "testing/gtest/include/gtest/gtest.h"
|
||||||
|
|||||||
@@ -11,7 +11,6 @@
|
|||||||
#include "api/license_whitebox_test_base.h"
|
#include "api/license_whitebox_test_base.h"
|
||||||
#include "api/test_data.h"
|
#include "api/test_data.h"
|
||||||
#include "api/test_license_builder.h"
|
#include "api/test_license_builder.h"
|
||||||
#include "base/logging.h"
|
|
||||||
#include "crypto_utils/crypto_util.h"
|
#include "crypto_utils/crypto_util.h"
|
||||||
#include "crypto_utils/rsa_key.h"
|
#include "crypto_utils/rsa_key.h"
|
||||||
#include "testing/gtest/include/gtest/gtest.h"
|
#include "testing/gtest/include/gtest/gtest.h"
|
||||||
|
|||||||
@@ -8,7 +8,6 @@
|
|||||||
#include "api/result.h"
|
#include "api/result.h"
|
||||||
#include "api/test_data.h"
|
#include "api/test_data.h"
|
||||||
#include "api/test_license_builder.h"
|
#include "api/test_license_builder.h"
|
||||||
#include "base/logging.h"
|
|
||||||
#include "benchmarking/measurements.h"
|
#include "benchmarking/measurements.h"
|
||||||
#include "testing/gtest/include/gtest/gtest.h"
|
#include "testing/gtest/include/gtest/gtest.h"
|
||||||
|
|
||||||
|
|||||||
@@ -74,6 +74,26 @@ TEST_F(LicenseWhiteboxProcessLicenseResponseTest,
|
|||||||
WB_RESULT_OK);
|
WB_RESULT_OK);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_F(LicenseWhiteboxProcessLicenseResponseTest,
|
||||||
|
InvalidParameterWithMultipleSigningKeys) {
|
||||||
|
TestLicenseBuilder builder;
|
||||||
|
builder.AddSigningKey(TestLicenseBuilder::DefaultSigningKey(),
|
||||||
|
TestLicenseBuilder::PKSC8Padding());
|
||||||
|
builder.AddSigningKey(TestLicenseBuilder::DefaultSigningKey(),
|
||||||
|
TestLicenseBuilder::PKSC8Padding());
|
||||||
|
builder.AddStubbedContentKey();
|
||||||
|
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_INVALID_PARAMETER);
|
||||||
|
}
|
||||||
|
|
||||||
class LicenseWhiteboxProcessLicenseResponseErrorTest
|
class LicenseWhiteboxProcessLicenseResponseErrorTest
|
||||||
: public LicenseWhiteboxProcessLicenseResponseTest {
|
: public LicenseWhiteboxProcessLicenseResponseTest {
|
||||||
protected:
|
protected:
|
||||||
|
|||||||
@@ -11,7 +11,6 @@
|
|||||||
#include "api/result.h"
|
#include "api/result.h"
|
||||||
#include "api/test_data.h"
|
#include "api/test_data.h"
|
||||||
#include "api/test_license_builder.h"
|
#include "api/test_license_builder.h"
|
||||||
#include "base/logging.h"
|
|
||||||
#include "benchmarking/data_source.h"
|
#include "benchmarking/data_source.h"
|
||||||
#include "benchmarking/measurements.h"
|
#include "benchmarking/measurements.h"
|
||||||
#include "testing/gtest/include/gtest/gtest.h"
|
#include "testing/gtest/include/gtest/gtest.h"
|
||||||
|
|||||||
@@ -11,7 +11,6 @@
|
|||||||
#include "api/result.h"
|
#include "api/result.h"
|
||||||
#include "api/test_data.h"
|
#include "api/test_data.h"
|
||||||
#include "api/test_license_builder.h"
|
#include "api/test_license_builder.h"
|
||||||
#include "base/logging.h"
|
|
||||||
#include "benchmarking/data_source.h"
|
#include "benchmarking/data_source.h"
|
||||||
#include "benchmarking/measurements.h"
|
#include "benchmarking/measurements.h"
|
||||||
#include "testing/gtest/include/gtest/gtest.h"
|
#include "testing/gtest/include/gtest/gtest.h"
|
||||||
|
|||||||
@@ -4,7 +4,8 @@
|
|||||||
|
|
||||||
#include <ctime>
|
#include <ctime>
|
||||||
|
|
||||||
#include "base/logging.h"
|
#include "base/check.h"
|
||||||
|
#include "base/check_op.h"
|
||||||
#include "cdm/keys/certs.h"
|
#include "cdm/keys/certs.h"
|
||||||
#include "crypto_utils/aes_cbc_encryptor.h"
|
#include "crypto_utils/aes_cbc_encryptor.h"
|
||||||
#include "crypto_utils/crypto_util.h"
|
#include "crypto_utils/crypto_util.h"
|
||||||
|
|||||||
@@ -2,7 +2,11 @@
|
|||||||
|
|
||||||
cc_library(
|
cc_library(
|
||||||
name = "glog",
|
name = "glog",
|
||||||
hdrs = ["logging.h"],
|
hdrs = [
|
||||||
|
"check.h",
|
||||||
|
"check_op.h",
|
||||||
|
"logging.h"
|
||||||
|
],
|
||||||
strip_include_prefix = "//chromium_deps",
|
strip_include_prefix = "//chromium_deps",
|
||||||
visibility = ["//visibility:public"],
|
visibility = ["//visibility:public"],
|
||||||
deps = [
|
deps = [
|
||||||
|
|||||||
13
chromium_deps/base/check.h
Normal file
13
chromium_deps/base/check.h
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
// Copyright 2020 Google LLC. All Rights Reserved.
|
||||||
|
|
||||||
|
#ifndef BASE_CHECK_H_
|
||||||
|
#define BASE_CHECK_H_
|
||||||
|
|
||||||
|
// Chromium has split CHECK/DCHECK out of logging.h into it's own header.
|
||||||
|
// However, it's all still together in the GLOG header, so just include
|
||||||
|
// it. This may cause problems when importing into Alcatraz as code using
|
||||||
|
// anyone of these headers gets all the functions, not just the ones
|
||||||
|
// represented by this header.
|
||||||
|
#include "base/logging.h"
|
||||||
|
|
||||||
|
#endif // BASE_CHECK_H_
|
||||||
13
chromium_deps/base/check_op.h
Normal file
13
chromium_deps/base/check_op.h
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
// Copyright 2020 Google LLC. All Rights Reserved.
|
||||||
|
|
||||||
|
#ifndef BASE_CHECK_OP_H_
|
||||||
|
#define BASE_CHECK_OP_H_
|
||||||
|
|
||||||
|
// Chromium has split CHECK_EQ/etc. out of logging.h into it's own header.
|
||||||
|
// However, it's all still together in the GLOG header, so just include
|
||||||
|
// it. This may cause problems when importing into Alcatraz as code using
|
||||||
|
// anyone of these headers gets all the functions, not just the ones
|
||||||
|
// represented by this header.
|
||||||
|
#include "base/logging.h"
|
||||||
|
|
||||||
|
#endif // BASE_CHECK_OP_H_
|
||||||
@@ -6,6 +6,7 @@
|
|||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
|
#include "base/check.h"
|
||||||
#include "base/logging.h"
|
#include "base/logging.h"
|
||||||
|
|
||||||
namespace widevine {
|
namespace widevine {
|
||||||
|
|||||||
@@ -6,6 +6,7 @@
|
|||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
|
#include "base/check.h"
|
||||||
#include "base/logging.h"
|
#include "base/logging.h"
|
||||||
|
|
||||||
namespace widevine {
|
namespace widevine {
|
||||||
|
|||||||
@@ -6,6 +6,7 @@
|
|||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
|
#include "base/check.h"
|
||||||
#include "base/logging.h"
|
#include "base/logging.h"
|
||||||
|
|
||||||
namespace widevine {
|
namespace widevine {
|
||||||
|
|||||||
@@ -10,7 +10,7 @@
|
|||||||
|
|
||||||
#include "crypto_utils/crypto_util.h"
|
#include "crypto_utils/crypto_util.h"
|
||||||
|
|
||||||
#include "base/logging.h"
|
#include "base/check.h"
|
||||||
#include "third_party/boringssl/src/include/openssl/aes.h"
|
#include "third_party/boringssl/src/include/openssl/aes.h"
|
||||||
#include "third_party/boringssl/src/include/openssl/cmac.h"
|
#include "third_party/boringssl/src/include/openssl/cmac.h"
|
||||||
#include "third_party/boringssl/src/include/openssl/evp.h"
|
#include "third_party/boringssl/src/include/openssl/evp.h"
|
||||||
|
|||||||
@@ -8,7 +8,7 @@
|
|||||||
|
|
||||||
#include "crypto_utils/random_util.h"
|
#include "crypto_utils/random_util.h"
|
||||||
|
|
||||||
#include "base/logging.h"
|
#include "base/check.h"
|
||||||
#include "third_party/boringssl/src/include/openssl/rand.h"
|
#include "third_party/boringssl/src/include/openssl/rand.h"
|
||||||
|
|
||||||
namespace widevine {
|
namespace widevine {
|
||||||
|
|||||||
@@ -25,6 +25,7 @@
|
|||||||
|
|
||||||
#include "crypto_utils/rsa_key.h"
|
#include "crypto_utils/rsa_key.h"
|
||||||
|
|
||||||
|
#include "base/check.h"
|
||||||
#include "base/logging.h"
|
#include "base/logging.h"
|
||||||
#include "crypto_utils/rsa_util.h"
|
#include "crypto_utils/rsa_util.h"
|
||||||
#include "crypto_utils/sha_util.h"
|
#include "crypto_utils/sha_util.h"
|
||||||
|
|||||||
@@ -17,6 +17,7 @@
|
|||||||
#include <cstring>
|
#include <cstring>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
|
||||||
|
#include "base/check.h"
|
||||||
#include "base/logging.h"
|
#include "base/logging.h"
|
||||||
#include "crypto_utils/private_key_util.h"
|
#include "crypto_utils/private_key_util.h"
|
||||||
#include "third_party/boringssl/src/include/openssl/pem.h"
|
#include "third_party/boringssl/src/include/openssl/pem.h"
|
||||||
|
|||||||
@@ -5,6 +5,7 @@
|
|||||||
#include <memory>
|
#include <memory>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
|
#include "base/check_op.h"
|
||||||
#include "base/logging.h"
|
#include "base/logging.h"
|
||||||
#include "crypto_utils/crypto_util.h"
|
#include "crypto_utils/crypto_util.h"
|
||||||
#include "impl/reference/memory_util.h"
|
#include "impl/reference/memory_util.h"
|
||||||
|
|||||||
@@ -8,6 +8,8 @@
|
|||||||
#include <string>
|
#include <string>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
|
#include "base/check.h"
|
||||||
|
#include "base/check_op.h"
|
||||||
#include "base/logging.h"
|
#include "base/logging.h"
|
||||||
#include "cdm/protos/license_protocol.pb.h"
|
#include "cdm/protos/license_protocol.pb.h"
|
||||||
#include "crypto_utils/aes_cbc_decryptor.h"
|
#include "crypto_utils/aes_cbc_decryptor.h"
|
||||||
@@ -50,12 +52,12 @@ struct ContentKey {
|
|||||||
bool Decrypt(AesCbcDecryptor& decryptor,
|
bool Decrypt(AesCbcDecryptor& decryptor,
|
||||||
const std::string& iv,
|
const std::string& iv,
|
||||||
const std::string& encrypted,
|
const std::string& encrypted,
|
||||||
std::vector<uint8_t>* decrypted) {
|
std::string* decrypted) {
|
||||||
DCHECK_GE(decrypted->size(), encrypted.size());
|
DCHECK_GE(decrypted->size(), encrypted.size());
|
||||||
return decryptor.Decrypt(reinterpret_cast<const uint8_t*>(iv.data()),
|
return decryptor.Decrypt(
|
||||||
iv.size(),
|
reinterpret_cast<const uint8_t*>(iv.data()), iv.size(),
|
||||||
reinterpret_cast<const uint8_t*>(encrypted.data()),
|
reinterpret_cast<const uint8_t*>(encrypted.data()), encrypted.size(),
|
||||||
encrypted.size(), decrypted->data());
|
reinterpret_cast<uint8_t*>(&decrypted->front()));
|
||||||
}
|
}
|
||||||
|
|
||||||
bool IsOdkVersionSupported(uint16_t major_version, uint16_t minor_version) {
|
bool IsOdkVersionSupported(uint16_t major_version, uint16_t minor_version) {
|
||||||
@@ -124,7 +126,7 @@ video_widevine::License_KeyContainer_SecurityLevel ExtractLevel(
|
|||||||
ContentKey CreateContentKey(
|
ContentKey CreateContentKey(
|
||||||
video_widevine::License_KeyContainer_SecurityLevel level,
|
video_widevine::License_KeyContainer_SecurityLevel level,
|
||||||
bool is_hw_verified,
|
bool is_hw_verified,
|
||||||
const std::vector<uint8_t>& key) {
|
const std::string& key) {
|
||||||
ContentKey content_key;
|
ContentKey content_key;
|
||||||
|
|
||||||
switch (level) {
|
switch (level) {
|
||||||
@@ -149,7 +151,7 @@ ContentKey CreateContentKey(
|
|||||||
// it will only risk exposing it. We only have an entry for it so we can
|
// it will only risk exposing it. We only have an entry for it so we can
|
||||||
// handle errors correctly.
|
// handle errors correctly.
|
||||||
if (content_key.allow_decrypt || content_key.allow_masked_decrypt) {
|
if (content_key.allow_decrypt || content_key.allow_masked_decrypt) {
|
||||||
content_key.key = key;
|
content_key.key.assign(key.begin(), key.end());
|
||||||
}
|
}
|
||||||
|
|
||||||
return content_key;
|
return content_key;
|
||||||
@@ -515,9 +517,9 @@ WB_Result WB_License_ProcessLicenseResponse(WB_License_Whitebox* whitebox,
|
|||||||
decryptor.SetKey(reinterpret_cast<const uint8_t*>(decryption_key.data()),
|
decryptor.SetKey(reinterpret_cast<const uint8_t*>(decryption_key.data()),
|
||||||
decryption_key.size()));
|
decryption_key.size()));
|
||||||
|
|
||||||
std::string server_renewal_key;
|
|
||||||
std::string client_renewal_key;
|
|
||||||
std::map<std::string, ContentKey> content_keys;
|
std::map<std::string, ContentKey> content_keys;
|
||||||
|
std::vector<std::string> server_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
|
||||||
@@ -529,7 +531,8 @@ WB_Result WB_License_ProcessLicenseResponse(WB_License_Whitebox* whitebox,
|
|||||||
const std::string signing_key_iv =
|
const std::string signing_key_iv =
|
||||||
ExtractItem(parsed_license.enc_mac_keys_iv, message_str);
|
ExtractItem(parsed_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::vector<uint8_t> unwrapped_signing_key(signing_key_encrypted.size());
|
std::string unwrapped_signing_key(signing_key_encrypted);
|
||||||
|
|
||||||
if (!Decrypt(decryptor, signing_key_iv, signing_key_encrypted,
|
if (!Decrypt(decryptor, signing_key_iv, signing_key_encrypted,
|
||||||
&unwrapped_signing_key)) {
|
&unwrapped_signing_key)) {
|
||||||
DVLOG(1) << "Invalid parameter: Invalid enc_mac_keys.";
|
DVLOG(1) << "Invalid parameter: Invalid enc_mac_keys.";
|
||||||
@@ -541,11 +544,10 @@ WB_Result WB_License_ProcessLicenseResponse(WB_License_Whitebox* whitebox,
|
|||||||
return WB_RESULT_INVALID_PARAMETER;
|
return WB_RESULT_INVALID_PARAMETER;
|
||||||
}
|
}
|
||||||
|
|
||||||
const std::string signing_key(unwrapped_signing_key.begin(),
|
server_renewal_keys.push_back(
|
||||||
unwrapped_signing_key.end());
|
unwrapped_signing_key.substr(0, kSigningKeySizeBytes));
|
||||||
server_renewal_key = signing_key.substr(0, kSigningKeySizeBytes);
|
client_renewal_keys.push_back(unwrapped_signing_key.substr(
|
||||||
client_renewal_key =
|
kSigningKeySizeBytes, kSigningKeySizeBytes));
|
||||||
signing_key.substr(kSigningKeySizeBytes, kSigningKeySizeBytes);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Now extract all the content keys.
|
// Now extract all the content keys.
|
||||||
@@ -555,7 +557,7 @@ WB_Result WB_License_ProcessLicenseResponse(WB_License_Whitebox* whitebox,
|
|||||||
|
|
||||||
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);
|
const std::string wrapped_key = ExtractItem(key.key_data, message_str);
|
||||||
std::vector<uint8_t> unwrapped_key(wrapped_key.size());
|
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.";
|
||||||
@@ -604,7 +606,7 @@ WB_Result WB_License_ProcessLicenseResponse(WB_License_Whitebox* whitebox,
|
|||||||
}
|
}
|
||||||
|
|
||||||
const std::string wrapped_key = key.key();
|
const std::string wrapped_key = key.key();
|
||||||
std::vector<uint8_t> unwrapped_key(wrapped_key.size());
|
std::string unwrapped_key(wrapped_key);
|
||||||
|
|
||||||
if (!Decrypt(decryptor, key.iv(), wrapped_key, &unwrapped_key)) {
|
if (!Decrypt(decryptor, key.iv(), wrapped_key, &unwrapped_key)) {
|
||||||
// The input has to be a specific length, so if it is not, it means that
|
// The input has to be a specific length, so if it is not, it means that
|
||||||
@@ -619,11 +621,10 @@ WB_Result WB_License_ProcessLicenseResponse(WB_License_Whitebox* whitebox,
|
|||||||
return WB_RESULT_INVALID_PARAMETER;
|
return WB_RESULT_INVALID_PARAMETER;
|
||||||
}
|
}
|
||||||
|
|
||||||
const std::string signing_key(unwrapped_key.begin(),
|
server_renewal_keys.push_back(
|
||||||
unwrapped_key.end());
|
unwrapped_key.substr(0, kSigningKeySizeBytes));
|
||||||
server_renewal_key = signing_key.substr(0, kSigningKeySizeBytes);
|
client_renewal_keys.push_back(
|
||||||
client_renewal_key =
|
unwrapped_key.substr(kSigningKeySizeBytes, kSigningKeySizeBytes));
|
||||||
signing_key.substr(kSigningKeySizeBytes, kSigningKeySizeBytes);
|
|
||||||
} else if (key.type() == KeyContainer::CONTENT) {
|
} else if (key.type() == KeyContainer::CONTENT) {
|
||||||
constexpr size_t kContentKeySizeBytes = 16;
|
constexpr size_t kContentKeySizeBytes = 16;
|
||||||
|
|
||||||
@@ -642,10 +643,19 @@ WB_Result WB_License_ProcessLicenseResponse(WB_License_Whitebox* whitebox,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
DCHECK_EQ(server_renewal_keys.size(), client_renewal_keys.size());
|
||||||
|
if (server_renewal_keys.size() > 1) {
|
||||||
|
return WB_RESULT_INVALID_PARAMETER;
|
||||||
|
}
|
||||||
|
// Add an empty string so that we can always reference a valid string. By
|
||||||
|
// adding it last, a valid key will get priority over this fallback value.
|
||||||
|
server_renewal_keys.push_back("");
|
||||||
|
client_renewal_keys.push_back("");
|
||||||
|
|
||||||
// Copy the loaded state over to the white-box instance now that we know we
|
// Copy the loaded state over to the white-box instance now that we know we
|
||||||
// have a valid state.
|
// have a valid state.
|
||||||
whitebox->server_signing_key.swap(server_renewal_key);
|
whitebox->server_signing_key.swap(server_renewal_keys[0]);
|
||||||
whitebox->client_signing_key.swap(client_renewal_key);
|
whitebox->client_signing_key.swap(client_renewal_keys[0]);
|
||||||
whitebox->content_keys.swap(content_keys);
|
whitebox->content_keys.swap(content_keys);
|
||||||
|
|
||||||
return WB_RESULT_OK;
|
return WB_RESULT_OK;
|
||||||
|
|||||||
Reference in New Issue
Block a user