Source release 16.3.0
This commit is contained in:
@@ -31,8 +31,10 @@
|
||||
#include "core_message_serialize.h"
|
||||
#include "disallow_copy_and_assign.h"
|
||||
#include "log.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"
|
||||
@@ -162,6 +164,10 @@ void RoundTrip<CoreRequest, PrepAndSignRequest, CoreResponse,
|
||||
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;
|
||||
if (ShouldGenerateCorpus()) {
|
||||
WriteRequestApiCorpus<CoreRequest>(gen_signature_length,
|
||||
core_message_length, data);
|
||||
}
|
||||
|
||||
vector<uint8_t> gen_signature(gen_signature_length);
|
||||
ASSERT_EQ(PrepAndSignRequest(session()->session_id(), data.data(),
|
||||
@@ -177,6 +183,27 @@ void RoundTrip<CoreRequest, PrepAndSignRequest, CoreResponse,
|
||||
VerifyRequestSignature(data, gen_signature, core_message_length);
|
||||
}
|
||||
|
||||
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,
|
||||
@@ -309,8 +336,35 @@ void ProvisioningRoundTrip::EncryptAndSignResponse() {
|
||||
&response_signature_);
|
||||
}
|
||||
|
||||
void ProvisioningRoundTrip::InjectFuzzedResponseData(const uint8_t* data,
|
||||
size_t size) {
|
||||
// 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;
|
||||
const OEMCryptoResult sts = LoadResponseNoRetry(session, &wrapped_key_length);
|
||||
if (sts != OEMCrypto_ERROR_SHORT_BUFFER) return sts;
|
||||
@@ -472,6 +526,69 @@ void LicenseRoundTrip::CreateDefaultResponse() {
|
||||
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) {
|
||||
// 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 |=
|
||||
@@ -540,17 +657,28 @@ void LicenseRoundTrip::EncryptAndSignResponse() {
|
||||
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);
|
||||
// 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);
|
||||
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);
|
||||
@@ -588,6 +716,21 @@ void LicenseRoundTrip::EncryptAndSignResponse() {
|
||||
|
||||
OEMCryptoResult LicenseRoundTrip::LoadResponse(Session* session) {
|
||||
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
|
||||
@@ -630,7 +773,7 @@ OEMCryptoResult LicenseRoundTrip::LoadResponse(Session* session) {
|
||||
|
||||
// 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();
|
||||
if (license_type_ == OEMCrypto_ContentLicense) VerifyTestKeys(session);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
@@ -644,12 +787,12 @@ OEMCryptoResult LicenseRoundTrip::ReloadResponse(Session* session) {
|
||||
// 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() {
|
||||
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,
|
||||
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) {
|
||||
@@ -906,7 +1049,46 @@ void RenewalRoundTrip::EncryptAndSignResponse() {
|
||||
&response_signature_);
|
||||
}
|
||||
|
||||
void RenewalRoundTrip::InjectFuzzedResponseData(
|
||||
OEMCrypto_Renewal_Response_Fuzz& fuzzed_data,
|
||||
const uint8_t* renewal_response, const size_t renewal_response_size) {
|
||||
// Serializing core message.
|
||||
// This call also sets nonce in core response to match with session nonce.
|
||||
oemcrypto_core_message::serialize::CreateCoreRenewalResponse(
|
||||
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(),
|
||||
@@ -1009,7 +1191,7 @@ void Session::GenerateDerivedKeysFromSessionKey() {
|
||||
// Uses test certificate.
|
||||
vector<uint8_t> session_key;
|
||||
vector<uint8_t> enc_session_key;
|
||||
if (public_rsa_ == nullptr) PreparePublicKey();
|
||||
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));
|
||||
@@ -1297,12 +1479,11 @@ 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";
|
||||
ASSERT_NE(public_rsa_, nullptr) << "No public RSA key loaded in test code.";
|
||||
|
||||
EXPECT_EQ(static_cast<size_t>(RSA_size(public_rsa_)), signature_length)
|
||||
ASSERT_EQ(static_cast<size_t>(RSA_size(public_rsa_)), signature_length)
|
||||
<< "Signature size is wrong. " << signature_length << ", should be "
|
||||
<< RSA_size(public_rsa_) << "\n";
|
||||
<< RSA_size(public_rsa_);
|
||||
|
||||
if (padding_scheme == kSign_RSASSA_PSS) {
|
||||
boringssl_ptr<EVP_PKEY, EVP_PKEY_free> pkey(EVP_PKEY_new());
|
||||
@@ -1471,7 +1652,10 @@ void Session::VerifyReport(Test_PST_Report expected,
|
||||
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_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
|
||||
@@ -1482,4 +1666,41 @@ void Session::VerifyReport(Test_PST_Report expected,
|
||||
: 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.
|
||||
AppendToFile(file_name, reinterpret_cast<const char*>(&signature_length),
|
||||
sizeof(signature_length));
|
||||
AppendToFile(file_name, reinterpret_cast<const char*>(&core_message_length),
|
||||
sizeof(core_message_length));
|
||||
AppendToFile(file_name, reinterpret_cast<const char*>(data.data()),
|
||||
data.size());
|
||||
}
|
||||
} // namespace wvoec
|
||||
|
||||
Reference in New Issue
Block a user