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:
@@ -5,6 +5,7 @@
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
#include "base/check_op.h"
|
||||
#include "base/logging.h"
|
||||
#include "crypto_utils/crypto_util.h"
|
||||
#include "impl/reference/memory_util.h"
|
||||
|
||||
@@ -8,6 +8,8 @@
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "base/check.h"
|
||||
#include "base/check_op.h"
|
||||
#include "base/logging.h"
|
||||
#include "cdm/protos/license_protocol.pb.h"
|
||||
#include "crypto_utils/aes_cbc_decryptor.h"
|
||||
@@ -50,12 +52,12 @@ struct ContentKey {
|
||||
bool Decrypt(AesCbcDecryptor& decryptor,
|
||||
const std::string& iv,
|
||||
const std::string& encrypted,
|
||||
std::vector<uint8_t>* decrypted) {
|
||||
std::string* decrypted) {
|
||||
DCHECK_GE(decrypted->size(), encrypted.size());
|
||||
return decryptor.Decrypt(reinterpret_cast<const uint8_t*>(iv.data()),
|
||||
iv.size(),
|
||||
reinterpret_cast<const uint8_t*>(encrypted.data()),
|
||||
encrypted.size(), decrypted->data());
|
||||
return decryptor.Decrypt(
|
||||
reinterpret_cast<const uint8_t*>(iv.data()), iv.size(),
|
||||
reinterpret_cast<const uint8_t*>(encrypted.data()), encrypted.size(),
|
||||
reinterpret_cast<uint8_t*>(&decrypted->front()));
|
||||
}
|
||||
|
||||
bool IsOdkVersionSupported(uint16_t major_version, uint16_t minor_version) {
|
||||
@@ -124,7 +126,7 @@ video_widevine::License_KeyContainer_SecurityLevel ExtractLevel(
|
||||
ContentKey CreateContentKey(
|
||||
video_widevine::License_KeyContainer_SecurityLevel level,
|
||||
bool is_hw_verified,
|
||||
const std::vector<uint8_t>& key) {
|
||||
const std::string& key) {
|
||||
ContentKey content_key;
|
||||
|
||||
switch (level) {
|
||||
@@ -149,7 +151,7 @@ ContentKey CreateContentKey(
|
||||
// it will only risk exposing it. We only have an entry for it so we can
|
||||
// handle errors correctly.
|
||||
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;
|
||||
@@ -515,9 +517,9 @@ WB_Result WB_License_ProcessLicenseResponse(WB_License_Whitebox* whitebox,
|
||||
decryptor.SetKey(reinterpret_cast<const uint8_t*>(decryption_key.data()),
|
||||
decryption_key.size()));
|
||||
|
||||
std::string server_renewal_key;
|
||||
std::string client_renewal_key;
|
||||
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
|
||||
// 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 =
|
||||
ExtractItem(parsed_license.enc_mac_keys_iv, message_str);
|
||||
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,
|
||||
&unwrapped_signing_key)) {
|
||||
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;
|
||||
}
|
||||
|
||||
const std::string signing_key(unwrapped_signing_key.begin(),
|
||||
unwrapped_signing_key.end());
|
||||
server_renewal_key = signing_key.substr(0, kSigningKeySizeBytes);
|
||||
client_renewal_key =
|
||||
signing_key.substr(kSigningKeySizeBytes, kSigningKeySizeBytes);
|
||||
server_renewal_keys.push_back(
|
||||
unwrapped_signing_key.substr(0, kSigningKeySizeBytes));
|
||||
client_renewal_keys.push_back(unwrapped_signing_key.substr(
|
||||
kSigningKeySizeBytes, kSigningKeySizeBytes));
|
||||
}
|
||||
|
||||
// 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 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)) {
|
||||
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();
|
||||
std::vector<uint8_t> unwrapped_key(wrapped_key.size());
|
||||
std::string unwrapped_key(wrapped_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
|
||||
@@ -619,11 +621,10 @@ WB_Result WB_License_ProcessLicenseResponse(WB_License_Whitebox* whitebox,
|
||||
return WB_RESULT_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
const std::string signing_key(unwrapped_key.begin(),
|
||||
unwrapped_key.end());
|
||||
server_renewal_key = signing_key.substr(0, kSigningKeySizeBytes);
|
||||
client_renewal_key =
|
||||
signing_key.substr(kSigningKeySizeBytes, kSigningKeySizeBytes);
|
||||
server_renewal_keys.push_back(
|
||||
unwrapped_key.substr(0, kSigningKeySizeBytes));
|
||||
client_renewal_keys.push_back(
|
||||
unwrapped_key.substr(kSigningKeySizeBytes, kSigningKeySizeBytes));
|
||||
} else if (key.type() == KeyContainer::CONTENT) {
|
||||
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
|
||||
// have a valid state.
|
||||
whitebox->server_signing_key.swap(server_renewal_key);
|
||||
whitebox->client_signing_key.swap(client_renewal_key);
|
||||
whitebox->server_signing_key.swap(server_renewal_keys[0]);
|
||||
whitebox->client_signing_key.swap(client_renewal_keys[0]);
|
||||
whitebox->content_keys.swap(content_keys);
|
||||
|
||||
return WB_RESULT_OK;
|
||||
|
||||
Reference in New Issue
Block a user