Files
whitebox/whitebox/reference/impl/odk_license_parser.cc
John Rummell adb98d80d3 Update to latest version including support for Partner Keys
Includes change to WB_License_Create() so that it includes a separate
parameter that specifies the init data used for Provider Keys.

This updates the repo to match the internal repo at commit:
8c1c4338906a32eed83eb63702690d1f02ff7cd0
2021-12-13 16:40:24 -08:00

199 lines
6.9 KiB
C++

// Copyright 2021 Google LLC. All Rights Reserved.
#include "reference/impl/odk_license_parser.h"
#include "base/check_op.h"
#include "base/logging.h"
#include "crypto_utils/crypto_util.h"
namespace widevine {
namespace {
bool ExtractLevel(
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
// fields. The fields are defined to be in big-endian byte order.
//
// Bytes 0..3: Verification.
// Constant bytes “kctl”, “kc09”, “kc10”, “kc11”, ... “kc15”.
// Bytes 4..7: Obsolete.
// Bytes 8..11: Nonce.
// Bytes 12..15: Control Bits
// Bits 27..26: Security_Level (Only for L3 white-box implementations)
// 0 = SW_SECURE_CRYPTO
// 1 = SW_SECURE_DECODE
// 2 = HW_SECURE_CRYPTO
// 3 = HW_SECURE_DECODE or HW_SECURE_ALL
// Make sure this is a valid key control block. Ideally the signature
// verification should have taken care of this.
if ((key_control_block.size() != 16u) || (key_control_block[0] != 'k') ||
(key_control_block[1] != 'c')) {
return false;
}
// Extract bits 26..27 from Control Bits.
switch ((key_control_block[12] & 0x0C) >> 2) {
case 0:
*security_level =
video_widevine::License_KeyContainer_SecurityLevel_SW_SECURE_CRYPTO;
break;
case 1:
*security_level =
video_widevine::License_KeyContainer_SecurityLevel_SW_SECURE_DECODE;
break;
case 2:
*security_level =
video_widevine::License_KeyContainer_SecurityLevel_HW_SECURE_CRYPTO;
break;
default:
*security_level =
video_widevine::License_KeyContainer_SecurityLevel_HW_SECURE_DECODE;
break;
}
return true;
}
// Helper function to extract the substring |item| from the provided |buffer|.
std::string ExtractItem(const OEMCrypto_Substring& item,
const std::string& buffer) {
return buffer.substr(item.offset, item.length);
}
} // namespace
WB_Result OdkLicenseParser::Parse(const std::string& decryption_key,
const ODKContext& odk_context,
const std::string& message,
const std::vector<ProviderKey>& provider_keys,
size_t provider_key_id) {
// Extract the signing keys.
const std::string signing_key_encrypted =
ExtractItem(odk_context.license.enc_mac_keys, message);
const std::string signing_key_iv =
ExtractItem(odk_context.license.enc_mac_keys_iv, message);
if (!signing_key_encrypted.empty() || !signing_key_iv.empty()) {
renewal_keys_.push_back(ParseSigningKeys(
decryption_key, signing_key_encrypted, signing_key_iv));
}
// Extract the content keys.
for (size_t i = 0; i < odk_context.license.key_array_length; ++i) {
const OEMCrypto_KeyObject& key = odk_context.license.key_array[i];
const std::string key_id = ExtractItem(key.key_id, message);
// If there is no key id, we can't add an invalid entry since there would
// be no way to query it.
if (key_id.empty()) {
VLOG(3) << "Invalid key at index " << i << " : no key id.";
continue;
}
// Add the key right away. The key will be invalid, so if we fail to
// parse the key, we'll already have an entry for the invalid key.
content_keys_[key_id] = ParseContentKey(decryption_key, message, key,
provider_keys, provider_key_id);
}
return WB_RESULT_OK;
}
const RenewalKey* OdkLicenseParser::GetRenewalKey() const {
CHECK_LE(renewal_keys_.size(), 1u);
return renewal_keys_.empty() ? nullptr : &renewal_keys_[0];
}
const std::map<std::string, ContentKey>& OdkLicenseParser::GetContentKeys()
const {
return content_keys_;
}
RenewalKey OdkLicenseParser::ParseSigningKeys(const std::string& decryption_key,
const std::string& key,
const std::string& iv) const {
// This should have been verified before calling the parser.
CHECK_EQ(decryption_key.size(), 16u) << "Incorrect decryption key size.";
const size_t kSizeWithoutPadding = 2 * crypto_util::kSigningKeySizeBytes;
const size_t kSizeWithPadding = 2 * crypto_util::kSigningKeySizeBytes + 16u;
if (key.size() != kSizeWithoutPadding && key.size() != kSizeWithPadding) {
VLOG(3) << "Invalid renewal key size (" << key.size() << ").";
return RenewalKey();
}
if (iv.size() != 16u) {
VLOG(3) << "Invalid iv size.";
return RenewalKey();
}
const std::string wrapped_key = key.substr(0, kSizeWithoutPadding);
std::string unwrapped_key(wrapped_key);
if (!Decrypt(decryption_key, iv, wrapped_key, &unwrapped_key)) {
VLOG(3) << "Failed to decrypt renewal key.";
return RenewalKey();
}
RenewalKey renewal_key;
renewal_key.status = WB_KEY_STATUS_SIGNING_KEY_VALID;
std::copy(unwrapped_key.begin(),
unwrapped_key.begin() + crypto_util::kSigningKeySizeBytes,
renewal_key.server.begin());
std::copy(unwrapped_key.begin() + crypto_util::kSigningKeySizeBytes,
unwrapped_key.end(), renewal_key.client.begin());
return renewal_key;
}
ContentKey OdkLicenseParser::ParseContentKey(
const std::string& decryption_key,
const std::string& message,
const OEMCrypto_KeyObject& key,
const std::vector<ProviderKey>& provider_keys,
size_t provider_key_id) const {
// This should have been verified before calling the parser.
CHECK_EQ(decryption_key.size(), 16u) << "Incorrect decryption key size.";
const std::string iv = ExtractItem(key.key_data_iv, message);
if (iv.size() != 16u) {
VLOG(3) << "Invalid content iv size.";
return ContentKey();
}
std::string wrapped_key = ExtractItem(key.key_data, message);
// Unlike with protobufs, we don't need to handle padding here. The ODK will
// not include the padding as part of the key's size.
if (wrapped_key.size() != 16u) {
VLOG(3) << "Invalid content key size (" << wrapped_key.size() << ").";
return ContentKey();
}
std::string unwrapped_key;
if (!UnwrapContentKey(wrapped_key, provider_keys, provider_key_id,
decryption_key, iv, &unwrapped_key)) {
VLOG(3) << "Failed to decrypt content key.";
return ContentKey();
}
const std::string key_control_block = ExtractItem(key.key_control, message);
video_widevine::License_KeyContainer_SecurityLevel security_level;
CHECK(ExtractLevel(key_control_block, &security_level));
// When the platform is hardware verified, all keys are unlocked and are
// available to be used with either decrypt function. The license server
// adjusts the level returned inside the key control block to handle
// this.
const bool is_hw_verified = false;
return CreateContentKey(security_level, is_hw_verified, unwrapped_key);
}
} // namespace widevine