// 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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "OEMCryptoCENC.h" #include "clock.h" #include "core_message_deserialize.h" #include "core_message_serialize.h" #include "disallow_copy_and_assign.h" #include "log.h" #include "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; using testing::AnyOf; // 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& value, ostream* os) { *os << wvutil::b2a_hex(value); } } // namespace std namespace wvoec { namespace { using oemcrypto_core_message::features::CoreMessageFeatures; constexpr size_t kTestSubsampleSectionSize = 256; // Fill objects by consuming a source buffer of fuzzed data. class FuzzedData { public: FuzzedData(const uint8_t* source, size_t source_size) : source_(source), source_size_(source_size) {} // Fill the destination buffer with fuzzed data. void Fill(void* destination, size_t destination_size) { if (source_ && destination) { const size_t fill_size = std::min(source_size_, destination_size); memcpy(destination, source_, fill_size); source_ += fill_size; source_size_ -= fill_size; } } private: const uint8_t* source_; size_t source_size_; }; // 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(const vector& key_handle, const uint8_t* key, vector* expected_data, vector* actual_data) { vector 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(key_handle.data(), key_handle.size(), &sample_description, 1, &pattern); } } // namespace // Encrypt a block of data using CTR mode. void EncryptCTR(const vector& in_buffer, const uint8_t* key, const uint8_t* starting_iv, vector* 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); } } 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(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& in, std::vector& 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(&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 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), seconds_since_license_received(0), seconds_since_first_decrypt(0), seconds_since_last_decrypt(0), pst(pst_in), time_created(wvutil::Clock().GetCurrentTime()) {} template OEMCryptoResult RoundTrip:: 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; const vector context = session()->GetDefaultContext(); const size_t small_size = context.size(); // arbitrary. if (RequestHasNonce()) { session()->GenerateNonce(); } uint32_t session_id = session()->session_id(); GetDefaultRequestSignatureAndCoreMessageLengths( 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 data(message_size); memcpy(&data[core_message_length], context.data(), context.size()); for (size_t i = context.size() + core_message_length; i < data.size(); i++) { data[i] = i & 0xFF; } if (ShouldGenerateCorpus()) { WriteRequestApiCorpus(gen_signature_length, core_message_length, data); } vector 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; std::string core_message(reinterpret_cast(data.data()), core_message_length); FillAndVerifyCoreRequest(core_message); VerifyRequestSignature(data, gen_signature, core_message_length); return result; } template void RoundTrip::SetEncryptAndSignResponseLengths() { encrypted_response_length_ = encrypted_response_.size(); response_signature_length_ = response_signature_.size(); } template void RoundTrip::VerifyEncryptAndSignResponseLengths() const { EXPECT_NE(encrypted_response_length_, 0u); EXPECT_EQ(encrypted_response_length_, encrypted_response_.size()); EXPECT_NE(response_signature_length_, 0u); EXPECT_EQ(response_signature_length_, response_signature_.size()); } template void GetDefaultRequestSignatureAndCoreMessageLengths( uint32_t& session_id, const size_t& small_size, size_t* gen_signature_length, size_t* core_message_length) { vector 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 void RoundTrip::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 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 OEMCrypto_Substring RoundTrip::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(pointer) - reinterpret_cast(&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) { keybox_ = &keybox; } else if (global_features.provisioning_method == OEMCrypto_BootCertificateChain) { // TODO(chelu): change this to CSR provisioning. session_->LoadOEMCert(true); session_->GenerateRsaSessionKey(); encryptor_.set_enc_key(session_->session_key()); } else { EXPECT_EQ(global_features.provisioning_method, OEMCrypto_OEMCertificate); session_->LoadOEMCert(true); session_->GenerateRsaSessionKey(); encryptor_.set_enc_key(session_->session_key()); } } void ProvisioningRoundTrip::VerifyRequestSignature( const vector& data, const vector& generated_signature, size_t core_message_length) { if (keybox_ == nullptr) { session()->VerifyRsaSignature(data, generated_signature.data(), generated_signature.size(), kSign_RSASSA_PSS); } else { // Setup the derived keys using the proto message (ignoring the core // message). ASSERT_LE(core_message_length, data.size()); const std::vector base_message(data.begin() + core_message_length, data.end()); session()->GenerateDerivedKeysFromKeybox(*keybox_, base_message); encryptor_ = session()->key_deriver(); request_ = base_message; EXPECT_EQ(global_features.provisioning_method, OEMCrypto_Keybox); ASSERT_EQ(HMAC_SHA256_SIGNATURE_SIZE, generated_signature.size()); std::vector 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); } 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 (session_->enc_session_key().size() > 0) { ASSERT_LE(session_->enc_session_key().size(), kMaxTestRSAKeyLength); memcpy(response_data_.enc_message_key, session_->enc_session_key().data(), session_->enc_session_key().size()); response_data_.enc_message_key_length = session_->enc_session_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() { 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(&encrypted_response_data_), sizeof(encrypted_response_data_)); session()->key_deriver().ServerSignBuffer(encrypted_response_.data(), encrypted_response_.size(), &response_signature_); SetEncryptAndSignResponseLengths(); } void ProvisioningRoundTrip::InjectFuzzedResponseData(const uint8_t* data, size_t size) { // Interpreting fuzz data as unencrypted core_response + message_data FuzzedData fuzzed_data(data, size); // Copy core_response from data and serialize. fuzzed_data.Fill(&core_response_, sizeof(core_response_)); // Copy provisioning message data into response_data. fuzzed_data.Fill(&response_data_, 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(&core_response_), sizeof(ODK_ParsedProvisioning)); AppendToFile(file_name, reinterpret_cast(&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; } template const T* ProvisioningRoundTrip::RemapPointer(const T* response_pointer) const { const uint8_t* original_pointer = reinterpret_cast(response_pointer); size_t delta = original_pointer - reinterpret_cast(&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(new_pointer); } OEMCryptoResult ProvisioningRoundTrip::LoadResponseNoRetry( Session* session, size_t* wrapped_key_length) { EXPECT_NE(session, nullptr); VerifyEncryptAndSignResponseLengths(); if (allowed_schemes_ == kSign_RSASSA_PSS) { return OEMCrypto_LoadProvisioning( session->session_id(), request_.data(), request_.size(), encrypted_response_.data(), encrypted_response_.size(), serialized_core_message_.size(), response_signature_.data(), response_signature_.size(), wrapped_rsa_key_.data(), wrapped_key_length); } else { // TODO(b/316053127): Clean this up a lot. const uint8_t* derivation_key = nullptr; const size_t derivation_key_length = 0; return OEMCrypto_LoadProvisioningCast( session->session_id(), derivation_key, derivation_key_length, request_.data(), request_.size(), encrypted_response_.data(), encrypted_response_.size(), serialized_core_message_.size(), response_signature_.data(), response_signature_.size(), wrapped_rsa_key_.data(), wrapped_key_length); } } void ProvisioningRoundTrip::VerifyLoadFailed() { if (wrapped_rsa_key_.size() == 0) return; std::vector zero(wrapped_rsa_key_.size(), 0); ASSERT_EQ(zero, wrapped_rsa_key_); } void Provisioning40RoundTrip::PrepareSession(bool is_oem_key) { const size_t buffer_size = 5000; // Make sure it is large enough. std::vector public_key(buffer_size); size_t public_key_size = buffer_size; std::vector public_key_signature(buffer_size); size_t public_key_signature_size = buffer_size; std::vector wrapped_private_key(buffer_size); size_t wrapped_private_key_size = buffer_size; OEMCrypto_PrivateKeyType key_type; ASSERT_EQ( OEMCrypto_SUCCESS, OEMCrypto_GenerateCertificateKeyPair( session()->session_id(), public_key.data(), &public_key_size, public_key_signature.data(), &public_key_signature_size, wrapped_private_key.data(), &wrapped_private_key_size, &key_type)); wrapped_private_key.resize(wrapped_private_key_size); public_key.resize(public_key_size); if (is_oem_key) { wrapped_oem_key_ = std::move(wrapped_private_key); oem_public_key_ = std::move(public_key); oem_key_type_ = key_type; } else { wrapped_drm_key_ = std::move(wrapped_private_key); drm_public_key_ = std::move(public_key); drm_key_type_ = key_type; } } void Provisioning40RoundTrip::FillAndVerifyCoreRequest( const std::string& core_message_string) { EXPECT_TRUE( oemcrypto_core_message::deserialize::CoreProvisioning40RequestFromMessage( 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); } void Provisioning40RoundTrip::VerifyRequestSignature( const vector& data, const vector& generated_signature, size_t /* core_message_length */) { ASSERT_NO_FATAL_FAILURE( session()->VerifySignature(data, generated_signature.data(), generated_signature.size(), kSign_RSASSA_PSS)); } OEMCryptoResult Provisioning40RoundTrip::LoadOEMCertResponse() { EXPECT_GE(wrapped_oem_key_.size(), 0UL); return OEMCrypto_InstallOemPrivateKey( session()->session_id(), oem_key_type_, reinterpret_cast(wrapped_oem_key_.data()), wrapped_oem_key_.size()); } OEMCryptoResult Provisioning40RoundTrip::LoadDRMCertResponse() { EXPECT_GE(wrapped_drm_key_.size(), 0UL); return OEMCrypto_LoadDRMPrivateKey(session()->session_id(), drm_key_type_, wrapped_drm_key_.data(), wrapped_drm_key_.size()); } void Provisioning40CastRoundTrip::PrepareSession() { const size_t buffer_size = 5000; // Make sure it is large enough. std::vector public_key(buffer_size); size_t public_key_size = buffer_size; std::vector public_key_signature(buffer_size); size_t public_key_signature_size = buffer_size; std::vector wrapped_private_key(buffer_size); size_t wrapped_private_key_size = buffer_size; OEMCrypto_PrivateKeyType key_type; ASSERT_EQ( OEMCrypto_SUCCESS, OEMCrypto_GenerateCertificateKeyPair( session()->session_id(), public_key.data(), &public_key_size, public_key_signature.data(), &public_key_signature_size, wrapped_private_key.data(), &wrapped_private_key_size, &key_type)); wrapped_private_key.resize(wrapped_private_key_size); public_key.resize(public_key_size); wrapped_drm_key_ = std::move(wrapped_private_key); drm_public_key_ = std::move(public_key); drm_key_type_ = key_type; } void Provisioning40CastRoundTrip::LoadDRMPrivateKey() { ASSERT_EQ(OEMCrypto_SUCCESS, OEMCrypto_LoadDRMPrivateKey(session()->session_id(), drm_key_type_, wrapped_drm_key_.data(), wrapped_drm_key_.size())); } void Provisioning40CastRoundTrip::FillAndVerifyCoreRequest( const std::string& core_message_string) { EXPECT_TRUE( oemcrypto_core_message::deserialize::CoreProvisioning40RequestFromMessage( 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); } void Provisioning40CastRoundTrip::VerifyRequestSignature( const vector& data, const vector& generated_signature, size_t /* core_message_length */) { ASSERT_NO_FATAL_FAILURE( session()->VerifySignature(data, generated_signature.data(), generated_signature.size(), kSign_RSASSA_PSS)); } // Creates a prov2 response void Provisioning40CastRoundTrip::CreateDefaultResponse() { 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(); response_data_.nonce = session_->nonce(); 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 Provisioning40CastRoundTrip::EncryptAndSignResponse() { session()->key_deriver().PadAndEncryptProvisioningMessage( &response_data_, &encrypted_response_data_); core_response_.enc_private_key.length = encrypted_response_data_.rsa_key_length; SignResponse(); } void Provisioning40CastRoundTrip::SignResponse() { CoreMessageFeatures features = CoreMessageFeatures::DefaultFeatures(ODK_MAJOR_VERSION); // Create prov 2 request struct from prov 4 request oemcrypto_core_message::ODK_ProvisioningRequest core_request_prov2; core_request_prov2.api_minor_version = core_request_.api_minor_version; core_request_prov2.api_major_version = core_request_.api_major_version; core_request_prov2.nonce = core_request_.nonce; core_request_prov2.session_id = core_request_.session_id; memcpy(&core_request_prov2.counter_info, &core_request_.counter_info, sizeof(core_request_.counter_info)); ASSERT_TRUE(oemcrypto_core_message::serialize::CreateCoreProvisioningResponse( features, core_response_, core_request_prov2, &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(&encrypted_response_data_), sizeof(encrypted_response_data_)); session()->key_deriver().ServerSignBuffer(encrypted_response_.data(), encrypted_response_.size(), &response_signature_); SetEncryptAndSignResponseLengths(); } OEMCryptoResult Provisioning40CastRoundTrip::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(&core_response_), sizeof(ODK_ParsedProvisioning)); AppendToFile(file_name, reinterpret_cast(&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; } OEMCryptoResult Provisioning40CastRoundTrip::LoadResponseNoRetry( Session* session, size_t* wrapped_key_length) { EXPECT_NE(session, nullptr); VerifyEncryptAndSignResponseLengths(); const std::vector context = session->GetDefaultContext(); return OEMCrypto_LoadProvisioningCast( session->session_id(), session->enc_session_key().data(), session->enc_session_key().size(), context.data(), context.size(), encrypted_response_.data(), encrypted_response_.size(), serialized_core_message_.size(), response_signature_.data(), response_signature_.size(), wrapped_rsa_key_.data(), wrapped_key_length); } void LicenseRoundTrip::VerifyRequestSignature( const vector& data, const vector& 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; vector sign_source; if (global_features.api_version < 17) { sign_source.assign(data.begin() + core_message_length, data.end()); } else if (global_features.api_version < 19) { sign_source = data; } else { sign_source.resize(SHA512_DIGEST_LENGTH); SHA512(data.data(), data.size(), sign_source.data()); } session()->VerifySignature(sign_source, 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); } 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( 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_; core_response_.license_type = license_type_; FillCoreResponseSubstrings(); } void LicenseRoundTrip::ConvertDataToValidBools(ODK_Packing_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 + response_data + // key_array FuzzedData fuzzed_data(data, size); // Copy core_response from data. fuzzed_data.Fill(&core_response_, sizeof(core_response_)); // Copy response_data from data. fuzzed_data.Fill(&response_data_, sizeof(response_data_)); // If key_array_length is more than kMaxNumKeys, we set it to kMaxNumKeys to // prevent it from going out of bounds. 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); // Copy key_array from data. key_array_.resize(num_keys_); core_response_.key_array = key_array_.data(); fuzzed_data.Fill(core_response_.key_array, num_keys_ * sizeof(*core_response_.key_array)); 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); } } // Set nonce to match one in request to pass nonce validations. for (uint32_t i = 0; i < num_keys_; ++i) { response_data_.keys[i].control.nonce = htonl(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_; key_array_.clear(); for (unsigned int i = 0; i < num_keys_; i++) { OEMCrypto_KeyObject obj; obj.key_id = FindSubstring(response_data_.keys[i].key_id, response_data_.keys[i].key_id_length); obj.key_data_iv = FindSubstring(response_data_.keys[i].key_iv, sizeof(response_data_.keys[i].key_iv)); obj.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)) { obj.key_control_iv = FindSubstring(response_data_.keys[i].control_iv, sizeof(response_data_.keys[i].control_iv)); } else { obj.key_control_iv = FindSubstring(nullptr, 0); } obj.key_control = FindSubstring(&response_data_.keys[i].control, sizeof(response_data_.keys[i].control)); key_array_.push_back(obj); } core_response_.key_array = key_array_.data(); core_response_.key_array_length = static_cast(key_array_.size()); } void LicenseRoundTrip::EncryptResponse(bool force_clear_kcb) { const auto context = session_->GetDefaultContext(!skip_request_hash_); ASSERT_NO_FATAL_FAILURE(session_->GenerateDerivedKeysFromSessionKey(context)); 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 (!force_clear_kcb && 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(&response_data_.keys[i].control), reinterpret_cast( &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); } } } void LicenseRoundTrip::CreateCoreLicenseResponseWithFeatures( const CoreMessageFeatures& features) { 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(request_hash_), sizeof(request_hash_)); 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())); } void LicenseRoundTrip::SignEncryptedResponse() { // 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(&encrypted_response_data_), sizeof(encrypted_response_data_)); session()->key_deriver().ServerSignBuffer(encrypted_response_.data(), encrypted_response_.size(), &response_signature_); SetEncryptAndSignResponseLengths(); } void LicenseRoundTrip::EncryptAndSignResponse() { EncryptResponse(); // 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(ODK_MAJOR_VERSION))); CreateCoreLicenseResponseWithFeatures(features); SignEncryptedResponse(); } void LicenseRoundTrip::EncryptAndSignResponseWithCoreMessageFeatures( const CoreMessageFeatures& features, bool force_clear_kcb) { EncryptResponse(force_clear_kcb); CreateCoreLicenseResponseWithFeatures(features); SignEncryptedResponse(); } 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 + key_array. AppendToFile(file_name, reinterpret_cast(&core_response_), sizeof(core_response_)); AppendToFile(file_name, reinterpret_cast(&response_data_), sizeof(response_data_)); AppendToFile( file_name, reinterpret_cast(core_response_.key_array), core_response_.key_array_length * sizeof(*core_response_.key_array)); } const vector context = session->GetDefaultContext(!skip_request_hash_); // 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. VerifyEncryptAndSignResponseLengths(); std::vector double_message = encrypted_response_; double_message.insert( double_message.end(), reinterpret_cast(&encrypted_response_data_), reinterpret_cast(&encrypted_response_data_) + sizeof(encrypted_response_data_)); OEMCryptoResult result = OEMCrypto_LoadLicense( session->session_id(), context.data(), context.size(), session->enc_session_key().data(), session->enc_session_key().size(), 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 GetKeyHandle, 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 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(&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)); EXPECT_EQ(1, GetRandBytes(key_data->content_iv, sizeof(key_data->content_iv))); key_data->content_iv_length = sizeof(key_data->content_iv); offsets->content_iv = FindSubstring(key_data->content_iv, key_data->content_iv_length); } 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(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(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(ptr) - reinterpret_cast(entitled_key_data_); substring.length = size; } return substring; } void EntitledMessage::LoadKeys(bool expected_success) { EncryptContentKey(); if (expected_success) { ASSERT_EQ(OEMCrypto_SUCCESS, OEMCrypto_LoadEntitledContentKeys( entitled_key_session_, reinterpret_cast(entitled_key_data_), sizeof(entitled_key_data_), num_keys_, entitled_key_array_)); } else { ASSERT_NE(OEMCrypto_SUCCESS, OEMCrypto_LoadEntitledContentKeys( entitled_key_session_, reinterpret_cast(entitled_key_data_), sizeof(entitled_key_data_), num_keys_, entitled_key_array_)); return; } VerifyKCBs(); VerifyDecrypt(); } OEMCryptoResult EntitledMessage::LoadKeys(const vector& 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(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(entitled_key_data_), num_keys_ * sizeof(EntitledContentKeyData)); AppendSeparator(file_name); AppendToFile(file_name, reinterpret_cast(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 = entitled_key_array_[0].content_iv; even_key.cipher_mode = OEMCrypto_CipherMode_CBC; } 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 = entitled_key_array_[1].content_iv; odd_key.cipher_mode = OEMCrypto_CipherMode_CBC; } OEMCryptoResult sts = OEMCrypto_LoadCasECMKeys( entitled_key_session_, reinterpret_cast(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(&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]; vector key_handle; OEMCryptoResult result = GetKeyHandleIntoVector( entitled_key_session_, key_data->content_key_id, key_data->content_key_id_length, OEMCrypto_CipherMode_CENC, key_handle); ASSERT_EQ(result, OEMCrypto_SUCCESS) << "For key " << i; vector expected_data; vector actual_data; result = DecryptCTR(key_handle, 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& data, const vector& generated_signature, size_t core_message_length) { ASSERT_EQ(HMAC_SHA256_SIGNATURE_SIZE, generated_signature.size()); std::vector expected_signature; session()->key_deriver().ClientSignBuffer(data, &expected_signature); ASSERT_EQ(expected_signature, generated_signature); } void RenewalRoundTrip::FillAndVerifyCoreRequest( const std::string& core_message_string) { if (is_release_) { // 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); } } // Nothing is needed for this function but it needs a definition since it's // declared as a virtual function in the RoundTrip class. void RenewalRoundTrip::CreateDefaultResponse() {} void RenewalRoundTrip::EncryptAndSignResponse() { // Renewal messages are not encrypted. encrypted_response_data_ = response_data_; // Create a core response for a call to LoadRenewal. // 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(&encrypted_response_data_), sizeof(encrypted_response_data_)); session()->key_deriver().ServerSignBuffer(encrypted_response_.data(), encrypted_response_.size(), &response_signature_); SetEncryptAndSignResponseLengths(); } 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(&renewal_response_fuzz), sizeof(renewal_response_fuzz)); AppendToFile(file_name, reinterpret_cast(&encrypted_response_data_), sizeof(encrypted_response_data_)); } VerifyEncryptAndSignResponseLengths(); return OEMCrypto_LoadRenewal( session->session_id(), encrypted_response_.data(), encrypted_response_.size(), serialized_core_message_.size(), response_signature_.data(), response_signature_.size()); } void ReleaseRoundTrip::VerifyRequestSignature( const vector& data, const vector& generated_signature, size_t core_message_length) { ASSERT_EQ(HMAC_SHA256_SIGNATURE_SIZE, generated_signature.size()); std::vector expected_signature; session()->key_deriver().ClientSignBuffer(data, &expected_signature); ASSERT_EQ(expected_signature, generated_signature); } void ReleaseRoundTrip::FillAndVerifyCoreRequest( const std::string& core_message_string) { EXPECT_TRUE( oemcrypto_core_message::deserialize::CoreReleaseRequestFromMessage( 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); } // Nothing is needed for this function but it needs a definition since it's // declared as a virtual function in the RoundTrip class. void ReleaseRoundTrip::CreateDefaultResponse() {} void ReleaseRoundTrip::EncryptAndSignResponse() { // Release messages are not encrypted. encrypted_response_data_ = response_data_; // Create a core response for a call to LoadRelease. // TODO(b/191724203): Test release 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::CreateCoreReleaseResponse( features, core_request_, seconds_since_license_received_, seconds_since_first_decrypt_, &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(&encrypted_response_data_), sizeof(encrypted_response_data_)); session()->key_deriver().ServerSignBuffer(encrypted_response_.data(), encrypted_response_.size(), &response_signature_); SetEncryptAndSignResponseLengths(); } OEMCryptoResult ReleaseRoundTrip::LoadResponse(Session* session) { // TODO(vickymin): Write corpus for oemcrypto_load_release_fuzz. VerifyEncryptAndSignResponseLengths(); return OEMCrypto_LoadRelease( session->session_id(), encrypted_response_.data(), encrypted_response_.size(), serialized_core_message_.size(), response_signature_.data(), response_signature_.size()); } std::unordered_map, std::hash> Session::server_ephemeral_keys_; std::mutex Session::ephemeral_key_map_lock_; Session::Session() {} Session::~Session() { if (!forced_session_id_ && open_) close(); } 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_)); } } vector Session::GetDefaultContext(bool do_hash) { /* Context string * This context string is normally created by the CDM layer * from a license request message. * They are used to test MAC and ENC key generation. */ auto ret = wvutil::a2b_hex( "0a4c08001248000000020000101907d9ffde13aa95c122678053362136bdf840" "8f8276e4c2d87ec52b61aa1b9f646e58734930acebe899b3e464189a14a87202" "fb02574e70640bd22ef44b2d7e3912250a230a14080112100915007caa9b5931" "b76a3a85f046523e10011a09393837363534333231180120002a0c3138383637" "38373430350000"); if (do_hash) { uint8_t hash[SHA512_DIGEST_LENGTH]; SHA512(ret.data(), ret.size(), hash); ret.assign(hash, hash + sizeof(hash)); } return ret; } // 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) { return GenerateDerivedKeysFromKeybox(keybox, GetDefaultContext()); } void Session::GenerateDerivedKeysFromKeybox( const wvoec::WidevineKeybox& keybox, const std::vector& context) { key_deriver_.DeriveKeys(keybox.device_key_, sizeof(keybox.device_key_), context); } void Session::GenerateDerivedKeysFromSessionKey() { GenerateDerivedKeysFromSessionKey(GetDefaultContext()); } void Session::GenerateDerivedKeysFromSessionKey( const std::vector& context) { // Uses test certificate. ASSERT_TRUE(GenerateSessionKey()); key_deriver_.DeriveKeys(session_key_.data(), session_key_.size(), context); } void Session::TestDecryptCTR(bool get_fresh_key_handle_first, OEMCryptoResult expected_result, size_t key_index) { OEMCryptoResult getkeyhandle_result = OEMCrypto_SUCCESS; if (get_fresh_key_handle_first) { // Select the key (from FillSimpleMessage) getkeyhandle_result = GetKeyHandle(key_handle_, key_index); } vector unencrypted_data; vector output_buffer; const OEMCryptoResult decrypt_result = DecryptCTR(key_handle_, 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, getkeyhandle_result, decrypt_result)) << "Either GetKeyHandle or DecryptCENC should return " << expected_result << ", but they returned " << getkeyhandle_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::TestDecryptEntitled(OEMCryptoResult expected_result, OEMCrypto_SESSION session_id, const uint8_t* content_key_id, size_t content_key_id_length) { // Select the key (from FillSimpleMessage) const OEMCryptoResult getkeyhandle_result = GetKeyHandleIntoVector(session_id, content_key_id, content_key_id_length, OEMCrypto_CipherMode_CENC, key_handle_); vector unencrypted_data; vector output_buffer; vector encrypted_data(kTestSubsampleSectionSize); vector in_buffer(256); vector out_buffer(in_buffer.size()); OEMCrypto_SampleDescription sample_description; OEMCrypto_SubSampleDescription subsample_description; ASSERT_NO_FATAL_FAILURE(GenerateSimpleSampleDescription( in_buffer, out_buffer, &sample_description, &subsample_description)); OEMCrypto_CENCEncryptPatternDesc pattern = {0, 0}; EncryptCTR(unencrypted_data, content_key_id, &sample_description.iv[0], &encrypted_data); // Try to decrypt the data with oemcrypto session id. const OEMCryptoResult decrypt_result = OEMCrypto_DecryptCENC( key_handle_.data(), key_handle_.size(), &sample_description, 1, &pattern); // We only have a few errors that we test are reported. ASSERT_NO_FATAL_FAILURE( TestDecryptResult(expected_result, getkeyhandle_result, decrypt_result)) << "Either GetKeyHandle or DecryptCENC should return" << expected_result << ", but they returned " << getkeyhandle_result << " and " << decrypt_result << ", respectively."; } OEMCryptoResult Session::GetKeyHandle(vector& key_handle, size_t key_index, OEMCryptoCipherMode cipher_mode) { return GetKeyHandleIntoVector(session_id(), license_.keys[key_index].key_id, license_.keys[key_index].key_id_length, cipher_mode, key_handle); } void Session::TestDecryptResult(OEMCryptoResult expected_result, OEMCryptoResult actual_getkeyhandle_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_getkeyhandle_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_getkeyhandle_result == expected_result || actual_decrypt_result == expected_result); } else if (expected_result == OEMCrypto_WARNING_MIXED_OUTPUT_PROTECTION) { // OEMCrypto is allowed to report either this warning or // OEMCrypto_ERROR_INSUFFICIENT_HDCP depending on if it can disable // restricted displays. ASSERT_TRUE( actual_getkeyhandle_result == OEMCrypto_WARNING_MIXED_OUTPUT_PROTECTION || actual_getkeyhandle_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_getkeyhandle_result != OEMCrypto_SUCCESS || actual_decrypt_result != OEMCrypto_SUCCESS); } } void Session::TestGetKeyHandleExpired(size_t key_index) { if (global_features.api_version >= 13) { OEMCryptoResult status = GetKeyHandle(key_handle_, key_index); // It is OK for GetKeyHandle 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 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(public_cert.data()); long cert_size = static_cast(public_cert.size()); boringssl_ptr 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(sk_X509_num(certs)); ++i) { X509* x509_cert = sk_X509_value(certs, static_cast(i)); boringssl_ptr pubkey(X509_get_pubkey(x509_cert)); ASSERT_TRUE(pubkey.NotNull()); if (i == 0) { public_rsa_ = util::RsaPublicKey::FromSslHandle(EVP_PKEY_get0_RSA(pubkey.get())); ASSERT_TRUE(public_rsa_) << "Failed to extract public RSA key from OEM certificate"; } if (verify_cert) { vector 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(buffer.size()))); boringssl_ptr store(X509_STORE_new()); ASSERT_TRUE(store.NotNull()); boringssl_ptr 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::SetTestRsaPublicKey() { public_ec_.reset(); public_rsa_ = util::RsaPublicKey::LoadPrivateKeyInfo( kTestRSAPKCS8PrivateKeyInfo2_2048, sizeof(kTestRSAPKCS8PrivateKeyInfo2_2048)); ASSERT_TRUE(public_rsa_) << "Could not parse test RSA public key #2"; } void Session::SetPublicKeyFromPrivateKeyInfo(OEMCrypto_PrivateKeyType key_type, const uint8_t* buffer, size_t length) { switch (key_type) { case OEMCrypto_RSA_Private_Key: ASSERT_NO_FATAL_FAILURE( SetRsaPublicKeyFromPrivateKeyInfo(buffer, length)); return; case OEMCrypto_ECC_Private_Key: ASSERT_NO_FATAL_FAILURE( SetEccPublicKeyFromPrivateKeyInfo(buffer, length)); return; } FAIL() << "Unknown key type: " << static_cast(key_type); } void Session::SetRsaPublicKeyFromPrivateKeyInfo(const uint8_t* buffer, size_t length) { public_ec_.reset(); public_rsa_ = util::RsaPublicKey::LoadPrivateKeyInfo(buffer, length); ASSERT_TRUE(public_rsa_) << "Could not parse RSA public key"; } void Session::SetEccPublicKeyFromPrivateKeyInfo(const uint8_t* buffer, size_t length) { public_rsa_.reset(); public_ec_ = util::EccPublicKey::LoadPrivateKeyInfo(buffer, length); ASSERT_TRUE(public_ec_) << "Could not parse ECC public key"; } void Session::SetPublicKeyFromSubjectPublicKey( OEMCrypto_PrivateKeyType key_type, const uint8_t* buffer, size_t length) { switch (key_type) { case OEMCrypto_RSA_Private_Key: ASSERT_NO_FATAL_FAILURE( SetRsaPublicKeyFromSubjectPublicKey(buffer, length)); return; case OEMCrypto_ECC_Private_Key: ASSERT_NO_FATAL_FAILURE( SetEccPublicKeyFromSubjectPublicKey(buffer, length)); return; } FAIL() << "Unknown key type: " << static_cast(key_type); } void Session::SetRsaPublicKeyFromSubjectPublicKey(const uint8_t* buffer, size_t length) { public_ec_.reset(); public_rsa_ = util::RsaPublicKey::Load(buffer, length); ASSERT_TRUE(public_rsa_) << "Could not parse RSA public key"; } void Session::SetEccPublicKeyFromSubjectPublicKey(const uint8_t* buffer, size_t length) { public_rsa_.reset(); public_ec_ = util::EccPublicKey::Load(buffer, length); ASSERT_TRUE(public_ec_) << "Could not parse ECC public key"; } void Session::VerifyRsaSignature(const vector& message, const uint8_t* signature, size_t signature_length, RSA_Padding_Scheme padding_scheme) { ASSERT_TRUE(public_rsa_) << "No public RSA key loaded in test code"; if (padding_scheme != kSign_RSASSA_PSS && padding_scheme != kSign_PKCS1_Block1) { FAIL() << "Padding scheme not supported: " << padding_scheme; return; } const util::RsaSignatureAlgorithm algorithm = padding_scheme == kSign_RSASSA_PSS ? util::kRsaPssDefault : util::kRsaPkcs1Cast; OEMCrypto_SignatureHashAlgorithm hash_algorithm = OEMCrypto_SHA1; if (algorithm == util::kRsaPssDefault) { ASSERT_THAT( OEMCrypto_GetSignatureHashAlgorithm(session_id(), &hash_algorithm), AnyOf(OEMCrypto_SUCCESS, OEMCrypto_ERROR_NOT_IMPLEMENTED)); } const OEMCryptoResult result = public_rsa_->VerifySignature(message.data(), message.size(), signature, signature_length, algorithm, hash_algorithm); ASSERT_EQ(result, OEMCrypto_SUCCESS) << "RSA signature check failed"; } void Session::VerifyEccSignature(const vector& message, const uint8_t* signature, size_t signature_length) { ASSERT_TRUE(public_ec_) << "No public ECC key loaded in test code"; const OEMCryptoResult result = public_ec_->VerifySignature( message.data(), message.size(), signature, signature_length); ASSERT_EQ(result, OEMCrypto_SUCCESS) << "ECC signature check failed"; } void Session::VerifySignature(const vector& message, const uint8_t* signature, size_t signature_length, RSA_Padding_Scheme padding_scheme) { if (public_rsa_ != nullptr) { return VerifyRsaSignature(message, signature, signature_length, padding_scheme); } else if (public_ec_ != nullptr) { return VerifyEccSignature(message, signature, signature_length); } FAIL() << "No public RSA or ECC key loaded in test code"; } bool Session::GenerateRsaSessionKey() { if (!public_rsa_) { cerr << "No public RSA key loaded in test code\n"; return false; } session_key_ = wvutil::a2b_hex("6fa479c731d2770b6a61a5d1420bb9d1"); enc_session_key_ = public_rsa_->EncryptSessionKey(session_key_); return !enc_session_key_.empty(); } bool Session::GenerateEccSessionKey() { if (!public_ec_) { cerr << "No public ECC key loaded in test code\n"; return false; } std::unique_lock lock(Session::ephemeral_key_map_lock_); const util::EccCurve curve = public_ec_->curve(); if (server_ephemeral_keys_.count(curve) == 0) { server_ephemeral_keys_[curve] = util::EccPrivateKey::New(curve); } if (server_ephemeral_keys_.count(curve) == 0) { cerr << "Failed to find/create server ECC key for curve " << util::EccCurveToString(curve) << std::endl; return false; } session_key_ = server_ephemeral_keys_[curve]->DeriveSessionKey(*public_ec_); if (session_key_.empty()) { return false; } enc_session_key_ = server_ephemeral_keys_[curve]->SerializeAsPublicKey(); if (enc_session_key_.empty()) { session_key_.clear(); return false; } return true; } bool Session::GenerateSessionKey() { if (public_rsa_ != nullptr) { return GenerateRsaSessionKey(); } else if (public_ec_ != nullptr) { return GenerateEccSessionKey(); } cerr << "No public RSA or ECC key loaded in test code\n"; return false; } void Session::LoadWrappedDrmKey(OEMCrypto_PrivateKeyType key_type, const vector& wrapped_drm_key) { ASSERT_EQ(OEMCrypto_SUCCESS, OEMCrypto_LoadDRMPrivateKey(session_id(), key_type, wrapped_drm_key.data(), wrapped_drm_key.size())); } void Session::LoadWrappedRsaDrmKey(const vector& wrapped_rsa_key) { ASSERT_NO_FATAL_FAILURE( LoadWrappedDrmKey(OEMCrypto_RSA_Private_Key, wrapped_rsa_key)); } void Session::LoadWrappedEccDrmKey(const vector& wrapped_ecc_key) { ASSERT_NO_FATAL_FAILURE( LoadWrappedDrmKey(OEMCrypto_ECC_Private_Key, wrapped_ecc_key)); } 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* 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& buffer) { ASSERT_EQ(OEMCrypto_SUCCESS, OEMCrypto_LoadUsageEntry(session_id(), index, buffer.data(), buffer.size())); } void Session::MoveUsageEntry(uint32_t new_index, std::vector* 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(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(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 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(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 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(in); for (size_t i = 0; i < sizeof(bool); i++) { if (buf[i]) { return true; } } return false; } template void WriteRequestApiCorpus(size_t signature_length, size_t core_message_length, vector& data) { std::string file_name; if (std::is_same::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::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(&request_fuzz_struct), sizeof(OEMCrypto_Request_Fuzz)); AppendToFile(file_name, reinterpret_cast(data.data()), data.size()); } OEMCryptoResult GetKeyHandleIntoVector(OEMCrypto_SESSION session, const uint8_t* key_id, size_t key_id_length, OEMCryptoCipherMode cipher_mode, vector& key_handle) { size_t key_handle_length = 0; const OEMCryptoResult result = OEMCrypto_GetKeyHandle( session, key_id, key_id_length, cipher_mode, nullptr, &key_handle_length); if (result == OEMCrypto_SUCCESS) { LOGE( "OEMCrypto_GetKeyHandle returned SUCCESS despite getting no key handle " "buffer"); return OEMCrypto_ERROR_UNKNOWN_FAILURE; } else if (result != OEMCrypto_ERROR_SHORT_BUFFER) { return result; } key_handle.resize(key_handle_length); return OEMCrypto_GetKeyHandle(session, key_id, key_id_length, cipher_mode, key_handle.data(), &key_handle_length); } } // namespace wvoec