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
199 lines
6.9 KiB
C++
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
|