In order to support both single-key and dual-key RSA implementations where single-key will use key 0 for both sign and encryption and where dual-key will use key 0 for sign and key 1 for encryption. Additional changes in this code drop: - Added VMP / RA override enabled tests - Added VMP / RA override disabled tests This brings the partner repo in sync with the internal repo at commit 71760b6da1ec546c65b56e2f86b39b73b53f6734.
208 lines
7.0 KiB
C++
208 lines
7.0 KiB
C++
// Copyright 2021 Google LLC. All Rights Reserved.
|
|
|
|
#include "reference/impl/protobuf_license_parser.h"
|
|
|
|
#include "base/check_op.h"
|
|
#include "base/logging.h"
|
|
#include "crypto_utils/crypto_util.h"
|
|
|
|
namespace widevine {
|
|
namespace {
|
|
|
|
// WV_ENABLE_HW_VERIFICATION = 0: prevent VMP/RA from overriding the key
|
|
// security level.
|
|
//
|
|
// WV_ENABLE_HW_VERIFICATION = 1: allow VMP/RA to override the key security
|
|
// level.
|
|
#if WV_ENABLE_HW_VERIFICATION == 0
|
|
constexpr bool kEnableHwVerification = false;
|
|
#elif WV_ENABLE_HW_VERIFICATION == 1
|
|
constexpr bool kEnableHwVerification = true;
|
|
#else
|
|
#error Undefined or unknown value for WV_ENABLE_HW_VERIFICATION
|
|
#endif // WV_ENABLE_HW_VERIFICATION
|
|
|
|
// We use "remote_attestation_verified" and "platform_verification_status" to
|
|
// determine whether the platform is hardware verified.
|
|
//
|
|
// Each variable can be in one of three states. Each variable has a missing
|
|
// value, a true value, and a false value.
|
|
//
|
|
// |----------------------------------------------------------|
|
|
// | | RA N/A | RA VERIFIED | RA NOT VERIFIED |
|
|
// |----------------------------------------------------------|
|
|
// | VMP N/A | 0 | 1 | 0 |
|
|
// | VMP HW_VERIFIED | 1 | 1 | 0 |
|
|
// | VMP OTHER | 0 | 0 | 0 |
|
|
// |----------------------------------------------------------|
|
|
bool IsPlatformHardwareVerified(const video_widevine::License& license) {
|
|
int ra;
|
|
if (!license.has_remote_attestation_verified()) {
|
|
ra = 0;
|
|
} else if (license.remote_attestation_verified()) {
|
|
ra = 1;
|
|
} else {
|
|
ra = 2;
|
|
}
|
|
|
|
int vmp;
|
|
if (!license.has_platform_verification_status()) {
|
|
vmp = 0;
|
|
} else if (license.platform_verification_status() ==
|
|
video_widevine::PLATFORM_HARDWARE_VERIFIED) {
|
|
vmp = 1;
|
|
} else {
|
|
vmp = 2;
|
|
}
|
|
|
|
// Use int to match the table we have in the comment above.
|
|
const int table[3][3] = {
|
|
{0, 1, 0},
|
|
{1, 1, 0},
|
|
{0, 0, 0},
|
|
};
|
|
|
|
return table[vmp][ra] == 1;
|
|
}
|
|
} // namespace
|
|
|
|
WB_Result ProtobufLicenseParser::Parse(const std::string& decryption_key,
|
|
const ODKContext& odk_context,
|
|
const std::string& message) {
|
|
video_widevine::License license;
|
|
if (!license.ParseFromString(message)) {
|
|
VLOG(3) << "Invalid parameter: Invalid license.";
|
|
return WB_RESULT_INVALID_PARAMETER;
|
|
}
|
|
|
|
// 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 =
|
|
kEnableHwVerification && IsPlatformHardwareVerified(license);
|
|
|
|
for (const auto& key : license.key()) {
|
|
// The default `type()` value will be SIGNING when not present in the
|
|
// protobuf.
|
|
switch (key.type()) {
|
|
case video_widevine::License_KeyContainer::SIGNING:
|
|
// The ODK will not write the key to the core message if there is no key
|
|
// data. This is odd, since it does not check any other fields. To be
|
|
// consistent, we have the protobuf implementation do the same.
|
|
if (key.key().empty()) {
|
|
VLOG(3) << "Skipping signing key : no key data.";
|
|
} else {
|
|
renewal_keys_.push_back(ParseSigningKey(decryption_key, key));
|
|
}
|
|
break;
|
|
|
|
case video_widevine::License_KeyContainer::CONTENT:
|
|
if (key.id().empty()) {
|
|
VLOG(3) << "Skipping content key : no key id.";
|
|
} else {
|
|
content_keys_[key.id()] =
|
|
ParseContentKey(decryption_key, key, is_verified);
|
|
}
|
|
break;
|
|
default:
|
|
VLOG(3) << "Skipping key of type " << key.type() << ".";
|
|
}
|
|
}
|
|
|
|
// This should not happen, but if it happens, the implementation is free to
|
|
// use any of them.
|
|
if (renewal_keys_.size() > 1) {
|
|
VLOG(3) << "Multiple renewal keys found. Extra keys will be ignored.";
|
|
}
|
|
|
|
return WB_RESULT_OK;
|
|
}
|
|
|
|
const widevine::RenewalKey* ProtobufLicenseParser::GetRenewalKey() const {
|
|
return renewal_keys_.empty() ? nullptr : &renewal_keys_[0];
|
|
}
|
|
|
|
const std::map<std::string, ContentKey>& ProtobufLicenseParser::GetContentKeys()
|
|
const {
|
|
return content_keys_;
|
|
}
|
|
|
|
RenewalKey ProtobufLicenseParser::ParseSigningKey(
|
|
const std::string& decryption_key,
|
|
const video_widevine::License_KeyContainer& key) const {
|
|
CHECK_EQ(decryption_key.size(), 16u);
|
|
CHECK_EQ(key.type(), video_widevine::License_KeyContainer::SIGNING);
|
|
|
|
const size_t kSizeWithoutPadding = 2 * crypto_util::kSigningKeySizeBytes;
|
|
const size_t kSizeWithPadding = 2 * crypto_util::kSigningKeySizeBytes + 16u;
|
|
|
|
if (key.key().size() != kSizeWithoutPadding &&
|
|
key.key().size() != kSizeWithPadding) {
|
|
VLOG(3) << "Invalid renewal key size (" << key.key().size() << ").";
|
|
return RenewalKey();
|
|
}
|
|
|
|
if (key.iv().size() != 16u) {
|
|
VLOG(3) << "Invalid iv size.";
|
|
return RenewalKey();
|
|
}
|
|
|
|
const std::string wrapped_key = key.key().substr(0, kSizeWithoutPadding);
|
|
std::string unwrapped_key(wrapped_key);
|
|
|
|
if (!Decrypt(decryption_key, 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.
|
|
VLOG(3) << "Failed to decrypt renewal key.";
|
|
return RenewalKey();
|
|
}
|
|
|
|
widevine::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 ProtobufLicenseParser::ParseContentKey(
|
|
const std::string& decryption_key,
|
|
const video_widevine::License_KeyContainer& key,
|
|
bool is_verified) const {
|
|
// This should have been verified before calling the parser.
|
|
CHECK_EQ(decryption_key.size(), 16u) << "Incorrect decryption key size.";
|
|
CHECK_EQ(key.type(), video_widevine::License_KeyContainer::CONTENT);
|
|
|
|
constexpr size_t kKeySizeWithoutPadding = 16u;
|
|
constexpr size_t kKeySizeWithPadding = 32u;
|
|
|
|
if (key.key().size() != kKeySizeWithoutPadding &&
|
|
key.key().size() != kKeySizeWithPadding) {
|
|
VLOG(3) << "Invalid content key size (" << key.key().size() << ").";
|
|
return ContentKey();
|
|
}
|
|
|
|
if (key.iv().size() != 16u) {
|
|
VLOG(3) << "Invalid iv size.";
|
|
return ContentKey();
|
|
}
|
|
|
|
std::string wrapped_key = key.key().substr(0, kKeySizeWithoutPadding);
|
|
std::string unwrapped_key(wrapped_key);
|
|
|
|
if (!Decrypt(decryption_key, key.iv(), wrapped_key, &unwrapped_key)) {
|
|
VLOG(3) << "Failed to decrypt content key.";
|
|
return ContentKey();
|
|
}
|
|
|
|
// The default value for `level()` will be SW_SECURE_CRYPTO. This means that
|
|
// if the level value was not set, it will be treated as SW_SECURE_CRYPTO.
|
|
return CreateContentKey(key.level(), is_verified, unwrapped_key);
|
|
}
|
|
|
|
} // namespace widevine
|