Files
ce_cdm/oemcrypto/test/oec_session_util.cpp
2020-04-10 16:13:07 -07:00

1486 lines
62 KiB
C++

// Copyright 2018 Google LLC. All Rights Reserved. This file and proprietary
// source code may only be used and distributed under the Widevine Master
// License Agreement.
//
// OEMCrypto unit tests
//
#include "oec_session_util.h"
#include <openssl/aes.h>
#include <openssl/bio.h>
#include <openssl/cmac.h>
#include <openssl/err.h>
#include <openssl/hmac.h>
#include <openssl/pem.h>
#include <openssl/rand.h>
#include <openssl/x509.h>
#include <openssl/x509_vfy.h>
#include <stdint.h>
#include <gtest/gtest.h>
#include <algorithm>
#include <iostream>
#include <memory>
#include <string>
#include <vector>
#include "OEMCryptoCENC.h"
#include "clock.h"
#include "core_message_deserialize.h"
#include "core_message_serialize.h"
#include "disallow_copy_and_assign.h"
#include "log.h"
#include "oec_device_features.h"
#include "oec_test_data.h"
#include "oemcrypto_types.h"
#include "platform.h"
#include "string_conversions.h"
#include "test_sleep.h"
using namespace std;
// GTest requires PrintTo to be in the same namespace as the thing it prints,
// which is std::vector in this case.
namespace std {
void PrintTo(const vector<uint8_t>& value, ostream* os) {
*os << wvcdm::b2a_hex(value);
}
} // namespace std
namespace {
constexpr size_t kTestSubsampleSectionSize = 256;
} // namespace
namespace wvoec {
int GetRandBytes(unsigned char* buf, int num) {
// returns 1 on success, -1 if not supported, or 0 if other failure.
return RAND_bytes(buf, num);
}
// Does the boilerplate to fill out sample and subsample descriptions for
// decrypting a single contiguous block of encrypted data to clear memory, which
// is a common operation for tests. Generates a random IV which can be used to
// encrypt the input buffer.
void GenerateSimpleSampleDescription(
const std::vector<uint8_t>& in, std::vector<uint8_t>& out,
OEMCrypto_SampleDescription* sample,
OEMCrypto_SubSampleDescription* subsample) {
ASSERT_NE(sample, nullptr);
ASSERT_NE(subsample, nullptr);
// Generate test data
EXPECT_EQ(GetRandBytes(&sample->iv[0], KEY_IV_SIZE), 1);
// Describe the test data
sample->buffers.input_data = in.data();
sample->buffers.input_data_length = in.size();
subsample->num_bytes_clear = 0;
subsample->num_bytes_encrypted = sample->buffers.input_data_length;
subsample->subsample_flags =
OEMCrypto_FirstSubsample | OEMCrypto_LastSubsample;
subsample->block_offset = 0;
sample->subsamples = subsample;
sample->subsamples_length = 1;
// Describe the output
OEMCrypto_DestBufferDesc& out_buffer_descriptor =
sample->buffers.output_descriptor;
out_buffer_descriptor.type = OEMCrypto_BufferType_Clear;
out_buffer_descriptor.buffer.clear.address = out.data();
out_buffer_descriptor.buffer.clear.address_length = out.size();
}
// Increment counter for AES-CTR. The CENC spec specifies we increment only
// the low 64 bits of the IV counter, and leave the high 64 bits alone. This
// is different from the BoringSSL implementation, so we implement the CTR loop
// ourselves.
void ctr128_inc64(int64_t increaseBy, uint8_t* iv) {
ASSERT_NE(nullptr, iv);
uint64_t* counterBuffer = reinterpret_cast<uint64_t*>(&iv[8]);
(*counterBuffer) =
wvcdm::htonll64(wvcdm::ntohll64(*counterBuffer) + increaseBy);
}
// Some compilers don't like the macro htonl within an ASSERT_EQ.
uint32_t htonl_fnc(uint32_t x) { return htonl(x); }
void dump_boringssl_error() {
while (unsigned long err = ERR_get_error()) {
char buffer[120];
ERR_error_string_n(err, buffer, sizeof(buffer));
cout << "BoringSSL Error -- " << buffer << "\n";
}
}
// A smart pointer for BoringSSL objects. It uses the specified free function
// to release resources and free memory when the pointer is deleted.
template <typename T, void (*func)(T*)>
class boringssl_ptr {
public:
explicit boringssl_ptr(T* p = nullptr) : ptr_(p) {}
~boringssl_ptr() {
if (ptr_) func(ptr_);
}
T& operator*() const { return *ptr_; }
T* operator->() const { return ptr_; }
T* get() const { return ptr_; }
bool NotNull() const { return ptr_; }
private:
T* ptr_;
CORE_DISALLOW_COPY_AND_ASSIGN(boringssl_ptr);
};
Test_PST_Report::Test_PST_Report(const std::string& pst_in,
OEMCrypto_Usage_Entry_Status status_in)
: status(status_in), pst(pst_in) {
time_created = wvcdm::Clock().GetCurrentTime();
}
template <class CoreRequest, PrepAndSignRequest_t PrepAndSignRequest,
class CoreResponse, class ResponseData>
void RoundTrip<CoreRequest, PrepAndSignRequest, CoreResponse,
ResponseData>::SignAndVerifyRequest() {
// In the real world, a message should be signed by the client and
// verified by the server. This simulates that.
size_t gen_signature_length = 0;
size_t core_message_length = 0;
constexpr size_t small_size = 42; // arbitrary.
size_t message_size =
std::max(required_message_size_, core_message_length + small_size);
vector<uint8_t> data(message_size, 0);
for (size_t i = 0; i < data.size(); i++) data[i] = i & 0xFF;
ASSERT_EQ(
PrepAndSignRequest(session()->session_id(), data.data(), data.size(),
&core_message_length, nullptr, &gen_signature_length),
OEMCrypto_ERROR_SHORT_BUFFER);
// Make the message buffer a little bigger than the core message, or the
// required size, whichever is larger.
message_size =
std::max(required_message_size_, core_message_length + small_size);
data.resize(message_size);
for (size_t i = 0; i < data.size(); i++) data[i] = i & 0xFF;
vector<uint8_t> gen_signature(gen_signature_length);
ASSERT_EQ(PrepAndSignRequest(session()->session_id(), data.data(),
data.size(), &core_message_length,
gen_signature.data(), &gen_signature_length),
OEMCrypto_SUCCESS);
if (global_features.api_version >= kCoreMessagesAPI) {
ASSERT_GT(data.size(), core_message_length);
std::string core_message(reinterpret_cast<char*>(data.data()),
core_message_length);
FillAndVerifyCoreRequest(core_message);
}
VerifyRequestSignature(data, gen_signature, core_message_length);
}
template <class CoreRequest, PrepAndSignRequest_t PrepAndSignRequest,
class CoreResponse, class ResponseData>
OEMCrypto_Substring RoundTrip<CoreRequest, PrepAndSignRequest, CoreResponse,
ResponseData>::FindSubstring(const void* pointer,
size_t length) {
OEMCrypto_Substring substring;
if (length == 0 || pointer == nullptr) {
substring.offset = 0;
substring.length = 0;
} else {
substring.offset = reinterpret_cast<const uint8_t*>(pointer) -
reinterpret_cast<const uint8_t*>(&response_data_);
substring.length = length;
}
return substring;
}
void ProvisioningRoundTrip::PrepareSession(
const wvoec::WidevineKeybox& keybox) {
ASSERT_NO_FATAL_FAILURE(session_->open());
session_->GenerateNonce();
if (global_features.provisioning_method == OEMCrypto_Keybox) {
session_->GenerateDerivedKeysFromKeybox(keybox);
encryptor_ = session_->key_deriver();
} else {
EXPECT_EQ(global_features.provisioning_method, OEMCrypto_OEMCertificate);
session_->LoadOEMCert(true);
session_->GenerateRSASessionKey(&message_key_, &encrypted_message_key_);
encryptor_.set_enc_key(message_key_);
}
}
void ProvisioningRoundTrip::VerifyRequestSignature(
const vector<uint8_t>& data, const vector<uint8_t>& generated_signature,
size_t /* core_message_length */) {
if (global_features.provisioning_method == OEMCrypto_OEMCertificate) {
session()->VerifyRSASignature(data, generated_signature.data(),
generated_signature.size(), kSign_RSASSA_PSS);
} else {
EXPECT_EQ(global_features.provisioning_method, OEMCrypto_Keybox);
ASSERT_EQ(HMAC_SHA256_SIGNATURE_SIZE, generated_signature.size());
std::vector<uint8_t> expected_signature;
session()->key_deriver().ClientSignBuffer(data, &expected_signature);
ASSERT_EQ(expected_signature, generated_signature);
}
}
void ProvisioningRoundTrip::FillAndVerifyCoreRequest(
const std::string& core_message_string) {
EXPECT_TRUE(
oemcrypto_core_message::deserialize::CoreProvisioningRequestFromMessage(
core_message_string, &core_request_));
EXPECT_EQ(global_features.api_version, core_request_.api_major_version);
EXPECT_EQ(session()->nonce(), core_request_.nonce);
EXPECT_EQ(session()->session_id(), core_request_.session_id);
size_t device_id_length = core_request_.device_id.size();
std::vector<uint8_t> device_id(device_id_length);
EXPECT_EQ(OEMCrypto_SUCCESS,
OEMCrypto_GetDeviceID(device_id.data(), &device_id_length));
EXPECT_EQ(core_request_.device_id.size(), device_id_length);
std::string device_id_string(reinterpret_cast<char*>(device_id.data()),
device_id_length);
EXPECT_EQ(device_id_string, core_request_.device_id);
}
void ProvisioningRoundTrip::CreateDefaultResponse() {
if (allowed_schemes_ != kSign_RSASSA_PSS) {
uint32_t algorithm_n = htonl(allowed_schemes_);
memcpy(response_data_.rsa_key, "SIGN", 4);
memcpy(response_data_.rsa_key + 4, &algorithm_n, 4);
memcpy(response_data_.rsa_key + 8, encoded_rsa_key_.data(),
encoded_rsa_key_.size());
response_data_.rsa_key_length = 8 + encoded_rsa_key_.size();
} else {
memcpy(response_data_.rsa_key, encoded_rsa_key_.data(),
encoded_rsa_key_.size());
response_data_.rsa_key_length = encoded_rsa_key_.size();
}
response_data_.nonce = session_->nonce();
if (encrypted_message_key_.size() > 0) {
ASSERT_LE(encrypted_message_key_.size(), kMaxTestRSAKeyLength);
memcpy(response_data_.enc_message_key, encrypted_message_key_.data(),
encrypted_message_key_.size());
response_data_.enc_message_key_length = encrypted_message_key_.size();
} else {
response_data_.enc_message_key_length = 0;
}
core_response_.key_type = OEMCrypto_RSA_Private_Key;
core_response_.enc_private_key =
FindSubstring(response_data_.rsa_key, response_data_.rsa_key_length);
core_response_.enc_private_key_iv = FindSubstring(
response_data_.rsa_key_iv, sizeof(response_data_.rsa_key_iv));
core_response_.encrypted_message_key = FindSubstring(
response_data_.enc_message_key, response_data_.enc_message_key_length);
}
void ProvisioningRoundTrip::EncryptAndSignResponse() {
encryptor_.PadAndEncryptProvisioningMessage(&response_data_,
&encrypted_response_data_);
core_response_.enc_private_key.length =
encrypted_response_data_.rsa_key_length;
if (global_features.api_version >= kCoreMessagesAPI) {
ASSERT_TRUE(
oemcrypto_core_message::serialize::CreateCoreProvisioningResponse(
core_response_, core_request_, &serialized_core_message_));
}
// Make the message buffer a just big enough, or the
// required size, whichever is larger.
const size_t message_size =
std::max(required_message_size_, serialized_core_message_.size() +
sizeof(encrypted_response_data_));
// Stripe the encrypted message.
encrypted_response_.resize(message_size);
for (size_t i = 0; i < encrypted_response_.size(); i++) {
encrypted_response_[i] = i & 0xFF;
}
ASSERT_GE(encrypted_response_.size(), serialized_core_message_.size());
memcpy(encrypted_response_.data(), serialized_core_message_.data(),
serialized_core_message_.size());
ASSERT_GE(encrypted_response_.size(),
serialized_core_message_.size() + sizeof(encrypted_response_data_));
memcpy(encrypted_response_.data() + serialized_core_message_.size(),
reinterpret_cast<const uint8_t*>(&encrypted_response_data_),
sizeof(encrypted_response_data_));
if (global_features.provisioning_method == OEMCrypto_OEMCertificate) {
session()->GenerateDerivedKeysFromSessionKey();
}
session()->key_deriver().ServerSignBuffer(encrypted_response_.data(),
encrypted_response_.size(),
&response_signature_);
}
OEMCryptoResult ProvisioningRoundTrip::LoadResponse(Session* session) {
EXPECT_NE(session, nullptr);
size_t wrapped_key_length = 0;
const OEMCryptoResult sts = LoadResponseNoRetry(session, &wrapped_key_length);
if (sts != OEMCrypto_ERROR_SHORT_BUFFER) return sts;
wrapped_rsa_key_.clear();
wrapped_rsa_key_.assign(wrapped_key_length, 0);
return LoadResponseNoRetry(session, &wrapped_key_length);
}
#ifdef TEST_OEMCRYPTO_V15
// If this platform supports v15 functions, then will test with them:
# define OEMCrypto_RewrapDeviceRSAKey_V15 OEMCrypto_RewrapDeviceRSAKey
# define OEMCrypto_RewrapDeviceRSAKey30_V15 OEMCrypto_RewrapDeviceRSAKey30
#else
// If this platform does not support v15 functions, we just need to stub these
// out so that the tests compile.
OEMCryptoResult OEMCrypto_RewrapDeviceRSAKey30_V15(
OEMCrypto_SESSION, const uint32_t*, const uint8_t*, size_t, const uint8_t*,
size_t, const uint8_t*, uint8_t*, size_t*) {
LOGE("Support for v15 functions not included. Define TEST_OEMCRYPTO_V15.");
return OEMCrypto_ERROR_NOT_IMPLEMENTED;
}
OEMCryptoResult OEMCrypto_RewrapDeviceRSAKey_V15(
OEMCrypto_SESSION, const uint8_t*, size_t, const uint8_t*, size_t,
const uint32_t*, const uint8_t*, size_t, const uint8_t*, uint8_t*,
size_t*) {
LOGE("Support for v15 functions not included. Define TEST_OEMCRYPTO_V15.");
return OEMCrypto_ERROR_NOT_IMPLEMENTED;
}
#endif
template <typename T>
const T* ProvisioningRoundTrip::RemapPointer(const T* response_pointer) const {
const uint8_t* original_pointer =
reinterpret_cast<const uint8_t*>(response_pointer);
size_t delta =
original_pointer - reinterpret_cast<const uint8_t*>(&response_data_);
// Base offset should be 0 if this is a v15 message, which is the only time
// this function is called.
size_t base_offset = serialized_core_message_.size();
const uint8_t* new_pointer = encrypted_response_.data() + delta + base_offset;
return reinterpret_cast<const T*>(new_pointer);
}
OEMCryptoResult ProvisioningRoundTrip::LoadResponseNoRetry(
Session* session, size_t* wrapped_key_length) {
EXPECT_NE(session, nullptr);
if (global_features.api_version >= kCoreMessagesAPI) {
return OEMCrypto_LoadProvisioning(
session->session_id(), encrypted_response_.data(),
encrypted_response_.size(), serialized_core_message_.size(),
response_signature_.data(), response_signature_.size(),
wrapped_rsa_key_.data(), wrapped_key_length);
} else if (global_features.provisioning_method == OEMCrypto_Keybox) {
return OEMCrypto_RewrapDeviceRSAKey_V15(
session->session_id(), encrypted_response_.data(),
encrypted_response_.size(), response_signature_.data(),
response_signature_.size(), RemapPointer(&response_data_.nonce),
RemapPointer(response_data_.rsa_key),
encrypted_response_data_.rsa_key_length,
RemapPointer(response_data_.rsa_key_iv), wrapped_rsa_key_.data(),
wrapped_key_length);
} else {
return OEMCrypto_RewrapDeviceRSAKey30_V15(
session->session_id(), &encrypted_response_data_.nonce,
RemapPointer(response_data_.enc_message_key),
response_data_.enc_message_key_length,
RemapPointer(response_data_.rsa_key),
encrypted_response_data_.rsa_key_length,
RemapPointer(response_data_.rsa_key_iv), wrapped_rsa_key_.data(),
wrapped_key_length);
}
}
void ProvisioningRoundTrip::VerifyLoadFailed() {
if (wrapped_rsa_key_.size() == 0) return;
std::vector<uint8_t> zero(wrapped_rsa_key_.size(), 0);
ASSERT_EQ(zero, wrapped_rsa_key_);
}
void LicenseRoundTrip::VerifyRequestSignature(
const vector<uint8_t>& data, const vector<uint8_t>& generated_signature,
size_t core_message_length) {
const std::vector<uint8_t> subdata(data.begin() + core_message_length,
data.end());
session()->VerifyRSASignature(subdata, generated_signature.data(),
generated_signature.size(), kSign_RSASSA_PSS);
SHA256(data.data(), core_message_length, request_hash_);
// If the api version was not set by the test, then we record the api version
// from the request. Also, if the api was set to be higher than oemcrypto
// supports, then we lower it. This version will be used in the response.
if (api_version_ == 0) api_version_ = core_request_.api_major_version;
if (api_version_ > global_features.api_version)
api_version_ = global_features.api_version;
}
void LicenseRoundTrip::FillAndVerifyCoreRequest(
const std::string& core_message_string) {
EXPECT_TRUE(
oemcrypto_core_message::deserialize::CoreLicenseRequestFromMessage(
core_message_string, &core_request_));
EXPECT_EQ(global_features.api_version, core_request_.api_major_version);
// If we are testing the latest OEMCrypto version, make sure it is built with
// the latest ODK version, too:
if (global_features.api_version == ODK_MAJOR_VERSION) {
EXPECT_EQ(ODK_MINOR_VERSION, core_request_.api_minor_version);
}
if (expect_request_has_correct_nonce_) {
EXPECT_EQ(session()->nonce(), core_request_.nonce);
}
EXPECT_EQ(session()->session_id(), core_request_.session_id);
}
void LicenseRoundTrip::CreateDefaultResponse() {
EXPECT_EQ(1, GetRandBytes(response_data_.mac_key_iv,
sizeof(response_data_.mac_key_iv)));
memset(response_data_.padding, 0, sizeof(response_data_.padding));
EXPECT_EQ(1, GetRandBytes(response_data_.mac_keys,
sizeof(response_data_.mac_keys)));
// For backwards compatibility, we use the largest limit in timer_limits for
// each key's duration.
uint32_t key_duration = static_cast<uint32_t>(
std::max({core_response_.timer_limits.rental_duration_seconds,
core_response_.timer_limits.total_playback_duration_seconds,
core_response_.timer_limits.initial_renewal_duration_seconds}));
// The key data for an entitlement license is an AES-256 key, otherwise the
// default is an AES_128 key.
uint32_t default_key_size =
(license_type_ == OEMCrypto_EntitlementLicense) ? KEY_SIZE * 2 : KEY_SIZE;
for (unsigned int i = 0; i < num_keys_; i++) {
memset(response_data_.keys[i].key_id, 0, kTestKeyIdMaxLength);
response_data_.keys[i].key_id_length = kDefaultKeyIdLength;
memset(response_data_.keys[i].key_id, i,
response_data_.keys[i].key_id_length);
EXPECT_EQ(1, GetRandBytes(response_data_.keys[i].key_data,
sizeof(response_data_.keys[i].key_data)));
response_data_.keys[i].key_data_length = default_key_size;
EXPECT_EQ(1, GetRandBytes(response_data_.keys[i].key_iv,
sizeof(response_data_.keys[i].key_iv)));
EXPECT_EQ(1, GetRandBytes(response_data_.keys[i].control_iv,
sizeof(response_data_.keys[i].control_iv)));
std::string kcVersion = "kc" + std::to_string(api_version_);
memcpy(response_data_.keys[i].control.verification, kcVersion.c_str(), 4);
response_data_.keys[i].control.duration = htonl(key_duration);
response_data_.keys[i].control.nonce = htonl(session_->nonce());
response_data_.keys[i].control.control_bits = htonl(control_);
response_data_.keys[i].cipher_mode = OEMCrypto_CipherMode_CTR;
}
// Fill in the default core_response_ fields, except the substrings, which are
// filled in the next function.
core_response_.nonce_required =
((wvoec::kControlNonceEnabled | wvoec::kControlNonceOrEntry |
wvoec::kControlNonceRequired) &
control_)
? 1
: 0;
core_response_.license_type = license_type_;
FillCoreResponseSubstrings();
}
void LicenseRoundTrip::CreateResponseWithGenericCryptoKeys() {
CreateDefaultResponse();
response_data_.keys[0].control.control_bits |=
htonl(wvoec::kControlAllowEncrypt);
response_data_.keys[1].control.control_bits |=
htonl(wvoec::kControlAllowDecrypt);
response_data_.keys[2].control.control_bits |=
htonl(wvoec::kControlAllowSign);
response_data_.keys[3].control.control_bits |=
htonl(wvoec::kControlAllowVerify);
response_data_.keys[2].key_data_length = wvoec::MAC_KEY_SIZE;
response_data_.keys[3].key_data_length = wvoec::MAC_KEY_SIZE;
FillCoreResponseSubstrings();
}
void LicenseRoundTrip::FillCoreResponseSubstrings() {
if (update_mac_keys_) {
core_response_.enc_mac_keys_iv = FindSubstring(
response_data_.mac_key_iv, sizeof(response_data_.mac_key_iv));
core_response_.enc_mac_keys =
FindSubstring(response_data_.mac_keys, sizeof(response_data_.mac_keys));
}
if (pst_.size() > 0) {
ASSERT_LE(pst_.size(), sizeof(response_data_.pst));
memcpy(response_data_.pst, pst_.c_str(),
min(sizeof(response_data_.pst), pst_.length()));
core_response_.pst = FindSubstring(response_data_.pst, pst_.size());
}
if (minimum_srm_version_ > 0) {
const std::string verification = "HDCPDATA";
ASSERT_EQ(verification.size(),
sizeof(response_data_.srm_restriction_data.verification));
memcpy(response_data_.srm_restriction_data.verification,
verification.c_str(), verification.size());
response_data_.srm_restriction_data.minimum_srm_version =
htonl(minimum_srm_version_);
core_response_.srm_restriction_data =
FindSubstring(&response_data_.srm_restriction_data,
sizeof(response_data_.srm_restriction_data));
}
core_response_.key_array_length = num_keys_;
for (unsigned int i = 0; i < num_keys_; i++) {
core_response_.key_array[i].key_id = FindSubstring(
response_data_.keys[i].key_id, response_data_.keys[i].key_id_length);
core_response_.key_array[i].key_data_iv = FindSubstring(
response_data_.keys[i].key_iv, sizeof(response_data_.keys[i].key_iv));
core_response_.key_array[i].key_data =
FindSubstring(response_data_.keys[i].key_data,
response_data_.keys[i].key_data_length);
core_response_.key_array[i].key_control_iv =
FindSubstring(response_data_.keys[i].control_iv,
sizeof(response_data_.keys[i].control_iv));
core_response_.key_array[i].key_control =
FindSubstring(&response_data_.keys[i].control,
sizeof(response_data_.keys[i].control));
}
}
void LicenseRoundTrip::EncryptAndSignResponse() {
ASSERT_NO_FATAL_FAILURE(session_->GenerateDerivedKeysFromSessionKey());
encrypted_response_data_ = response_data_;
uint8_t iv_buffer[KEY_IV_SIZE];
memcpy(iv_buffer, &response_data_.mac_key_iv[0], KEY_IV_SIZE);
session_->key_deriver().CBCEncrypt(
&response_data_.mac_keys[0], &encrypted_response_data_.mac_keys[0],
2 * MAC_KEY_SIZE, response_data_.mac_key_iv);
for (unsigned int i = 0; i < num_keys_; i++) {
memcpy(iv_buffer, &response_data_.keys[i].control_iv[0], KEY_IV_SIZE);
AES_KEY aes_key;
AES_set_encrypt_key(&response_data_.keys[i].key_data[0], 128, &aes_key);
AES_cbc_encrypt(
reinterpret_cast<const uint8_t*>(&response_data_.keys[i].control),
reinterpret_cast<uint8_t*>(&encrypted_response_data_.keys[i].control),
KEY_SIZE, &aes_key, iv_buffer, AES_ENCRYPT);
session_->key_deriver().CBCEncrypt(
&response_data_.keys[i].key_data[0],
&encrypted_response_data_.keys[i].key_data[0],
response_data_.keys[i].key_data_length, response_data_.keys[i].key_iv);
}
if (api_version_ < kCoreMessagesAPI) {
serialized_core_message_.resize(0);
} else {
std::string request_hash_string(
reinterpret_cast<const char*>(request_hash_), sizeof(request_hash_));
ASSERT_TRUE(oemcrypto_core_message::serialize::CreateCoreLicenseResponse(
core_response_, core_request_, request_hash_string,
&serialized_core_message_));
}
// Make the message buffer a just big enough, or the
// required size, whichever is larger.
const size_t message_size =
std::max(required_message_size_, serialized_core_message_.size() +
sizeof(encrypted_response_data_));
// Stripe the encrypted message.
encrypted_response_.resize(message_size);
for (size_t i = 0; i < encrypted_response_.size(); i++) {
encrypted_response_[i] = i % 0x100;
}
ASSERT_GE(kMaxCoreMessage, serialized_core_message_.size());
ASSERT_GE(encrypted_response_.size(), serialized_core_message_.size());
memcpy(encrypted_response_.data(), serialized_core_message_.data(),
serialized_core_message_.size());
ASSERT_GE(encrypted_response_.size(),
serialized_core_message_.size() + sizeof(encrypted_response_data_));
memcpy(encrypted_response_.data() + serialized_core_message_.size(),
reinterpret_cast<const uint8_t*>(&encrypted_response_data_),
sizeof(encrypted_response_data_));
session()->key_deriver().ServerSignBuffer(encrypted_response_.data(),
encrypted_response_.size(),
&response_signature_);
}
OEMCryptoResult LicenseRoundTrip::LoadResponse(Session* session) {
EXPECT_NE(session, nullptr);
// Some tests adjust the offset to be beyond the length of the message. Here,
// we create a duplicate of the main message buffer so that these offsets do
// not point to garbage data. The goal is to make sure OEMCrypto is verifying
// that the offset points outside of the message -- we don't want OEMCrypto to
// look at what offset points to and return an error if the data is
// garbage. Since the memory after the message buffer is an exact copy of the
// message, we can increment the offset by the message size and get valid
// data.
std::vector<uint8_t> double_message = encrypted_response_;
double_message.insert(
double_message.end(),
reinterpret_cast<const uint8_t*>(&encrypted_response_data_),
reinterpret_cast<const uint8_t*>(&encrypted_response_data_) +
sizeof(encrypted_response_data_));
OEMCryptoResult result;
if (api_version_ < kCoreMessagesAPI) {
result = OEMCrypto_LoadKeys(
session->session_id(), double_message.data(),
encrypted_response_.size(), response_signature_.data(),
response_signature_.size(), core_response_.enc_mac_keys_iv,
core_response_.enc_mac_keys, core_response_.key_array_length,
core_response_.key_array, core_response_.pst,
core_response_.srm_restriction_data,
static_cast<OEMCrypto_LicenseType>(core_response_.license_type));
} else {
result = OEMCrypto_LoadLicense(
session->session_id(), double_message.data(),
encrypted_response_.size(), serialized_core_message_.size(),
response_signature_.data(), response_signature_.size());
}
if (result == OEMCrypto_SUCCESS) {
// Give the session object a copy of the license truth data so that it can
// call SelectKey, use key control information, and so that it has key data
// to verify decrypt operations.
session->set_license(response_data_);
// Also, if the license has new mac keys, then install them now.
if (core_response_.enc_mac_keys.length > 0) {
session->set_mac_keys(response_data_.mac_keys);
}
// Note: we verify content licenses here. For entitlement license, we verify
// the key control blocks after loading entitled content keys.
if (license_type_ == OEMCrypto_ContentLicense) VerifyTestKeys();
}
return result;
}
OEMCryptoResult LicenseRoundTrip::ReloadResponse(Session* session) {
session->GenerateDerivedKeysFromSessionKey();
return LoadResponse(session);
}
// This function verifies that the key control block reported by OEMCrypto agree
// with the truth key control block. Failures in this function probably
// indicate the OEMCrypto_LoadLicense/LoadKeys did not correctly process the key
// control block.
void LicenseRoundTrip::VerifyTestKeys() {
for (unsigned int i = 0; i < num_keys_; i++) {
KeyControlBlock block;
size_t size = sizeof(block);
OEMCryptoResult sts = OEMCrypto_QueryKeyControl(
session_->session_id(), response_data_.keys[i].key_id,
response_data_.keys[i].key_id_length,
reinterpret_cast<uint8_t*>(&block), &size);
if (sts != OEMCrypto_ERROR_NOT_IMPLEMENTED) {
ASSERT_EQ(OEMCrypto_SUCCESS, sts);
ASSERT_EQ(sizeof(block), size);
// Note: we do not assume that duration is stored with each key after v16.
// control bits stored in network byte order. For printing
// we change to host byte order.
ASSERT_EQ(htonl_fnc(response_data_.keys[i].control.control_bits),
htonl_fnc(block.control_bits))
<< "For key " << i;
}
}
}
void LicenseRoundTrip::SetKeyId(size_t index, const string& key_id) {
ASSERT_LT(index, num_keys_);
MessageKeyData& key = response_data_.keys[index];
key.key_id_length = key_id.length();
ASSERT_LE(key.key_id_length, kTestKeyIdMaxLength);
memcpy(key.key_id, key_id.data(), key.key_id_length);
}
void EntitledMessage::FillKeyArray() {
for (size_t i = 0; i < license_messages_->num_keys(); ++i) {
MakeOneKey(i);
}
}
void EntitledMessage::MakeOneKey(size_t entitlement_key_index) {
ASSERT_LT(entitlement_key_index, kMaxNumKeys);
ASSERT_LT(num_keys_, kMaxNumKeys);
EntitledContentKeyData* key_data = &entitled_key_data_[num_keys_];
MessageKeyData* entitlement_key =
&license_messages_->response_data().keys[entitlement_key_index];
OEMCrypto_EntitledContentKeyObject* offsets = &entitled_key_array_[num_keys_];
num_keys_++;
key_data->key_index = entitlement_key_index;
ASSERT_LE(entitlement_key->key_id_length, kTestKeyIdMaxLength);
memcpy(key_data->entitlement_key_id, entitlement_key->key_id,
entitlement_key->key_id_length);
key_data->entitlement_key_id_length = entitlement_key->key_id_length;
offsets->entitlement_key_id = FindSubstring(key_data->entitlement_key_id,
entitlement_key->key_id_length);
key_data->content_key_id_length = kDefaultKeyIdLength;
// Fill the key ID as CnCnCnCn... so it's easy to see in debug logs.
memset(key_data->content_key_id, 0xC0 + num_keys_,
key_data->content_key_id_length);
offsets->content_key_id =
FindSubstring(key_data->content_key_id, key_data->content_key_id_length);
EXPECT_EQ(1, GetRandBytes(key_data->content_key_data,
sizeof(key_data->content_key_data)));
// Note: we give the encrypted content key to OEMCrypto, not the clear
// content key.
offsets->content_key_data =
FindSubstring(key_data->encrypted_content_key_data,
sizeof(key_data->encrypted_content_key_data));
EXPECT_EQ(1, GetRandBytes(key_data->content_key_data_iv,
sizeof(key_data->content_key_data_iv)));
offsets->content_key_data_iv = FindSubstring(
key_data->content_key_data_iv, sizeof(key_data->content_key_data_iv));
}
void EntitledMessage::SetEntitlementKeyId(unsigned int index,
const std::string& key_id) {
ASSERT_LT(index, num_keys_);
ASSERT_LE(key_id.size(), kTestKeyIdMaxLength);
entitled_key_data_[index].entitlement_key_id_length = key_id.size();
memcpy(entitled_key_data_[index].entitlement_key_id,
reinterpret_cast<const uint8_t*>(key_id.c_str()), key_id.length());
entitled_key_array_[index].entitlement_key_id = FindSubstring(
entitled_key_data_[index].entitlement_key_id, key_id.length());
}
OEMCrypto_Substring EntitledMessage::FindSubstring(const void* ptr,
size_t size) {
OEMCrypto_Substring substring{0, 0};
if (ptr != nullptr) {
substring.offset = reinterpret_cast<const uint8_t*>(ptr) -
reinterpret_cast<const uint8_t*>(entitled_key_data_);
substring.length = size;
}
return substring;
}
void EntitledMessage::LoadKeys(OEMCryptoResult expected_sts) {
for (size_t i = 0; i < num_keys_; ++i) {
EntitledContentKeyData* key_data = &entitled_key_data_[i];
const size_t entitlement_key_index = key_data->key_index;
MessageKeyData* entitlement_key =
&license_messages_->response_data().keys[entitlement_key_index];
// Load the entitlement key from |key_array_|.
AES_KEY aes_key;
AES_set_encrypt_key(entitlement_key->key_data, 256, &aes_key);
// Encrypt the content key with the entitlement key.
uint8_t iv[16];
memcpy(&iv[0], key_data->content_key_data_iv, KEY_IV_SIZE);
AES_cbc_encrypt(key_data->content_key_data,
key_data->encrypted_content_key_data, KEY_SIZE, &aes_key,
iv, AES_ENCRYPT);
}
ASSERT_EQ(expected_sts,
OEMCrypto_LoadEntitledContentKeys(
license_messages_->session()->session_id(),
reinterpret_cast<const uint8_t*>(entitled_key_data_),
sizeof(entitled_key_data_), num_keys_, entitled_key_array_));
if (expected_sts != OEMCrypto_SUCCESS) {
return;
}
VerifyEntitlementTestKeys();
}
// This function verifies that the key control block reported by OEMCrypto agree
// with the truth key control block. Failures in this function probably
// indicate the OEMCrypto_LoadEntitledKeys did not correctly process the key
// control block.
void EntitledMessage::VerifyEntitlementTestKeys() {
for (unsigned int i = 0; i < num_keys_; i++) {
EntitledContentKeyData* key_data = &entitled_key_data_[i];
const size_t entitlement_key_index = key_data->key_index;
MessageKeyData* entitlement_key =
&license_messages_->response_data().keys[entitlement_key_index];
KeyControlBlock block;
size_t size = sizeof(block);
OEMCryptoResult sts = OEMCrypto_QueryKeyControl(
license_messages_->session()->session_id(), key_data->content_key_id,
key_data->content_key_id_length, reinterpret_cast<uint8_t*>(&block),
&size);
if (sts != OEMCrypto_ERROR_NOT_IMPLEMENTED) {
ASSERT_EQ(OEMCrypto_SUCCESS, sts);
ASSERT_EQ(sizeof(block), size);
// control duration and bits stored in network byte order. For printing
// we change to host byte order.
ASSERT_EQ((htonl_fnc(entitlement_key->control.duration)),
(htonl_fnc(block.duration)))
<< "For key " << i;
ASSERT_EQ(htonl_fnc(entitlement_key->control.control_bits),
htonl_fnc(block.control_bits))
<< "For key " << i;
}
}
}
void RenewalRoundTrip::VerifyRequestSignature(
const vector<uint8_t>& data, const vector<uint8_t>& generated_signature,
size_t core_message_length) {
ASSERT_EQ(HMAC_SHA256_SIGNATURE_SIZE, generated_signature.size());
std::vector<uint8_t> expected_signature;
if (license_messages_->api_version() < kCoreMessagesAPI) {
// For v15 or earlier, we only sign the message body. Ignore the core
// message.
std::vector<uint8_t> subdata(data.begin() + core_message_length,
data.end());
session()->key_deriver().ClientSignBuffer(subdata, &expected_signature);
} else {
session()->key_deriver().ClientSignBuffer(data, &expected_signature);
}
ASSERT_EQ(expected_signature, generated_signature);
}
void RenewalRoundTrip::FillAndVerifyCoreRequest(
const std::string& core_message_string) {
if (license_messages_->api_version() < kCoreMessagesAPI || is_release_) {
// For v15 or for a release, we expect that no core request was created.
EXPECT_FALSE(
oemcrypto_core_message::deserialize::CoreRenewalRequestFromMessage(
core_message_string, &core_request_));
} else {
EXPECT_TRUE(
oemcrypto_core_message::deserialize::CoreRenewalRequestFromMessage(
core_message_string, &core_request_));
EXPECT_EQ(license_messages_->core_request().api_major_version,
core_request_.api_major_version);
EXPECT_EQ(license_messages_->core_request().nonce, core_request_.nonce);
EXPECT_EQ(license_messages_->core_request().session_id,
core_request_.session_id);
}
}
void RenewalRoundTrip::CreateDefaultResponse() {
if (license_messages_->api_version() < kCoreMessagesAPI || is_release_) {
uint32_t control = 0;
uint32_t nonce = 0;
// If this is a v15 device, and a v15 license, and the license used a nonce,
// then the response should require a new nonce, too.
if (global_features.api_version < kCoreMessagesAPI &&
(license_messages_->control() & wvoec::kControlNonceEnabled)) {
control = wvoec::kControlNonceEnabled;
session_->GenerateNonce();
nonce = session_->nonce();
}
// A single key object with no key id should update all keys.
constexpr size_t index = 0;
response_data_.keys[index].key_id_length = 0;
response_data_.keys[index].key_id[0] = '\0';
const uint32_t renewal_api =
std::max<uint32_t>(core_request_.api_major_version, 15u);
std::string kcVersion = "kc" + std::to_string(renewal_api);
memcpy(response_data_.keys[index].control.verification, kcVersion.c_str(),
4);
const uint32_t duration = static_cast<uint32_t>(
license_messages_->core_response()
.timer_limits.initial_renewal_duration_seconds);
response_data_.keys[index].control.duration = htonl(duration);
response_data_.keys[index].control.nonce = htonl(nonce);
response_data_.keys[index].control.control_bits = htonl(control);
}
}
void RenewalRoundTrip::EncryptAndSignResponse() {
// Renewal messages are not encrypted.
encrypted_response_data_ = response_data_;
// Either create a KeyRefreshObject for a call to RefreshKeys or a core
// response for a call to LoadRenewal.
if (license_messages_->api_version() < kCoreMessagesAPI) {
refresh_object_.key_id = FindSubstring(nullptr, 0);
refresh_object_.key_control_iv = FindSubstring(nullptr, 0);
refresh_object_.key_control =
FindSubstring(&response_data_.keys[0].control,
sizeof(response_data_.keys[0].control));
serialized_core_message_.resize(0);
} else {
ASSERT_TRUE(oemcrypto_core_message::serialize::CreateCoreRenewalResponse(
core_request_, renewal_duration_seconds_, &serialized_core_message_));
}
// Make the message buffer a just big enough, or the
// required size, whichever is larger.
const size_t message_size =
std::max(required_message_size_, serialized_core_message_.size() +
sizeof(encrypted_response_data_));
// Stripe the encrypted message.
encrypted_response_.resize(message_size);
for (size_t i = 0; i < encrypted_response_.size(); i++) {
encrypted_response_[i] = i % 0x100;
}
// Concatenate the core message and the response.
ASSERT_GE(kMaxCoreMessage, serialized_core_message_.size());
ASSERT_GE(encrypted_response_.size(), serialized_core_message_.size());
memcpy(encrypted_response_.data(), serialized_core_message_.data(),
serialized_core_message_.size());
ASSERT_GE(encrypted_response_.size(),
serialized_core_message_.size() + sizeof(encrypted_response_data_));
memcpy(encrypted_response_.data() + serialized_core_message_.size(),
reinterpret_cast<const uint8_t*>(&encrypted_response_data_),
sizeof(encrypted_response_data_));
session()->key_deriver().ServerSignBuffer(encrypted_response_.data(),
encrypted_response_.size(),
&response_signature_);
}
OEMCryptoResult RenewalRoundTrip::LoadResponse(Session* session) {
if (license_messages_->api_version() < kCoreMessagesAPI) {
return OEMCrypto_RefreshKeys(
session->session_id(), encrypted_response_.data(),
encrypted_response_.size(), response_signature_.data(),
response_signature_.size(), 1, &refresh_object_);
} else {
return OEMCrypto_LoadRenewal(
session->session_id(), encrypted_response_.data(),
encrypted_response_.size(), serialized_core_message_.size(),
response_signature_.data(), response_signature_.size());
}
}
Session::Session()
: open_(false),
forced_session_id_(false),
session_id_(0),
nonce_(0),
public_rsa_(0) {}
Session::~Session() {
if (!forced_session_id_ && open_) close();
if (public_rsa_) RSA_free(public_rsa_);
}
void Session::open() {
EXPECT_FALSE(forced_session_id_);
EXPECT_FALSE(open_);
ASSERT_EQ(OEMCrypto_SUCCESS, OEMCrypto_OpenSession(&session_id_));
open_ = true;
}
void Session::SetSessionId(uint32_t session_id) {
EXPECT_FALSE(open_);
session_id_ = session_id;
forced_session_id_ = true;
}
void Session::close() {
EXPECT_TRUE(open_ || forced_session_id_);
if (open_) {
ASSERT_EQ(OEMCrypto_SUCCESS, OEMCrypto_CloseSession(session_id_));
}
forced_session_id_ = false;
open_ = false;
}
void Session::GenerateNonce(int* error_counter) {
// We make one attempt. If it fails, we assume there was a nonce flood.
if (OEMCrypto_SUCCESS == OEMCrypto_GenerateNonce(session_id(), &nonce_)) {
return;
}
if (error_counter) {
(*error_counter)++;
} else {
wvcdm::TestSleep::Sleep(1); // wait a second, then try again.
// The following is after a 1 second pause, so it cannot be from a nonce
// flood.
ASSERT_EQ(OEMCrypto_SUCCESS,
OEMCrypto_GenerateNonce(session_id(), &nonce_));
}
}
void Session::FillDefaultContext(vector<uint8_t>* mac_context,
vector<uint8_t>* enc_context) {
/* Context strings
* These context strings are normally created by the CDM layer
* from a license request message.
* They are used to test MAC and ENC key generation.
*/
*mac_context = wvcdm::a2b_hex(
"41555448454e5449434154494f4e000a4c08001248000000020000101907d9ff"
"de13aa95c122678053362136bdf8408f8276e4c2d87ec52b61aa1b9f646e5873"
"4930acebe899b3e464189a14a87202fb02574e70640bd22ef44b2d7e3912250a"
"230a14080112100915007caa9b5931b76a3a85f046523e10011a093938373635"
"34333231180120002a0c31383836373837343035000000000200");
*enc_context = wvcdm::a2b_hex(
"454e4352595054494f4e000a4c08001248000000020000101907d9ffde13aa95"
"c122678053362136bdf8408f8276e4c2d87ec52b61aa1b9f646e58734930aceb"
"e899b3e464189a14a87202fb02574e70640bd22ef44b2d7e3912250a230a1408"
"0112100915007caa9b5931b76a3a85f046523e10011a09393837363534333231"
"180120002a0c31383836373837343035000000000080");
}
// This should only be called if the device uses Provisioning 2.0. A failure in
// this function is probably caused by a bad keybox.
void Session::GenerateDerivedKeysFromKeybox(
const wvoec::WidevineKeybox& keybox) {
vector<uint8_t> mac_context;
vector<uint8_t> enc_context;
FillDefaultContext(&mac_context, &enc_context);
ASSERT_EQ(OEMCrypto_SUCCESS,
OEMCrypto_GenerateDerivedKeys(
session_id(), mac_context.data(), mac_context.size(),
enc_context.data(), enc_context.size()));
key_deriver_.DeriveKeys(keybox.device_key_, mac_context, enc_context);
}
void Session::GenerateDerivedKeysFromSessionKey() {
// Uses test certificate.
vector<uint8_t> session_key;
vector<uint8_t> enc_session_key;
if (public_rsa_ == nullptr) PreparePublicKey();
// A failure here probably indicates that there is something wrong with the
// test program and its dependency on BoringSSL.
ASSERT_TRUE(GenerateRSASessionKey(&session_key, &enc_session_key));
vector<uint8_t> mac_context;
vector<uint8_t> enc_context;
FillDefaultContext(&mac_context, &enc_context);
// A failure here is probably caused by having the wrong RSA key loaded.
ASSERT_EQ(OEMCrypto_SUCCESS,
OEMCrypto_DeriveKeysFromSessionKey(
session_id(), enc_session_key.data(), enc_session_key.size(),
mac_context.data(), mac_context.size(), enc_context.data(),
enc_context.size()));
key_deriver_.DeriveKeys(session_key.data(), mac_context, enc_context);
}
void Session::EncryptCTR(const vector<uint8_t>& in_buffer, const uint8_t* key,
const uint8_t* starting_iv,
vector<uint8_t>* out_buffer) {
ASSERT_NE(nullptr, key);
ASSERT_NE(nullptr, starting_iv);
ASSERT_NE(nullptr, out_buffer);
AES_KEY aes_key;
AES_set_encrypt_key(key, AES_BLOCK_SIZE * 8, &aes_key);
out_buffer->resize(in_buffer.size());
uint8_t iv[AES_BLOCK_SIZE]; // Current iv.
memcpy(iv, &starting_iv[0], AES_BLOCK_SIZE);
size_t l = 0; // byte index into encrypted subsample.
while (l < in_buffer.size()) {
uint8_t aes_output[AES_BLOCK_SIZE];
AES_encrypt(iv, aes_output, &aes_key);
for (size_t n = 0; n < AES_BLOCK_SIZE && l < in_buffer.size(); n++, l++) {
(*out_buffer)[l] = aes_output[n] ^ in_buffer[l];
}
ctr128_inc64(1, iv);
}
}
void Session::TestDecryptCTR(bool select_key_first,
OEMCryptoResult expected_result, int key_index) {
OEMCryptoResult select_result = OEMCrypto_SUCCESS;
if (select_key_first) {
// Select the key (from FillSimpleMessage)
select_result = OEMCrypto_SelectKey(
session_id(), license_.keys[key_index].key_id,
license_.keys[key_index].key_id_length, OEMCrypto_CipherMode_CTR);
}
// Create test sample description
vector<uint8_t> unencrypted_data(kTestSubsampleSectionSize);
vector<uint8_t> encrypted_data(unencrypted_data.size());
vector<uint8_t> output_buffer(unencrypted_data.size());
OEMCrypto_SampleDescription sample_description;
OEMCrypto_SubSampleDescription subsample_description;
ASSERT_NO_FATAL_FAILURE(GenerateSimpleSampleDescription(
encrypted_data, output_buffer, &sample_description,
&subsample_description));
// Generate test data
EXPECT_EQ(GetRandBytes(unencrypted_data.data(), unencrypted_data.size()), 1);
EncryptCTR(unencrypted_data, license_.keys[key_index].key_data,
&sample_description.iv[0], &encrypted_data);
// Create the pattern description (always 0,0 for CTR)
OEMCrypto_CENCEncryptPatternDesc pattern = {0, 0};
// Decrypt the data
const OEMCryptoResult decrypt_result =
OEMCrypto_DecryptCENC(session_id(), &sample_description, 1, &pattern);
// We only have a few errors that we test are reported.
ASSERT_NO_FATAL_FAILURE(
TestDecryptResult(expected_result, select_result, decrypt_result))
<< "Either SelectKey or DecryptCENC should return " << expected_result
<< ", but they returned " << select_result << " and " << decrypt_result
<< ", respectively.";
if (expected_result == OEMCrypto_SUCCESS) { // No error.
ASSERT_EQ(unencrypted_data, output_buffer);
} else {
ASSERT_NE(unencrypted_data, output_buffer);
}
}
void Session::TestDecryptResult(OEMCryptoResult expected_result,
OEMCryptoResult actual_select_result,
OEMCryptoResult actual_decrypt_result) {
// In most cases, we expect the result to come from either the select key or
// from the decrypt call.
if (expected_result == OEMCrypto_SUCCESS) { // No error.
ASSERT_EQ(OEMCrypto_SUCCESS, actual_select_result);
ASSERT_EQ(OEMCrypto_SUCCESS, actual_decrypt_result);
} else if (expected_result == OEMCrypto_ERROR_KEY_EXPIRED ||
expected_result == OEMCrypto_ERROR_INSUFFICIENT_HDCP ||
expected_result == OEMCrypto_ERROR_ANALOG_OUTPUT) {
// Key expired or output problems may be reported from select key or
// decrypt, but must be reported.
ASSERT_TRUE(actual_select_result == expected_result ||
actual_decrypt_result == expected_result);
} else if (expected_result == OEMCrypto_WARNING_MIXED_OUTPUT_PROTECTION &&
global_features.api_version >= kCoreMessagesAPI) {
// OEMCrypto is allowed to report either this warning or
// OEMCrypto_ERROR_INSUFFICIENT_HDCP depending on if it can disable
// restricted displays.
ASSERT_TRUE(
actual_select_result == OEMCrypto_WARNING_MIXED_OUTPUT_PROTECTION ||
actual_select_result == OEMCrypto_ERROR_INSUFFICIENT_HDCP ||
actual_decrypt_result == OEMCrypto_WARNING_MIXED_OUTPUT_PROTECTION ||
actual_decrypt_result == OEMCrypto_ERROR_INSUFFICIENT_HDCP);
} else {
// OEM's can fine tune other error codes for debugging.
ASSERT_TRUE(actual_select_result != OEMCrypto_SUCCESS ||
actual_decrypt_result != OEMCrypto_SUCCESS);
}
}
void Session::TestSelectExpired(unsigned int key_index) {
if (global_features.api_version >= 13) {
OEMCryptoResult status =
OEMCrypto_SelectKey(session_id(), license().keys[key_index].key_id,
license().keys[key_index].key_id_length,
OEMCrypto_CipherMode_CTR);
// It is OK for SelectKey to succeed with an expired key, but if there is
// an error, it must be OEMCrypto_ERROR_KEY_EXIRED.
if (status != OEMCrypto_SUCCESS) {
ASSERT_EQ(OEMCrypto_ERROR_KEY_EXPIRED, status);
}
}
}
void Session::LoadOEMCert(bool verify_cert) {
// Get the OEM Public Cert from OEMCrypto
vector<uint8_t> public_cert;
size_t public_cert_length = 0;
ASSERT_EQ(OEMCrypto_ERROR_SHORT_BUFFER,
OEMCrypto_GetOEMPublicCertificate(nullptr, &public_cert_length));
ASSERT_LT(0u, public_cert_length);
public_cert.resize(public_cert_length);
ASSERT_EQ(OEMCrypto_SUCCESS, OEMCrypto_GetOEMPublicCertificate(
public_cert.data(), &public_cert_length));
ASSERT_EQ(OEMCrypto_SUCCESS, OEMCrypto_LoadOEMPrivateKey(session_id()));
// The cert is a PKCS7 signed data type. First, parse it into an OpenSSL
// structure and find the certificate list.
//
// We must make defensive copies of public_cert's properties because of how
// d2i_PKCS7() works.
const unsigned char* cert_data =
reinterpret_cast<const unsigned char*>(public_cert.data());
long cert_size = static_cast<long>(public_cert.size());
boringssl_ptr<PKCS7, PKCS7_free> pkcs7(
d2i_PKCS7(NULL, &cert_data, cert_size));
ASSERT_TRUE(pkcs7.NotNull()) << "Error parsing PKCS7 message";
ASSERT_TRUE(PKCS7_type_is_signed(pkcs7.get()))
<< "Unexpected PKCS7 message type";
STACK_OF(X509)* certs = pkcs7->d.sign->cert;
// Load the public cert's key into public_rsa_ and verify, if requested
for (size_t i = 0; certs && i < static_cast<size_t>(sk_X509_num(certs));
++i) {
X509* x509_cert = sk_X509_value(certs, i);
boringssl_ptr<EVP_PKEY, EVP_PKEY_free> pubkey(X509_get_pubkey(x509_cert));
ASSERT_TRUE(pubkey.NotNull());
if (i == 0) {
public_rsa_ = EVP_PKEY_get1_RSA(pubkey.get());
if (!public_rsa_) {
cout << "d2i_RSAPrivateKey failed.\n";
dump_boringssl_error();
ASSERT_TRUE(nullptr != public_rsa_);
}
}
if (verify_cert) {
vector<char> buffer(80);
X509_NAME* name = X509_get_subject_name(x509_cert);
printf(" OEM Certificate Name: %s\n",
X509_NAME_oneline(name, buffer.data(), buffer.size()));
boringssl_ptr<X509_STORE, X509_STORE_free> store(X509_STORE_new());
ASSERT_TRUE(store.NotNull());
boringssl_ptr<X509_STORE_CTX, X509_STORE_CTX_free> store_ctx(
X509_STORE_CTX_new());
ASSERT_TRUE(store_ctx.NotNull());
X509_STORE_CTX_init(store_ctx.get(), store.get(), x509_cert, nullptr);
// TODO(fredgc): Verify cert is signed by Google.
int result = X509_verify_cert(store_ctx.get());
ASSERT_GE(0, result) << " OEM Cert not valid. " <<
X509_verify_cert_error_string(
X509_STORE_CTX_get_error(store_ctx.get()));
if (result == 0) {
printf("Cert not verified: %s.\n",
X509_verify_cert_error_string(
X509_STORE_CTX_get_error(store_ctx.get())));
}
}
}
}
void Session::PreparePublicKey(const uint8_t* rsa_key, size_t rsa_key_length) {
if (rsa_key == nullptr) {
rsa_key = kTestRSAPKCS8PrivateKeyInfo2_2048;
rsa_key_length = sizeof(kTestRSAPKCS8PrivateKeyInfo2_2048);
}
uint8_t* p = const_cast<uint8_t*>(rsa_key);
boringssl_ptr<BIO, BIO_vfree> bio(BIO_new_mem_buf(p, rsa_key_length));
ASSERT_TRUE(bio.NotNull());
boringssl_ptr<PKCS8_PRIV_KEY_INFO, PKCS8_PRIV_KEY_INFO_free> pkcs8_pki(
d2i_PKCS8_PRIV_KEY_INFO_bio(bio.get(), nullptr));
ASSERT_TRUE(pkcs8_pki.NotNull());
boringssl_ptr<EVP_PKEY, EVP_PKEY_free> evp(EVP_PKCS82PKEY(pkcs8_pki.get()));
ASSERT_TRUE(evp.NotNull());
if (public_rsa_) RSA_free(public_rsa_);
public_rsa_ = EVP_PKEY_get1_RSA(evp.get());
if (!public_rsa_) {
cout << "d2i_RSAPrivateKey failed. ";
dump_boringssl_error();
FAIL() << "Could not parse public RSA key.";
}
switch (RSA_check_key(public_rsa_)) {
case 1: // valid.
return;
case 0: // not valid.
dump_boringssl_error();
FAIL() << "[rsa key not valid] ";
default: // -1 == check failed.
dump_boringssl_error();
FAIL() << "[error checking rsa key] ";
}
}
bool Session::VerifyPSSSignature(EVP_PKEY* pkey, const uint8_t* message,
size_t message_length,
const uint8_t* signature,
size_t signature_length) {
boringssl_ptr<EVP_MD_CTX, EVP_MD_CTX_free> md_ctx(EVP_MD_CTX_new());
EVP_PKEY_CTX* pkey_ctx = nullptr;
if (EVP_DigestVerifyInit(md_ctx.get(), &pkey_ctx, EVP_sha1(),
nullptr /* no ENGINE */, pkey) != 1) {
LOGE("EVP_DigestVerifyInit failed in VerifyPSSSignature");
goto err;
}
if (EVP_PKEY_CTX_set_signature_md(pkey_ctx,
const_cast<EVP_MD *>(EVP_sha1())) != 1) {
LOGE("EVP_PKEY_CTX_set_signature_md failed in VerifyPSSSignature");
goto err;
}
if (EVP_PKEY_CTX_set_rsa_padding(pkey_ctx, RSA_PKCS1_PSS_PADDING) != 1) {
LOGE("EVP_PKEY_CTX_set_rsa_padding failed in VerifyPSSSignature");
goto err;
}
if (EVP_PKEY_CTX_set_rsa_pss_saltlen(pkey_ctx, SHA_DIGEST_LENGTH) != 1) {
LOGE("EVP_PKEY_CTX_set_rsa_pss_saltlen failed in VerifyPSSSignature");
goto err;
}
if (EVP_DigestVerifyUpdate(md_ctx.get(), message, message_length) != 1) {
LOGE("EVP_DigestVerifyUpdate failed in VerifyPSSSignature");
goto err;
}
if (EVP_DigestVerifyFinal(md_ctx.get(), const_cast<uint8_t*>(signature),
signature_length) != 1) {
LOGE(
"EVP_DigestVerifyFinal failed in VerifyPSSSignature. (Probably a bad "
"signature.)");
goto err;
}
return true;
err:
dump_boringssl_error();
return false;
}
void Session::VerifyRSASignature(const vector<uint8_t>& message,
const uint8_t* signature,
size_t signature_length,
RSA_Padding_Scheme padding_scheme) {
EXPECT_TRUE(nullptr != public_rsa_)
<< "No public RSA key loaded in test code.\n";
EXPECT_EQ(static_cast<size_t>(RSA_size(public_rsa_)), signature_length)
<< "Signature size is wrong. " << signature_length << ", should be "
<< RSA_size(public_rsa_) << "\n";
if (padding_scheme == kSign_RSASSA_PSS) {
boringssl_ptr<EVP_PKEY, EVP_PKEY_free> pkey(EVP_PKEY_new());
ASSERT_EQ(1, EVP_PKEY_set1_RSA(pkey.get(), public_rsa_));
const bool ok = VerifyPSSSignature(
pkey.get(), message.data(), message.size(), signature,
signature_length);
EXPECT_TRUE(ok) << "PSS signature check failed.";
} else if (padding_scheme == kSign_PKCS1_Block1) {
vector<uint8_t> padded_digest(signature_length);
int size;
// RSA_public_decrypt decrypts the signature, and then verifies that
// it was padded with RSA PKCS1 padding.
size = RSA_public_decrypt(
signature_length, signature, padded_digest.data(), public_rsa_,
RSA_PKCS1_PADDING);
EXPECT_GT(size, 0);
padded_digest.resize(size);
EXPECT_EQ(message, padded_digest);
} else {
EXPECT_TRUE(false) << "Padding scheme not supported.";
}
}
bool Session::GenerateRSASessionKey(vector<uint8_t>* session_key,
vector<uint8_t>* enc_session_key) {
if (!public_rsa_) {
cout << "No public RSA key loaded in test code.\n";
return false;
}
*session_key = wvcdm::a2b_hex("6fa479c731d2770b6a61a5d1420bb9d1");
enc_session_key->assign(RSA_size(public_rsa_), 0);
int status = RSA_public_encrypt(session_key->size(), &(session_key->front()),
&(enc_session_key->front()), public_rsa_,
RSA_PKCS1_OAEP_PADDING);
int size = static_cast<int>(RSA_size(public_rsa_));
if (status != size) {
cout << "GenerateRSASessionKey error encrypting session key.\n";
dump_boringssl_error();
return false;
}
return true;
}
void Session::InstallRSASessionTestKey(const vector<uint8_t>& wrapped_rsa_key) {
ASSERT_EQ(OEMCrypto_SUCCESS,
OEMCrypto_LoadDRMPrivateKey(session_id(), OEMCrypto_RSA_Private_Key,
wrapped_rsa_key.data(),
wrapped_rsa_key.size()));
}
void Session::CreateNewUsageEntry(OEMCryptoResult* status) {
OEMCryptoResult result =
OEMCrypto_CreateNewUsageEntry(session_id(), &usage_entry_number_);
if (status) {
*status = result;
return;
}
ASSERT_EQ(OEMCrypto_SUCCESS, result);
}
void Session::UpdateUsageEntry(std::vector<uint8_t>* header_buffer) {
size_t header_buffer_length = 0;
size_t entry_buffer_length = 0;
ASSERT_EQ(
OEMCrypto_ERROR_SHORT_BUFFER,
OEMCrypto_UpdateUsageEntry(session_id(), nullptr, &header_buffer_length,
nullptr, &entry_buffer_length));
ASSERT_LT(0u, header_buffer_length);
header_buffer->resize(header_buffer_length);
ASSERT_LT(0u, entry_buffer_length);
encrypted_usage_entry_.resize(entry_buffer_length);
ASSERT_EQ(OEMCrypto_SUCCESS,
OEMCrypto_UpdateUsageEntry(
session_id(), header_buffer->data(), &header_buffer_length,
encrypted_usage_entry_.data(), &entry_buffer_length));
}
void Session::LoadUsageEntry(uint32_t index, const vector<uint8_t>& buffer) {
usage_entry_number_ = index;
encrypted_usage_entry_ = buffer;
ASSERT_EQ(
OEMCrypto_SUCCESS,
OEMCrypto_LoadUsageEntry(
session_id(), index, buffer.data(), buffer.size()));
}
void Session::MoveUsageEntry(uint32_t new_index,
std::vector<uint8_t>* header_buffer,
OEMCryptoResult expect_result) {
ASSERT_NO_FATAL_FAILURE(open());
ASSERT_NO_FATAL_FAILURE(ReloadUsageEntry());
ASSERT_EQ(expect_result, OEMCrypto_MoveEntry(session_id(), new_index));
if (expect_result == OEMCrypto_SUCCESS) {
usage_entry_number_ = new_index;
ASSERT_NO_FATAL_FAILURE(UpdateUsageEntry(header_buffer));
}
ASSERT_NO_FATAL_FAILURE(close());
}
void Session::GenerateReport(const std::string& pst,
OEMCryptoResult expected_result,
Session* other) {
ASSERT_TRUE(open_);
if (other) { // If other is specified, copy mac keys.
key_deriver_ = other->key_deriver_;
}
size_t length = 0;
OEMCryptoResult sts = OEMCrypto_ReportUsage(
session_id(), reinterpret_cast<const uint8_t*>(pst.c_str()), pst.length(),
pst_report_buffer_.data(), &length);
if (expected_result == OEMCrypto_SUCCESS) {
ASSERT_EQ(OEMCrypto_ERROR_SHORT_BUFFER, sts);
}
if (sts == OEMCrypto_ERROR_SHORT_BUFFER) {
pst_report_buffer_.assign(length, 0xFF); // Fill with garbage values.
}
sts = OEMCrypto_ReportUsage(session_id(),
reinterpret_cast<const uint8_t*>(pst.c_str()),
pst.length(), pst_report_buffer_.data(), &length);
ASSERT_EQ(expected_result, sts);
if (expected_result != OEMCrypto_SUCCESS) {
return;
}
EXPECT_EQ(wvcdm::Unpacked_PST_Report::report_size(pst.length()), length);
vector<uint8_t> computed_signature(SHA_DIGEST_LENGTH);
key_deriver_.ClientSignPstReport(pst_report_buffer_, &computed_signature);
EXPECT_EQ(0, memcmp(computed_signature.data(), pst_report().signature(),
SHA_DIGEST_LENGTH));
EXPECT_GE(kInactiveUnused, pst_report().status());
EXPECT_GE(kHardwareSecureClock, pst_report().clock_security_level());
EXPECT_EQ(pst.length(), pst_report().pst_length());
EXPECT_EQ(0, memcmp(pst.c_str(), pst_report().pst(), pst.length()));
}
void Session::VerifyPST(const Test_PST_Report& expected) {
wvcdm::Unpacked_PST_Report computed = pst_report();
EXPECT_EQ(expected.status, computed.status());
char* pst_ptr = reinterpret_cast<char *>(computed.pst());
std::string computed_pst(pst_ptr, pst_ptr + computed.pst_length());
ASSERT_EQ(expected.pst, computed_pst);
int64_t now = wvcdm::Clock().GetCurrentTime();
int64_t age = now - expected.time_created; // How old is this report.
EXPECT_NEAR(expected.seconds_since_license_received + age,
computed.seconds_since_license_received(),
kTimeTolerance);
// Decrypt times only valid on licenses that have been active.
if (expected.status == kActive || expected.status == kInactiveUsed) {
EXPECT_NEAR(expected.seconds_since_first_decrypt + age,
computed.seconds_since_first_decrypt(),
kUsageTableTimeTolerance);
EXPECT_NEAR(expected.seconds_since_last_decrypt + age,
computed.seconds_since_last_decrypt(),
kUsageTableTimeTolerance);
}
std::vector<uint8_t> signature(SHA_DIGEST_LENGTH);
key_deriver_.ClientSignPstReport(pst_report_buffer_, &signature);
EXPECT_EQ(0, memcmp(computed.signature(), signature.data(),
SHA_DIGEST_LENGTH));
}
void Session::VerifyReport(Test_PST_Report expected,
int64_t time_license_received,
int64_t time_first_decrypt,
int64_t time_last_decrypt) {
const int64_t now = wvcdm::Clock().GetCurrentTime();
expected.seconds_since_license_received = now - time_license_received;
expected.seconds_since_first_decrypt =
(time_first_decrypt > 0 && time_first_decrypt < now)
? now - time_first_decrypt
: 0;
expected.seconds_since_last_decrypt =
(time_last_decrypt > 0 && time_last_decrypt < now)
? now - time_last_decrypt
: 0;
ASSERT_NO_FATAL_FAILURE(VerifyPST(expected));
}
} // namespace wvoec