// Copyright 2023 Google LLC. All Rights Reserved. This file and proprietary // source code may only be used and distributed under the Widevine // License Agreement. // // Test data for OEMCrypto unit tests. // #ifndef CDM_OEMCRYPTO_USAGE_TABLE_TEST_ #define CDM_OEMCRYPTO_USAGE_TABLE_TEST_ #include #include #include #include "OEMCryptoCENC.h" #include "log.h" #include "oemcrypto_basic_test.h" #include "oemcrypto_license_test.h" #include "test_sleep.h" namespace wvoec { // This class is for testing the generic crypto functionality. class OEMCryptoGenericCryptoTest : public OEMCryptoRefreshTest { protected: // buffer_size_ must be a multiple of encryption block size, 16. We'll use a // reasonable number of blocks for most of the tests. OEMCryptoGenericCryptoTest() : buffer_size_(160) {} void SetUp() override { OEMCryptoRefreshTest::SetUp(); if (!wvoec::global_features.generic_crypto) { GTEST_SKIP() << "Test for devices with generic crypto API only"; } ASSERT_NO_FATAL_FAILURE(license_messages_.SignAndVerifyRequest()); ASSERT_NO_FATAL_FAILURE( license_messages_.CreateResponseWithGenericCryptoKeys()); InitializeClearBuffer(); } void InitializeClearBuffer() { clear_buffer_.assign(buffer_size_, 0); for (size_t i = 0; i < clear_buffer_.size(); i++) { clear_buffer_[i] = 1 + i % 250; } for (size_t i = 0; i < wvoec::KEY_IV_SIZE; i++) { iv_[i] = i; } } void ResizeBuffer(size_t new_size) { buffer_size_ = new_size; InitializeClearBuffer(); // Re-initialize the clear buffer. } void EncryptAndLoadKeys() { ASSERT_NO_FATAL_FAILURE(license_messages_.EncryptAndSignResponse()); ASSERT_EQ(OEMCrypto_SUCCESS, license_messages_.LoadResponse()); } // Encrypt the buffer with the specified key made in // CreateResponseWithGenericCryptoKeys. void EncryptBuffer(unsigned int key_index, const vector& in_buffer, vector* out_buffer) { EncryptBufferWithKey(session_.license().keys[key_index].key_data, in_buffer, out_buffer); } // Encrypt the buffer with the specified key. void EncryptBufferWithKey(const uint8_t* key_data, const vector& in_buffer, vector* out_buffer) { AES_KEY aes_key; ASSERT_EQ(0, AES_set_encrypt_key(key_data, AES_BLOCK_SIZE * 8, &aes_key)); uint8_t iv_buffer[wvoec::KEY_IV_SIZE]; memcpy(iv_buffer, iv_, wvoec::KEY_IV_SIZE); out_buffer->resize(in_buffer.size()); ASSERT_GT(in_buffer.size(), 0u); ASSERT_EQ(0u, in_buffer.size() % AES_BLOCK_SIZE); AES_cbc_encrypt(in_buffer.data(), out_buffer->data(), in_buffer.size(), &aes_key, iv_buffer, AES_ENCRYPT); } // Sign the buffer with the specified key made in // CreateResponseWithGenericCryptoKeys. void SignBuffer(unsigned int key_index, const vector& in_buffer, vector* signature) { SignBufferWithKey(session_.license().keys[key_index].key_data, in_buffer, signature); } // Sign the buffer with the specified key. void SignBufferWithKey(const uint8_t* key_data, const vector& in_buffer, vector* signature) { unsigned int md_len = SHA256_DIGEST_LENGTH; signature->resize(SHA256_DIGEST_LENGTH); HMAC(EVP_sha256(), key_data, wvoec::MAC_KEY_SIZE, in_buffer.data(), in_buffer.size(), signature->data(), &md_len); } OEMCryptoResult GenericEncrypt(const uint8_t* key_handle, size_t key_handle_length, const uint8_t* clear_buffer, size_t clear_buffer_length, const uint8_t* iv, OEMCrypto_Algorithm algorithm, uint8_t* out_buffer) { if (ShouldGenerateCorpus()) { const std::string file_name = GetFileName("oemcrypto_generic_encrypt_fuzz_seed_corpus"); OEMCrypto_Generic_Api_Fuzz fuzzed_structure; fuzzed_structure.cipher_mode = OEMCrypto_CipherMode_CENC; fuzzed_structure.algorithm = algorithm; // Cipher mode and algorithm. AppendToFile(file_name, reinterpret_cast(&fuzzed_structure), sizeof(fuzzed_structure)); AppendToFile(file_name, reinterpret_cast(iv), wvoec::KEY_IV_SIZE); AppendSeparator(file_name); AppendToFile(file_name, reinterpret_cast(clear_buffer), clear_buffer_length); } return OEMCrypto_Generic_Encrypt(key_handle, key_handle_length, clear_buffer, clear_buffer_length, iv, algorithm, out_buffer); } OEMCryptoResult GenericDecrypt( const uint8_t* key_handle, size_t key_handle_length, const uint8_t* encrypted_buffer, size_t encrypted_buffer_length, const uint8_t* iv, OEMCrypto_Algorithm algorithm, uint8_t* out_buffer) { if (ShouldGenerateCorpus()) { const std::string file_name = GetFileName("oemcrypto_generic_decrypt_fuzz_seed_corpus"); OEMCrypto_Generic_Api_Fuzz fuzzed_structure; fuzzed_structure.cipher_mode = OEMCrypto_CipherMode_CENC; fuzzed_structure.algorithm = algorithm; // Cipher mode and algorithm. AppendToFile(file_name, reinterpret_cast(&fuzzed_structure), sizeof(fuzzed_structure)); AppendToFile(file_name, reinterpret_cast(iv), wvoec::KEY_IV_SIZE); AppendSeparator(file_name); AppendToFile(file_name, reinterpret_cast(encrypted_buffer), encrypted_buffer_length); } return OEMCrypto_Generic_Decrypt(key_handle, key_handle_length, encrypted_buffer, encrypted_buffer_length, iv, algorithm, out_buffer); } OEMCryptoResult GenericVerify(const uint8_t* key_handle, size_t key_handle_length, const uint8_t* clear_buffer, size_t clear_buffer_length, OEMCrypto_Algorithm algorithm, const uint8_t* signature, size_t signature_length) { if (ShouldGenerateCorpus()) { const std::string file_name = GetFileName("oemcrypto_generic_verify_fuzz_seed_corpus"); OEMCrypto_Generic_Api_Fuzz fuzzed_structure; fuzzed_structure.cipher_mode = OEMCrypto_CipherMode_CENC; fuzzed_structure.algorithm = algorithm; // Cipher mode and algorithm. AppendToFile(file_name, reinterpret_cast(&fuzzed_structure), sizeof(fuzzed_structure)); AppendToFile(file_name, reinterpret_cast(clear_buffer), clear_buffer_length); AppendSeparator(file_name); AppendToFile(file_name, reinterpret_cast(signature), signature_length); } return OEMCrypto_Generic_Verify(key_handle, key_handle_length, clear_buffer, clear_buffer_length, algorithm, signature, signature_length); } OEMCryptoResult GenericSign(const uint8_t* key_handle, size_t key_handle_length, const uint8_t* clear_buffer, size_t clear_buffer_length, OEMCrypto_Algorithm algorithm, uint8_t* signature, size_t* signature_length) { if (ShouldGenerateCorpus()) { const std::string file_name = GetFileName("oemcrypto_generic_sign_fuzz_seed_corpus"); OEMCrypto_Generic_Api_Fuzz fuzzed_structure; fuzzed_structure.cipher_mode = OEMCrypto_CipherMode_CENC; fuzzed_structure.algorithm = algorithm; // Cipher mode and algorithm. AppendToFile(file_name, reinterpret_cast(&fuzzed_structure), sizeof(fuzzed_structure)); AppendToFile(file_name, reinterpret_cast(clear_buffer), clear_buffer_length); } return OEMCrypto_Generic_Sign(key_handle, key_handle_length, clear_buffer, clear_buffer_length, algorithm, signature, signature_length); } // This asks OEMCrypto to encrypt with the specified key, and expects a // failure. void BadEncrypt(unsigned int key_index, OEMCrypto_Algorithm algorithm, size_t buffer_length) { OEMCryptoResult sts; vector expected_encrypted; EncryptBuffer(key_index, clear_buffer_, &expected_encrypted); vector key_handle; sts = GetKeyHandleIntoVector( session_.session_id(), session_.license().keys[key_index].key_id, session_.license().keys[key_index].key_id_length, OEMCrypto_CipherMode_CENC, key_handle); ASSERT_EQ(OEMCrypto_SUCCESS, sts); vector encrypted(buffer_length); sts = GenericEncrypt(key_handle.data(), key_handle.size(), clear_buffer_.data(), buffer_length, iv_, algorithm, encrypted.data()); EXPECT_NE(OEMCrypto_SUCCESS, sts); expected_encrypted.resize(buffer_length); EXPECT_NE(encrypted, expected_encrypted); } // This asks OEMCrypto to decrypt with the specified key, and expects a // failure. void BadDecrypt(unsigned int key_index, OEMCrypto_Algorithm algorithm, size_t buffer_length) { OEMCryptoResult sts; vector encrypted; EncryptBuffer(key_index, clear_buffer_, &encrypted); vector key_handle; sts = GetKeyHandleIntoVector( session_.session_id(), session_.license().keys[key_index].key_id, session_.license().keys[key_index].key_id_length, OEMCrypto_CipherMode_CENC, key_handle); ASSERT_EQ(OEMCrypto_SUCCESS, sts); vector resultant(encrypted.size()); sts = GenericDecrypt(key_handle.data(), key_handle.size(), encrypted.data(), buffer_length, iv_, algorithm, resultant.data()); EXPECT_NE(OEMCrypto_SUCCESS, sts); EXPECT_NE(clear_buffer_, resultant); } // This asks OEMCrypto to sign with the specified key, and expects a // failure. void BadSign(unsigned int key_index, OEMCrypto_Algorithm algorithm) { OEMCryptoResult sts; vector expected_signature; SignBuffer(key_index, clear_buffer_, &expected_signature); vector key_handle; sts = GetKeyHandleIntoVector( session_.session_id(), session_.license().keys[key_index].key_id, session_.license().keys[key_index].key_id_length, OEMCrypto_CipherMode_CENC, key_handle); ASSERT_EQ(OEMCrypto_SUCCESS, sts); size_t signature_length = (size_t)SHA256_DIGEST_LENGTH; vector signature(SHA256_DIGEST_LENGTH); sts = GenericSign(key_handle.data(), key_handle.size(), clear_buffer_.data(), clear_buffer_.size(), algorithm, signature.data(), &signature_length); EXPECT_NE(OEMCrypto_SUCCESS, sts); EXPECT_NE(signature, expected_signature); } // This asks OEMCrypto to verify a signature with the specified key, and // expects a failure. void BadVerify(unsigned int key_index, OEMCrypto_Algorithm algorithm, size_t signature_size, bool alter_data) { OEMCryptoResult sts; vector signature; SignBuffer(key_index, clear_buffer_, &signature); if (alter_data) { signature[0] ^= 42; } if (signature.size() < signature_size) { signature.resize(signature_size); } vector key_handle; sts = GetKeyHandleIntoVector( session_.session_id(), session_.license().keys[key_index].key_id, session_.license().keys[key_index].key_id_length, OEMCrypto_CipherMode_CENC, key_handle); ASSERT_EQ(OEMCrypto_SUCCESS, sts); sts = GenericVerify(key_handle.data(), key_handle.size(), clear_buffer_.data(), clear_buffer_.size(), algorithm, signature.data(), signature_size); EXPECT_NE(OEMCrypto_SUCCESS, sts); } // This must be a multiple of encryption block size. size_t buffer_size_; vector clear_buffer_; vector encrypted_buffer_; uint8_t iv_[wvoec::KEY_IV_SIZE]; }; class OEMCryptoUsageTableTest : public OEMCryptoGenericCryptoTest { public: void SetUp() override { OEMCryptoGenericCryptoTest::SetUp(); } virtual void ShutDown() { ASSERT_NO_FATAL_FAILURE(session_.close()); ASSERT_EQ(OEMCrypto_SUCCESS, OEMCrypto_Terminate()); } virtual void Restart() { OEMCrypto_SetSandbox(kTestSandbox, sizeof(kTestSandbox)); ASSERT_EQ(OEMCrypto_SUCCESS, OEMCrypto_Initialize()); (void)OEMCrypto_SetMaxAPIVersion(kCurrentAPI); (void)OEMCrypto_EnterTestMode(); EnsureTestROT(); ASSERT_NO_FATAL_FAILURE(session_.open()); } void PrintDotsWhileSleep(int64_t total_seconds, int64_t interval_seconds) { int64_t dot_time = interval_seconds; int64_t elapsed_time = 0; const int64_t start_time = wvutil::Clock().GetCurrentTime(); do { wvutil::TestSleep::Sleep(1); elapsed_time = wvutil::Clock().GetCurrentTime() - start_time; if (elapsed_time >= dot_time) { cout << "."; cout.flush(); dot_time += interval_seconds; } } while (elapsed_time < total_seconds); cout << endl; } OEMCryptoResult LoadUsageTableHeader( const vector& encrypted_usage_header) { return OEMCrypto_LoadUsageTableHeader(encrypted_usage_header.data(), encrypted_usage_header.size()); } }; } // namespace wvoec #endif // CDM_OEMCRYPTO_USAGE_TABLE_TEST_