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:
Aaron Vaage
2020-08-21 17:18:28 -07:00
parent 789377fed2
commit 69ea909ff5
20 changed files with 95 additions and 34 deletions

View File

@@ -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"

View File

@@ -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"

View File

@@ -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"

View File

@@ -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"

View File

@@ -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:

View File

@@ -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"

View File

@@ -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"

View File

@@ -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"

View File

@@ -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 = [

View 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_

View 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_

View File

@@ -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 {

View File

@@ -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 {

View File

@@ -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 {

View File

@@ -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"

View File

@@ -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 {

View File

@@ -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"

View File

@@ -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"

View File

@@ -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"

View File

@@ -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;