|
|
|
|
@@ -15,6 +15,10 @@
|
|
|
|
|
#include "crypto_utils/crypto_util.h"
|
|
|
|
|
#include "crypto_utils/rsa_key.h"
|
|
|
|
|
#include "impl/reference/memory_util.h"
|
|
|
|
|
#include "oemcrypto/odk/include/odk.h"
|
|
|
|
|
#include "oemcrypto/odk/include/odk_structs.h"
|
|
|
|
|
#include "oemcrypto/odk/src/odk_serialize.h"
|
|
|
|
|
#include "oemcrypto/odk/src/serialization_base.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/err.h"
|
|
|
|
|
@@ -39,6 +43,145 @@ struct ContentKey {
|
|
|
|
|
// Key used to decrypt content.
|
|
|
|
|
std::vector<uint8_t> key;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// Helper function to decrypt |encrypted| into |decrypted| using |decryptor|
|
|
|
|
|
// and |iv|. Done as the protobuf and ODK code use std::string, AesCbcDecryptor
|
|
|
|
|
// requires uint8_t* + size_t parameters.
|
|
|
|
|
bool Decrypt(AesCbcDecryptor& decryptor,
|
|
|
|
|
const std::string& iv,
|
|
|
|
|
const std::string& encrypted,
|
|
|
|
|
std::vector<uint8_t>* 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());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool IsOdkVersionSupported(uint16_t major_version, uint16_t minor_version) {
|
|
|
|
|
// Only ODK v16.5 and later support the fields needed.
|
|
|
|
|
constexpr uint16_t first_major_version_supported = 16;
|
|
|
|
|
constexpr uint16_t first_minor_version_supported = 5;
|
|
|
|
|
|
|
|
|
|
return (major_version > first_major_version_supported) ||
|
|
|
|
|
((major_version == first_major_version_supported) &&
|
|
|
|
|
(minor_version >= first_minor_version_supported));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 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);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
video_widevine::License_KeyContainer_SecurityLevel ExtractLevel(
|
|
|
|
|
const std::string& key_control_block) {
|
|
|
|
|
// 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
|
|
|
|
|
|
|
|
|
|
// Limited checks to verify that this is proper key control block.
|
|
|
|
|
// If not valid, assume it's the highest level.
|
|
|
|
|
if ((key_control_block.size() != 16u) || (key_control_block[0] != 'k') ||
|
|
|
|
|
(key_control_block[1] != 'c')) {
|
|
|
|
|
return video_widevine::License_KeyContainer_SecurityLevel_HW_SECURE_DECODE;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Extract bits 26..27 from Control Bits.
|
|
|
|
|
int security_level = (key_control_block[12] & 0x0C) >> 2;
|
|
|
|
|
switch (security_level) {
|
|
|
|
|
case 0:
|
|
|
|
|
return video_widevine::
|
|
|
|
|
License_KeyContainer_SecurityLevel_SW_SECURE_CRYPTO;
|
|
|
|
|
case 1:
|
|
|
|
|
return video_widevine::
|
|
|
|
|
License_KeyContainer_SecurityLevel_SW_SECURE_DECODE;
|
|
|
|
|
case 2:
|
|
|
|
|
return video_widevine::
|
|
|
|
|
License_KeyContainer_SecurityLevel_HW_SECURE_CRYPTO;
|
|
|
|
|
default:
|
|
|
|
|
return video_widevine::
|
|
|
|
|
License_KeyContainer_SecurityLevel_HW_SECURE_DECODE;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Creates and returns a ContentKey based on the values provided.
|
|
|
|
|
// |level| determines whether decrypt or masked_decrypt is allowed.
|
|
|
|
|
// |is_hw_verified|, if set, overrides |level| so that both decrypt and
|
|
|
|
|
// masked_decrypt is allowed. |Key| is the decryption key, and is only
|
|
|
|
|
// returned in ContentKey if decrypt or masked_decrypt is allowed.
|
|
|
|
|
// Otherwise |key| is dropped.
|
|
|
|
|
ContentKey CreateContentKey(
|
|
|
|
|
video_widevine::License_KeyContainer_SecurityLevel level,
|
|
|
|
|
bool is_hw_verified,
|
|
|
|
|
const std::vector<uint8_t>& key) {
|
|
|
|
|
ContentKey content_key;
|
|
|
|
|
|
|
|
|
|
switch (level) {
|
|
|
|
|
case video_widevine::License_KeyContainer_SecurityLevel_SW_SECURE_CRYPTO:
|
|
|
|
|
content_key.allow_decrypt = true;
|
|
|
|
|
content_key.allow_masked_decrypt = true;
|
|
|
|
|
break;
|
|
|
|
|
case video_widevine::License_KeyContainer_SecurityLevel_SW_SECURE_DECODE:
|
|
|
|
|
content_key.allow_decrypt = false;
|
|
|
|
|
content_key.allow_masked_decrypt = true;
|
|
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
content_key.allow_decrypt = false;
|
|
|
|
|
content_key.allow_masked_decrypt = false;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
content_key.allow_decrypt |= is_hw_verified;
|
|
|
|
|
content_key.allow_masked_decrypt |= is_hw_verified;
|
|
|
|
|
|
|
|
|
|
// Unless we are going to use the key, we don't want to save this key as
|
|
|
|
|
// 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;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return content_key;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// This function uses 16 non-linear bijections that are applied to a byte.
|
|
|
|
|
// This is "Example Masking Function 1" from the shared document
|
|
|
|
|
// https://docs.google.com/document/d/1xWPwlFHyjT8YzWhY3TyaC02SQvclC_dkEpliGPOUk_o#heading=h.j64j2z3b9v99
|
|
|
|
|
uint8_t MaskingFunction1(uint8_t input) {
|
|
|
|
|
const size_t x = 97 * input;
|
|
|
|
|
const size_t y = x + 96;
|
|
|
|
|
return y % 257;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// This function performs the reverse of MaskingFunction1().
|
|
|
|
|
uint8_t InverseMaskingFunction1(uint8_t input) {
|
|
|
|
|
// Rather than compute the byte each time, generate a lookup table for all
|
|
|
|
|
// 256 values the first time this is called.
|
|
|
|
|
static bool initialized = false;
|
|
|
|
|
static uint8_t mapping[256];
|
|
|
|
|
if (!initialized) {
|
|
|
|
|
initialized = true;
|
|
|
|
|
uint8_t byte = 0;
|
|
|
|
|
do {
|
|
|
|
|
mapping[MaskingFunction1(byte)] = byte;
|
|
|
|
|
++byte;
|
|
|
|
|
} while (byte != 0);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return mapping[input];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
} // namespace
|
|
|
|
|
|
|
|
|
|
// The white-box type can't be in the namespace as it is defined in the header.
|
|
|
|
|
@@ -177,6 +320,7 @@ bool IsPlatformHardwareVerified(const video_widevine::License& license) {
|
|
|
|
|
// false values, then we know something was found to be invalid.
|
|
|
|
|
return flags[0] && flags[1];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
} // namespace
|
|
|
|
|
|
|
|
|
|
WB_Result WB_License_Create(const uint8_t* whitebox_init_data,
|
|
|
|
|
@@ -230,7 +374,7 @@ WB_Result WB_License_SignLicenseRequest(const WB_License_Whitebox* whitebox,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
std::string result;
|
|
|
|
|
DCHECK(whitebox->key->GenerateSignature(
|
|
|
|
|
CHECK(whitebox->key->GenerateSignature(
|
|
|
|
|
std::string(license_request, license_request + license_request_size),
|
|
|
|
|
&result));
|
|
|
|
|
|
|
|
|
|
@@ -246,6 +390,8 @@ WB_Result WB_License_SignLicenseRequest(const WB_License_Whitebox* whitebox,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
WB_Result WB_License_ProcessLicenseResponse(WB_License_Whitebox* whitebox,
|
|
|
|
|
const uint8_t* core_message,
|
|
|
|
|
size_t core_message_size,
|
|
|
|
|
const uint8_t* message,
|
|
|
|
|
size_t message_size,
|
|
|
|
|
const uint8_t* signature,
|
|
|
|
|
@@ -262,6 +408,13 @@ WB_Result WB_License_ProcessLicenseResponse(WB_License_Whitebox* whitebox,
|
|
|
|
|
return WB_RESULT_INVALID_PARAMETER;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// |core_message| is optional. If provided, both |core_message| and
|
|
|
|
|
// |core_message_size| need to be set.
|
|
|
|
|
if (core_message_size > 0 && !core_message) {
|
|
|
|
|
DVLOG(1) << "Invalid parameter: core_message null pointer.";
|
|
|
|
|
return WB_RESULT_INVALID_PARAMETER;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// If we have already loaded a license, ProcessLicenseResponse() may not be
|
|
|
|
|
// called again. The white-box needs to be destroyed and a new one needs
|
|
|
|
|
// to be created.
|
|
|
|
|
@@ -298,16 +451,8 @@ WB_Result WB_License_ProcessLicenseResponse(WB_License_Whitebox* whitebox,
|
|
|
|
|
DVLOG(1) << "Invalid parameter: invalid session key size.";
|
|
|
|
|
return WB_RESULT_INVALID_PARAMETER;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const std::string server_signing_key =
|
|
|
|
|
signing_key_material.substr(0, kSigningKeySizeBytes);
|
|
|
|
|
if (!widevine::crypto_util::VerifySignatureHmacSha256(
|
|
|
|
|
server_signing_key,
|
|
|
|
|
std::string(signature, signature + signature_size),
|
|
|
|
|
std::string(message, message + message_size))) {
|
|
|
|
|
DVLOG(1) << "Failed to verify signed message.";
|
|
|
|
|
return WB_RESULT_INVALID_SIGNATURE;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
std::string decryption_key = widevine::crypto_util::DeriveKey(
|
|
|
|
|
decrypted_session_key, widevine::crypto_util::kWrappingKeyLabel,
|
|
|
|
|
@@ -318,61 +463,106 @@ WB_Result WB_License_ProcessLicenseResponse(WB_License_Whitebox* whitebox,
|
|
|
|
|
return WB_RESULT_INVALID_SIGNATURE;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const std::string message_str(message, message + message_size);
|
|
|
|
|
std::string core_message_str;
|
|
|
|
|
if (core_message_size > 0) {
|
|
|
|
|
core_message_str.assign(reinterpret_cast<const char*>(core_message),
|
|
|
|
|
core_message_size);
|
|
|
|
|
}
|
|
|
|
|
const std::string combined_message_str = core_message_str + message_str;
|
|
|
|
|
if (!widevine::crypto_util::VerifySignatureHmacSha256(
|
|
|
|
|
server_signing_key,
|
|
|
|
|
std::string(signature, signature + signature_size),
|
|
|
|
|
combined_message_str)) {
|
|
|
|
|
DVLOG(1) << "Failed to verify signed message.";
|
|
|
|
|
return WB_RESULT_INVALID_SIGNATURE;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// If |core_message| is provided, parse it into |parsed_license| and validate
|
|
|
|
|
// that it's an appropriate version.
|
|
|
|
|
uint16_t odk_major_version = 0;
|
|
|
|
|
uint16_t odk_minor_version = 0;
|
|
|
|
|
ODK_ParsedLicense parsed_license;
|
|
|
|
|
if (core_message_size > 0) {
|
|
|
|
|
// Decode |core_message|.
|
|
|
|
|
Message* msg = NULL;
|
|
|
|
|
AllocateMessage(&msg, message_block);
|
|
|
|
|
InitMessage(msg,
|
|
|
|
|
reinterpret_cast<uint8_t*>(
|
|
|
|
|
const_cast<char*>(combined_message_str.data())),
|
|
|
|
|
combined_message_str.size());
|
|
|
|
|
// The core message is at the beginning of the buffer, and is the part to be
|
|
|
|
|
// parsed.
|
|
|
|
|
SetSize(msg, core_message_size);
|
|
|
|
|
|
|
|
|
|
ODK_LicenseResponse license_response = {{{0}}, &parsed_license, {0}};
|
|
|
|
|
Unpack_ODK_LicenseResponse(msg, &license_response);
|
|
|
|
|
|
|
|
|
|
odk_major_version =
|
|
|
|
|
license_response.request.core_message.nonce_values.api_major_version;
|
|
|
|
|
odk_minor_version =
|
|
|
|
|
license_response.request.core_message.nonce_values.api_minor_version;
|
|
|
|
|
if ((GetStatus(msg) != MESSAGE_STATUS_OK) ||
|
|
|
|
|
(license_response.request.core_message.message_type !=
|
|
|
|
|
ODK_License_Response_Type)) {
|
|
|
|
|
DVLOG(1) << "Failed to validate core message.";
|
|
|
|
|
return WB_RESULT_INVALID_SIGNATURE;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
AesCbcDecryptor decryptor;
|
|
|
|
|
CHECK(
|
|
|
|
|
decryptor.SetKey(reinterpret_cast<const uint8_t*>(decryption_key.data()),
|
|
|
|
|
decryption_key.size()));
|
|
|
|
|
|
|
|
|
|
video_widevine::License license;
|
|
|
|
|
if (!license.ParseFromArray(message, message_size)) {
|
|
|
|
|
DVLOG(1) << "Invalid parameter: Invalid license.";
|
|
|
|
|
return WB_RESULT_INVALID_PARAMETER;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
std::string server_renewal_key;
|
|
|
|
|
std::string client_renewal_key;
|
|
|
|
|
std::map<std::string, ContentKey> content_keys;
|
|
|
|
|
|
|
|
|
|
// When the platform is hardware verified, all keys are unlocked and are
|
|
|
|
|
// available to be used with either decrypt function. Use this flag to
|
|
|
|
|
// overwrite the default values internal our internal policies to enable
|
|
|
|
|
// this behaviour.
|
|
|
|
|
const bool is_verified = IsPlatformHardwareVerified(license);
|
|
|
|
|
// 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
|
|
|
|
|
// as if |core_message| was not provided.
|
|
|
|
|
if (IsOdkVersionSupported(odk_major_version, odk_minor_version)) {
|
|
|
|
|
// Start by extracting the signing key.
|
|
|
|
|
const std::string signing_key_encrypted =
|
|
|
|
|
ExtractItem(parsed_license.enc_mac_keys, message_str);
|
|
|
|
|
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());
|
|
|
|
|
if (!Decrypt(decryptor, signing_key_iv, signing_key_encrypted,
|
|
|
|
|
&unwrapped_signing_key)) {
|
|
|
|
|
DVLOG(1) << "Invalid parameter: Invalid enc_mac_keys.";
|
|
|
|
|
return WB_RESULT_INVALID_PARAMETER;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for (const auto& key : license.key()) {
|
|
|
|
|
// If this is not a key we're interested in, skip it as soon as possible.
|
|
|
|
|
// Don't even bother unwrapping it.
|
|
|
|
|
if (key.type() != KeyContainer::SIGNING &&
|
|
|
|
|
key.type() != KeyContainer::CONTENT) {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const std::string wrapped_key = key.key();
|
|
|
|
|
std::vector<uint8_t> unwrapped_key(wrapped_key.size());
|
|
|
|
|
|
|
|
|
|
if (!decryptor.Decrypt(reinterpret_cast<const uint8_t*>(key.iv().data()),
|
|
|
|
|
key.iv().size(),
|
|
|
|
|
reinterpret_cast<const uint8_t*>(wrapped_key.data()),
|
|
|
|
|
wrapped_key.size(), unwrapped_key.data())) {
|
|
|
|
|
// The input has to be a specific length, so if it is not, it means that
|
|
|
|
|
// something is wrong with the license.
|
|
|
|
|
DVLOG(1) << "Invalid parameter: Invalid license.";
|
|
|
|
|
return WB_RESULT_INVALID_PARAMETER;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (key.type() == KeyContainer::SIGNING) {
|
|
|
|
|
if (unwrapped_key.size() < kSigningKeySizeBytes * 2) {
|
|
|
|
|
if (unwrapped_signing_key.size() < kSigningKeySizeBytes * 2) {
|
|
|
|
|
DVLOG(1) << "Invalid parameter: Invalid signing key.";
|
|
|
|
|
return WB_RESULT_INVALID_PARAMETER;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const std::string signing_key(unwrapped_key.begin(), unwrapped_key.end());
|
|
|
|
|
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);
|
|
|
|
|
} else if (key.type() == KeyContainer::CONTENT) {
|
|
|
|
|
constexpr size_t kContentKeySizeBytes = 16;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Now extract all the content keys.
|
|
|
|
|
for (size_t i = 0; i < parsed_license.key_array_length; ++i) {
|
|
|
|
|
const OEMCrypto_KeyObject& key = parsed_license.key_array[i];
|
|
|
|
|
const std::string key_id = ExtractItem(key.key_id, message_str);
|
|
|
|
|
|
|
|
|
|
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());
|
|
|
|
|
|
|
|
|
|
if (!Decrypt(decryptor, iv, wrapped_key, &unwrapped_key)) {
|
|
|
|
|
DVLOG(1) << "Invalid parameter: Invalid key.key_data.";
|
|
|
|
|
return WB_RESULT_INVALID_PARAMETER;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
constexpr size_t kContentKeySizeBytes = 16;
|
|
|
|
|
if (unwrapped_key.size() < kContentKeySizeBytes) {
|
|
|
|
|
DVLOG(1) << "Invalid parameter: Invalid content key.";
|
|
|
|
|
return WB_RESULT_INVALID_PARAMETER;
|
|
|
|
|
@@ -380,39 +570,75 @@ WB_Result WB_License_ProcessLicenseResponse(WB_License_Whitebox* whitebox,
|
|
|
|
|
|
|
|
|
|
unwrapped_key.resize(kContentKeySizeBytes);
|
|
|
|
|
|
|
|
|
|
ContentKey content_key;
|
|
|
|
|
// 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 std::string key_control_block =
|
|
|
|
|
ExtractItem(key.key_control, message_str);
|
|
|
|
|
content_keys[key_id] =
|
|
|
|
|
CreateContentKey(ExtractLevel(key_control_block),
|
|
|
|
|
/* is_hw_verified */ false, unwrapped_key);
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
// Core message not provided or an old version, so extract the keys from
|
|
|
|
|
// the protobuf.
|
|
|
|
|
video_widevine::License license;
|
|
|
|
|
if (!license.ParseFromArray(message, message_size)) {
|
|
|
|
|
DVLOG(1) << "Invalid parameter: Invalid license.";
|
|
|
|
|
return WB_RESULT_INVALID_PARAMETER;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
switch (key.level()) {
|
|
|
|
|
case video_widevine::
|
|
|
|
|
License_KeyContainer_SecurityLevel_SW_SECURE_CRYPTO:
|
|
|
|
|
content_key.allow_decrypt = true;
|
|
|
|
|
content_key.allow_masked_decrypt = true;
|
|
|
|
|
break;
|
|
|
|
|
case video_widevine::
|
|
|
|
|
License_KeyContainer_SecurityLevel_SW_SECURE_DECODE:
|
|
|
|
|
content_key.allow_decrypt = false;
|
|
|
|
|
content_key.allow_masked_decrypt = true;
|
|
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
content_key.allow_decrypt = false;
|
|
|
|
|
content_key.allow_masked_decrypt = false;
|
|
|
|
|
break;
|
|
|
|
|
// When the platform is hardware verified, all keys are unlocked and are
|
|
|
|
|
// available to be used with either decrypt function. Use this flag to
|
|
|
|
|
// overwrite the calculated values for the internal policies to enable
|
|
|
|
|
// this behaviour.
|
|
|
|
|
const bool is_verified = IsPlatformHardwareVerified(license);
|
|
|
|
|
|
|
|
|
|
for (const auto& key : license.key()) {
|
|
|
|
|
// If this is not a key we're interested in, skip it as soon as possible.
|
|
|
|
|
// Don't even bother unwrapping it.
|
|
|
|
|
if (key.type() != KeyContainer::SIGNING &&
|
|
|
|
|
key.type() != KeyContainer::CONTENT) {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
content_key.allow_decrypt |= is_verified;
|
|
|
|
|
content_key.allow_masked_decrypt |= is_verified;
|
|
|
|
|
const std::string wrapped_key = key.key();
|
|
|
|
|
std::vector<uint8_t> unwrapped_key(wrapped_key.size());
|
|
|
|
|
|
|
|
|
|
// Unless we are going to use the key, we don't want to save this key as
|
|
|
|
|
// 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 = std::move(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
|
|
|
|
|
// something is wrong with the license.
|
|
|
|
|
DVLOG(1) << "Invalid parameter: Invalid license.";
|
|
|
|
|
return WB_RESULT_INVALID_PARAMETER;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
content_keys[key.id()] = content_key;
|
|
|
|
|
} else {
|
|
|
|
|
// We should have already skipped over this key.
|
|
|
|
|
CHECK(false);
|
|
|
|
|
if (key.type() == KeyContainer::SIGNING) {
|
|
|
|
|
if (unwrapped_key.size() < kSigningKeySizeBytes * 2) {
|
|
|
|
|
DVLOG(1) << "Invalid parameter: Invalid signing key.";
|
|
|
|
|
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);
|
|
|
|
|
} else if (key.type() == KeyContainer::CONTENT) {
|
|
|
|
|
constexpr size_t kContentKeySizeBytes = 16;
|
|
|
|
|
|
|
|
|
|
if (unwrapped_key.size() < kContentKeySizeBytes) {
|
|
|
|
|
DVLOG(1) << "Invalid parameter: Invalid content key.";
|
|
|
|
|
return WB_RESULT_INVALID_PARAMETER;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
unwrapped_key.resize(kContentKeySizeBytes);
|
|
|
|
|
content_keys[key.id()] =
|
|
|
|
|
CreateContentKey(key.level(), is_verified, unwrapped_key);
|
|
|
|
|
} else {
|
|
|
|
|
// We should have already skipped over this key.
|
|
|
|
|
CHECK(false);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@@ -662,14 +888,13 @@ WB_Result WB_License_MaskedDecrypt(const WB_License_Whitebox* whitebox,
|
|
|
|
|
return result;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Trivial implementation that simply takes the decrypted output and XORs it
|
|
|
|
|
// with a fixed pattern. |output|'s size is based on |masked_output_data| so
|
|
|
|
|
// we shouldn't need to worry about overflow. This logic must be mirrored in
|
|
|
|
|
// Now apply the masking function to the data. This logic must be mirrored in
|
|
|
|
|
// Unmask().
|
|
|
|
|
const uint8_t* mask = kSecretStringPattern;
|
|
|
|
|
const size_t mask_size = kSecretStringPatternSize;
|
|
|
|
|
for (size_t i = 0; i < output.size(); ++i) {
|
|
|
|
|
masked_output_data[i] = output[i] ^ mask[i % mask_size];
|
|
|
|
|
masked_output_data[i] =
|
|
|
|
|
InverseMaskingFunction1(output[i] ^ mask[i % mask_size]);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return WB_RESULT_OK;
|
|
|
|
|
@@ -689,6 +914,6 @@ void WB_License_Unmask(const uint8_t* masked_data,
|
|
|
|
|
for (size_t local_i = 0; local_i < size; local_i++) {
|
|
|
|
|
const size_t global_i = offset + local_i;
|
|
|
|
|
const uint8_t mask = secret_string[global_i % secret_string_size];
|
|
|
|
|
unmasked_data[local_i] = masked_data[global_i] ^ mask;
|
|
|
|
|
unmasked_data[local_i] = MaskingFunction1(masked_data[global_i]) ^ mask;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|