Source release 16.3.0

This commit is contained in:
John W. Bruce
2020-07-24 14:30:03 -07:00
parent b830b1d1fb
commit 160df9f57a
74 changed files with 4632 additions and 2561 deletions

View File

@@ -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