Code Drop Three (Update Two)
In this update we have:
- Added the verified platform tests. These tests show how some
platforms, when verified are allowed to by pass the normal policy
restrictions. This is done with ChromeOS, thus the name of the
tests use "chrome_os".
- Removed WB_RESULT_INVALID_PADDING. This error was when we the
non-license APIs exposed a AES function with padding. However,
those functions have been removed from the API and this error is
no longer used by the API.
- Tests have been updated to avoid signed-vs-unsigned comparison
and to use the Chromium path to gTest (which is mocked in this
library).
- Tests have been updated to use a new test base and golden data
system to make them easier to read.
This commit is contained in:
@@ -9,14 +9,12 @@
|
||||
#include <vector>
|
||||
|
||||
#include "base/logging.h"
|
||||
#include "base/strings/string_number_conversions.h"
|
||||
#include "cdm/protos/license_protocol.pb.h"
|
||||
#include "crypto_utils/aes_cbc_decryptor.h"
|
||||
#include "crypto_utils/aes_ctr_encryptor.h"
|
||||
#include "crypto_utils/crypto_util.h"
|
||||
#include "crypto_utils/rsa_key.h"
|
||||
#include "impl/reference/memory_util.h"
|
||||
#include "impl/reference/string_view_util.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"
|
||||
@@ -66,10 +64,6 @@ const size_t kSecretStringPatternSize = sizeof(kSecretStringPattern);
|
||||
static_assert(kSecretStringPatternSize == AES_BLOCK_SIZE,
|
||||
"Secret string must be AES_BLOCK_SIZE.");
|
||||
|
||||
std::string AsString(const uint8_t* buffer, size_t buffer_size) {
|
||||
return std::string(reinterpret_cast<const char*>(buffer), buffer_size);
|
||||
}
|
||||
|
||||
void ApplyPattern(const uint8_t* input_buffer,
|
||||
size_t input_buffer_size,
|
||||
const uint8_t* pattern,
|
||||
@@ -92,7 +86,7 @@ const ContentKey* FindKey(const WB_License_Whitebox* whitebox,
|
||||
DCHECK(whitebox);
|
||||
DCHECK(id);
|
||||
DCHECK_GT(id_size, 0u);
|
||||
const std::string key_id = AsString(id, id_size);
|
||||
const std::string key_id(id, id + id_size);
|
||||
const auto found = whitebox->content_keys.find(key_id);
|
||||
return found == whitebox->content_keys.end() ? nullptr : &found->second;
|
||||
}
|
||||
@@ -160,6 +154,46 @@ WB_Result DecryptBuffer(WB_CipherMode mode,
|
||||
|
||||
return WB_RESULT_OK;
|
||||
}
|
||||
|
||||
// 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) {
|
||||
const bool has_ra = license.has_remote_attestation_verified();
|
||||
const bool has_vmp = license.has_platform_verification_status();
|
||||
|
||||
if (!has_ra && !has_vmp) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// We trust "missing" more than "false". Now that we know we have some values,
|
||||
// default to "it's okay" and only override it if we have a new value from the
|
||||
// license.
|
||||
bool flags[2] = {true, true};
|
||||
|
||||
if (has_ra) {
|
||||
flags[0] = license.remote_attestation_verified();
|
||||
}
|
||||
|
||||
if (has_vmp) {
|
||||
flags[1] = license.platform_verification_status() ==
|
||||
video_widevine::PLATFORM_HARDWARE_VERIFIED;
|
||||
}
|
||||
|
||||
// If we were missing a value, that flag will still be true. But if we see any
|
||||
// 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,
|
||||
@@ -177,8 +211,8 @@ WB_Result WB_License_Create(const uint8_t* whitebox_init_data,
|
||||
|
||||
// |whitebox_init_data| is simply the bytes of a PKCS #8 PrivateKeyInfo block
|
||||
// of a RSA 2048 bit key.
|
||||
std::unique_ptr<RsaPrivateKey> key(RsaPrivateKey::Create(
|
||||
AsString(whitebox_init_data, whitebox_init_data_size)));
|
||||
std::unique_ptr<RsaPrivateKey> key(RsaPrivateKey::Create(std::string(
|
||||
whitebox_init_data, whitebox_init_data + whitebox_init_data_size)));
|
||||
if (!key) {
|
||||
DVLOG(1) << "Invalid parameter: invalid init data.";
|
||||
return WB_RESULT_INVALID_PARAMETER;
|
||||
@@ -214,7 +248,8 @@ WB_Result WB_License_SignLicenseRequest(const WB_License_Whitebox* whitebox,
|
||||
|
||||
std::string result;
|
||||
DCHECK(whitebox->key->GenerateSignature(
|
||||
AsString(license_request, license_request_size), &result));
|
||||
std::string(license_request, license_request + license_request_size),
|
||||
&result));
|
||||
|
||||
if (!widevine::MemCopy(result.data(), result.size(), signature,
|
||||
*signature_size)) {
|
||||
@@ -264,15 +299,16 @@ WB_Result WB_License_ProcessLicenseResponse(WB_License_Whitebox* whitebox,
|
||||
}
|
||||
|
||||
std::string decrypted_session_key;
|
||||
if (!whitebox->key->Decrypt(AsString(session_key, session_key_size),
|
||||
&decrypted_session_key)) {
|
||||
if (!whitebox->key->Decrypt(
|
||||
std::string(session_key, session_key + session_key_size),
|
||||
&decrypted_session_key)) {
|
||||
DVLOG(1) << "Invalid parameter: invalid session key.";
|
||||
return WB_RESULT_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
std::string signing_key_material = widevine::crypto_util::DeriveKey(
|
||||
decrypted_session_key, widevine::crypto_util::kSigningKeyLabel,
|
||||
widevine::AsStringView(license_request, license_request_size),
|
||||
std::string(license_request, license_request + license_request_size),
|
||||
widevine::crypto_util::kSigningKeySizeBits * 2);
|
||||
|
||||
if (signing_key_material.size() < kSigningKeySizeBytes * 2) {
|
||||
@@ -283,15 +319,16 @@ WB_Result WB_License_ProcessLicenseResponse(WB_License_Whitebox* whitebox,
|
||||
const std::string server_signing_key =
|
||||
signing_key_material.substr(0, kSigningKeySizeBytes);
|
||||
if (!widevine::crypto_util::VerifySignatureHmacSha256(
|
||||
server_signing_key, widevine::AsStringView(signature, signature_size),
|
||||
widevine::AsStringView(message, message_size))) {
|
||||
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,
|
||||
widevine::AsStringView(license_request, license_request_size),
|
||||
std::string(license_request, license_request + license_request_size),
|
||||
widevine::crypto_util::kWrappingKeySizeBits);
|
||||
if (decryption_key.empty()) {
|
||||
DVLOG(1) << "Failed to decrypt decryption key";
|
||||
@@ -313,6 +350,12 @@ WB_Result WB_License_ProcessLicenseResponse(WB_License_Whitebox* whitebox,
|
||||
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);
|
||||
|
||||
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.
|
||||
@@ -340,8 +383,7 @@ WB_Result WB_License_ProcessLicenseResponse(WB_License_Whitebox* whitebox,
|
||||
return WB_RESULT_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
const std::string signing_key =
|
||||
AsString(unwrapped_key.data(), unwrapped_key.size());
|
||||
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);
|
||||
@@ -362,23 +404,28 @@ WB_Result WB_License_ProcessLicenseResponse(WB_License_Whitebox* whitebox,
|
||||
License_KeyContainer_SecurityLevel_SW_SECURE_CRYPTO:
|
||||
content_key.allow_decrypt = true;
|
||||
content_key.allow_masked_decrypt = true;
|
||||
content_key.key = std::move(unwrapped_key);
|
||||
break;
|
||||
case video_widevine::
|
||||
License_KeyContainer_SecurityLevel_SW_SECURE_DECODE:
|
||||
content_key.allow_decrypt = false;
|
||||
content_key.allow_masked_decrypt = true;
|
||||
content_key.key = std::move(unwrapped_key);
|
||||
break;
|
||||
default:
|
||||
content_key.allow_decrypt = false;
|
||||
content_key.allow_masked_decrypt = false;
|
||||
// Don't set key. We don't want to save this key as we should never
|
||||
// be using it. We only have an entry so that we can handle errors
|
||||
// correctly.
|
||||
break;
|
||||
}
|
||||
|
||||
content_key.allow_decrypt |= is_verified;
|
||||
content_key.allow_masked_decrypt |= is_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 = std::move(unwrapped_key);
|
||||
}
|
||||
|
||||
content_keys[key.id()] = content_key;
|
||||
} else {
|
||||
// We should have already skipped over this key.
|
||||
@@ -423,7 +470,7 @@ WB_Result WB_License_SignRenewalRequest(const WB_License_Whitebox* whitebox,
|
||||
const std::string computed_signature =
|
||||
widevine::crypto_util::CreateSignatureHmacSha256(
|
||||
whitebox->client_signing_key,
|
||||
widevine::AsStringView(message, message_size));
|
||||
std::string(message, message + message_size));
|
||||
|
||||
if (!widevine::MemCopy(computed_signature.data(), computed_signature.size(),
|
||||
signature, *signature_size)) {
|
||||
@@ -465,14 +512,15 @@ WB_Result WB_License_VerifyRenewalResponse(const WB_License_Whitebox* whitebox,
|
||||
const std::string computed_signature =
|
||||
widevine::crypto_util::CreateSignatureHmacSha256(
|
||||
whitebox->server_signing_key,
|
||||
widevine::AsStringView(message, message_size));
|
||||
std::string(message, message + message_size));
|
||||
|
||||
if (signature_size != computed_signature.size()) {
|
||||
DVLOG(1) << "Invalid parameters: invalid signature size.";
|
||||
return WB_RESULT_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
if (computed_signature != widevine::AsStringView(signature, signature_size)) {
|
||||
if (computed_signature !=
|
||||
std::string(signature, signature + signature_size)) {
|
||||
DVLOG(1) << "Data verification error: signatures do not match.";
|
||||
return WB_RESULT_INVALID_SIGNATURE;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user