[ Merge of http://go/wvgerrit/151191 ] Within the CDM and OEMCrypto tests, there were a few OEMCrypto function calls where the final size of the output buffers were not being resized. For several of these functions, an initial call is made with zero-length output buffers, expecting OEMCrypto to return ERROR_SHORT_BUFFER; followed by a call with buffers at least as large as specified by OEMCrypto. However, for some operations, OEMCrypto makes an estimate on the final size on the first call, specifying the exact size only after performing the operations. This is the case for the wrapped key returned by OEMCrypto_LoadProvisioning(). The provisioning response contains a padded + encrypted DRM key. OEMCrypto does not know the actual size of the key until decrypted, and the actual DRM key might be smaller. There was a OEMCrypto test for OEMCrypto_BuildInformation() which was enforcing the wrong behaviour. This has been updated. Bug: 230661565 Test: oemcrypto_test Change-Id: Iad297d56ffbb085894641fdf8698ce5fd18edbf2
2022 lines
85 KiB
C++
2022 lines
85 KiB
C++
// Copyright 2018 Google LLC. All Rights Reserved. This file and proprietary
|
|
// source code may only be used and distributed under the Widevine
|
|
// License Agreement.
|
|
//
|
|
// OEMCrypto unit tests
|
|
//
|
|
|
|
#include "oec_session_util.h"
|
|
|
|
#include <gtest/gtest.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 <algorithm>
|
|
#include <iostream>
|
|
#include <memory>
|
|
#include <string>
|
|
#include <vector>
|
|
|
|
#include "OEMCryptoCENC.h"
|
|
#include "clock.h"
|
|
#include "core_message_deserialize.h"
|
|
#include "core_message_features.h"
|
|
#include "core_message_serialize.h"
|
|
#include "disallow_copy_and_assign.h"
|
|
#include "log.h"
|
|
#include "odk_attributes.h"
|
|
#include "odk_structs.h"
|
|
#include "oec_device_features.h"
|
|
#include "oec_test_data.h"
|
|
#include "oemcrypto_corpus_generator_helper.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 << wvutil::b2a_hex(value);
|
|
}
|
|
} // namespace std
|
|
|
|
namespace wvoec {
|
|
namespace {
|
|
using oemcrypto_core_message::features::CoreMessageFeatures;
|
|
|
|
constexpr size_t kTestSubsampleSectionSize = 256;
|
|
|
|
// Encrypt a block of data using CTR mode.
|
|
void 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);
|
|
}
|
|
}
|
|
|
|
// Uses OEMCrypto to decrypt some random data in 'cenc' mode. This function
|
|
// assumes that the correct key is already selected in the session. It requires
|
|
// the plaintext of that key so that it can encrypt the test data. It resizes
|
|
// the provided vectors and fills them with the expected and actual decrypt
|
|
// results. Returns the result of OEMCrypto_DecryptCENC().
|
|
OEMCryptoResult DecryptCTR(OEMCrypto_SESSION session_id, const uint8_t* key,
|
|
vector<uint8_t>* expected_data,
|
|
vector<uint8_t>* actual_data) {
|
|
vector<uint8_t> encrypted_data(kTestSubsampleSectionSize);
|
|
expected_data->resize(encrypted_data.size());
|
|
actual_data->resize(encrypted_data.size());
|
|
|
|
// Create test sample description
|
|
OEMCrypto_SampleDescription sample_description;
|
|
OEMCrypto_SubSampleDescription subsample_description;
|
|
GenerateSimpleSampleDescription(encrypted_data, *actual_data,
|
|
&sample_description, &subsample_description);
|
|
|
|
// Generate test data
|
|
EXPECT_EQ(GetRandBytes(expected_data->data(), expected_data->size()), 1);
|
|
EncryptCTR(*expected_data, key, &sample_description.iv[0], &encrypted_data);
|
|
|
|
// Create the pattern description (always 0,0 for 'cenc')
|
|
OEMCrypto_CENCEncryptPatternDesc pattern = {0, 0};
|
|
|
|
// Decrypt the data
|
|
return OEMCrypto_DecryptCENC(session_id, &sample_description, 1, &pattern);
|
|
}
|
|
|
|
} // namespace
|
|
|
|
int GetRandBytes(unsigned char* buf, size_t num) {
|
|
// returns 1 on success, -1 if not supported, or 0 if other failure.
|
|
return RAND_bytes(buf, static_cast<int>(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) {
|
|
// 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.clear_buffer = out.data();
|
|
out_buffer_descriptor.buffer.clear.clear_buffer_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) =
|
|
wvutil::htonll64(wvutil::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() {
|
|
// BoringSSL and OpenSSL disagree about what the type of an error code is, so
|
|
// we must use "auto" here.
|
|
while (auto err = ERR_get_error()) {
|
|
char buffer[120];
|
|
ERR_error_string_n(err, buffer, sizeof(buffer));
|
|
cerr << "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 = wvutil::Clock().GetCurrentTime();
|
|
}
|
|
|
|
template <class CoreRequest, PrepAndSignRequest_t PrepAndSignRequest,
|
|
class CoreResponse, class ResponseData>
|
|
OEMCryptoResult
|
|
RoundTrip<CoreRequest, PrepAndSignRequest, CoreResponse, ResponseData>::
|
|
SignAndCreateRequestWithCustomBufferLengths(bool verify_request) {
|
|
// 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.
|
|
if (RequestHasNonce()) {
|
|
session()->GenerateNonce();
|
|
}
|
|
uint32_t session_id = session()->session_id();
|
|
GetDefaultRequestSignatureAndCoreMessageLengths<PrepAndSignRequest>(
|
|
session_id, small_size, &gen_signature_length, &core_message_length);
|
|
// Used to test request APIs with varying lengths of core message.
|
|
core_message_length =
|
|
std::max(core_message_length, required_core_message_size_);
|
|
// Used to test request APIs with varying lengths of signature.
|
|
gen_signature_length =
|
|
std::max(gen_signature_length, required_request_signature_size_);
|
|
// Make the message buffer a little bigger than the core message, or the
|
|
// required size, whichever is larger.
|
|
size_t message_size =
|
|
std::max(required_message_size_, core_message_length + small_size);
|
|
vector<uint8_t> data(message_size);
|
|
for (size_t i = 0; i < data.size(); i++) data[i] = i & 0xFF;
|
|
if (ShouldGenerateCorpus()) {
|
|
WriteRequestApiCorpus<CoreRequest>(gen_signature_length,
|
|
core_message_length, data);
|
|
}
|
|
|
|
vector<uint8_t> gen_signature(gen_signature_length);
|
|
OEMCryptoResult result = PrepAndSignRequest(
|
|
session()->session_id(), data.data(), data.size(), &core_message_length,
|
|
gen_signature.data(), &gen_signature_length);
|
|
// We need to fill in core request and verify signature only for calls other
|
|
// than OEMCryptoMemory buffer overflow test. Any test other than buffer
|
|
// overflow will pass true.
|
|
if (result == OEMCrypto_SUCCESS) {
|
|
gen_signature.resize(gen_signature_length);
|
|
}
|
|
if (!verify_request || result != OEMCrypto_SUCCESS) return result;
|
|
if (global_features.api_version >= kCoreMessagesAPI) {
|
|
std::string core_message(reinterpret_cast<char*>(data.data()),
|
|
core_message_length);
|
|
FillAndVerifyCoreRequest(core_message);
|
|
}
|
|
VerifyRequestSignature(data, gen_signature, core_message_length);
|
|
return result;
|
|
}
|
|
|
|
template <PrepAndSignRequest_t PrepAndSignRequest>
|
|
void GetDefaultRequestSignatureAndCoreMessageLengths(
|
|
uint32_t& session_id, const size_t& small_size,
|
|
size_t* gen_signature_length, size_t* core_message_length) {
|
|
vector<uint8_t> data(small_size);
|
|
for (size_t i = 0; i < data.size(); i++) data[i] = i & 0xFF;
|
|
ASSERT_EQ(
|
|
PrepAndSignRequest(session_id, data.data(), data.size(),
|
|
core_message_length, nullptr, gen_signature_length),
|
|
OEMCrypto_ERROR_SHORT_BUFFER);
|
|
}
|
|
|
|
template <class CoreRequest, PrepAndSignRequest_t PrepAndSignRequest,
|
|
class CoreResponse, class ResponseData>
|
|
void RoundTrip<CoreRequest, PrepAndSignRequest, CoreResponse,
|
|
ResponseData>::InjectFuzzedRequestData(uint8_t* data,
|
|
size_t size) {
|
|
OEMCrypto_Request_Fuzz fuzz_structure;
|
|
// Copy data into fuzz structure, cap signature length at 1mb as it will be
|
|
// used to initialize signature vector.
|
|
memcpy(&fuzz_structure, data, sizeof(fuzz_structure));
|
|
fuzz_structure.signature_length =
|
|
std::min(fuzz_structure.signature_length, MB);
|
|
vector<uint8_t> signature(fuzz_structure.signature_length);
|
|
|
|
// Interpret rest of data as actual message buffer to request APIs.
|
|
uint8_t* message_ptr = data + sizeof(fuzz_structure);
|
|
size_t message_size = size - sizeof(fuzz_structure);
|
|
PrepAndSignRequest(session()->session_id(), message_ptr, message_size,
|
|
&fuzz_structure.core_message_length, signature.data(),
|
|
&fuzz_structure.signature_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());
|
|
if (global_features.provisioning_method == OEMCrypto_Keybox) {
|
|
session_->GenerateDerivedKeysFromKeybox(keybox);
|
|
encryptor_ = session_->key_deriver();
|
|
} else if (global_features.provisioning_method ==
|
|
OEMCrypto_BootCertificateChain) {
|
|
// TODO(chelu): change this to CSR provisioning.
|
|
session_->LoadOEMCert(true);
|
|
session_->GenerateRSASessionKey(&message_key_, &encrypted_message_key_);
|
|
encryptor_.set_enc_key(message_key_);
|
|
} 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;
|
|
SignResponse();
|
|
}
|
|
|
|
// We need this for provisioning response out of range tests where
|
|
// core response substring lengths are modified.
|
|
void ProvisioningRoundTrip::
|
|
EncryptAndSignResponseWithoutUpdatingEncPrivateKeyLength() {
|
|
encryptor_.PadAndEncryptProvisioningMessage(&response_data_,
|
|
&encrypted_response_data_);
|
|
SignResponse();
|
|
}
|
|
|
|
void ProvisioningRoundTrip::SignResponse() {
|
|
if (global_features.api_version >= kCoreMessagesAPI) {
|
|
CoreMessageFeatures features =
|
|
CoreMessageFeatures::DefaultFeatures(ODK_MAJOR_VERSION);
|
|
ASSERT_TRUE(
|
|
oemcrypto_core_message::serialize::CreateCoreProvisioningResponse(
|
|
features, core_response_, core_request_,
|
|
&serialized_core_message_));
|
|
// Resizing for huge core message length unit tests.
|
|
serialized_core_message_.resize(
|
|
std::max(required_core_message_size_, serialized_core_message_.size()));
|
|
}
|
|
// 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_);
|
|
}
|
|
|
|
void ProvisioningRoundTrip::InjectFuzzedResponseData(const uint8_t* data,
|
|
size_t size UNUSED) {
|
|
// Interpreting fuzz data as unencrypted core_response + message_data
|
|
const size_t core_response_size = sizeof(ODK_ParsedProvisioning);
|
|
// Copy core_response from data and serialize.
|
|
memcpy(&core_response_, data, core_response_size);
|
|
|
|
// Copy provisioning message data into response_data.
|
|
memcpy(&response_data_, data + core_response_size, sizeof(response_data_));
|
|
// Set nonce to one from session to pass nonce checks.
|
|
response_data_.nonce = session()->nonce();
|
|
}
|
|
|
|
OEMCryptoResult ProvisioningRoundTrip::LoadResponse(Session* session) {
|
|
EXPECT_NE(session, nullptr);
|
|
// Write corpus for oemcrypto_load_provisioning_fuzz. Fuzz script expects
|
|
// unencrypted response from provisioning server as input corpus data.
|
|
// Data will be encrypted and signed again explicitly by fuzzer script after
|
|
// mutations.
|
|
if (ShouldGenerateCorpus()) {
|
|
const std::string file_name =
|
|
GetFileName("oemcrypto_load_provisioning_fuzz_seed_corpus");
|
|
// Corpus for license response fuzzer should be in the format:
|
|
// unencrypted (core_response + response_data).
|
|
AppendToFile(file_name, reinterpret_cast<const char*>(&core_response_),
|
|
sizeof(ODK_ParsedProvisioning));
|
|
AppendToFile(file_name, reinterpret_cast<const char*>(&response_data_),
|
|
sizeof(response_data_));
|
|
}
|
|
size_t wrapped_key_length = 0;
|
|
OEMCryptoResult sts = LoadResponseNoRetry(session, &wrapped_key_length);
|
|
if (sts != OEMCrypto_ERROR_SHORT_BUFFER) return sts;
|
|
wrapped_rsa_key_.assign(wrapped_key_length, 0);
|
|
sts = LoadResponseNoRetry(session, &wrapped_key_length);
|
|
if (sts == OEMCrypto_SUCCESS) {
|
|
wrapped_rsa_key_.resize(wrapped_key_length);
|
|
}
|
|
return sts;
|
|
}
|
|
|
|
#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) {
|
|
// 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;
|
|
|
|
if (global_features.api_version < 17) {
|
|
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_);
|
|
} else {
|
|
session()->VerifyRSASignature(data, generated_signature.data(),
|
|
generated_signature.size(), kSign_RSASSA_PSS);
|
|
SHA256(data.data(), core_message_length, request_hash_);
|
|
}
|
|
}
|
|
|
|
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 (global_features.api_version == 16) {
|
|
// We support either 16.3-16.4 for OEMCrypto 16 public release, and v16.5
|
|
// for L3 release only.
|
|
EXPECT_LE(3, core_request_.api_minor_version);
|
|
EXPECT_GE(5, core_request_.api_minor_version);
|
|
} else if (global_features.api_version == ODK_MAJOR_VERSION) {
|
|
// We do not expect older tests to work with a newer OEMCrypto.
|
|
EXPECT_GE(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_CENC;
|
|
}
|
|
// 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::ConvertDataToValidBools(ODK_ParsedLicense* t) {
|
|
t->nonce_required = ConvertByteToValidBoolean(&t->nonce_required);
|
|
t->timer_limits.soft_enforce_playback_duration = ConvertByteToValidBoolean(
|
|
&t->timer_limits.soft_enforce_playback_duration);
|
|
t->timer_limits.soft_enforce_rental_duration =
|
|
ConvertByteToValidBoolean(&t->timer_limits.soft_enforce_rental_duration);
|
|
}
|
|
|
|
void LicenseRoundTrip::InjectFuzzedTimerLimits(
|
|
OEMCrypto_Renewal_Response_Fuzz& fuzzed_data) {
|
|
// Interpreting fuzz data as timer limits.
|
|
// Copy timer limits from data.
|
|
memcpy(&core_response_.timer_limits, &fuzzed_data.timer_limits,
|
|
sizeof(fuzzed_data.timer_limits));
|
|
ConvertDataToValidBools(&core_response_);
|
|
}
|
|
|
|
void LicenseRoundTrip::InjectFuzzedResponseData(const uint8_t* data,
|
|
size_t size UNUSED) {
|
|
// Interpreting fuzz data as unencrypted core_response + message_data
|
|
const size_t core_response_size = sizeof(ODK_ParsedLicense);
|
|
// Copy core_response from data.
|
|
memcpy(&core_response_, data, core_response_size);
|
|
// Maximum number of keys could be kMaxNumKeys(30). key_array_length can be
|
|
// any random value as it is read from fuzz data.
|
|
// Key data array(MessageKeyData keys[kMaxNumKeys]) will be looped over
|
|
// key_array_length number of times during LoadLicense. If key_array_length is
|
|
// more than kMaxNumKeys, setting it to max value of kMaxNumKeys as we should
|
|
// not go out of bounds of this array length. For corpus, this value is
|
|
// already hard coded to 4.
|
|
if (core_response_.key_array_length > kMaxNumKeys) {
|
|
core_response_.key_array_length = kMaxNumKeys;
|
|
}
|
|
// For corpus data, this value gets set to 4, but we need to test other
|
|
// scenarios too, hence reading key_array_length value.
|
|
set_num_keys(core_response_.key_array_length);
|
|
ConvertDataToValidBools(&core_response_);
|
|
// TODO(b/157520981): Once assertion bug is fixed, for loop can be removed.
|
|
// Workaround for the above bug: key_data.length and key_id.length are being
|
|
// used in AES decryption process and are expected to be a multiple of 16. An
|
|
// assertion in AES decryption fails if this condition is not met which will
|
|
// crash fuzzer.
|
|
for (uint32_t i = 0; i < num_keys_; ++i) {
|
|
size_t key_data_length = core_response_.key_array[i].key_data.length;
|
|
size_t key_id_length = core_response_.key_array[i].key_id.length;
|
|
if (key_data_length % 16 != 0) {
|
|
core_response_.key_array[i].key_data.length =
|
|
key_data_length - (key_data_length % 16);
|
|
}
|
|
if (key_id_length % 16 != 0) {
|
|
core_response_.key_array[i].key_id.length =
|
|
key_id_length - (key_id_length % 16);
|
|
}
|
|
}
|
|
|
|
// Copy response_data from data and set nonce to match one in request to pass
|
|
// nonce validations.
|
|
memcpy(&response_data_, data + core_response_size, sizeof(response_data_));
|
|
for (uint32_t i = 0; i < num_keys_; ++i) {
|
|
response_data_.keys[i].control.nonce = session()->nonce();
|
|
}
|
|
}
|
|
|
|
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);
|
|
if (core_request().api_major_version < kClearControlBlockAPIMajor ||
|
|
(core_request().api_major_version == kClearControlBlockAPIMajor &&
|
|
core_request().api_minor_version < kClearControlBlockAPIMinor)) {
|
|
core_response_.key_array[i].key_control_iv =
|
|
FindSubstring(response_data_.keys[i].control_iv,
|
|
sizeof(response_data_.keys[i].control_iv));
|
|
} else {
|
|
core_response_.key_array[i].key_control_iv = FindSubstring(nullptr, 0);
|
|
}
|
|
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++) {
|
|
// OEMCrypto Fuzzing skip encryption: key_data_length can be any number when
|
|
// called from fuzzer. We want to skip encryption if that happens and let
|
|
// LoadLicense be called with unencrypted data for that key. OEMCrypto
|
|
// Fuzzing skip encryption: key_data_length being a random value will
|
|
// encrypt data which is not expected to, there by leading to inefficient
|
|
// fuzzing.
|
|
if (response_data_.keys[i].key_data_length <=
|
|
sizeof(response_data_.keys[i].key_data) &&
|
|
response_data_.keys[i].key_data_length % 16 == 0) {
|
|
memcpy(iv_buffer, &response_data_.keys[i].control_iv[0], KEY_IV_SIZE);
|
|
if (core_request_.api_major_version < kClearControlBlockAPIMajor ||
|
|
(core_request_.api_major_version == kClearControlBlockAPIMajor &&
|
|
core_request_.api_minor_version < kClearControlBlockAPIMinor)) {
|
|
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);
|
|
} else {
|
|
encrypted_response_data_.keys[i].control =
|
|
response_data_.keys[i].control;
|
|
}
|
|
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 {
|
|
if (core_request_.api_major_version == 0) {
|
|
// If we don't have a valid request, then we should at least set the
|
|
// version number of the request so that CreateCoreLicenseResponse can
|
|
// compute the version number of the response.
|
|
core_request_.api_major_version = ODK_MAJOR_VERSION;
|
|
core_request_.api_minor_version = ODK_MINOR_VERSION;
|
|
}
|
|
std::string request_hash_string(
|
|
reinterpret_cast<const char*>(request_hash_), sizeof(request_hash_));
|
|
// We might try to test a future api_version_, but we can only make a core
|
|
// message with at most the current ODK version. This is only done to verify
|
|
// that OEMCrypto does not attempt to load a future version.
|
|
CoreMessageFeatures features = CoreMessageFeatures::DefaultFeatures(
|
|
std::min(api_version_, static_cast<uint32_t>(ODK_MAJOR_VERSION)));
|
|
ASSERT_TRUE(oemcrypto_core_message::serialize::CreateCoreLicenseResponse(
|
|
features, core_response_, core_request_, request_hash_string,
|
|
&serialized_core_message_));
|
|
// Resize serialize core message to be just big enough or required core
|
|
// message size, whichever is larger.
|
|
serialized_core_message_.resize(
|
|
std::max(required_core_message_size_, serialized_core_message_.size()));
|
|
}
|
|
|
|
// 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(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) {
|
|
return LoadResponse(session, true);
|
|
}
|
|
|
|
OEMCryptoResult LicenseRoundTrip::LoadResponse(Session* session,
|
|
bool verify_keys) {
|
|
EXPECT_NE(session, nullptr);
|
|
// Write corpus for oemcrypto_load_license_fuzz. Fuzz script expects
|
|
// unecnrypted response from license server as input corpus data.
|
|
// Data will be encrypted and signed again explicitly by fuzzer script
|
|
// after mutations.
|
|
if (ShouldGenerateCorpus()) {
|
|
const std::string file_name =
|
|
GetFileName("oemcrypto_load_license_fuzz_seed_corpus");
|
|
// Corpus for license response fuzzer should be in the format:
|
|
// core_response + response_data.
|
|
AppendToFile(file_name, reinterpret_cast<const char*>(&core_response_),
|
|
sizeof(ODK_ParsedLicense));
|
|
AppendToFile(file_name, reinterpret_cast<const char*>(&response_data_),
|
|
sizeof(response_data_));
|
|
}
|
|
|
|
// 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 (verify_keys && 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(session);
|
|
}
|
|
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(Session* session) {
|
|
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));
|
|
}
|
|
|
|
OEMCrypto_EntitledContentKeyObject* EntitledMessage::entitled_key_array() {
|
|
return entitled_key_array_;
|
|
}
|
|
|
|
EntitledContentKeyData* EntitledMessage::entitled_key_data() {
|
|
return entitled_key_data_;
|
|
}
|
|
|
|
size_t EntitledMessage::entitled_key_data_size() {
|
|
return sizeof(entitled_key_data_);
|
|
}
|
|
|
|
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());
|
|
}
|
|
|
|
void EntitledMessage::SetContentKeyId(unsigned int index,
|
|
const std::string& key_id) {
|
|
ASSERT_LT(index, num_keys_);
|
|
ASSERT_LE(key_id.size(), kTestKeyIdMaxLength);
|
|
entitled_key_data_[index].content_key_id_length = key_id.size();
|
|
memcpy(entitled_key_data_[index].content_key_id,
|
|
reinterpret_cast<const uint8_t*>(key_id.c_str()), key_id.length());
|
|
entitled_key_array_[index].content_key_id =
|
|
FindSubstring(entitled_key_data_[index].content_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) {
|
|
EncryptContentKey();
|
|
ASSERT_EQ(expected_sts,
|
|
OEMCrypto_LoadEntitledContentKeys(
|
|
entitled_key_session_,
|
|
reinterpret_cast<const uint8_t*>(entitled_key_data_),
|
|
sizeof(entitled_key_data_), num_keys_, entitled_key_array_));
|
|
if (expected_sts != OEMCrypto_SUCCESS) {
|
|
return;
|
|
}
|
|
VerifyKCBs();
|
|
VerifyDecrypt();
|
|
}
|
|
|
|
OEMCryptoResult EntitledMessage::LoadKeys(const vector<uint8_t>& message) {
|
|
return OEMCrypto_LoadEntitledContentKeys(entitled_key_session_,
|
|
message.data(), message.size(),
|
|
num_keys_, entitled_key_array_);
|
|
}
|
|
|
|
OEMCryptoResult EntitledMessage::LoadKeys() {
|
|
return OEMCrypto_LoadEntitledContentKeys(
|
|
entitled_key_session_,
|
|
reinterpret_cast<const uint8_t*>(entitled_key_data_),
|
|
sizeof(entitled_key_data_), num_keys_, entitled_key_array_);
|
|
}
|
|
|
|
void EntitledMessage::EncryptContentKey() {
|
|
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);
|
|
}
|
|
if (ShouldGenerateCorpus()) {
|
|
const std::string file_name =
|
|
GetFileName("oemcrypto_load_entitled_content_keys_fuzz_seed_corpus");
|
|
// Corpus for load entitled keys fuzzer should be in the format:
|
|
// message buffer to be verified | entitled content key object array.
|
|
AppendToFile(file_name, reinterpret_cast<const char*>(entitled_key_data_),
|
|
num_keys_ * sizeof(EntitledContentKeyData));
|
|
AppendSeparator(file_name);
|
|
AppendToFile(file_name, reinterpret_cast<const char*>(entitled_key_array_),
|
|
num_keys_ * sizeof(OEMCrypto_EntitledContentKeyObject));
|
|
}
|
|
}
|
|
|
|
void EntitledMessage::LoadCasKeys(bool load_even, bool load_odd,
|
|
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);
|
|
}
|
|
|
|
// Convert the OEMCrypto_EntitledContentKeyObject to
|
|
// OEMCrypto_EntitledCasKeyObject. Only the first two key object is used.
|
|
OEMCrypto_EntitledContentKeyObject even_key;
|
|
OEMCrypto_EntitledContentKeyObject odd_key;
|
|
bool has_even = load_even && num_keys_ >= 1;
|
|
bool has_odd = load_odd && num_keys_ >= 2;
|
|
if (has_even) {
|
|
even_key.entitlement_key_id = entitled_key_array_[0].entitlement_key_id;
|
|
even_key.content_key_id = entitled_key_array_[0].content_key_id;
|
|
even_key.content_key_data_iv = entitled_key_array_[0].content_key_data_iv;
|
|
even_key.content_key_data = entitled_key_array_[0].content_key_data;
|
|
even_key.content_iv.length = 0;
|
|
}
|
|
if (has_odd) {
|
|
odd_key.entitlement_key_id = entitled_key_array_[1].entitlement_key_id;
|
|
odd_key.content_key_id = entitled_key_array_[1].content_key_id;
|
|
odd_key.content_key_data_iv = entitled_key_array_[1].content_key_data_iv;
|
|
odd_key.content_key_data = entitled_key_array_[1].content_key_data;
|
|
odd_key.content_iv.length = 0;
|
|
}
|
|
|
|
OEMCryptoResult sts = OEMCrypto_LoadCasECMKeys(
|
|
entitled_key_session_,
|
|
reinterpret_cast<const uint8_t*>(entitled_key_data_),
|
|
sizeof(entitled_key_data_), has_even ? &even_key : nullptr,
|
|
has_odd ? &odd_key : nullptr);
|
|
ASSERT_EQ(expected_sts, sts);
|
|
if (expected_sts != OEMCrypto_SUCCESS) {
|
|
return;
|
|
}
|
|
|
|
if (has_even) {
|
|
VerifyEntitlementTestKey(0);
|
|
}
|
|
if (has_odd) {
|
|
VerifyEntitlementTestKey(1);
|
|
}
|
|
}
|
|
|
|
// This function verifies that the key control blocks 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::VerifyKCBs() {
|
|
for (unsigned int i = 0; i < num_keys_; i++) {
|
|
VerifyEntitlementTestKey(i);
|
|
}
|
|
}
|
|
|
|
void EntitledMessage::VerifyEntitlementTestKey(size_t index) {
|
|
ASSERT_GE(num_keys_, index);
|
|
|
|
EntitledContentKeyData* key_data = &entitled_key_data_[index];
|
|
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(entitled_key_session_, 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 " << index;
|
|
ASSERT_EQ(htonl_fnc(entitlement_key->control.control_bits),
|
|
htonl_fnc(block.control_bits))
|
|
<< "For key " << index;
|
|
}
|
|
}
|
|
|
|
void EntitledMessage::VerifyDecrypt() {
|
|
// Loop through all the keys and try decrypt with each one.
|
|
for (unsigned int i = 0; i < num_keys_; i++) {
|
|
const EntitledContentKeyData* const key_data = &entitled_key_data_[i];
|
|
|
|
OEMCryptoResult result = OEMCrypto_SelectKey(
|
|
entitled_key_session_, key_data->content_key_id,
|
|
key_data->content_key_id_length, OEMCrypto_CipherMode_CENC);
|
|
ASSERT_EQ(result, OEMCrypto_SUCCESS) << "For key " << i;
|
|
|
|
vector<uint8_t> expected_data;
|
|
vector<uint8_t> actual_data;
|
|
result = DecryptCTR(entitled_key_session_, key_data->content_key_data,
|
|
&expected_data, &actual_data);
|
|
EXPECT_EQ(result, OEMCrypto_SUCCESS) << "For key " << i;
|
|
EXPECT_EQ(actual_data, expected_data) << "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_->api_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 {
|
|
// TODO(b/191724203): Test renewal server has different version from license
|
|
// server.
|
|
ASSERT_NE(license_messages_, nullptr);
|
|
CoreMessageFeatures features =
|
|
CoreMessageFeatures::DefaultFeatures(license_messages_->api_version());
|
|
ASSERT_TRUE(oemcrypto_core_message::serialize::CreateCoreRenewalResponse(
|
|
features, core_request_, renewal_duration_seconds_,
|
|
&serialized_core_message_));
|
|
// Resize serialize core message to be just big enough or required core
|
|
// message size, whichever is larger.
|
|
serialized_core_message_.resize(
|
|
std::max(required_core_message_size_, serialized_core_message_.size()));
|
|
}
|
|
// 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(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_);
|
|
}
|
|
|
|
void RenewalRoundTrip::InjectFuzzedResponseData(
|
|
OEMCrypto_Renewal_Response_Fuzz& fuzzed_data,
|
|
const uint8_t* renewal_response, const size_t renewal_response_size) {
|
|
// TODO(b/191724203): Test renewal server has different version from license
|
|
// server.
|
|
ASSERT_NE(license_messages_, nullptr);
|
|
CoreMessageFeatures features =
|
|
CoreMessageFeatures::DefaultFeatures(license_messages_->api_version());
|
|
// Serializing core message.
|
|
// This call also sets nonce in core response to match with session nonce.
|
|
oemcrypto_core_message::serialize::CreateCoreRenewalResponse(
|
|
features, fuzzed_data.core_request, fuzzed_data.renewal_duration_seconds,
|
|
&serialized_core_message_);
|
|
|
|
// Copy serialized core message and encrypted response from data and
|
|
// calculate signature. Now we will have a valid signature for data
|
|
// generated by fuzzer.
|
|
encrypted_response_.assign(serialized_core_message_.begin(),
|
|
serialized_core_message_.end());
|
|
encrypted_response_.insert(encrypted_response_.end(), renewal_response,
|
|
renewal_response + renewal_response_size);
|
|
session()->key_deriver().ServerSignBuffer(encrypted_response_.data(),
|
|
encrypted_response_.size(),
|
|
&response_signature_);
|
|
}
|
|
|
|
OEMCryptoResult RenewalRoundTrip::LoadResponse(Session* session) {
|
|
// Write corpus for oemcrypto_load_renewal_fuzz. Fuzz script expects
|
|
// encrypted response from Renewal server as input corpus data.
|
|
// Data will be signed again explicitly by fuzzer script after mutations.
|
|
if (ShouldGenerateCorpus()) {
|
|
const std::string file_name =
|
|
GetFileName("oemcrypto_load_renewal_fuzz_seed_corpus");
|
|
// Corpus for renewal response fuzzer should be in the format:
|
|
// OEMCrypto_Renewal_Response_Fuzz + license_renewal_response.
|
|
OEMCrypto_Renewal_Response_Fuzz renewal_response_fuzz;
|
|
renewal_response_fuzz.core_request = core_request_;
|
|
renewal_response_fuzz.renewal_duration_seconds = renewal_duration_seconds_;
|
|
AppendToFile(file_name,
|
|
reinterpret_cast<const char*>(&renewal_response_fuzz),
|
|
sizeof(renewal_response_fuzz));
|
|
AppendToFile(file_name,
|
|
reinterpret_cast<const char*>(&encrypted_response_data_),
|
|
sizeof(encrypted_response_data_));
|
|
}
|
|
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_(nullptr) {}
|
|
|
|
Session::~Session() {
|
|
if (!forced_session_id_ && open_) close();
|
|
if (public_rsa_) RSA_free(public_rsa_);
|
|
if (public_ec_) EC_KEY_free(public_ec_);
|
|
}
|
|
|
|
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 {
|
|
wvutil::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 = wvutil::a2b_hex(
|
|
"41555448454e5449434154494f4e000a4c08001248000000020000101907d9ff"
|
|
"de13aa95c122678053362136bdf8408f8276e4c2d87ec52b61aa1b9f646e5873"
|
|
"4930acebe899b3e464189a14a87202fb02574e70640bd22ef44b2d7e3912250a"
|
|
"230a14080112100915007caa9b5931b76a3a85f046523e10011a093938373635"
|
|
"34333231180120002a0c31383836373837343035000000000200");
|
|
*enc_context = wvutil::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;
|
|
ASSERT_NE(public_rsa_, nullptr) << "No public RSA key loaded in test code.";
|
|
// 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::TestDecryptCTR(bool select_key_first,
|
|
OEMCryptoResult expected_result,
|
|
size_t 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_CENC);
|
|
}
|
|
|
|
vector<uint8_t> unencrypted_data;
|
|
vector<uint8_t> output_buffer;
|
|
const OEMCryptoResult decrypt_result =
|
|
DecryptCTR(session_id(), license_.keys[key_index].key_data,
|
|
&unencrypted_data, &output_buffer);
|
|
|
|
// 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(size_t 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_CENC);
|
|
// 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));
|
|
public_cert.resize(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(nullptr, &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, static_cast<int>(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_) {
|
|
cerr << "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(),
|
|
static_cast<int>(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, static_cast<int>(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_) {
|
|
cerr << "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] ";
|
|
}
|
|
}
|
|
|
|
void Session::SetRsaPublicKey(const uint8_t* buffer, size_t length) {
|
|
if (public_rsa_) {
|
|
RSA_free(public_rsa_);
|
|
public_rsa_ = nullptr;
|
|
}
|
|
if (public_ec_) {
|
|
EC_KEY_free(public_ec_);
|
|
public_ec_ = nullptr;
|
|
}
|
|
public_rsa_ = d2i_RSA_PUBKEY(nullptr, &buffer, length);
|
|
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] ";
|
|
}
|
|
}
|
|
|
|
void Session::SetEcPublicKey(const uint8_t* buffer, size_t length) {
|
|
if (public_rsa_) {
|
|
RSA_free(public_rsa_);
|
|
public_rsa_ = nullptr;
|
|
}
|
|
if (public_ec_) {
|
|
EC_KEY_free(public_ec_);
|
|
public_ec_ = nullptr;
|
|
}
|
|
public_ec_ = d2i_EC_PUBKEY(nullptr, &buffer, length);
|
|
if (!public_ec_) {
|
|
cout << "d2i_RSAPrivateKey failed. ";
|
|
dump_boringssl_error();
|
|
FAIL() << "Could not parse public RSA key.";
|
|
}
|
|
switch (EC_KEY_check_key(public_ec_)) {
|
|
case 1: // valid.
|
|
return;
|
|
case 0: // not valid.
|
|
default:
|
|
dump_boringssl_error();
|
|
FAIL() << "[ec key not valid] ";
|
|
}
|
|
}
|
|
|
|
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) {
|
|
ASSERT_NE(public_rsa_, nullptr) << "No public RSA key loaded in test code.";
|
|
|
|
ASSERT_EQ(static_cast<size_t>(RSA_size(public_rsa_)), signature_length)
|
|
<< "Signature size is wrong. " << signature_length << ", should be "
|
|
<< RSA_size(public_rsa_);
|
|
|
|
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(static_cast<int>(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_) {
|
|
cerr << "No public RSA key loaded in test code.\n";
|
|
return false;
|
|
}
|
|
*session_key = wvutil::a2b_hex("6fa479c731d2770b6a61a5d1420bb9d1");
|
|
enc_session_key->assign(RSA_size(public_rsa_), 0);
|
|
int status = RSA_public_encrypt(
|
|
static_cast<int>(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) {
|
|
cerr << "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));
|
|
header_buffer->resize(header_buffer_length);
|
|
encrypted_usage_entry_.resize(entry_buffer_length);
|
|
}
|
|
|
|
void Session::LoadUsageEntry(uint32_t index, const vector<uint8_t>& 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;
|
|
}
|
|
pst_report_buffer_.resize(length);
|
|
EXPECT_EQ(wvutil::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) {
|
|
wvutil::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 = wvutil::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 = wvutil::Clock().GetCurrentTime();
|
|
expected.seconds_since_license_received =
|
|
(time_license_received > 0 && time_license_received < now)
|
|
? now - time_license_received
|
|
: 0;
|
|
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));
|
|
}
|
|
|
|
bool ConvertByteToValidBoolean(const bool* in) {
|
|
const char* buf = reinterpret_cast<const char*>(in);
|
|
for (size_t i = 0; i < sizeof(bool); i++) {
|
|
if (buf[i]) {
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
template <class CoreRequest>
|
|
void WriteRequestApiCorpus(size_t signature_length, size_t core_message_length,
|
|
vector<uint8_t>& data) {
|
|
std::string file_name;
|
|
if (std::is_same<CoreRequest,
|
|
oemcrypto_core_message::ODK_LicenseRequest>::value) {
|
|
file_name = GetFileName("oemcrypto_license_request_fuzz_seed_corpus");
|
|
} else if (std::is_same<
|
|
CoreRequest,
|
|
oemcrypto_core_message::ODK_ProvisioningRequest>::value) {
|
|
file_name = GetFileName("oemcrypto_provisioning_request_fuzz_seed_corpus");
|
|
} else if (std::is_same<CoreRequest,
|
|
oemcrypto_core_message::ODK_RenewalRequest>::value) {
|
|
file_name = GetFileName("oemcrypto_renewal_request_fuzz_seed_corpus");
|
|
} else {
|
|
LOGE("Invalid CoreRequest type while writing request api corups.");
|
|
}
|
|
// Corpus for request APIs should be signature_length + core_message_length +
|
|
// data pointer.
|
|
OEMCrypto_Request_Fuzz request_fuzz_struct;
|
|
request_fuzz_struct.core_message_length = core_message_length;
|
|
request_fuzz_struct.signature_length = signature_length;
|
|
AppendToFile(file_name, reinterpret_cast<const char*>(&request_fuzz_struct),
|
|
sizeof(OEMCrypto_Request_Fuzz));
|
|
AppendToFile(file_name, reinterpret_cast<const char*>(data.data()),
|
|
data.size());
|
|
}
|
|
} // namespace wvoec
|