// Copyright 2018 Google LLC. All Rights Reserved. This file and proprietary // source code may only be used and distributed under the Widevine Master // License Agreement. // // OEMCrypto unit tests // #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "OEMCryptoCENC.h" #include "clock.h" #include "log.h" #include "oec_decrypt_fallback_chain.h" #include "oec_device_features.h" #include "oec_session_util.h" #include "oec_test_data.h" #include "oemcrypto_session_tests_helper.h" #include "oemcrypto_types.h" #include "platform.h" #include "string_conversions.h" #include "test_sleep.h" #include "wvcrc32.h" using ::testing::Bool; using ::testing::Combine; using ::testing::Range; using ::testing::tuple; using ::testing::Values; using ::testing::WithParamInterface; using namespace std; namespace std { // GTest wants PrintTo to be in the std namespace. void PrintTo(const tuple& param, ostream* os) { OEMCrypto_CENCEncryptPatternDesc pattern = ::testing::get<0>(param); OEMCryptoCipherMode mode = ::testing::get<1>(param); wvoec::OutputType output = ::testing::get<2>(param); bool decrypt_inplace = output.decrypt_inplace; OEMCryptoBufferType type = output.type; *os << ((mode == OEMCrypto_CipherMode_CTR) ? "CTR mode" : "CBC mode") << ", pattern=(encrypt:" << pattern.encrypt << ", skip:" << pattern.skip << ")"; switch (type) { case OEMCrypto_BufferType_Clear: *os << ", BufferType = Clear"; break; case OEMCrypto_BufferType_Secure: *os << ", BufferType = Secure"; break; case OEMCrypto_BufferType_Direct: *os << ", BufferType = Direct"; break; default: *os << ", type = "; break; } if (decrypt_inplace) *os << " (in place)"; } } // namespace std namespace wvoec { namespace { constexpr size_t kBufferOverrunPadding = 16; // Resource tiers: constexpr size_t KiB = 1024; constexpr size_t MiB = 1024 * 1024; // With OEMCrypto v15 and above, we have different resource requirements // depending on the resource rating reported by OEMCrypto. This function looks // up the required value for the specified resource for the target OEMCrypto // library. template T GetResourceValue(T (&resource_values)[N]) { if (global_features.resource_rating < 1) return resource_values[0]; if (global_features.resource_rating > N) return resource_values[N - 1]; return resource_values[global_features.resource_rating - 1]; } // After API 16, we require 300 entries in the usage table. Before API 16, we // required 200. size_t RequiredUsageSize() { return global_features.api_version < 16 ? 200 : 300; } // These are the maximum sizes we test. That means it is the minimum size that // OEMCrypto must support. // clang-format off const size_t kMaxSampleSize[] = { 1*MiB, 2*MiB, 4*MiB, 16*MiB}; const size_t kMaxNumberSubsamples[] = { 10, 16, 32, 64}; const size_t kMaxSubsampleSize[] = { 100*KiB, 500*KiB, 1*MiB, 4*MiB}; const size_t kMaxGenericBuffer[] = { 10*KiB, 100*KiB, 500*KiB, 1*MiB}; const size_t kMaxConcurrentSession[] = { 10, 20, 20, 30}; const size_t kMaxKeysPerSession[] = { 4, 20, 20, 30}; const size_t kMaxTotalKeys[] = { 16, 40, 80, 90}; const size_t kLargeMessageSize[] = { 8*KiB, 8*KiB, 16*KiB, 32*KiB}; // Note: Frame rate and simultaneous playback are specified by resource rating, // but are tested at the system level, so there are no unit tests for frame // rate. Similarly, number of subsamples for AV1 // const size_t kAV1NumberSubsamples[] = { 72, 144, 288, 576}; // clang-format on } // namespace class OEMCryptoClientTest : public ::testing::Test, public SessionUtil { protected: OEMCryptoClientTest() {} void SetUp() override { ::testing::Test::SetUp(); wvcdm::TestSleep::SyncFakeClock(); const ::testing::TestInfo* const test_info = ::testing::UnitTest::GetInstance()->current_test_info(); LOGD("Running test %s.%s", test_info->test_case_name(), test_info->name()); OEMCrypto_SetSandbox(kTestSandbox, sizeof(kTestSandbox)); ASSERT_EQ(OEMCrypto_SUCCESS, OEMCrypto_Initialize()); } void TearDown() override { OEMCrypto_Terminate(); ::testing::Test::TearDown(); } const uint8_t* find(const vector& message, const vector& substring) { vector::const_iterator pos = search( message.begin(), message.end(), substring.begin(), substring.end()); if (pos == message.end()) { return nullptr; } return &(*pos); } }; // // General tests. // This test is first, becuase it might give an idea why other // tests are failing when the device has the wrong keybox installed. TEST_F(OEMCryptoClientTest, VersionNumber) { const std::string log_message = "OEMCrypto unit tests for API 16.2. Tests last updated 2020-03-27"; cout << " " << log_message << "\n"; LOGI("%s", log_message.c_str()); // If any of the following fail, then it is time to update the log message // above. EXPECT_EQ(ODK_MAJOR_VERSION, 16); EXPECT_EQ(ODK_MINOR_VERSION, 2); EXPECT_EQ(kCurrentAPI, 16u); const char* level = OEMCrypto_SecurityLevel(); ASSERT_NE(nullptr, level); ASSERT_EQ('L', level[0]); cout << " OEMCrypto Security Level is " << level << endl; uint32_t version = OEMCrypto_APIVersion(); cout << " OEMCrypto API version is " << version << endl; if (OEMCrypto_SupportsUsageTable()) { cout << " OEMCrypto supports usage tables" << endl; } else { cout << " OEMCrypto does not support usage tables" << endl; } if (version >= 15) { const uint32_t tier = OEMCrypto_ResourceRatingTier(); cout << " Resource Rating Tier: " << tier << endl; const char* build_info = OEMCrypto_BuildInformation(); ASSERT_NE(nullptr, build_info); ASSERT_TRUE(strnlen(build_info, 256) <= 256) << "BuildInformation should be a short printable string."; cout << " BuildInformation: " << build_info << endl; } ASSERT_GE(version, 8u); ASSERT_LE(version, kCurrentAPI); } // The resource rating is a number from 1 to 4. The first three levels were // initially defined in API 15 and they were expanded in API 16. TEST_F(OEMCryptoClientTest, ResourceRatingAPI15) { ASSERT_GE(OEMCrypto_ResourceRatingTier(), 1u); ASSERT_LE(OEMCrypto_ResourceRatingTier(), 4u); } // OEMCrypto must declare what type of provisioning scheme it uses. TEST_F(OEMCryptoClientTest, ProvisioningDeclaredAPI12) { OEMCrypto_ProvisioningMethod provisioning_method = OEMCrypto_GetProvisioningMethod(); cout << " Provisioning method = " << ProvisioningMethodName(provisioning_method) << endl; ASSERT_NE(OEMCrypto_ProvisioningError, provisioning_method); } const char* HDCPCapabilityAsString(OEMCrypto_HDCP_Capability value) { switch (value) { case HDCP_NONE: return "No HDCP supported, no secure data path"; case HDCP_V1: return "HDCP version 1.0"; case HDCP_V2: return "HDCP version 2.0"; case HDCP_V2_1: return "HDCP version 2.1"; case HDCP_V2_2: return "HDCP version 2.2"; case HDCP_V2_3: return "HDCP version 2.3"; case HDCP_NO_DIGITAL_OUTPUT: return "No HDCP device attached/using local display with secure path"; default: return ""; } } TEST_F(OEMCryptoClientTest, CheckHDCPCapabilityAPI09) { OEMCryptoResult sts; OEMCrypto_HDCP_Capability current, maximum; sts = OEMCrypto_GetHDCPCapability(¤t, &maximum); ASSERT_EQ(OEMCrypto_SUCCESS, sts); printf(" Current HDCP Capability: 0x%02x = %s.\n", current, HDCPCapabilityAsString(current)); printf(" Maximum HDCP Capability: 0x%02x = %s.\n", maximum, HDCPCapabilityAsString(maximum)); } TEST_F(OEMCryptoClientTest, CheckSRMCapabilityV13) { // This just tests some trivial functionality of the SRM update functions. bool supported = OEMCrypto_IsSRMUpdateSupported(); printf(" Update SRM Supported: %s.\n", supported ? "true" : "false"); uint16_t version = 0; OEMCryptoResult current_result = OEMCrypto_GetCurrentSRMVersion(&version); if (current_result == OEMCrypto_SUCCESS) { printf(" Current SRM Version: %d.\n", version); EXPECT_NE(OEMCrypto_SUCCESS, OEMCrypto_GetCurrentSRMVersion(nullptr)); } else if (current_result == OEMCrypto_LOCAL_DISPLAY_ONLY) { printf(" Current SRM Status: Local Display Only.\n"); } else { EXPECT_EQ(OEMCrypto_ERROR_NOT_IMPLEMENTED, current_result); } vector bad_srm(42); GetRandBytes(bad_srm.data(), bad_srm.size()); EXPECT_NE(OEMCrypto_SUCCESS, OEMCrypto_LoadSRM(bad_srm.data(), bad_srm.size())); } TEST_F(OEMCryptoClientTest, CheckMaxNumberOfSessionsAPI10) { size_t sessions_count; ASSERT_EQ(OEMCrypto_SUCCESS, OEMCrypto_GetNumberOfOpenSessions(&sessions_count)); ASSERT_EQ(0u, sessions_count); size_t maximum; OEMCryptoResult sts = OEMCrypto_GetMaxNumberOfSessions(&maximum); ASSERT_EQ(OEMCrypto_SUCCESS, sts); printf(" Max Number of Sessions: %zu.\n", maximum); size_t required_max = GetResourceValue(kMaxConcurrentSession); ASSERT_GE(maximum, required_max); } TEST_F(OEMCryptoClientTest, CheckUsageTableSizeAPI16) { const size_t maximum = OEMCrypto_MaximumUsageTableHeaderSize(); printf(" Max Usage Table Size: %zu.\n", maximum); // A maximum of 0 means the table is constrained by dynamic memory allocation. if (maximum > 0) ASSERT_GE(maximum, RequiredUsageSize()); } // // initialization tests // TEST_F(OEMCryptoClientTest, NormalInitTermination) { // Should be able to terminate OEMCrypto, and then restart it. ASSERT_EQ(OEMCrypto_SUCCESS, OEMCrypto_Terminate()); OEMCrypto_SetSandbox(kTestSandbox, sizeof(kTestSandbox)); ASSERT_EQ(OEMCrypto_SUCCESS, OEMCrypto_Initialize()); } // // Session Tests // TEST_F(OEMCryptoClientTest, NormalSessionOpenClose) { Session s; ASSERT_NO_FATAL_FAILURE(s.open()); ASSERT_NO_FATAL_FAILURE(s.close()); } TEST_F(OEMCryptoClientTest, TwoSessionsOpenClose) { Session s1; Session s2; ASSERT_NO_FATAL_FAILURE(s1.open()); ASSERT_NO_FATAL_FAILURE(s2.open()); ASSERT_NO_FATAL_FAILURE(s1.close()); ASSERT_NO_FATAL_FAILURE(s2.close()); } // This test verifies that OEMCrypto can open approximately as many sessions as // it claims. TEST_F(OEMCryptoClientTest, MaxSessionsOpenCloseAPI10) { size_t sessions_count; ASSERT_EQ(OEMCrypto_SUCCESS, OEMCrypto_GetNumberOfOpenSessions(&sessions_count)); ASSERT_EQ(0u, sessions_count); size_t max_sessions; ASSERT_EQ(OEMCrypto_SUCCESS, OEMCrypto_GetMaxNumberOfSessions(&max_sessions)); // We expect OEMCrypto implementations support at least this many sessions. size_t required_number = GetResourceValue(kMaxConcurrentSession); ASSERT_GE(max_sessions, required_number); // We allow GetMaxNumberOfSessions to return an estimate. This tests with a // pad of 5%. Even if it's just an estimate, we still require 8 sessions. size_t max_sessions_with_pad = max(max_sessions * 19 / 20, required_number); vector sessions; // Limit the number of sessions for testing. const size_t kMaxNumberOfSessionsForTesting = 0x100u; for (size_t i = 0; i < kMaxNumberOfSessionsForTesting; i++) { OEMCrypto_SESSION session_id; OEMCryptoResult sts = OEMCrypto_OpenSession(&session_id); // GetMaxNumberOfSessions might be an estimate. We allow OEMCrypto to report // a max that is less than what is actually supported. Assume the number // returned is |max|. OpenSessions shall not fail if number of active // sessions is less than |max|; OpenSessions should fail with // OEMCrypto_ERROR_TOO_MANY_SESSIONS if too many sessions are open. if (sts != OEMCrypto_SUCCESS) { ASSERT_EQ(OEMCrypto_ERROR_TOO_MANY_SESSIONS, sts); ASSERT_GE(i, max_sessions_with_pad); break; } ASSERT_EQ(OEMCrypto_SUCCESS, OEMCrypto_GetNumberOfOpenSessions(&sessions_count)); ASSERT_EQ(i + 1, sessions_count); sessions.push_back(session_id); } for (size_t i = 0; i < sessions.size(); i++) { ASSERT_EQ(OEMCrypto_SUCCESS, OEMCrypto_CloseSession(sessions[i])); ASSERT_EQ(OEMCrypto_SUCCESS, OEMCrypto_GetNumberOfOpenSessions(&sessions_count)); ASSERT_EQ(sessions.size() - i - 1, sessions_count); } if (sessions.size() == kMaxNumberOfSessionsForTesting) { printf( " MaxSessionsOpenClose: reaches " "kMaxNumberOfSessionsForTesting(%zu). GetMaxNumberOfSessions = %zu. " "ERROR_TOO_MANY_SESSIONS not tested.", kMaxNumberOfSessionsForTesting, max_sessions); } } // Verify that GetRandom does work, and does some sanity checks on how random // the data is. Basically, we say that calling GetRandom twice should not // generate much overlap. TEST_F(OEMCryptoClientTest, GetRandomLargeBuffer) { // 32 bytes. Not very large, but that's all we really need in one call. const size_t size = 32; uint8_t data1[size]; uint8_t data2[size]; memset(data1, 0, size); memset(data2, 0, size); ASSERT_EQ(OEMCrypto_SUCCESS, OEMCrypto_GetRandom(data1, size)); ASSERT_EQ(OEMCrypto_SUCCESS, OEMCrypto_GetRandom(data2, size)); // We don't have enough data to see that the data is really random, // so we'll just do a spot check that two calls don't return the same values. int count = 0; for (size_t i = 0; i < size; i++) { if (data1[i] == data2[i]) count++; } ASSERT_LE(count, 3); // P(count > 3) = 1/256^3 = 6e-8. } TEST_F(OEMCryptoClientTest, GenerateNonce) { Session s; ASSERT_NO_FATAL_FAILURE(s.open()); s.GenerateNonce(); } // Prevent a nonce flood even if each nonce is in a different session. TEST_F(OEMCryptoClientTest, PreventNonceFlood2API16) { int error_counter = 0; const int64_t test_start = wvcdm::Clock().GetCurrentTime(); // More than 200 nonces per second should generate an error. // To allow for some slop, we actually test for more. const int flood_cutoff = 200; const int loop_count = flood_cutoff * 2; for (int i = 0; i < loop_count; i++) { Session s; ASSERT_NO_FATAL_FAILURE(s.open()); s.GenerateNonce(&error_counter); } const int64_t test_end = wvcdm::Clock().GetCurrentTime(); int valid_counter = loop_count - error_counter; // Either oemcrypto should enforce a delay, or it should return an error from // GenerateNonce -- in either case the number of valid nonces is rate // limited. We add two seconds to allow for round off error in both // test_start and test_end. EXPECT_LE(valid_counter, flood_cutoff * (test_end - test_start + 2)); error_counter = 0; // After a pause, we should be able to regenerate nonces. wvcdm::TestSleep::Sleep(2); Session s; ASSERT_NO_FATAL_FAILURE(s.open()); s.GenerateNonce(&error_counter); EXPECT_EQ(0, error_counter); } // Prevent a nonce flood even if some nonces are in a different session. This // is different from the test above because there are several session open at // the same time. We want to make sure you can't get a flood of nonces by // opening a flood of sessions. TEST_F(OEMCryptoClientTest, PreventNonceFlood3API16) { int request_counter = 0; int error_counter = 0; const int64_t test_start = wvcdm::Clock().GetCurrentTime(); // More than 200 nonces per second should generate an error. // To allow for some slop, we actually test for more. const int flood_cutoff = 200; const size_t session_count = GetResourceValue(kMaxConcurrentSession); const size_t loop_count = 2 * flood_cutoff / session_count + 1; for (size_t i = 0; i < loop_count; i++) { std::vector s(session_count); for (size_t j = 0; j < session_count; j++) { ASSERT_NO_FATAL_FAILURE(s[j].open()); request_counter++; s[j].GenerateNonce(&error_counter); } } const int64_t test_end = wvcdm::Clock().GetCurrentTime(); int valid_counter = request_counter - error_counter; // Either oemcrypto should enforce a delay, or it should return an error from // GenerateNonce -- in either case the number of valid nonces is rate // limited. We add two seconds to allow for round off error in both // test_start and test_end. EXPECT_LE(valid_counter, flood_cutoff * (test_end - test_start + 2)); error_counter = 0; // After a pause, we should be able to regenerate nonces. wvcdm::TestSleep::Sleep(2); Session s; ASSERT_NO_FATAL_FAILURE(s.open()); s.GenerateNonce(&error_counter); EXPECT_EQ(0, error_counter); } // This verifies that CopyBuffer works, even before a license has been loaded. TEST_F(OEMCryptoClientTest, ClearCopyTestAPI10) { Session s; ASSERT_NO_FATAL_FAILURE(s.open()); const int kDataSize = 256; vector input_buffer(kDataSize); GetRandBytes(input_buffer.data(), input_buffer.size()); vector output_buffer(kDataSize); OEMCrypto_DestBufferDesc dest_buffer_descriptor; dest_buffer_descriptor.type = OEMCrypto_BufferType_Clear; dest_buffer_descriptor.buffer.clear.address = output_buffer.data(); dest_buffer_descriptor.buffer.clear.address_length = output_buffer.size(); ASSERT_EQ( OEMCrypto_SUCCESS, OEMCrypto_CopyBuffer(s.session_id(), input_buffer.data(), input_buffer.size(), &dest_buffer_descriptor, OEMCrypto_FirstSubsample | OEMCrypto_LastSubsample)); ASSERT_EQ(input_buffer, output_buffer); ASSERT_EQ( OEMCrypto_ERROR_INVALID_CONTEXT, OEMCrypto_CopyBuffer(s.session_id(), nullptr, input_buffer.size(), &dest_buffer_descriptor, OEMCrypto_FirstSubsample | OEMCrypto_LastSubsample)); ASSERT_EQ(OEMCrypto_ERROR_INVALID_CONTEXT, OEMCrypto_CopyBuffer( s.session_id(), input_buffer.data(), input_buffer.size(), nullptr, OEMCrypto_FirstSubsample | OEMCrypto_LastSubsample)); dest_buffer_descriptor.buffer.clear.address = nullptr; ASSERT_EQ( OEMCrypto_ERROR_INVALID_CONTEXT, OEMCrypto_CopyBuffer(s.session_id(), input_buffer.data(), input_buffer.size(), &dest_buffer_descriptor, OEMCrypto_FirstSubsample | OEMCrypto_LastSubsample)); dest_buffer_descriptor.buffer.clear.address = output_buffer.data(); dest_buffer_descriptor.buffer.clear.address_length = output_buffer.size() - 1; ASSERT_EQ( OEMCrypto_ERROR_SHORT_BUFFER, OEMCrypto_CopyBuffer(s.session_id(), input_buffer.data(), input_buffer.size(), &dest_buffer_descriptor, OEMCrypto_FirstSubsample | OEMCrypto_LastSubsample)); } // This verifies that CopyBuffer works on the maximum required buffer size. TEST_F(OEMCryptoClientTest, ClearCopyTestLargeSubsample) { Session s; ASSERT_NO_FATAL_FAILURE(s.open()); size_t max_size = GetResourceValue(kMaxSubsampleSize); vector input_buffer(max_size); GetRandBytes(input_buffer.data(), input_buffer.size()); vector output_buffer(max_size); OEMCrypto_DestBufferDesc dest_buffer_descriptor; dest_buffer_descriptor.type = OEMCrypto_BufferType_Clear; dest_buffer_descriptor.buffer.clear.address = output_buffer.data(); dest_buffer_descriptor.buffer.clear.address_length = output_buffer.size(); ASSERT_EQ( OEMCrypto_SUCCESS, OEMCrypto_CopyBuffer(s.session_id(), input_buffer.data(), input_buffer.size(), &dest_buffer_descriptor, OEMCrypto_FirstSubsample | OEMCrypto_LastSubsample)); ASSERT_EQ(input_buffer, output_buffer); } TEST_F(OEMCryptoClientTest, CanLoadTestKeys) { ASSERT_NE(DeviceFeatures::NO_METHOD, global_features.derive_key_method) << "Session tests cannot run with out a test keybox or RSA cert."; } // Tests using this class are only used for devices with a keybox. They are not // run for devices with an OEM Certificate. class OEMCryptoKeyboxTest : public OEMCryptoClientTest { void SetUp() override { OEMCryptoClientTest::SetUp(); OEMCryptoResult sts = OEMCrypto_IsKeyboxValid(); // If the production keybox is valid, use it for these tests. Most of the // other tests will use a test keybox anyway, but it's nice to check the // device ID for the real keybox if we can. if (sts == OEMCrypto_SUCCESS) return; printf("Production keybox is NOT valid. All tests use test keybox.\n"); ASSERT_EQ( OEMCrypto_SUCCESS, OEMCrypto_LoadTestKeybox(reinterpret_cast(&kTestKeybox), sizeof(kTestKeybox))); } }; // This test is used to print the device ID to stdout. TEST_F(OEMCryptoKeyboxTest, NormalGetDeviceId) { OEMCryptoResult sts; uint8_t dev_id[128] = {0}; size_t dev_id_len = 128; sts = OEMCrypto_GetDeviceID(dev_id, &dev_id_len); cout << " NormalGetDeviceId: dev_id = " << dev_id << " len = " << dev_id_len << endl; ASSERT_EQ(OEMCrypto_SUCCESS, sts); } TEST_F(OEMCryptoKeyboxTest, GetDeviceIdShortBuffer) { OEMCryptoResult sts; uint8_t dev_id[128]; for (int i = 0; i < 128; ++i) { dev_id[i] = 0x55; } dev_id[127] = '\0'; size_t dev_id_len = 0; sts = OEMCrypto_GetDeviceID(dev_id, &dev_id_len); ASSERT_EQ(OEMCrypto_ERROR_SHORT_BUFFER, sts); // On short buffer error, function should return minimum buffer length ASSERT_GT(dev_id_len, 0u); // Should also return short buffer if passed a zero length and a null buffer. dev_id_len = 0; sts = OEMCrypto_GetDeviceID(nullptr, &dev_id_len); ASSERT_EQ(OEMCrypto_ERROR_SHORT_BUFFER, sts); // On short buffer error, function should return minimum buffer length ASSERT_GT(dev_id_len, 0u); } TEST_F(OEMCryptoKeyboxTest, NormalGetKeyData) { OEMCryptoResult sts; uint8_t key_data[256]; size_t key_data_len = sizeof(key_data); sts = OEMCrypto_GetKeyData(key_data, &key_data_len); uint32_t* data = reinterpret_cast(key_data); printf(" NormalGetKeyData: system_id = %d = 0x%04X, version=%d\n", htonl(data[1]), htonl(data[1]), htonl(data[0])); ASSERT_EQ(OEMCrypto_SUCCESS, sts); } TEST_F(OEMCryptoKeyboxTest, GetKeyDataNullPointer) { OEMCryptoResult sts; uint8_t key_data[256]; sts = OEMCrypto_GetKeyData(key_data, nullptr); ASSERT_NE(OEMCrypto_SUCCESS, sts); } // This test makes sure the installed keybox is valid. It doesn't really check // that it is a production keybox. That must be done by an integration test. TEST_F(OEMCryptoKeyboxTest, ProductionKeyboxValid) { ASSERT_EQ(OEMCrypto_SUCCESS, OEMCrypto_IsKeyboxValid()); } // This tests GenerateDerivedKeys with an 8k context. TEST_F(OEMCryptoKeyboxTest, GenerateDerivedKeysFromKeyboxLargeBuffer) { Session s; ASSERT_NO_FATAL_FAILURE(s.open()); const size_t max_size = GetResourceValue(kLargeMessageSize); vector mac_context(max_size); vector enc_context(max_size); // Stripe the data so the two vectors are not identical, and not all zeroes. for (size_t i = 0; i < max_size; i++) { mac_context[i] = i % 0x100; enc_context[i] = (3 * i) % 0x100; } ASSERT_EQ(OEMCrypto_SUCCESS, OEMCrypto_GenerateDerivedKeys( s.session_id(), mac_context.data(), mac_context.size(), enc_context.data(), enc_context.size())); } // This class is for tests that have an OEM Certificate instead of a keybox. class OEMCryptoProv30Test : public OEMCryptoClientTest {}; // This verifies that the device really does claim to have a certificate. // It should be filtered out for devices that have a keybox. TEST_F(OEMCryptoProv30Test, DeviceClaimsOEMCertificate) { ASSERT_EQ(OEMCrypto_OEMCertificate, OEMCrypto_GetProvisioningMethod()); } TEST_F(OEMCryptoProv30Test, GetDeviceId) { OEMCryptoResult sts; std::vector dev_id(128, 0); size_t dev_id_len = dev_id.size(); sts = OEMCrypto_GetDeviceID(dev_id.data(), &dev_id_len); if (sts == OEMCrypto_ERROR_SHORT_BUFFER) { ASSERT_GT(dev_id_len, 0u); dev_id.resize(dev_id_len); sts = OEMCrypto_GetDeviceID(dev_id.data(), &dev_id_len); } ASSERT_EQ(OEMCrypto_SUCCESS, sts); dev_id.resize(dev_id_len); cout << " NormalGetDeviceId: dev_id = " << dev_id.data() << " len = " << dev_id_len << endl; ASSERT_EQ(OEMCrypto_SUCCESS, sts); } // The OEM certificate must be valid. TEST_F(OEMCryptoProv30Test, CertValidAPI15) { ASSERT_EQ(OEMCrypto_SUCCESS, OEMCrypto_IsKeyboxOrOEMCertValid()); } TEST_F(OEMCryptoProv30Test, OEMCertValid) { Session s; ASSERT_NO_FATAL_FAILURE(s.open()); bool kVerify = true; ASSERT_NO_FATAL_FAILURE(s.LoadOEMCert(kVerify)); // Load and verify. } // This verifies that the OEM Certificate cannot be used for other RSA padding // schemes. Those schemes should only be used by cast receiver certificates. TEST_F(OEMCryptoProv30Test, OEMCertForbiddenPaddingScheme) { Session s; ASSERT_NO_FATAL_FAILURE(s.open()); ASSERT_NO_FATAL_FAILURE(s.LoadOEMCert()); OEMCryptoResult sts; // Sign a Message vector data(500); GetRandBytes(data.data(), data.size()); size_t signature_length = 0; // We need a size one vector to pass as a pointer. vector signature(1, 0); vector zero(1, 0); sts = OEMCrypto_GenerateRSASignature(s.session_id(), data.data(), data.size(), signature.data(), &signature_length, kSign_PKCS1_Block1); if (OEMCrypto_ERROR_SHORT_BUFFER == sts) { // The OEMCrypto could complain about buffer length first, so let's // resize and check if it's writing to the signature again. signature.resize(signature_length, 0); zero.resize(signature_length, 0); sts = OEMCrypto_GenerateRSASignature(s.session_id(), data.data(), data.size(), signature.data(), &signature_length, kSign_PKCS1_Block1); } EXPECT_NE(OEMCrypto_SUCCESS, sts) << "OEM Cert Signed with forbidden kSign_PKCS1_Block1."; ASSERT_EQ(zero, signature); // signature should not be computed. } // Calling OEMCrypto_GetOEMPublicCertificate should not change the session's // private key. TEST_F(OEMCryptoProv30Test, GetCertOnlyAPI16) { if (wrapped_rsa_key_.size() == 0) { // If we don't have a wrapped key yet, create one. // This wrapped key will be shared by all sessions in the test. ASSERT_NO_FATAL_FAILURE(CreateWrappedRSAKey()); } Session s; ASSERT_NO_FATAL_FAILURE(s.open()); // Install the DRM Cert's RSA key. ASSERT_NO_FATAL_FAILURE(s.InstallRSASessionTestKey(wrapped_rsa_key_)); ASSERT_NO_FATAL_FAILURE(s.PreparePublicKey()); // Request the OEM Cert. -- This should NOT load the OEM Private key. 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)); // Derive keys from the session key -- this should use the DRM Cert's key. It // should NOT use the OEM Private key because that key should not have been // loaded. ASSERT_NO_FATAL_FAILURE(s.GenerateDerivedKeysFromSessionKey()); // Now fill a message and try to load it. LicenseRoundTrip license_messages(&s); license_messages.set_control(0); ASSERT_NO_FATAL_FAILURE(license_messages.SignAndVerifyRequest()); ASSERT_NO_FATAL_FAILURE(license_messages.CreateDefaultResponse()); ASSERT_NO_FATAL_FAILURE(license_messages.EncryptAndSignResponse()); ASSERT_EQ(OEMCrypto_SUCCESS, license_messages.LoadResponse()); } // // AddKey Tests // // These tests will use either a test keybox or a test certificate to derive // session keys. class OEMCryptoSessionTests : public OEMCryptoClientTest { public: vector encrypted_usage_header_; protected: OEMCryptoSessionTests() {} void SetUp() override { OEMCryptoClientTest::SetUp(); EnsureTestKeys(); if (global_features.usage_table) { CreateUsageTableHeader(); } } void CreateUsageTableHeader(bool expect_success = true) { size_t header_buffer_length = 0; OEMCryptoResult sts = OEMCrypto_CreateUsageTableHeader(nullptr, &header_buffer_length); if (expect_success) { ASSERT_EQ(OEMCrypto_ERROR_SHORT_BUFFER, sts); } else { ASSERT_NE(OEMCrypto_SUCCESS, sts); if (sts != OEMCrypto_ERROR_SHORT_BUFFER) return; } encrypted_usage_header_.resize(header_buffer_length); sts = OEMCrypto_CreateUsageTableHeader(encrypted_usage_header_.data(), &header_buffer_length); if (expect_success) { ASSERT_EQ(OEMCrypto_SUCCESS, sts); } else { ASSERT_NE(OEMCrypto_SUCCESS, sts); } } }; class OEMCryptoSessionTestKeyboxTest : public OEMCryptoSessionTests {}; TEST_F(OEMCryptoSessionTestKeyboxTest, TestKeyboxIsValid) { ASSERT_EQ(OEMCrypto_SUCCESS, OEMCrypto_IsKeyboxValid()); } // This class is for testing a single license with the default API version // of 16. class OEMCryptoLicenseTestAPI16 : public OEMCryptoSessionTests { public: OEMCryptoLicenseTestAPI16() : license_api_version_(kCurrentAPI), license_messages_(&session_) {} void SetUp() override { OEMCryptoSessionTests::SetUp(); ASSERT_NO_FATAL_FAILURE(session_.open()); ASSERT_NO_FATAL_FAILURE(InstallTestRSAKey(&session_)); } void TearDown() override { ASSERT_NO_FATAL_FAILURE(session_.close()); OEMCryptoSessionTests::TearDown(); } protected: Session session_; uint32_t license_api_version_; LicenseRoundTrip license_messages_; }; // This class is used to test a license that is from a server either that is // current or one version old. class OEMCryptoLicenseTest : public OEMCryptoLicenseTestAPI16, public WithParamInterface { protected: void SetUp() override { // The only difference between this class and its parent is that we use a // different license api: license_api_version_ = GetParam(); license_messages_.set_api_version(license_api_version_); OEMCryptoLicenseTestAPI16::SetUp(); } }; // This class is used to test a license that is only for v15 license. class OEMCryptoLicenseTestAPI15 : public OEMCryptoLicenseTestAPI16 { void SetUp() override { // The only difference between this class and its parent is that we use a // different license api: license_api_version_ = 15; license_messages_.set_api_version(license_api_version_); OEMCryptoLicenseTestAPI16::SetUp(); } }; // This class is used to test each key control block verification string in the // range kc09-kc1?. This test is parameterized by the API number in the key // control lock. class OEMCryptoLicenseTestRangeAPI : public OEMCryptoLicenseTest {}; // Verify that a license may be signed. TEST_P(OEMCryptoLicenseTest, SignLicenseRequest) { ASSERT_NO_FATAL_FAILURE(session_.GenerateNonce()); ASSERT_NO_FATAL_FAILURE(license_messages_.SignAndVerifyRequest()); } TEST_P(OEMCryptoLicenseTest, SignLicenseRequestNoNonce) { ASSERT_NO_FATAL_FAILURE(license_messages_.SignAndVerifyRequest()); } // Verify that a large license request may be signed. TEST_P(OEMCryptoLicenseTest, SignLargeLicenseRequest) { const size_t max_size = GetResourceValue(kLargeMessageSize); license_messages_.set_message_size(max_size); ASSERT_NO_FATAL_FAILURE(license_messages_.SignAndVerifyRequest()); } // Verify that a license may be loaded without a nonce. TEST_P(OEMCryptoLicenseTest, LoadKeyNoNonce) { ASSERT_NO_FATAL_FAILURE(license_messages_.SignAndVerifyRequest()); license_messages_.set_control(0); ASSERT_NO_FATAL_FAILURE(license_messages_.CreateDefaultResponse()); ASSERT_NO_FATAL_FAILURE(license_messages_.EncryptAndSignResponse()); ASSERT_EQ(OEMCrypto_SUCCESS, license_messages_.LoadResponse()); } // Verify that a preloaded license may be loaded without first signing the // request. This test is important for the preloaded licenses used by ATSC and // CAS. TEST_P(OEMCryptoLicenseTest, LoadKeyWithNoRequest) { if (license_api_version_ > global_features.api_version) { // We should not attempt to preload a license with an API higher than that // of OEMCrypto. license_api_version_ = global_features.api_version; license_messages_.set_api_version(license_api_version_); } license_messages_.set_control(0); // The test code uses the core request to create the core response. license_messages_.core_request().api_major_version = ODK_MAJOR_VERSION; license_messages_.core_request().api_minor_version = ODK_MINOR_VERSION; ASSERT_NO_FATAL_FAILURE(license_messages_.CreateDefaultResponse()); ASSERT_NO_FATAL_FAILURE(license_messages_.EncryptAndSignResponse()); // Load license in a different session, which did not create the request. Session session2; ASSERT_NO_FATAL_FAILURE(session2.open()); ASSERT_NO_FATAL_FAILURE(InstallTestRSAKey(&session2)); ASSERT_NO_FATAL_FAILURE(session2.GenerateDerivedKeysFromSessionKey()); ASSERT_EQ(OEMCrypto_SUCCESS, license_messages_.LoadResponse(&session2)); } // Verify that a license may be loaded with a nonce. TEST_P(OEMCryptoLicenseTest, LoadKeyWithNonce) { ASSERT_NO_FATAL_FAILURE(session_.GenerateNonce()); ASSERT_NO_FATAL_FAILURE(license_messages_.SignAndVerifyRequest()); ASSERT_NO_FATAL_FAILURE(license_messages_.CreateDefaultResponse()); ASSERT_NO_FATAL_FAILURE(license_messages_.EncryptAndSignResponse()); ASSERT_EQ(OEMCrypto_SUCCESS, license_messages_.LoadResponse()); } // Verify that a second license may not be loaded in a session. TEST_P(OEMCryptoLicenseTest, LoadKeyNoNonceTwiceAPI16) { ASSERT_NO_FATAL_FAILURE(license_messages_.SignAndVerifyRequest()); license_messages_.set_control(0); ASSERT_NO_FATAL_FAILURE(license_messages_.CreateDefaultResponse()); ASSERT_NO_FATAL_FAILURE(license_messages_.EncryptAndSignResponse()); ASSERT_EQ(OEMCrypto_SUCCESS, license_messages_.LoadResponse()); // A second load, should NOT succeed. ASSERT_EQ(OEMCrypto_ERROR_LICENSE_RELOAD, license_messages_.LoadResponse()); } // Verify that a second license may not be loaded in a session. TEST_P(OEMCryptoLicenseTest, LoadKeyWithNonceTwiceAPI16) { ASSERT_NO_FATAL_FAILURE(session_.GenerateNonce()); ASSERT_NO_FATAL_FAILURE(license_messages_.SignAndVerifyRequest()); ASSERT_NO_FATAL_FAILURE(license_messages_.CreateDefaultResponse()); ASSERT_NO_FATAL_FAILURE(license_messages_.EncryptAndSignResponse()); ASSERT_EQ(OEMCrypto_SUCCESS, license_messages_.LoadResponse()); // A second load, should NOT succeed. ASSERT_EQ(OEMCrypto_ERROR_LICENSE_RELOAD, license_messages_.LoadResponse()); } // This verifies that entitlement keys and entitled content keys can be loaded. TEST_P(OEMCryptoLicenseTest, LoadEntitlementKeysAPI14) { ASSERT_NO_FATAL_FAILURE(session_.GenerateNonce()); license_messages_.set_license_type(OEMCrypto_EntitlementLicense); ASSERT_NO_FATAL_FAILURE(license_messages_.SignAndVerifyRequest()); ASSERT_NO_FATAL_FAILURE(license_messages_.CreateDefaultResponse()); ASSERT_NO_FATAL_FAILURE(license_messages_.EncryptAndSignResponse()); ASSERT_EQ(OEMCrypto_SUCCESS, license_messages_.LoadResponse()); EntitledMessage entitled_message_1(&license_messages_); entitled_message_1.FillKeyArray(); ASSERT_NO_FATAL_FAILURE(entitled_message_1.LoadKeys(OEMCrypto_SUCCESS)); EntitledMessage entitled_message_2(&license_messages_); entitled_message_2.FillKeyArray(); ASSERT_NO_FATAL_FAILURE(entitled_message_2.LoadKeys(OEMCrypto_SUCCESS)); } // This verifies that entitled content keys cannot be loaded if we have not yet // loaded the entitlement keys. TEST_P(OEMCryptoLicenseTest, LoadEntitlementKeysNoEntitlementKeysAPI14) { ASSERT_NO_FATAL_FAILURE(session_.GenerateNonce()); license_messages_.set_license_type(OEMCrypto_EntitlementLicense); ASSERT_NO_FATAL_FAILURE(license_messages_.SignAndVerifyRequest()); ASSERT_NO_FATAL_FAILURE(license_messages_.CreateDefaultResponse()); ASSERT_NO_FATAL_FAILURE(license_messages_.EncryptAndSignResponse()); EntitledMessage entitled_message_1(&license_messages_); entitled_message_1.FillKeyArray(); ASSERT_NO_FATAL_FAILURE( entitled_message_1.LoadKeys(OEMCrypto_ERROR_INVALID_CONTEXT)); } // This verifies that entitled content keys cannot be loaded if we have loaded // the wrong entitlement keys. TEST_P(OEMCryptoLicenseTest, LoadEntitlementKeysWrongEntitlementKeysAPI14) { ASSERT_NO_FATAL_FAILURE(session_.GenerateNonce()); license_messages_.set_license_type(OEMCrypto_EntitlementLicense); ASSERT_NO_FATAL_FAILURE(license_messages_.SignAndVerifyRequest()); ASSERT_NO_FATAL_FAILURE(license_messages_.CreateDefaultResponse()); ASSERT_NO_FATAL_FAILURE(license_messages_.EncryptAndSignResponse()); ASSERT_EQ(OEMCrypto_SUCCESS, license_messages_.LoadResponse()); EntitledMessage entitled_message_1(&license_messages_); entitled_message_1.FillKeyArray(); const std::string key_id = "no_key"; entitled_message_1.SetEntitlementKeyId(0, key_id); ASSERT_NO_FATAL_FAILURE( entitled_message_1.LoadKeys(OEMCrypto_KEY_NOT_ENTITLED)); } // This tests load license with an 8k license response. TEST_P(OEMCryptoLicenseTest, LoadKeyLargeBuffer) { ASSERT_NO_FATAL_FAILURE(session_.GenerateNonce()); ASSERT_NO_FATAL_FAILURE(license_messages_.SignAndVerifyRequest()); const size_t max_size = GetResourceValue(kLargeMessageSize); license_messages_.set_message_size(max_size); ASSERT_NO_FATAL_FAILURE(license_messages_.CreateDefaultResponse()); ASSERT_NO_FATAL_FAILURE(license_messages_.EncryptAndSignResponse()); ASSERT_EQ(OEMCrypto_SUCCESS, license_messages_.LoadResponse()); } // Verify that you can't use LoadKeys on a v16 license. TEST_F(OEMCryptoLicenseTestAPI16, UseWrongLoadAPI16) { ASSERT_NO_FATAL_FAILURE(session_.GenerateNonce()); ASSERT_NO_FATAL_FAILURE(license_messages_.SignAndVerifyRequest()); ASSERT_NO_FATAL_FAILURE(license_messages_.CreateDefaultResponse()); ASSERT_NO_FATAL_FAILURE(license_messages_.EncryptAndSignResponse()); // After the license round trip was create for v16, we now tell it to use v15 // so it call LoadKeys instead of LoadLicense. This means the license request // was made from a v16 device, and the response was created and signed by a // v16 server. So OEMCrypto should only accept it if we load it using // LoadLicense. A call to LoadKeys should fail. license_messages_.set_api_version(kCoreMessagesAPI - 1); ASSERT_NE(OEMCrypto_SUCCESS, license_messages_.LoadResponse()); } //---------------------------------------------------------------------------// //---------------------------------------------------------------------------// // Each of the following LoadKeyWithBadRange_* tests is similar. They verify // that OEMCrypto_LoadLicense checks the range of all the pointers. It should // reject a message if the pointer does not point into the message buffer. //---------------------------------------------------------------------------// //---------------------------------------------------------------------------// TEST_P(OEMCryptoLicenseTest, LoadKeyWithBadRange_enc_mac_keys) { ASSERT_NO_FATAL_FAILURE(session_.GenerateNonce()); ASSERT_NO_FATAL_FAILURE(license_messages_.SignAndVerifyRequest()); ASSERT_NO_FATAL_FAILURE(license_messages_.CreateDefaultResponse()); // See the comment in LicenseRoundTrip::LoadResponse for why we increment by // the message size. license_messages_.core_response().enc_mac_keys.offset += sizeof(license_messages_.response_data()); ASSERT_NO_FATAL_FAILURE(license_messages_.EncryptAndSignResponse()); ASSERT_NE(OEMCrypto_SUCCESS, license_messages_.LoadResponse()); } TEST_P(OEMCryptoLicenseTest, LoadKeyWithBadRange_enc_mac_keys_iv) { ASSERT_NO_FATAL_FAILURE(session_.GenerateNonce()); ASSERT_NO_FATAL_FAILURE(license_messages_.SignAndVerifyRequest()); ASSERT_NO_FATAL_FAILURE(license_messages_.CreateDefaultResponse()); // See the comment in LicenseRoundTrip::LoadResponse for why we increment by // the message size. license_messages_.core_response().enc_mac_keys_iv.offset += sizeof(license_messages_.response_data()); ASSERT_NO_FATAL_FAILURE(license_messages_.EncryptAndSignResponse()); ASSERT_NE(OEMCrypto_SUCCESS, license_messages_.LoadResponse()); } TEST_P(OEMCryptoLicenseTest, LoadKeyWithBadRange_key_id) { ASSERT_NO_FATAL_FAILURE(session_.GenerateNonce()); ASSERT_NO_FATAL_FAILURE(license_messages_.SignAndVerifyRequest()); ASSERT_NO_FATAL_FAILURE(license_messages_.CreateDefaultResponse()); // See the comment in LicenseRoundTrip::LoadResponse for why we increment by // the message size. license_messages_.core_response().key_array[0].key_id.offset += sizeof(license_messages_.response_data()); ASSERT_NO_FATAL_FAILURE(license_messages_.EncryptAndSignResponse()); ASSERT_NE(OEMCrypto_SUCCESS, license_messages_.LoadResponse()); } TEST_P(OEMCryptoLicenseTest, LoadKeyWithBadRange_key_data) { ASSERT_NO_FATAL_FAILURE(session_.GenerateNonce()); ASSERT_NO_FATAL_FAILURE(license_messages_.SignAndVerifyRequest()); ASSERT_NO_FATAL_FAILURE(license_messages_.CreateDefaultResponse()); // See the comment in LicenseRoundTrip::LoadResponse for why we increment by // the message size. license_messages_.core_response().key_array[1].key_data.offset += sizeof(license_messages_.response_data()); ASSERT_NO_FATAL_FAILURE(license_messages_.EncryptAndSignResponse()); ASSERT_NE(OEMCrypto_SUCCESS, license_messages_.LoadResponse()); } TEST_P(OEMCryptoLicenseTest, LoadKeyWithBadRange_key_data_iv) { ASSERT_NO_FATAL_FAILURE(session_.GenerateNonce()); ASSERT_NO_FATAL_FAILURE(license_messages_.SignAndVerifyRequest()); ASSERT_NO_FATAL_FAILURE(license_messages_.CreateDefaultResponse()); // See the comment in LicenseRoundTrip::LoadResponse for why we increment by // the message size. license_messages_.core_response().key_array[1].key_data_iv.offset += sizeof(license_messages_.response_data()); ASSERT_NO_FATAL_FAILURE(license_messages_.EncryptAndSignResponse()); ASSERT_NE(OEMCrypto_SUCCESS, license_messages_.LoadResponse()); } TEST_P(OEMCryptoLicenseTest, LoadKeyWithBadRange_key_control) { ASSERT_NO_FATAL_FAILURE(session_.GenerateNonce()); ASSERT_NO_FATAL_FAILURE(license_messages_.SignAndVerifyRequest()); ASSERT_NO_FATAL_FAILURE(license_messages_.CreateDefaultResponse()); // See the comment in LicenseRoundTrip::LoadResponse for why we increment by // the message size. license_messages_.core_response().key_array[2].key_control.offset += sizeof(license_messages_.response_data()); ASSERT_NO_FATAL_FAILURE(license_messages_.EncryptAndSignResponse()); ASSERT_NE(OEMCrypto_SUCCESS, license_messages_.LoadResponse()); } TEST_P(OEMCryptoLicenseTest, LoadKeyWithBadRange_key_control_iv) { ASSERT_NO_FATAL_FAILURE(session_.GenerateNonce()); ASSERT_NO_FATAL_FAILURE(license_messages_.SignAndVerifyRequest()); ASSERT_NO_FATAL_FAILURE(license_messages_.CreateDefaultResponse()); // See the comment in LicenseRoundTrip::LoadResponse for why we increment by // the message size. license_messages_.core_response().key_array[2].key_control_iv.offset += sizeof(license_messages_.response_data()); ASSERT_NO_FATAL_FAILURE(license_messages_.EncryptAndSignResponse()); ASSERT_NE(OEMCrypto_SUCCESS, license_messages_.LoadResponse()); } TEST_P(OEMCryptoLicenseTest, LoadKeyWithBadRange_pst) { license_messages_.set_control(wvoec::kControlNonceOrEntry); license_messages_.set_pst("my_pst"); ASSERT_NO_FATAL_FAILURE(session_.GenerateNonce()); ASSERT_NO_FATAL_FAILURE(license_messages_.SignAndVerifyRequest()); ASSERT_NO_FATAL_FAILURE(license_messages_.CreateDefaultResponse()); // See the comment in LicenseRoundTrip::LoadResponse for why we increment by // the message size. license_messages_.core_response().pst.offset += sizeof(license_messages_.response_data()); ASSERT_NO_FATAL_FAILURE(license_messages_.EncryptAndSignResponse()); // If we have a pst, then we need a usage entry. ASSERT_NO_FATAL_FAILURE(session_.CreateNewUsageEntry()); ASSERT_NE(OEMCrypto_SUCCESS, license_messages_.LoadResponse()); } //---------------------------------------------------------------------------// //---------------------------------------------------------------------------// // The IV should not be identical to the data right before the encrypted mac // keys. This requirement was added in 15.2, so it frequently fails on // production devices. // This test is being restricted to v16 devices on rvc-dev branch because we // only required v15.1 on Android for Q. TEST_F(OEMCryptoLicenseTestAPI15, LoadKeyWithSuspiciousIVAPI16) { ASSERT_NO_FATAL_FAILURE(session_.GenerateNonce()); ASSERT_NO_FATAL_FAILURE(license_messages_.SignAndVerifyRequest()); ASSERT_NO_FATAL_FAILURE(license_messages_.CreateDefaultResponse()); // This is suspicious: the data right before the mac keys is identical to the // iv. memcpy(license_messages_.response_data().padding, license_messages_.response_data().mac_key_iv, sizeof(license_messages_.response_data().padding)); ASSERT_NO_FATAL_FAILURE(license_messages_.EncryptAndSignResponse()); ASSERT_NE(OEMCrypto_SUCCESS, license_messages_.LoadResponse()); } // Test that LoadKeys fails when a key is loaded with no key control block. TEST_P(OEMCryptoLicenseTest, LoadKeyWithNullKeyControl) { ASSERT_NO_FATAL_FAILURE(session_.GenerateNonce()); ASSERT_NO_FATAL_FAILURE(license_messages_.SignAndVerifyRequest()); ASSERT_NO_FATAL_FAILURE(license_messages_.CreateDefaultResponse()); license_messages_.core_response().key_array[2].key_control.offset = 0; license_messages_.core_response().key_array[2].key_control.length = 0; ASSERT_NO_FATAL_FAILURE(license_messages_.EncryptAndSignResponse()); ASSERT_NE(OEMCrypto_SUCCESS, license_messages_.LoadResponse()); } // Test that LoadKeys fails when the key control block encryption has a null IV. TEST_P(OEMCryptoLicenseTest, LoadKeyWithNullKeyControlIv) { ASSERT_NO_FATAL_FAILURE(session_.GenerateNonce()); ASSERT_NO_FATAL_FAILURE(license_messages_.SignAndVerifyRequest()); ASSERT_NO_FATAL_FAILURE(license_messages_.CreateDefaultResponse()); license_messages_.core_response().key_array[2].key_control_iv.offset = 0; license_messages_.core_response().key_array[2].key_control_iv.length = 0; ASSERT_NO_FATAL_FAILURE(license_messages_.EncryptAndSignResponse()); ASSERT_NE(OEMCrypto_SUCCESS, license_messages_.LoadResponse()); } // Verify that LoadKeys fails when a key's nonce is wrong. TEST_P(OEMCryptoLicenseTest, LoadKeyWithBadNonce) { ASSERT_NO_FATAL_FAILURE(session_.GenerateNonce()); ASSERT_NO_FATAL_FAILURE(license_messages_.SignAndVerifyRequest()); ASSERT_NO_FATAL_FAILURE(license_messages_.CreateDefaultResponse()); for (unsigned int i = 0; i < license_messages_.num_keys(); i++) license_messages_.response_data().keys[i].control.nonce ^= 42; ASSERT_NO_FATAL_FAILURE(license_messages_.EncryptAndSignResponse()); ASSERT_EQ(OEMCrypto_ERROR_INVALID_NONCE, license_messages_.LoadResponse()); } // Verify that LoadKeys fails when the core message's nonce is wrong. TEST_F(OEMCryptoLicenseTestAPI16, LoadKeyWithBadNonce2) { ASSERT_NO_FATAL_FAILURE(session_.GenerateNonce()); ASSERT_NO_FATAL_FAILURE(license_messages_.SignAndVerifyRequest()); ASSERT_NO_FATAL_FAILURE(license_messages_.CreateDefaultResponse()); license_messages_.core_request().nonce ^= 42; ASSERT_NO_FATAL_FAILURE(license_messages_.EncryptAndSignResponse()); ASSERT_EQ(OEMCrypto_ERROR_INVALID_NONCE, license_messages_.LoadResponse()); } // Verify that LoadKeys fails when the core message's session is wrong. TEST_F(OEMCryptoLicenseTestAPI16, LoadKeyWithBadNonce3) { ASSERT_NO_FATAL_FAILURE(session_.GenerateNonce()); ASSERT_NO_FATAL_FAILURE(license_messages_.SignAndVerifyRequest()); ASSERT_NO_FATAL_FAILURE(license_messages_.CreateDefaultResponse()); license_messages_.core_request().session_id++; ASSERT_NO_FATAL_FAILURE(license_messages_.EncryptAndSignResponse()); ASSERT_EQ(OEMCrypto_ERROR_INVALID_NONCE, license_messages_.LoadResponse()); } // Verify that LoadKeys fails when an attempt is made to use a nonce twice. TEST_P(OEMCryptoLicenseTest, LoadKeyWithRepeatNonce) { ASSERT_NO_FATAL_FAILURE(session_.GenerateNonce()); const uint32_t nonce = session_.nonce(); ASSERT_NO_FATAL_FAILURE(license_messages_.SignAndVerifyRequest()); ASSERT_NO_FATAL_FAILURE(license_messages_.CreateDefaultResponse()); ASSERT_NO_FATAL_FAILURE(license_messages_.EncryptAndSignResponse()); // This is the first attempt. It should succeed. ASSERT_EQ(OEMCrypto_SUCCESS, license_messages_.LoadResponse()); // Now, open a new session and try to load a license with the same nonce. session_.close(); ASSERT_NO_FATAL_FAILURE(session_.open()); ASSERT_NO_FATAL_FAILURE(InstallTestRSAKey(&session_)); license_messages_.skip_nonce_check(); ASSERT_NO_FATAL_FAILURE(license_messages_.SignAndVerifyRequest()); ASSERT_NO_FATAL_FAILURE(license_messages_.CreateDefaultResponse()); // Repeat the nonce. license_messages_.core_request().nonce = nonce; for (unsigned int i = 0; i < license_messages_.num_keys(); i++) license_messages_.response_data().keys[i].control.nonce = htonl(nonce); ASSERT_NO_FATAL_FAILURE(license_messages_.EncryptAndSignResponse()); ASSERT_EQ(OEMCrypto_ERROR_INVALID_NONCE, license_messages_.LoadResponse()); } // This tests that a nonce cannot be used in new session. This is similar to // the previous test, but does not use the nonce in the first session. The nonce // should be tied to a session, so generating a nonce in the first session and // then using it in the second session should fail. TEST_P(OEMCryptoLicenseTest, LoadKeyNonceReopenSession) { ASSERT_NO_FATAL_FAILURE(session_.GenerateNonce()); uint32_t nonce = session_.nonce(); // Do not use the nonce now. Close session and use it after re-opening. ASSERT_NO_FATAL_FAILURE(session_.close()); // Actually, this isn't the same session. OEMCrypto opens a new session, but // we are guarding against the possiblity that it re-uses the session data // and might not clear out the nonce correctly. ASSERT_NO_FATAL_FAILURE(session_.open()); ASSERT_NO_FATAL_FAILURE(InstallTestRSAKey(&session_)); license_messages_.skip_nonce_check(); ASSERT_NO_FATAL_FAILURE(license_messages_.SignAndVerifyRequest()); ASSERT_NO_FATAL_FAILURE(license_messages_.CreateDefaultResponse()); license_messages_.core_request().nonce = nonce; for (unsigned int i = 0; i < license_messages_.num_keys(); i++) license_messages_.response_data().keys[i].control.nonce = htonl(nonce); ASSERT_NO_FATAL_FAILURE(license_messages_.EncryptAndSignResponse()); ASSERT_EQ(OEMCrypto_ERROR_INVALID_NONCE, license_messages_.LoadResponse()); } // This tests that a nonce cannot be used in wrong session. This is similar to // the previous test, except we do not close session 1 before we open session 2. TEST_P(OEMCryptoLicenseTest, LoadKeyNonceWrongSession) { // First, open a session and generate a nonce. We don't use the nonce in this // session. Session s2; ASSERT_NO_FATAL_FAILURE(s2.open()); ASSERT_NO_FATAL_FAILURE(InstallTestRSAKey(&s2)); ASSERT_NO_FATAL_FAILURE(s2.GenerateNonce()); uint32_t nonce = s2.nonce(); // Do not use the nonce. Also, leave the session open. We want to make sure // that session_ and s2 do NOT share a nonce. This is different from // the LoadKeyNonceReopenSession in that we do not close s1. ASSERT_NO_FATAL_FAILURE(license_messages_.SignAndVerifyRequest()); ASSERT_NO_FATAL_FAILURE(license_messages_.CreateDefaultResponse()); license_messages_.core_request().nonce = nonce; for (unsigned int i = 0; i < license_messages_.num_keys(); i++) license_messages_.response_data().keys[i].control.nonce = htonl(nonce); ASSERT_NO_FATAL_FAILURE(license_messages_.EncryptAndSignResponse()); ASSERT_EQ(OEMCrypto_ERROR_INVALID_NONCE, license_messages_.LoadResponse()); } // LoadKeys should fail if the key control block as a bad verification string. TEST_P(OEMCryptoLicenseTest, LoadKeyWithBadVerification) { ASSERT_NO_FATAL_FAILURE(session_.GenerateNonce()); ASSERT_NO_FATAL_FAILURE(license_messages_.SignAndVerifyRequest()); ASSERT_NO_FATAL_FAILURE(license_messages_.CreateDefaultResponse()); license_messages_.response_data().keys[1].control.verification[2] = 'Z'; ASSERT_NO_FATAL_FAILURE(license_messages_.EncryptAndSignResponse()); ASSERT_NE(OEMCrypto_SUCCESS, license_messages_.LoadResponse()); } // This test verifies that LoadKeys still works when the message is not aligned // in memory on a word (2 or 4 byte) boundary. TEST_P(OEMCryptoLicenseTest, LoadKeyUnalignedMessageAPI16) { ASSERT_NO_FATAL_FAILURE(session_.GenerateNonce()); ASSERT_NO_FATAL_FAILURE(license_messages_.SignAndVerifyRequest()); ASSERT_NO_FATAL_FAILURE(license_messages_.CreateDefaultResponse()); ASSERT_NO_FATAL_FAILURE(license_messages_.EncryptAndSignResponse()); std::vector buffer(1, '0'); // A string of 1 byte long. size_t offset = buffer.size(); ASSERT_EQ(1u, offset); // We assume that vectors are allocated on as a small chunk of data that is // aligned on a word boundary. I.e. we assume buffer is word aligned. Next, // we append the message to buffer after the single padding byte. buffer.insert(buffer.end(), license_messages_.encrypted_response_buffer().begin(), license_messages_.encrypted_response_buffer().end()); // Thus, buffer[offset] is NOT word aligned. const uint8_t* unaligned_message = &buffer[offset]; if (license_api_version_ < kCoreMessagesAPI) { ASSERT_EQ(OEMCrypto_SUCCESS, OEMCrypto_LoadKeys( session_.session_id(), unaligned_message, license_messages_.encrypted_response_buffer().size(), license_messages_.response_signature().data(), license_messages_.response_signature().size(), license_messages_.core_response().enc_mac_keys_iv, license_messages_.core_response().enc_mac_keys, license_messages_.core_response().key_array_length, license_messages_.core_response().key_array, license_messages_.core_response().pst, license_messages_.core_response().srm_restriction_data, static_cast( license_messages_.core_response().license_type))); } else { ASSERT_EQ(OEMCrypto_SUCCESS, OEMCrypto_LoadLicense( session_.session_id(), unaligned_message, license_messages_.encrypted_response_buffer().size(), license_messages_.serialized_core_message().size(), license_messages_.response_signature().data(), license_messages_.response_signature().size())); } } // Verifies that a session can't reload a license without being closed and // reopened. TEST_P(OEMCryptoLicenseTest, LoadLicenseAgainFailureAPI16) { ASSERT_NO_FATAL_FAILURE(session_.GenerateNonce()); ASSERT_NO_FATAL_FAILURE(license_messages_.SignAndVerifyRequest()); ASSERT_NO_FATAL_FAILURE(license_messages_.CreateDefaultResponse()); ASSERT_NO_FATAL_FAILURE(license_messages_.EncryptAndSignResponse()); ASSERT_EQ(OEMCrypto_SUCCESS, license_messages_.LoadResponse()); ASSERT_EQ(OEMCrypto_ERROR_LICENSE_RELOAD, license_messages_.LoadResponse()); } TEST_P(OEMCryptoLicenseTestRangeAPI, LoadKeys) { ASSERT_NO_FATAL_FAILURE(session_.GenerateNonce()); ASSERT_NO_FATAL_FAILURE(license_messages_.SignAndVerifyRequest()); // Re-set the API version. The function VerifyRequestSignature sets the api to // be a sane value. But in this test, we want to verify an unsupported version // is rejected. license_messages_.set_api_version(license_api_version_); ASSERT_NO_FATAL_FAILURE(license_messages_.CreateDefaultResponse()); ASSERT_NO_FATAL_FAILURE(license_messages_.EncryptAndSignResponse()); // If this is a future API, then LoadKeys should fail. if (global_features.api_version < license_api_version_) { ASSERT_NE(OEMCrypto_SUCCESS, license_messages_.LoadResponse()) << "Load License succeeded for future api kc" << license_api_version_; } else { // Otherwise, LoadKeys should succeed. ASSERT_EQ(OEMCrypto_SUCCESS, license_messages_.LoadResponse()) << "Load License failed for key control block kc" << license_api_version_; } } // Range of API versions to test. This should start several versions old, and // go to the current API + 2. We use +2 because we want to test at least 1 // future API, and the ::testing::Range is not inclusive. INSTANTIATE_TEST_CASE_P(TestAll, OEMCryptoLicenseTestRangeAPI, Range(10, kCurrentAPI + 2)); TEST_P(OEMCryptoLicenseTest, LoadKeysBadSignatureAPI16) { ASSERT_NO_FATAL_FAILURE(session_.GenerateNonce()); ASSERT_NO_FATAL_FAILURE(license_messages_.SignAndVerifyRequest()); ASSERT_NO_FATAL_FAILURE(license_messages_.CreateDefaultResponse()); ASSERT_NO_FATAL_FAILURE(license_messages_.EncryptAndSignResponse()); license_messages_.response_signature()[0] ^= 42; ASSERT_EQ(OEMCrypto_ERROR_SIGNATURE_FAILURE, license_messages_.LoadResponse()); } TEST_F(OEMCryptoLicenseTestAPI16, BadCoreHashAPI16) { ASSERT_NO_FATAL_FAILURE(session_.GenerateNonce()); ASSERT_NO_FATAL_FAILURE(license_messages_.SignAndVerifyRequest()); ASSERT_NO_FATAL_FAILURE(license_messages_.CreateDefaultResponse()); license_messages_.BreakRequestHash(); ASSERT_NO_FATAL_FAILURE(license_messages_.EncryptAndSignResponse()); ASSERT_NE(OEMCrypto_SUCCESS, license_messages_.LoadResponse()); } // LoadKeys should fail if we try to load keys with no keys. TEST_P(OEMCryptoLicenseTest, LoadKeyNoKeys) { ASSERT_NO_FATAL_FAILURE(license_messages_.SignAndVerifyRequest()); license_messages_.set_control(0); license_messages_.set_num_keys(0); ASSERT_NO_FATAL_FAILURE(license_messages_.CreateDefaultResponse()); ASSERT_NO_FATAL_FAILURE(license_messages_.EncryptAndSignResponse()); ASSERT_NE(OEMCrypto_SUCCESS, license_messages_.LoadResponse()); } // Like the previous test, except we ask for a nonce first. TEST_P(OEMCryptoLicenseTest, LoadKeyNoKeyWithNonce) { ASSERT_NO_FATAL_FAILURE(session_.GenerateNonce()); ASSERT_NO_FATAL_FAILURE(license_messages_.SignAndVerifyRequest()); license_messages_.set_num_keys(0); ASSERT_NO_FATAL_FAILURE(license_messages_.CreateDefaultResponse()); ASSERT_NO_FATAL_FAILURE(license_messages_.EncryptAndSignResponse()); ASSERT_NE(OEMCrypto_SUCCESS, license_messages_.LoadResponse()); } // SelectKey should fail if we attempt to select a key that has not been loaded. // Also, the error should be NO_CONTENT_KEY. // This test should pass for v15 devices, except that the exact error code was // not specified until v16. TEST_P(OEMCryptoLicenseTest, SelectKeyNotThereAPI16) { ASSERT_NO_FATAL_FAILURE(session_.GenerateNonce()); ASSERT_NO_FATAL_FAILURE(license_messages_.SignAndVerifyRequest()); ASSERT_NO_FATAL_FAILURE(license_messages_.CreateDefaultResponse()); ASSERT_NO_FATAL_FAILURE(license_messages_.EncryptAndSignResponse()); ASSERT_EQ(OEMCrypto_SUCCESS, license_messages_.LoadResponse()); const char* key_id = "no_key"; OEMCryptoResult sts = OEMCrypto_SelectKey( session_.session_id(), reinterpret_cast(key_id), strlen(key_id), OEMCrypto_CipherMode_CTR); if (sts != OEMCrypto_SUCCESS) { EXPECT_EQ(OEMCrypto_ERROR_NO_CONTENT_KEY, sts); } else { // Delayed error code. If select key was a success, then we should // eventually see the error when we decrypt. 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)); // Generate test data for (size_t i = 0; i < in_buffer.size(); i++) in_buffer[i] = i % 256; // Create the pattern description (always 0,0 for CTR) OEMCrypto_CENCEncryptPatternDesc pattern = {0, 0}; // Try to decrypt the data sts = OEMCrypto_DecryptCENC(session_.session_id(), &sample_description, 1, &pattern); EXPECT_EQ(sts, OEMCrypto_ERROR_NO_CONTENT_KEY); } } // 'cens' mode is no longer supported in v16 TEST_P(OEMCryptoLicenseTest, RejectCensAPI16) { ASSERT_NO_FATAL_FAILURE(session_.GenerateNonce()); ASSERT_NO_FATAL_FAILURE(license_messages_.SignAndVerifyRequest()); ASSERT_NO_FATAL_FAILURE(license_messages_.CreateDefaultResponse()); ASSERT_NO_FATAL_FAILURE(license_messages_.EncryptAndSignResponse()); ASSERT_EQ(OEMCrypto_SUCCESS, license_messages_.LoadResponse()); OEMCryptoResult sts; sts = OEMCrypto_SelectKey( session_.session_id(), session_.license().keys[0].key_id, session_.license().keys[0].key_id_length, OEMCrypto_CipherMode_CTR); ASSERT_EQ(OEMCrypto_SUCCESS, sts); 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)); // Create a non-zero pattern to indicate this is 'cens' OEMCrypto_CENCEncryptPatternDesc pattern = {1, 9}; // Try to decrypt the data sts = OEMCrypto_DecryptCENC(session_.session_id(), &sample_description, 1, &pattern); EXPECT_EQ(OEMCrypto_ERROR_INVALID_CONTEXT, sts); } // 'cbc1' mode is no longer supported in v16 TEST_P(OEMCryptoLicenseTest, RejectCbc1API16) { ASSERT_NO_FATAL_FAILURE(session_.GenerateNonce()); ASSERT_NO_FATAL_FAILURE(license_messages_.SignAndVerifyRequest()); ASSERT_NO_FATAL_FAILURE(license_messages_.CreateDefaultResponse()); ASSERT_NO_FATAL_FAILURE(license_messages_.EncryptAndSignResponse()); ASSERT_EQ(OEMCrypto_SUCCESS, license_messages_.LoadResponse()); OEMCryptoResult sts; sts = OEMCrypto_SelectKey( session_.session_id(), session_.license().keys[0].key_id, session_.license().keys[0].key_id_length, OEMCrypto_CipherMode_CBC); ASSERT_EQ(OEMCrypto_SUCCESS, sts); 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)); // Create a zero pattern to indicate this is 'cbc1' OEMCrypto_CENCEncryptPatternDesc pattern = {0, 0}; // Try to decrypt the data sts = OEMCrypto_DecryptCENC(session_.session_id(), &sample_description, 1, &pattern); EXPECT_EQ(OEMCrypto_ERROR_INVALID_CONTEXT, sts); } TEST_P(OEMCryptoLicenseTest, RejectCbcsWithBlockOffset) { ASSERT_NO_FATAL_FAILURE(session_.GenerateNonce()); ASSERT_NO_FATAL_FAILURE(license_messages_.SignAndVerifyRequest()); ASSERT_NO_FATAL_FAILURE(license_messages_.CreateDefaultResponse()); ASSERT_NO_FATAL_FAILURE(license_messages_.EncryptAndSignResponse()); ASSERT_EQ(OEMCrypto_SUCCESS, license_messages_.LoadResponse()); OEMCryptoResult sts; sts = OEMCrypto_SelectKey( session_.session_id(), session_.license().keys[0].key_id, session_.license().keys[0].key_id_length, OEMCrypto_CipherMode_CBC); ASSERT_EQ(OEMCrypto_SUCCESS, sts); 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)); subsample_description.block_offset = 5; // Any value 1-15 will do. // Create a non-zero pattern to indicate this is 'cbcs'. OEMCrypto_CENCEncryptPatternDesc pattern = {1, 9}; // Try to decrypt the data sts = OEMCrypto_DecryptCENC(session_.session_id(), &sample_description, 1, &pattern); EXPECT_EQ(OEMCrypto_ERROR_INVALID_CONTEXT, sts); } // After loading keys, we should be able to query the key control block. If we // attempt to query a key that has not been loaded, the error should be // NO_CONTENT_KEY. TEST_P(OEMCryptoLicenseTest, QueryKeyControl) { ASSERT_NO_FATAL_FAILURE(session_.GenerateNonce()); ASSERT_NO_FATAL_FAILURE(license_messages_.SignAndVerifyRequest()); ASSERT_NO_FATAL_FAILURE(license_messages_.CreateDefaultResponse()); ASSERT_NO_FATAL_FAILURE(license_messages_.EncryptAndSignResponse()); ASSERT_EQ(OEMCrypto_SUCCESS, license_messages_.LoadResponse()); // Note: successful cases are tested in VerifyTestKeys. KeyControlBlock block; size_t size = sizeof(block) - 1; OEMCryptoResult sts = OEMCrypto_QueryKeyControl( session_.session_id(), license_messages_.response_data().keys[0].key_id, license_messages_.response_data().keys[0].key_id_length, reinterpret_cast(&block), &size); if (sts == OEMCrypto_ERROR_NOT_IMPLEMENTED) { return; } ASSERT_EQ(OEMCrypto_ERROR_SHORT_BUFFER, sts); const char* key_id = "no_key"; size = sizeof(block); ASSERT_EQ(OEMCrypto_ERROR_NO_CONTENT_KEY, OEMCrypto_QueryKeyControl( session_.session_id(), reinterpret_cast(key_id), strlen(key_id), reinterpret_cast(&block), &size)); } // If the device says it supports anti-rollback in the hardware, then it should // accept a key control block with the anti-rollback hardware bit set. // Otherwise, it should reject that key control block. TEST_P(OEMCryptoLicenseTest, AntiRollbackHardwareRequired) { ASSERT_NO_FATAL_FAILURE(session_.GenerateNonce()); ASSERT_NO_FATAL_FAILURE(license_messages_.SignAndVerifyRequest()); license_messages_.set_control(wvoec::kControlRequireAntiRollbackHardware); ASSERT_NO_FATAL_FAILURE(license_messages_.CreateDefaultResponse()); ASSERT_NO_FATAL_FAILURE(license_messages_.EncryptAndSignResponse()); OEMCryptoResult sts = license_messages_.LoadResponse(); if (OEMCrypto_IsAntiRollbackHwPresent()) { ASSERT_EQ(OEMCrypto_SUCCESS, sts); } else { ASSERT_EQ(OEMCrypto_ERROR_UNKNOWN_FAILURE, sts); } } // This test verifies that OEMCrypto can load the number of keys required for // the reported resource level. TEST_P(OEMCryptoLicenseTest, MinimumKeys) { const size_t num_keys = GetResourceValue(kMaxKeysPerSession); ASSERT_LE(num_keys, kMaxNumKeys) << "Test constants need updating."; license_messages_.set_num_keys(num_keys); ASSERT_NO_FATAL_FAILURE(session_.GenerateNonce()); ASSERT_NO_FATAL_FAILURE(license_messages_.SignAndVerifyRequest()); ASSERT_NO_FATAL_FAILURE(license_messages_.CreateDefaultResponse()); ASSERT_NO_FATAL_FAILURE(license_messages_.EncryptAndSignResponse()); ASSERT_EQ(OEMCrypto_SUCCESS, license_messages_.LoadResponse()); constexpr bool kSelectKeyFirst = true; for (size_t key_index = 0; key_index < num_keys; key_index++) { ASSERT_NO_FATAL_FAILURE( session_.TestDecryptCTR(kSelectKeyFirst, OEMCrypto_SUCCESS, key_index)); } } // This test verifies that OEMCrypto can load the total number of keys required // for the reported resource level. void TestMaxKeys(SessionUtil* util, size_t num_keys_per_session) { const size_t max_total_keys = GetResourceValue(kMaxTotalKeys); ASSERT_LE(num_keys_per_session, kMaxNumKeys) << "Update test constants."; std::vector> sessions; std::vector> licenses; size_t total_keys = 0; for (size_t i = 0; total_keys < max_total_keys; i++) { sessions.push_back(std::unique_ptr(new Session())); licenses.push_back(std::unique_ptr( new LicenseRoundTrip(sessions[i].get()))); const size_t num_keys = std::min(max_total_keys - total_keys, num_keys_per_session); licenses[i]->set_num_keys(num_keys); total_keys += num_keys; ASSERT_NO_FATAL_FAILURE(sessions[i]->open()); ASSERT_NO_FATAL_FAILURE(util->InstallTestRSAKey(sessions[i].get())); ASSERT_NO_FATAL_FAILURE(sessions[i]->GenerateNonce()); ASSERT_NO_FATAL_FAILURE(licenses[i]->SignAndVerifyRequest()); } for (size_t i = 0; i < licenses.size(); i++) { ASSERT_NO_FATAL_FAILURE(licenses[i]->CreateDefaultResponse()); ASSERT_NO_FATAL_FAILURE(licenses[i]->EncryptAndSignResponse()); ASSERT_EQ(OEMCrypto_SUCCESS, licenses[i]->LoadResponse()); } constexpr bool kSelectKeyFirst = true; for (size_t i = 0; i < licenses.size(); i++) { for (size_t key_index = 0; key_index < licenses[i]->num_keys(); key_index++) { ASSERT_NO_FATAL_FAILURE(sessions[i]->TestDecryptCTR( kSelectKeyFirst, OEMCrypto_SUCCESS, key_index)); } } // Second call to decrypt for each session. for (size_t i = 0; i < licenses.size(); i++) { for (size_t key_index = 0; key_index < licenses[i]->num_keys(); key_index++) { ASSERT_NO_FATAL_FAILURE(sessions[i]->TestDecryptCTR( kSelectKeyFirst, OEMCrypto_SUCCESS, key_index)); } } } // This test verifies that OEMCrypto can load the total number of keys required // for the reported resource level. This maximizes keys per session. TEST_P(OEMCryptoLicenseTest, MaxTotalKeysPerSession) { const size_t max_num_keys = GetResourceValue(kMaxKeysPerSession); TestMaxKeys(this, max_num_keys); } // This test verifies that OEMCrypto can load the total number of keys required // for the reported resource level. This maximizes number of sessions. TEST_P(OEMCryptoLicenseTest, MaxTotalKeysManySessions) { const size_t max_total_keys = GetResourceValue(kMaxTotalKeys); const size_t max_sessions = GetResourceValue(kMaxConcurrentSession); const size_t max_num_keys = max_total_keys / max_sessions + 1; TestMaxKeys(this, max_num_keys); } // This test verifies that the minimum patch level can be required. The device // should accept a key control block with the current patch level, and it should // reject any key control blocks with a future patch level. TEST_F(OEMCryptoSessionTests, CheckMinimumPatchLevel) { uint8_t patch_level = OEMCrypto_Security_Patch_Level(); printf(" Current Patch Level: %u.\n", patch_level); { Session s; ASSERT_NO_FATAL_FAILURE(s.open()); ASSERT_NO_FATAL_FAILURE(InstallTestRSAKey(&s)); LicenseRoundTrip license_messages(&s); license_messages.set_control(patch_level << wvoec::kControlSecurityPatchLevelShift); ASSERT_NO_FATAL_FAILURE(license_messages.SignAndVerifyRequest()); ASSERT_NO_FATAL_FAILURE(license_messages.CreateDefaultResponse()); ASSERT_NO_FATAL_FAILURE(license_messages.EncryptAndSignResponse()); EXPECT_EQ(global_features.api_version, license_messages.api_version()); ASSERT_EQ(OEMCrypto_SUCCESS, license_messages.LoadResponse()); } // Reject any future patch levels. if (patch_level < 0x3F) { Session s; ASSERT_NO_FATAL_FAILURE(s.open()); ASSERT_NO_FATAL_FAILURE(InstallTestRSAKey(&s)); LicenseRoundTrip license_messages(&s); license_messages.set_control((patch_level + 1) << wvoec::kControlSecurityPatchLevelShift); ASSERT_NO_FATAL_FAILURE(license_messages.SignAndVerifyRequest()); ASSERT_NO_FATAL_FAILURE(license_messages.CreateDefaultResponse()); ASSERT_NO_FATAL_FAILURE(license_messages.EncryptAndSignResponse()); ASSERT_EQ(OEMCrypto_ERROR_UNKNOWN_FAILURE, license_messages.LoadResponse()); } // Accept an old patch level. if (patch_level > 0) { Session s; ASSERT_NO_FATAL_FAILURE(s.open()); ASSERT_NO_FATAL_FAILURE(InstallTestRSAKey(&s)); LicenseRoundTrip license_messages(&s); license_messages.set_control((patch_level - 1) << wvoec::kControlSecurityPatchLevelShift); ASSERT_NO_FATAL_FAILURE(license_messages.SignAndVerifyRequest()); ASSERT_NO_FATAL_FAILURE(license_messages.CreateDefaultResponse()); ASSERT_NO_FATAL_FAILURE(license_messages.EncryptAndSignResponse()); ASSERT_EQ(OEMCrypto_SUCCESS, license_messages.LoadResponse()); } } // Used to test the different HDCP versions. This test is parameterized by the // required HDCP version in the key control block. class OEMCryptoSessionTestDecryptWithHDCP : public OEMCryptoSessionTests, public WithParamInterface { protected: void DecryptWithHDCP(OEMCrypto_HDCP_Capability version) { OEMCryptoResult sts; OEMCrypto_HDCP_Capability current, maximum; sts = OEMCrypto_GetHDCPCapability(¤t, &maximum); ASSERT_EQ(OEMCrypto_SUCCESS, sts); Session s; ASSERT_NO_FATAL_FAILURE(s.open()); ASSERT_NO_FATAL_FAILURE(InstallTestRSAKey(&s)); LicenseRoundTrip license_messages(&s); license_messages.set_control((version << wvoec::kControlHDCPVersionShift) | wvoec::kControlObserveHDCP | wvoec::kControlHDCPRequired); ASSERT_NO_FATAL_FAILURE(license_messages.SignAndVerifyRequest()); ASSERT_NO_FATAL_FAILURE(license_messages.CreateDefaultResponse()); ASSERT_NO_FATAL_FAILURE(license_messages.EncryptAndSignResponse()); ASSERT_EQ(OEMCrypto_SUCCESS, license_messages.LoadResponse()); if (version > current) { if (global_features.api_version >= 16) { // Can provide either OEMCrypto_WARNING_MIXED_OUTPUT_PROTECTION or // OEMCrypto_ERROR_INSUFFICIENT_HDCP. TestDecryptCTR allows either to be // reported if OEMCrypto_WARNING_MIXED_OUTPUT_PROTECTION is expected. ASSERT_NO_FATAL_FAILURE( s.TestDecryptCTR(true, OEMCrypto_WARNING_MIXED_OUTPUT_PROTECTION)) << "Failed when current HDCP = " << HDCPCapabilityAsString(current) << ", maximum HDCP = " << HDCPCapabilityAsString(maximum) << ", license HDCP = " << HDCPCapabilityAsString(version); } else { ASSERT_NO_FATAL_FAILURE( s.TestDecryptCTR(true, OEMCrypto_ERROR_INSUFFICIENT_HDCP)) << "Failed when current HDCP = " << HDCPCapabilityAsString(current) << ", maximum HDCP = " << HDCPCapabilityAsString(maximum) << ", license HDCP = " << HDCPCapabilityAsString(version); } } else { ASSERT_NO_FATAL_FAILURE(s.TestDecryptCTR(true, OEMCrypto_SUCCESS)) << "Failed when current HDCP = " << HDCPCapabilityAsString(current) << ", maximum HDCP = " << HDCPCapabilityAsString(maximum) << ", license HDCP = " << HDCPCapabilityAsString(version); } } }; TEST_P(OEMCryptoSessionTestDecryptWithHDCP, DecryptAPI09) { // Test parameterized by HDCP version. DecryptWithHDCP(static_cast(GetParam())); } INSTANTIATE_TEST_CASE_P(TestHDCP, OEMCryptoSessionTestDecryptWithHDCP, Range(1, 6)); // // Load, Refresh Keys Test // class OEMCryptoRefreshTest : public OEMCryptoLicenseTest { protected: void SetUp() override { OEMCryptoLicenseTest::SetUp(); // These values allow us to run a few simple duration tests or just start // playback right away. All times are in seconds since the license was // signed. // Soft expiry false means timers are strictly enforce. timer_limits_.soft_enforce_rental_duration = true; timer_limits_.soft_enforce_playback_duration = false; // Playback may begin immediately. timer_limits_.earliest_playback_start_seconds = 0; // First playback may be within the first two seconds. timer_limits_.rental_duration_seconds = kDuration; // Once started, playback may last two seconds without a renewal. timer_limits_.initial_renewal_duration_seconds = kDuration; // Total playback is not limited. timer_limits_.total_playback_duration_seconds = 0; } void LoadLicense() { // If we require a nonce, then generate one. if (license_messages_.control() & (wvoec::kControlNonceEnabled | wvoec::kControlNonceOrEntry | wvoec::kControlNonceRequired)) { ASSERT_NO_FATAL_FAILURE(session_.GenerateNonce()); } license_messages_.core_response().timer_limits = timer_limits_; ASSERT_NO_FATAL_FAILURE(license_messages_.SignAndVerifyRequest()); ASSERT_NO_FATAL_FAILURE(license_messages_.CreateDefaultResponse()); ASSERT_NO_FATAL_FAILURE(license_messages_.EncryptAndSignResponse()); ASSERT_EQ(OEMCrypto_SUCCESS, license_messages_.LoadResponse()); } void MakeRenewalRequest(RenewalRoundTrip* renewal_messages) { ASSERT_NO_FATAL_FAILURE(renewal_messages->SignAndVerifyRequest()); ASSERT_NO_FATAL_FAILURE(renewal_messages->CreateDefaultResponse()); } void LoadRenewal(RenewalRoundTrip* renewal_messages, OEMCryptoResult expected_result) { ASSERT_NO_FATAL_FAILURE(renewal_messages->EncryptAndSignResponse()); ASSERT_EQ(expected_result, renewal_messages->LoadResponse()); } ODK_TimerLimits timer_limits_; }; // This class is for the refresh tests that should only be run on licenses with // a core message. class OEMCryptoRefreshTestAPI16 : public OEMCryptoRefreshTest {}; // Refresh keys should work if the license uses a nonce. TEST_P(OEMCryptoRefreshTest, RefreshWithNonce) { LoadLicense(); RenewalRoundTrip renewal_messages(&license_messages_); MakeRenewalRequest(&renewal_messages); LoadRenewal(&renewal_messages, OEMCrypto_SUCCESS); } // Refresh keys should work if the license does not use a nonce. TEST_P(OEMCryptoRefreshTest, RefreshNoNonce) { license_messages_.set_control(0); LoadLicense(); RenewalRoundTrip renewal_messages(&license_messages_); MakeRenewalRequest(&renewal_messages); LoadRenewal(&renewal_messages, OEMCrypto_SUCCESS); } // Refresh keys should NOT work if a license has not been loaded. TEST_P(OEMCryptoRefreshTestAPI16, RefreshNoLicense) { Session s; s.open(); constexpr size_t message_size = kMaxCoreMessage + 42; std::vector data(message_size); for (size_t i = 0; i < data.size(); i++) data[i] = i & 0xFF; size_t gen_signature_length = 0; size_t core_message_length = 0; OEMCryptoResult sts = OEMCrypto_PrepAndSignRenewalRequest( s.session_id(), data.data(), data.size(), &core_message_length, nullptr, &gen_signature_length); ASSERT_LT(core_message_length, message_size); if (sts == OEMCrypto_ERROR_SHORT_BUFFER) { vector gen_signature(gen_signature_length); sts = OEMCrypto_PrepAndSignRenewalRequest( s.session_id(), data.data(), data.size(), &core_message_length, gen_signature.data(), &gen_signature_length); } ASSERT_NE(OEMCrypto_SUCCESS, sts); } // Refresh keys should fail if the nonce is not in the session. TEST_P(OEMCryptoRefreshTestAPI16, RefreshBadNonce) { LoadLicense(); RenewalRoundTrip renewal_messages(&license_messages_); MakeRenewalRequest(&renewal_messages); renewal_messages.core_request().nonce ^= 42; LoadRenewal(&renewal_messages, OEMCrypto_ERROR_INVALID_NONCE); } // Refresh keys should fail if the session_id does not match the license. TEST_P(OEMCryptoRefreshTestAPI16, RefreshBadSessionID) { LoadLicense(); RenewalRoundTrip renewal_messages(&license_messages_); MakeRenewalRequest(&renewal_messages); renewal_messages.core_request().session_id += 1; LoadRenewal(&renewal_messages, OEMCrypto_ERROR_INVALID_NONCE); } // Refresh keys should handle the maximum message size. TEST_P(OEMCryptoRefreshTest, RefreshLargeBuffer) { LoadLicense(); RenewalRoundTrip renewal_messages(&license_messages_); const size_t max_size = GetResourceValue(kLargeMessageSize); renewal_messages.set_message_size(max_size); MakeRenewalRequest(&renewal_messages); LoadRenewal(&renewal_messages, OEMCrypto_SUCCESS); } // This situation would occur if an app only uses one key in the license. When // that happens, SelectKey would be called before the first decrypt, and then // would not need to be called again, even if the license is refreshed. TEST_P(OEMCryptoRefreshTest, RefreshWithNoSelectKey) { LoadLicense(); // Call select key before the refresh. No calls below to TestDecryptCTR with // select key set to true. ASSERT_NO_FATAL_FAILURE(session_.TestDecryptCTR(true)); // This should still be valid key, even if the refresh failed, because this // is before the original license duration. wvcdm::TestSleep::Sleep(kShortSleep); ASSERT_NO_FATAL_FAILURE(session_.TestDecryptCTR(false)); // This should be after duration of the original license, but before the // expiration of the refresh message. This should fail until we have loaded // the renewal. wvcdm::TestSleep::Sleep(kShortSleep + kLongSleep); ASSERT_NO_FATAL_FAILURE( session_.TestDecryptCTR(false, OEMCrypto_ERROR_KEY_EXPIRED)); RenewalRoundTrip renewal_messages(&license_messages_); MakeRenewalRequest(&renewal_messages); LoadRenewal(&renewal_messages, OEMCrypto_SUCCESS); // After we've loaded the renewal, decrypt should succeed again. ASSERT_NO_FATAL_FAILURE(session_.TestDecryptCTR(false)); } INSTANTIATE_TEST_CASE_P(TestAll, OEMCryptoRefreshTest, Range(kCurrentAPI - 1, kCurrentAPI + 1)); // These tests only work when the license has a core message. INSTANTIATE_TEST_CASE_P(TestAPI16, OEMCryptoRefreshTestAPI16, Range(kCoreMessagesAPI, kCurrentAPI + 1)); // If the license does not allow a hash, then we should not compute one. TEST_P(OEMCryptoLicenseTest, HashForbiddenAPI15) { uint32_t hash_type = OEMCrypto_SupportsDecryptHash(); // If hash is not supported, or is vendor defined, don't try to test it. if (hash_type != OEMCrypto_CRC_Clear_Buffer) return; ASSERT_NO_FATAL_FAILURE(session_.GenerateNonce()); ASSERT_NO_FATAL_FAILURE(license_messages_.SignAndVerifyRequest()); ASSERT_NO_FATAL_FAILURE(license_messages_.CreateDefaultResponse()); ASSERT_NO_FATAL_FAILURE(license_messages_.EncryptAndSignResponse()); ASSERT_EQ(OEMCrypto_SUCCESS, license_messages_.LoadResponse()); uint32_t frame_number = 1; uint32_t hash = 42; // It is OK to set the hash before loading the keys ASSERT_EQ(OEMCrypto_SUCCESS, OEMCrypto_SetDecryptHash(session_.session_id(), frame_number, reinterpret_cast(&hash), sizeof(hash))); // It is OK to select the key and decrypt. ASSERT_NO_FATAL_FAILURE(session_.TestDecryptCTR()); // But the error code should be bad. ASSERT_EQ(OEMCrypto_ERROR_UNKNOWN_FAILURE, OEMCrypto_GetHashErrorCode(session_.session_id(), &frame_number)); } // // Decrypt Tests -- these test Decrypt CTR mode only. // TEST_P(OEMCryptoLicenseTest, Decrypt) { ASSERT_NO_FATAL_FAILURE(session_.GenerateNonce()); ASSERT_NO_FATAL_FAILURE(license_messages_.SignAndVerifyRequest()); license_messages_.core_response() .timer_limits.total_playback_duration_seconds = kDuration; ASSERT_NO_FATAL_FAILURE(license_messages_.CreateDefaultResponse()); ASSERT_NO_FATAL_FAILURE(license_messages_.EncryptAndSignResponse()); ASSERT_EQ(OEMCrypto_SUCCESS, license_messages_.LoadResponse()); ASSERT_NO_FATAL_FAILURE(session_.TestDecryptCTR()); } // Verify that a zero duration means infinite license duration. TEST_P(OEMCryptoLicenseTest, DecryptZeroDuration) { ASSERT_NO_FATAL_FAILURE(session_.GenerateNonce()); ASSERT_NO_FATAL_FAILURE(license_messages_.SignAndVerifyRequest()); license_messages_.core_response() .timer_limits.total_playback_duration_seconds = 0; ASSERT_NO_FATAL_FAILURE(license_messages_.CreateDefaultResponse()); ASSERT_NO_FATAL_FAILURE(license_messages_.EncryptAndSignResponse()); ASSERT_EQ(OEMCrypto_SUCCESS, license_messages_.LoadResponse()); ASSERT_NO_FATAL_FAILURE(session_.TestDecryptCTR()); } struct SubsampleSize { size_t clear_size; size_t encrypted_size; SubsampleSize(size_t clear, size_t encrypted) : clear_size(clear), encrypted_size(encrypted) {} }; // Struct for holding the data for one test sample in the decrypt tests. struct TestSample { // Encrypted data -- this is input to OEMCrypto, and output from EncryptData. std::vector encrypted_buffer; std::vector clear_buffer; // OEMCrypto store clear output here. std::vector truth_buffer; // Truth data for clear text. OEMCrypto_SampleDescription description; std::vector subsamples; int secure_buffer_fid; }; // A class of tests that test decryption for a variety of patterns and modes. // This test is parameterized by three parameters: // 1. The pattern used for pattern decryption. // 2. The cipher mode for decryption: either CTR or CBC. // 3. A boolean that determines if decrypt in place should be done. When the // output buffer is clear, it should be possible for the input and output // buffers to be the same. class OEMCryptoSessionTestsDecryptTests : public OEMCryptoLicenseTestAPI16, public WithParamInterface> { protected: void SetUp() override { OEMCryptoLicenseTestAPI16::SetUp(); pattern_ = ::testing::get<0>(GetParam()); cipher_mode_ = ::testing::get<1>(GetParam()); decrypt_inplace_ = ::testing::get<2>(GetParam()).decrypt_inplace; output_buffer_type_ = ::testing::get<2>(GetParam()).type; verify_crc_ = global_features.supports_crc; // Pick a random key. EXPECT_EQ(GetRandBytes(key_, sizeof(key_)), 1); // Pick a random starting iv. Some tests override this before using it. EXPECT_EQ(GetRandBytes(initial_iv_, sizeof(initial_iv_)), 1); } void TearDown() override { FreeSecureBuffers(); OEMCryptoLicenseTestAPI16::TearDown(); } void SetSubsampleSizes(std::vector subsample_sizes) { // This is just sugar for having one sample with the given subsamples in it. SetSampleSizes({subsample_sizes}); } void SetSampleSizes(std::vector> sample_sizes) { ASSERT_GT(sample_sizes.size(), 0u); samples_.clear(); samples_.reserve(sample_sizes.size()); // Convert all the size arrays to TestSample structs for (const std::vector& subsample_sizes : sample_sizes) { // This could be one line if we had C++17 samples_.emplace_back(); TestSample& sample = samples_.back(); ASSERT_GT(subsample_sizes.size(), 0u); sample.subsamples.reserve(subsample_sizes.size()); // Convert all the sizes to subsample descriptions and tally the total // sample size size_t sample_size = 0; size_t current_block_offset = 0; for (const SubsampleSize& size : subsample_sizes) { sample.subsamples.push_back(OEMCrypto_SubSampleDescription{ size.clear_size, size.encrypted_size, 0, // Subsample Flags, to be filled in after the loop current_block_offset}); // Update the rolling variables sample_size += size.clear_size + size.encrypted_size; if (cipher_mode_ == OEMCrypto_CipherMode_CTR) { current_block_offset = (current_block_offset + size.encrypted_size) % AES_BLOCK_SIZE; } } // Set the subsample flags now that all the subsamples are processed sample.subsamples.front().subsample_flags |= OEMCrypto_FirstSubsample; sample.subsamples.back().subsample_flags |= OEMCrypto_LastSubsample; // Set related information on the sample description sample.description.subsamples = sample.subsamples.data(); sample.description.subsamples_length = sample.subsamples.size(); sample.description.buffers.input_data_length = sample_size; } } // Set up the input buffer and either a clear or secure output buffer for each // test sample. This should be called after SetSubsampleSizes(). void MakeBuffers() { for (TestSample& sample : samples_) { const size_t total_size = sample.description.buffers.input_data_length; ASSERT_GT(total_size, 0u); sample.encrypted_buffer.clear(); sample.truth_buffer.clear(); sample.clear_buffer.clear(); sample.encrypted_buffer.resize(total_size); sample.truth_buffer.resize(total_size); for (size_t i = 0; i < total_size; i++) sample.truth_buffer[i] = i % 256; OEMCrypto_DestBufferDesc& output_descriptor = sample.description.buffers.output_descriptor; output_descriptor.type = output_buffer_type_; switch (output_descriptor.type) { case OEMCrypto_BufferType_Clear: if (decrypt_inplace_) { // Add some padding to verify there is no overrun. sample.encrypted_buffer.resize(total_size + kBufferOverrunPadding, 0xaa); output_descriptor.buffer.clear.address = sample.encrypted_buffer.data(); } else { // Add some padding to verify there is no overrun. sample.clear_buffer.resize(total_size + kBufferOverrunPadding, 0xaa); output_descriptor.buffer.clear.address = sample.clear_buffer.data(); } output_descriptor.buffer.clear.address_length = total_size; break; case OEMCrypto_BufferType_Secure: output_descriptor.buffer.secure.handle_length = total_size; ASSERT_EQ(OEMCrypto_AllocateSecureBuffer( session_.session_id(), total_size, &output_descriptor, &sample.secure_buffer_fid), OEMCrypto_SUCCESS); ASSERT_NE(output_descriptor.buffer.secure.handle, nullptr); // It is OK if OEMCrypto changes the maximum size, but there must // still be enough room for our data. ASSERT_GE(output_descriptor.buffer.secure.handle_length, total_size); output_descriptor.buffer.secure.offset = 0; break; case OEMCrypto_BufferType_Direct: output_descriptor.buffer.direct.is_video = false; break; } // switch (output_descriptor.type) } // sample loop } void FreeSecureBuffers() { for (TestSample& sample : samples_) { OEMCrypto_DestBufferDesc& output_descriptor = sample.description.buffers.output_descriptor; if (output_descriptor.type == OEMCrypto_BufferType_Secure) { ASSERT_EQ(OEMCrypto_FreeSecureBuffer(session_.session_id(), &output_descriptor, sample.secure_buffer_fid), OEMCrypto_SUCCESS); } } } void EncryptData() { AES_KEY aes_key; AES_set_encrypt_key(key_, AES_BLOCK_SIZE * 8, &aes_key); for (TestSample& sample : samples_) { uint8_t iv[KEY_IV_SIZE]; // Current IV memcpy(iv, initial_iv_, KEY_IV_SIZE); memcpy(sample.description.iv, initial_iv_, KEY_IV_SIZE); size_t buffer_index = 0; // byte index into in and out. size_t block_offset = 0; // byte index into current block. for (const OEMCrypto_SubSampleDescription& subsample : sample.subsamples) { // Copy clear content. if (subsample.num_bytes_clear > 0) { memcpy(&sample.encrypted_buffer[buffer_index], &sample.truth_buffer[buffer_index], subsample.num_bytes_clear); buffer_index += subsample.num_bytes_clear; } // The IV resets at the start of each subsample in the 'cbcs' schema. if (cipher_mode_ == OEMCrypto_CipherMode_CBC) { memcpy(iv, initial_iv_, KEY_IV_SIZE); } size_t pattern_offset = 0; const size_t subsample_end = buffer_index + subsample.num_bytes_encrypted; while (buffer_index < subsample_end) { const size_t size = min(subsample_end - buffer_index, AES_BLOCK_SIZE - block_offset); const size_t pattern_length = pattern_.encrypt + pattern_.skip; const bool skip_block = (pattern_offset >= pattern_.encrypt) && (pattern_length > 0); if (pattern_length > 0) { pattern_offset = (pattern_offset + 1) % pattern_length; } // CBC mode should just copy a partial block at the end. If there // is a partial block at the beginning, an error is returned, so we // can put whatever we want in the output buffer. if (skip_block || ((cipher_mode_ == OEMCrypto_CipherMode_CBC) && (size < AES_BLOCK_SIZE))) { memcpy(&sample.encrypted_buffer[buffer_index], &sample.truth_buffer[buffer_index], size); block_offset = 0; // Next block should be complete. } else { if (cipher_mode_ == OEMCrypto_CipherMode_CTR) { uint8_t aes_output[AES_BLOCK_SIZE]; AES_encrypt(iv, aes_output, &aes_key); for (size_t n = 0; n < size; n++) { sample.encrypted_buffer[buffer_index + n] = aes_output[n + block_offset] ^ sample.truth_buffer[buffer_index + n]; } if (size + block_offset < AES_BLOCK_SIZE) { // Partial block. Don't increment iv. Compute next block // offset. block_offset = block_offset + size; } else { EXPECT_EQ(block_offset + size, static_cast(AES_BLOCK_SIZE)); // Full block. Increment iv, and set offset to 0 for next // block. ctr128_inc64(1, iv); block_offset = 0; } } else { uint8_t aes_input[AES_BLOCK_SIZE]; for (size_t n = 0; n < size; n++) { aes_input[n] = sample.truth_buffer[buffer_index + n] ^ iv[n]; } AES_encrypt(aes_input, &sample.encrypted_buffer[buffer_index], &aes_key); memcpy(iv, &sample.encrypted_buffer[buffer_index], AES_BLOCK_SIZE); // CBC mode should always start on block boundary. block_offset = 0; } } buffer_index += size; } // encryption loop } // per-subsample loop } // per-sample loop } void LoadLicense() { ASSERT_NO_FATAL_FAILURE(session_.GenerateNonce()); uint32_t control = wvoec::kControlNonceEnabled; if (verify_crc_) control |= kControlAllowHashVerification; if (output_buffer_type_ == OEMCrypto_BufferType_Secure) control |= kControlObserveDataPath | kControlDataPathSecure; license_messages_.set_control(control); ASSERT_NO_FATAL_FAILURE(license_messages_.SignAndVerifyRequest()); ASSERT_NO_FATAL_FAILURE(license_messages_.CreateDefaultResponse()); license_messages_.core_response() .timer_limits.initial_renewal_duration_seconds = kDuration; memcpy(license_messages_.response_data().keys[0].key_data, key_, sizeof(key_)); license_messages_.response_data().keys[0].cipher_mode = cipher_mode_; ASSERT_NO_FATAL_FAILURE(license_messages_.EncryptAndSignResponse()); ASSERT_EQ(OEMCrypto_SUCCESS, license_messages_.LoadResponse()); ASSERT_EQ(OEMCrypto_SelectKey( session_.session_id(), session_.license().keys[0].key_id, session_.license().keys[0].key_id_length, cipher_mode_), OEMCrypto_SUCCESS); } void TestDecryptCENC() { OEMCryptoResult sts; // If supported, check the decrypt hashes. if (verify_crc_) { // OEMCrypto only supports providing a decrypt hash for the first sample // in the sample array. const TestSample& sample = samples_[0]; uint32_t hash = wvcrc32(sample.truth_buffer.data(), sample.truth_buffer.size()); ASSERT_EQ(OEMCrypto_SetDecryptHash( session_.session_id(), 1, reinterpret_cast(&hash), sizeof(hash)), OEMCrypto_SUCCESS); } // Build an array of just the sample descriptions. std::vector sample_descriptions; sample_descriptions.reserve(samples_.size()); for (TestSample& sample : samples_) { // This must be deferred until this point in case the test modifies the // buffer before testing decrypt. sample.description.buffers.input_data = sample.encrypted_buffer.data(); // Append to the description array. sample_descriptions.push_back(sample.description); } // Perform decryption using the test data that was previously set up. sts = DecryptFallbackChain::Decrypt( session_.session_id(), sample_descriptions.data(), sample_descriptions.size(), cipher_mode_, &pattern_); ASSERT_EQ(sts, OEMCrypto_SUCCESS); // Validate the decrypted data. for (TestSample& sample : samples_) { if (sample.description.buffers.output_descriptor.type == OEMCrypto_BufferType_Clear) { const size_t total_size = sample.description.buffers.input_data_length; // To verify there is no buffer overrun after decrypting, look at the // padded bytes just after the data buffer that was written. It should // not have changed from the original 0xaa that we set in MakeBuffer // function. if (decrypt_inplace_) { EXPECT_EQ(std::count(sample.encrypted_buffer.begin() + total_size, sample.encrypted_buffer.end(), 0xaa), static_cast(kBufferOverrunPadding)) << "Buffer overrun."; sample.encrypted_buffer.resize(total_size); // Remove padding. // We expect encrypted buffer to have been changed by OEMCrypto. EXPECT_EQ(sample.encrypted_buffer, sample.truth_buffer); } else { EXPECT_EQ(std::count(sample.clear_buffer.begin() + total_size, sample.clear_buffer.end(), 0xaa), static_cast(kBufferOverrunPadding)) << "Buffer overrun."; sample.clear_buffer.resize(total_size); // Remove padding. EXPECT_EQ(sample.clear_buffer, sample.truth_buffer); } } } if (global_features.supports_crc) { uint32_t frame; ASSERT_EQ(OEMCrypto_GetHashErrorCode(session_.session_id(), &frame), OEMCrypto_SUCCESS); } } // Parameters of test case OEMCrypto_CENCEncryptPatternDesc pattern_; OEMCryptoCipherMode cipher_mode_; bool decrypt_inplace_; // If true, input and output buffers are the same. OEMCryptoBufferType output_buffer_type_; bool verify_crc_; uint8_t key_[AES_BLOCK_SIZE]; // Encryption Key. uint8_t initial_iv_[KEY_IV_SIZE]; // Starting IV for every sample. std::vector samples_; }; TEST_P(OEMCryptoSessionTestsDecryptTests, SingleLargeSubsample) { // This subsample size is larger than a few encrypt/skip patterns. Most // test cases use a pattern length of 160, so we'll run through at least two // full patterns if we have more than 320 -- round up to 400. ASSERT_NO_FATAL_FAILURE(SetSubsampleSizes({ {0, 400}, })); ASSERT_NO_FATAL_FAILURE(LoadLicense()); ASSERT_NO_FATAL_FAILURE(MakeBuffers()); ASSERT_NO_FATAL_FAILURE(EncryptData()); ASSERT_NO_FATAL_FAILURE(TestDecryptCENC()); } // When the pattern length is 10 blocks, there is a discrepancy between the // HLS and the CENC standards for samples of size 160*N+16, for N = 1, 2, 3... // We require the CENC standard for OEMCrypto, and let a layer above us break // samples into pieces if they wish to use the HLS standard. TEST_P(OEMCryptoSessionTestsDecryptTests, PatternPlusOneBlock) { ASSERT_NO_FATAL_FAILURE(SetSubsampleSizes({ {0, 160 + 16}, })); ASSERT_NO_FATAL_FAILURE(LoadLicense()); ASSERT_NO_FATAL_FAILURE(MakeBuffers()); ASSERT_NO_FATAL_FAILURE(EncryptData()); ASSERT_NO_FATAL_FAILURE(TestDecryptCENC()); } // Test that a single block can be decrypted. TEST_P(OEMCryptoSessionTestsDecryptTests, OneBlock) { ASSERT_NO_FATAL_FAILURE(SetSubsampleSizes({ {0, 16}, })); ASSERT_NO_FATAL_FAILURE(LoadLicense()); ASSERT_NO_FATAL_FAILURE(MakeBuffers()); ASSERT_NO_FATAL_FAILURE(EncryptData()); ASSERT_NO_FATAL_FAILURE(TestDecryptCENC()); } // This tests the ability to decrypt multiple subsamples with no offset. // There is no offset within the block, used by CTR mode. TEST_P(OEMCryptoSessionTestsDecryptTests, NoOffset) { ASSERT_NO_FATAL_FAILURE(SetSubsampleSizes({ {25, 160}, {50, 256}, {25, 160}, })); ASSERT_NO_FATAL_FAILURE(LoadLicense()); ASSERT_NO_FATAL_FAILURE(MakeBuffers()); ASSERT_NO_FATAL_FAILURE(EncryptData()); ASSERT_NO_FATAL_FAILURE(TestDecryptCENC()); } // This tests an offset into the block for the second encrypted subsample. // This should only work for CTR mode, for CBC mode an error is expected in // the decrypt step. // If this test fails for CTR mode, then it is probably handling the // block_offset incorrectly. TEST_P(OEMCryptoSessionTestsDecryptTests, EvenOffset) { ASSERT_NO_FATAL_FAILURE(SetSubsampleSizes({ {25, 8}, {25, 32}, {25, 50}, })); ASSERT_NO_FATAL_FAILURE(LoadLicense()); ASSERT_NO_FATAL_FAILURE(MakeBuffers()); // CTR Mode is self-inverse -- i.e. We can pick the encrypted data and // compute the unencrypted data. By picking the encrypted data to be all 0, // it is easier to re-encrypt the data and debug problems. Similarly, we // pick an iv = 0. memset(initial_iv_, 0, KEY_IV_SIZE); TestSample& sample = samples_[0]; // There is only one sample in this test sample.truth_buffer.assign(sample.description.buffers.input_data_length, 0); ASSERT_NO_FATAL_FAILURE(EncryptData()); if (decrypt_inplace_) { const size_t total_size = sample.description.buffers.input_data_length; // In case of decrypt_inplace_, encrypted_buffer contains padded bytes // which is used for buffer overrun validation. Do not copy the padded // bytes to truth_buffer. sample.truth_buffer.assign(sample.encrypted_buffer.begin(), sample.encrypted_buffer.begin() + total_size); } else { sample.truth_buffer = sample.encrypted_buffer; // truth_buffer_ = encrypted zero buffer. } // Run EncryptData to re-encrypt this buffer. For CTR mode, we should get // back to zeros. ASSERT_NO_FATAL_FAILURE(EncryptData()); ASSERT_NO_FATAL_FAILURE(TestDecryptCENC()); } // If the EvenOffset test passes, but this one doesn't, then DecryptCENC might // be using the wrong definition of block offset. Adding the block offset to // the block boundary should give you the beginning of the encrypted data. // This should only work for CTR mode, for CBC mode, the block offset must be // 0, so an error is expected in the decrypt step. // Another way to view the block offset is with the formula: // block_boundary + block_offset = beginning of subsample. TEST_P(OEMCryptoSessionTestsDecryptTests, OddOffset) { ASSERT_NO_FATAL_FAILURE(SetSubsampleSizes({ {10, 50}, {10, 75}, {10, 75}, })); ASSERT_NO_FATAL_FAILURE(LoadLicense()); ASSERT_NO_FATAL_FAILURE(MakeBuffers()); ASSERT_NO_FATAL_FAILURE(EncryptData()); ASSERT_NO_FATAL_FAILURE(TestDecryptCENC()); } // This tests that the algorithm used to increment the counter for // AES-CTR mode is correct. There are two possible implementations: // 1) increment the counter as if it were a 128 bit number, // 2) increment the low 64 bits as a 64 bit number and leave the high bits // alone. // For CENC, the algorithm we should use is the second one. OpenSSL defaults to // the first. If this test is not passing, you should look at the way you // increment the counter. Look at the example code in ctr128_inc64 above. // If you start with an IV of 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE, after you // increment twice, you should get 0xFFFFFFFFFFFFFFFF0000000000000000. TEST_P(OEMCryptoSessionTestsDecryptTests, DecryptWithNearWrap) { memcpy(initial_iv_, wvcdm::a2b_hex("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE").data(), KEY_IV_SIZE); ASSERT_NO_FATAL_FAILURE(SetSubsampleSizes({ {0, 256}, })); ASSERT_NO_FATAL_FAILURE(LoadLicense()); ASSERT_NO_FATAL_FAILURE(MakeBuffers()); ASSERT_NO_FATAL_FAILURE(EncryptData()); ASSERT_NO_FATAL_FAILURE(TestDecryptCENC()); } // This tests the case where an encrypted sample is not an even number of // blocks. For CTR mode, the partial block is encrypted. For CBC mode the // partial block should be a copy of the clear data. TEST_P(OEMCryptoSessionTestsDecryptTests, PartialBlock) { // Note: for more complete test coverage, we want a sample size that is in // the encrypted range for some tests, e.g. (3,7), and in the skip range for // other tests, e.g. (7, 3). 3*16 < 50 and 7*16 > 50. ASSERT_NO_FATAL_FAILURE(SetSubsampleSizes({ {0, 50}, })); ASSERT_NO_FATAL_FAILURE(LoadLicense()); ASSERT_NO_FATAL_FAILURE(MakeBuffers()); ASSERT_NO_FATAL_FAILURE(EncryptData()); ASSERT_NO_FATAL_FAILURE(TestDecryptCENC()); } // Based on the resource rating, OEMCrypto should be able to handle the maximum // amount of data that can be passed to it. This is the lesser of: // // 1) The maximum total sample size // 2) The maximum number of subsamples multiplied by the maximum subsample size TEST_P(OEMCryptoSessionTestsDecryptTests, DecryptMaxSampleAPI16) { const size_t max_sample_size = GetResourceValue(kMaxSampleSize); const size_t max_subsample_size = GetResourceValue(kMaxSubsampleSize); const size_t max_num_subsamples = GetResourceValue(kMaxNumberSubsamples); // The +1 on this line ensures that, even in cases where max_sample_size is // not evenly divisible by max_num_subsamples and thus the division gets // truncated, (max_num_subsamples * subsample_size) will be greater than // max_sample_size. const size_t subsample_size = std::min(max_sample_size / max_num_subsamples + 1, max_subsample_size); size_t bytes_remaining = max_sample_size; std::vector subsample_sizes; while (bytes_remaining > 0 && subsample_sizes.size() < max_num_subsamples) { const size_t this_subsample_size = std::min(subsample_size, bytes_remaining); const size_t clear_size = this_subsample_size / 2; const size_t encrypted_size = this_subsample_size - clear_size; subsample_sizes.push_back({clear_size, encrypted_size}); bytes_remaining -= this_subsample_size; } ASSERT_NO_FATAL_FAILURE(SetSubsampleSizes(subsample_sizes)); ASSERT_NO_FATAL_FAILURE(LoadLicense()); ASSERT_NO_FATAL_FAILURE(MakeBuffers()); ASSERT_NO_FATAL_FAILURE(EncryptData()); ASSERT_NO_FATAL_FAILURE(TestDecryptCENC()); } // Based on the resource rating, OEMCrypto should be able to handle the maximum // subsample size. TEST_P(OEMCryptoSessionTestsDecryptTests, DecryptMaxSubsample) { const size_t max = GetResourceValue(kMaxSubsampleSize); const size_t half_max = max / 2; // This test assumes that the maximum sample size is always more than three // times the maximum subsample size. ASSERT_NO_FATAL_FAILURE(SetSubsampleSizes({ {max, 0}, {0, max}, {half_max, max - half_max}, })); ASSERT_NO_FATAL_FAILURE(LoadLicense()); ASSERT_NO_FATAL_FAILURE(MakeBuffers()); ASSERT_NO_FATAL_FAILURE(EncryptData()); ASSERT_NO_FATAL_FAILURE(TestDecryptCENC()); } // There are probably no frames this small, but we should handle them anyway. TEST_P(OEMCryptoSessionTestsDecryptTests, DecryptSmallBuffer) { ASSERT_NO_FATAL_FAILURE(SetSubsampleSizes({ {5, 5}, })); ASSERT_NO_FATAL_FAILURE(LoadLicense()); ASSERT_NO_FATAL_FAILURE(MakeBuffers()); ASSERT_NO_FATAL_FAILURE(EncryptData()); ASSERT_NO_FATAL_FAILURE(TestDecryptCENC()); } // Test the case where there is only a clear subsample and no encrypted // subsample. TEST_P(OEMCryptoSessionTestsDecryptTests, DecryptUnencrypted) { ASSERT_NO_FATAL_FAILURE(SetSubsampleSizes({ {256, 0}, })); ASSERT_NO_FATAL_FAILURE(LoadLicense()); ASSERT_NO_FATAL_FAILURE(MakeBuffers()); ASSERT_NO_FATAL_FAILURE(EncryptData()); ASSERT_NO_FATAL_FAILURE(TestDecryptCENC()); } TEST_P(OEMCryptoSessionTestsDecryptTests, DecryptUnencryptedNoKey) { // Do not try to compute the CRC because we have not loaded a license. verify_crc_ = false; // Single clear subsample ASSERT_NO_FATAL_FAILURE(SetSubsampleSizes({ {400, 0}, })); ASSERT_NO_FATAL_FAILURE(MakeBuffers()); // Clear data should be copied even if there is no key selected, and no // license loaded. // ASSERT_NO_FATAL_FAILURE(LoadLicense()); ASSERT_NO_FATAL_FAILURE(EncryptData()); ASSERT_NO_FATAL_FAILURE(TestDecryptCENC()); } // This tests the ability to decrypt multiple samples at once. TEST_P(OEMCryptoSessionTestsDecryptTests, MultipleSamples) { ASSERT_NO_FATAL_FAILURE(SetSampleSizes({ { {52, 160}, {25, 256}, {25, 320}, }, { {300, 64}, {50, 160}, {2, 160}, {24, 160}, {128, 256}, }, { {70, 320}, {160, 160}, }, })); ASSERT_NO_FATAL_FAILURE(LoadLicense()); ASSERT_NO_FATAL_FAILURE(MakeBuffers()); ASSERT_NO_FATAL_FAILURE(EncryptData()); ASSERT_NO_FATAL_FAILURE(TestDecryptCENC()); } // Used to construct a specific pattern. constexpr OEMCrypto_CENCEncryptPatternDesc MakePattern(size_t encrypt, size_t skip) { return {encrypt, skip}; } INSTANTIATE_TEST_CASE_P( CTRTests, OEMCryptoSessionTestsDecryptTests, Combine(Values(MakePattern(0, 0)), Values(OEMCrypto_CipherMode_CTR), ::testing::ValuesIn(global_features.GetOutputTypes()))); // Decrypt in place for CBC tests was only required in v13. INSTANTIATE_TEST_CASE_P( CBCTestsAPI14, OEMCryptoSessionTestsDecryptTests, Combine( Values(MakePattern(3, 7), MakePattern(9, 1), // HLS edge cases. We should follow the CENC spec, not HLS spec. MakePattern(1, 9), MakePattern(1, 0), // AV1 patterns not already covered above. MakePattern(5, 5), MakePattern(10, 0)), Values(OEMCrypto_CipherMode_CBC), ::testing::ValuesIn(global_features.GetOutputTypes()))); // A request to decrypt data to a clear buffer when the key control block // requires a secure data path. TEST_P(OEMCryptoLicenseTest, DecryptSecureToClear) { ASSERT_NO_FATAL_FAILURE(session_.GenerateNonce()); ASSERT_NO_FATAL_FAILURE(license_messages_.SignAndVerifyRequest()); license_messages_.set_control(wvoec::kControlObserveDataPath | wvoec::kControlDataPathSecure); ASSERT_NO_FATAL_FAILURE(license_messages_.CreateDefaultResponse()); ASSERT_NO_FATAL_FAILURE(license_messages_.EncryptAndSignResponse()); ASSERT_EQ(OEMCrypto_SUCCESS, license_messages_.LoadResponse()); ASSERT_NO_FATAL_FAILURE( session_.TestDecryptCTR(true, OEMCrypto_ERROR_UNKNOWN_FAILURE)); } // If analog is forbidden, then decrypt to a clear buffer should be forbidden. TEST_P(OEMCryptoLicenseTest, DecryptNoAnalogToClearAPI13) { ASSERT_NO_FATAL_FAILURE(session_.GenerateNonce()); ASSERT_NO_FATAL_FAILURE(license_messages_.SignAndVerifyRequest()); license_messages_.set_control(wvoec::kControlDisableAnalogOutput); ASSERT_NO_FATAL_FAILURE(license_messages_.CreateDefaultResponse()); ASSERT_NO_FATAL_FAILURE(license_messages_.EncryptAndSignResponse()); ASSERT_EQ(OEMCrypto_SUCCESS, license_messages_.LoadResponse()); ASSERT_NO_FATAL_FAILURE( session_.TestDecryptCTR(true, OEMCrypto_ERROR_ANALOG_OUTPUT)); } // Test that key duration is honored. TEST_P(OEMCryptoLicenseTest, KeyDuration) { ASSERT_NO_FATAL_FAILURE(session_.GenerateNonce()); ASSERT_NO_FATAL_FAILURE(license_messages_.SignAndVerifyRequest()); license_messages_.core_response() .timer_limits.total_playback_duration_seconds = kDuration; ASSERT_NO_FATAL_FAILURE(license_messages_.CreateDefaultResponse()); ASSERT_NO_FATAL_FAILURE(license_messages_.EncryptAndSignResponse()); ASSERT_EQ(OEMCrypto_SUCCESS, license_messages_.LoadResponse()); ASSERT_NO_FATAL_FAILURE(session_.TestDecryptCTR(true, OEMCrypto_SUCCESS)); wvcdm::TestSleep::Sleep(kShortSleep); // Should still be valid key. ASSERT_NO_FATAL_FAILURE(session_.TestDecryptCTR(false, OEMCrypto_SUCCESS)); wvcdm::TestSleep::Sleep(kLongSleep); // Should be expired key. ASSERT_NO_FATAL_FAILURE( session_.TestDecryptCTR(false, OEMCrypto_ERROR_KEY_EXPIRED)); ASSERT_NO_FATAL_FAILURE(session_.TestSelectExpired(0)); } INSTANTIATE_TEST_CASE_P(TestAll, OEMCryptoLicenseTest, Range(kCurrentAPI - 1, kCurrentAPI + 1)); // // Certificate Root of Trust Tests // class OEMCryptoLoadsCertificate : public OEMCryptoSessionTestKeyboxTest {}; // This test verifies that we can create a wrapped RSA key, and then reload it. TEST_F(OEMCryptoLoadsCertificate, LoadRSASessionKey) { CreateWrappedRSAKey(); Session s; ASSERT_NO_FATAL_FAILURE(s.open()); ASSERT_NO_FATAL_FAILURE(s.InstallRSASessionTestKey(wrapped_rsa_key_)); } TEST_F(OEMCryptoLoadsCertificate, SignProvisioningRequest) { Session s; ASSERT_NO_FATAL_FAILURE(s.open()); if (global_features.provisioning_method == OEMCrypto_OEMCertificate) { s.LoadOEMCert(true); } else { EXPECT_EQ(global_features.provisioning_method, OEMCrypto_Keybox); s.GenerateDerivedKeysFromKeybox(keybox_); } s.GenerateNonce(); ProvisioningRoundTrip provisioning_messages(&s, encoded_rsa_key_); ASSERT_NO_FATAL_FAILURE(provisioning_messages.SignAndVerifyRequest()); } // This tests a large message size. The size is larger than we required in v15. TEST_F(OEMCryptoLoadsCertificate, SignLargeProvisioningRequestAPI16) { Session s; ASSERT_NO_FATAL_FAILURE(s.open()); if (global_features.provisioning_method == OEMCrypto_OEMCertificate) { s.LoadOEMCert(true); } else { EXPECT_EQ(global_features.provisioning_method, OEMCrypto_Keybox); s.GenerateDerivedKeysFromKeybox(keybox_); } s.GenerateNonce(); ProvisioningRoundTrip provisioning_messages(&s, encoded_rsa_key_); const size_t max_size = GetResourceValue(kLargeMessageSize); provisioning_messages.set_message_size(max_size); ASSERT_NO_FATAL_FAILURE(provisioning_messages.SignAndVerifyRequest()); } // This creates a wrapped RSA key, and then does the sanity check that the // unencrypted key is not found in the wrapped key. The wrapped key should be // encrypted. TEST_F(OEMCryptoLoadsCertificate, CertificateProvision) { Session s; ProvisioningRoundTrip provisioning_messages(&s, encoded_rsa_key_); provisioning_messages.PrepareSession(keybox_); ASSERT_NO_FATAL_FAILURE(provisioning_messages.SignAndVerifyRequest()); ASSERT_NO_FATAL_FAILURE(provisioning_messages.CreateDefaultResponse()); ASSERT_NO_FATAL_FAILURE(provisioning_messages.EncryptAndSignResponse()); ASSERT_EQ(OEMCrypto_SUCCESS, provisioning_messages.LoadResponse()); // We should not be able to find the rsa key in the wrapped key. It should // be encrypted. EXPECT_EQ(nullptr, find(provisioning_messages.wrapped_rsa_key(), provisioning_messages.encoded_rsa_key())); } // Verify that RewrapDeviceRSAKey checks pointers are within the provisioning // message. TEST_F(OEMCryptoLoadsCertificate, CertificateProvisionBadRange1_API16) { Session s; ProvisioningRoundTrip provisioning_messages(&s, encoded_rsa_key_); provisioning_messages.PrepareSession(keybox_); ASSERT_NO_FATAL_FAILURE(provisioning_messages.SignAndVerifyRequest()); ASSERT_NO_FATAL_FAILURE(provisioning_messages.CreateDefaultResponse()); provisioning_messages.core_response().enc_private_key.offset = provisioning_messages.encrypted_response_buffer().size() + 1; ASSERT_NO_FATAL_FAILURE(provisioning_messages.EncryptAndSignResponse()); ASSERT_NE(OEMCrypto_SUCCESS, provisioning_messages.LoadResponse()); provisioning_messages.VerifyLoadFailed(); } // Verify that RewrapDeviceRSAKey checks pointers are within the provisioning // message. TEST_F(OEMCryptoLoadsCertificate, CertificateProvisionBadRange2_API16) { Session s; ProvisioningRoundTrip provisioning_messages(&s, encoded_rsa_key_); provisioning_messages.PrepareSession(keybox_); ASSERT_NO_FATAL_FAILURE(provisioning_messages.SignAndVerifyRequest()); ASSERT_NO_FATAL_FAILURE(provisioning_messages.CreateDefaultResponse()); provisioning_messages.core_response().enc_private_key_iv.offset = provisioning_messages.encrypted_response_buffer().size() + 1; ASSERT_NO_FATAL_FAILURE(provisioning_messages.EncryptAndSignResponse()); ASSERT_NE(OEMCrypto_SUCCESS, provisioning_messages.LoadResponse()); provisioning_messages.VerifyLoadFailed(); } // Verify that RewrapDeviceRSAKey checks pointers are within the provisioning // message. TEST_F(OEMCryptoLoadsCertificate, CertificateProvisionBadRange3_API16) { Session s; ProvisioningRoundTrip provisioning_messages(&s, encoded_rsa_key_); provisioning_messages.PrepareSession(keybox_); ASSERT_NO_FATAL_FAILURE(provisioning_messages.SignAndVerifyRequest()); ASSERT_NO_FATAL_FAILURE(provisioning_messages.CreateDefaultResponse()); // If the offset is before the end, but the offset+length is bigger, then // the message should be rejected. provisioning_messages.core_response().enc_private_key.offset = provisioning_messages.encrypted_response_buffer().size() - 5; ASSERT_NO_FATAL_FAILURE(provisioning_messages.EncryptAndSignResponse()); ASSERT_NE(OEMCrypto_SUCCESS, provisioning_messages.LoadResponse()); provisioning_messages.VerifyLoadFailed(); } // Verify that RewrapDeviceRSAKey checks pointers are within the provisioning // message. TEST_F(OEMCryptoLoadsCertificate, CertificateProvisionBadRange4_API16) { Session s; ProvisioningRoundTrip provisioning_messages(&s, encoded_rsa_key_); provisioning_messages.PrepareSession(keybox_); ASSERT_NO_FATAL_FAILURE(provisioning_messages.SignAndVerifyRequest()); ASSERT_NO_FATAL_FAILURE(provisioning_messages.CreateDefaultResponse()); // If the offset is before the end, but the offset+length is bigger, then // the message should be rejected. provisioning_messages.core_response().enc_private_key_iv.offset = provisioning_messages.encrypted_response_buffer().size() - 5; ASSERT_NO_FATAL_FAILURE(provisioning_messages.EncryptAndSignResponse()); ASSERT_NE(OEMCrypto_SUCCESS, provisioning_messages.LoadResponse()); provisioning_messages.VerifyLoadFailed(); } // Verify that RewrapDeviceRSAKey checks pointers are within the provisioning // message. TEST_F(OEMCryptoLoadsCertificate, CertificateProvisionBadRange5Prov30_API16) { Session s; ProvisioningRoundTrip provisioning_messages(&s, encoded_rsa_key_); provisioning_messages.PrepareSession(keybox_); ASSERT_NO_FATAL_FAILURE(provisioning_messages.SignAndVerifyRequest()); ASSERT_NO_FATAL_FAILURE(provisioning_messages.CreateDefaultResponse()); // If the offset is before the end, but the offset+length is bigger, then // the message should be rejected. provisioning_messages.core_response().encrypted_message_key.offset = provisioning_messages.encrypted_response_buffer().size() + 1; ASSERT_NO_FATAL_FAILURE(provisioning_messages.EncryptAndSignResponse()); ASSERT_NE(OEMCrypto_SUCCESS, provisioning_messages.LoadResponse()); provisioning_messages.VerifyLoadFailed(); } // Test that RewrapDeviceRSAKey verifies the message signature. // TODO(b/144186970): This test should also run on Prov 3.0 devices. TEST_F(OEMCryptoLoadsCertificate, CertificateProvisionBadSignatureKeyboxTestAPI16) { Session s; ProvisioningRoundTrip provisioning_messages(&s, encoded_rsa_key_); provisioning_messages.PrepareSession(keybox_); ASSERT_NO_FATAL_FAILURE(provisioning_messages.SignAndVerifyRequest()); ASSERT_NO_FATAL_FAILURE(provisioning_messages.CreateDefaultResponse()); ASSERT_NO_FATAL_FAILURE(provisioning_messages.EncryptAndSignResponse()); provisioning_messages.response_signature()[4] ^= 42; // bad signature. ASSERT_EQ(OEMCrypto_ERROR_SIGNATURE_FAILURE, provisioning_messages.LoadResponse()); provisioning_messages.VerifyLoadFailed(); } // Test that RewrapDeviceRSAKey verifies the nonce is current. TEST_F(OEMCryptoLoadsCertificate, CertificateProvisionBadNonce_API16) { Session s; ProvisioningRoundTrip provisioning_messages(&s, encoded_rsa_key_); provisioning_messages.PrepareSession(keybox_); ASSERT_NO_FATAL_FAILURE(provisioning_messages.SignAndVerifyRequest()); provisioning_messages.core_request().nonce ^= 42; // bad nonce. ASSERT_NO_FATAL_FAILURE(provisioning_messages.CreateDefaultResponse()); ASSERT_NO_FATAL_FAILURE(provisioning_messages.EncryptAndSignResponse()); ASSERT_EQ(OEMCrypto_ERROR_INVALID_NONCE, provisioning_messages.LoadResponse()); provisioning_messages.VerifyLoadFailed(); } // Test that RewrapDeviceRSAKey verifies the RSA key is valid. TEST_F(OEMCryptoLoadsCertificate, CertificateProvisionBadRSAKey) { Session s; ProvisioningRoundTrip provisioning_messages(&s, encoded_rsa_key_); provisioning_messages.PrepareSession(keybox_); ASSERT_NO_FATAL_FAILURE(provisioning_messages.SignAndVerifyRequest()); ASSERT_NO_FATAL_FAILURE(provisioning_messages.CreateDefaultResponse()); provisioning_messages.response_data().rsa_key[4] ^= 42; // bad key. ASSERT_NO_FATAL_FAILURE(provisioning_messages.EncryptAndSignResponse()); ASSERT_NE(OEMCrypto_SUCCESS, provisioning_messages.LoadResponse()); provisioning_messages.VerifyLoadFailed(); } // Test that RewrapDeviceRSAKey verifies the RSA key is valid. // TODO(b/144186970): This test should also run on Prov 3.0 devices. TEST_F(OEMCryptoLoadsCertificate, CertificateProvisionBadRSAKeyKeyboxTestAPI16) { Session s; ProvisioningRoundTrip provisioning_messages(&s, encoded_rsa_key_); provisioning_messages.PrepareSession(keybox_); ASSERT_NO_FATAL_FAILURE(provisioning_messages.SignAndVerifyRequest()); ASSERT_NO_FATAL_FAILURE(provisioning_messages.CreateDefaultResponse()); ASSERT_NO_FATAL_FAILURE(provisioning_messages.EncryptAndSignResponse()); size_t rsa_offset = provisioning_messages.core_response().enc_private_key.offset; // Offsets are relative to the message body, after the core message. rsa_offset += provisioning_messages.serialized_core_message().size(); rsa_offset += 4; // Change the middle of the key. provisioning_messages.encrypted_response_buffer()[rsa_offset] ^= 42; ASSERT_EQ(OEMCrypto_ERROR_SIGNATURE_FAILURE, provisioning_messages.LoadResponse()); provisioning_messages.VerifyLoadFailed(); } // Test that RewrapDeviceRSAKey accepts the maximum message size. TEST_F(OEMCryptoLoadsCertificate, CertificateProvisionLargeBuffer) { Session s; ProvisioningRoundTrip provisioning_messages(&s, encoded_rsa_key_); const size_t max_size = GetResourceValue(kLargeMessageSize); provisioning_messages.set_message_size(max_size); provisioning_messages.PrepareSession(keybox_); ASSERT_NO_FATAL_FAILURE(provisioning_messages.SignAndVerifyRequest()); ASSERT_NO_FATAL_FAILURE(provisioning_messages.CreateDefaultResponse()); ASSERT_NO_FATAL_FAILURE(provisioning_messages.EncryptAndSignResponse()); ASSERT_EQ(OEMCrypto_SUCCESS, provisioning_messages.LoadResponse()); // We should not be able to find the rsa key in the wrapped key. It should // be encrypted. EXPECT_EQ(nullptr, find(provisioning_messages.wrapped_rsa_key(), provisioning_messages.encoded_rsa_key())); } // Test that a wrapped RSA key can be loaded. TEST_F(OEMCryptoLoadsCertificate, LoadWrappedRSAKey) { OEMCryptoResult sts; CreateWrappedRSAKey(); Session s; ASSERT_NO_FATAL_FAILURE(s.open()); sts = OEMCrypto_LoadDRMPrivateKey(s.session_id(), OEMCrypto_RSA_Private_Key, wrapped_rsa_key_.data(), wrapped_rsa_key_.size()); ASSERT_EQ(OEMCrypto_SUCCESS, sts); } // Test a 3072 bit RSA key certificate. TEST_F(OEMCryptoLoadsCertificate, TestLargeRSAKey3072) { encoded_rsa_key_.assign(kTestRSAPKCS8PrivateKeyInfo3_3072, kTestRSAPKCS8PrivateKeyInfo3_3072 + sizeof(kTestRSAPKCS8PrivateKeyInfo3_3072)); CreateWrappedRSAKey(); Session s; ASSERT_NO_FATAL_FAILURE(s.open()); ASSERT_NO_FATAL_FAILURE( s.PreparePublicKey(encoded_rsa_key_.data(), encoded_rsa_key_.size())); ASSERT_NO_FATAL_FAILURE(s.InstallRSASessionTestKey(wrapped_rsa_key_)); LicenseRoundTrip license_messages(&s); ASSERT_NO_FATAL_FAILURE(s.GenerateNonce()); ASSERT_NO_FATAL_FAILURE(license_messages.SignAndVerifyRequest()); ASSERT_NO_FATAL_FAILURE(license_messages.CreateDefaultResponse()); ASSERT_NO_FATAL_FAILURE(license_messages.EncryptAndSignResponse()); ASSERT_EQ(OEMCrypto_SUCCESS, license_messages.LoadResponse()); ASSERT_NO_FATAL_FAILURE(s.TestDecryptCTR()); } // Test an RSA key certificate which has a private key generated using the // Carmichael totient. TEST_F(OEMCryptoLoadsCertificate, TestCarmichaelRSAKey) { encoded_rsa_key_.assign( kTestKeyRSACarmichael_2048, kTestKeyRSACarmichael_2048 + sizeof(kTestKeyRSACarmichael_2048)); CreateWrappedRSAKey(); Session s; ASSERT_NO_FATAL_FAILURE(s.open()); ASSERT_NO_FATAL_FAILURE( s.PreparePublicKey(encoded_rsa_key_.data(), encoded_rsa_key_.size())); ASSERT_NO_FATAL_FAILURE(s.InstallRSASessionTestKey(wrapped_rsa_key_)); LicenseRoundTrip license_messages(&s); ASSERT_NO_FATAL_FAILURE(s.GenerateNonce()); ASSERT_NO_FATAL_FAILURE(license_messages.SignAndVerifyRequest()); ASSERT_NO_FATAL_FAILURE(license_messages.CreateDefaultResponse()); ASSERT_NO_FATAL_FAILURE(license_messages.EncryptAndSignResponse()); ASSERT_EQ(OEMCrypto_SUCCESS, license_messages.LoadResponse()); ASSERT_NO_FATAL_FAILURE(s.TestDecryptCTR()); } // This tests that two sessions can use different RSA keys simultaneously. TEST_F(OEMCryptoLoadsCertificate, TestMultipleRSAKeys) { CreateWrappedRSAKey(); Session s1; // Session s1 loads the default rsa key, but doesn't use it // until after s2 uses its key. ASSERT_NO_FATAL_FAILURE(s1.open()); ASSERT_NO_FATAL_FAILURE( s1.PreparePublicKey(encoded_rsa_key_.data(), encoded_rsa_key_.size())); ASSERT_EQ(OEMCrypto_SUCCESS, OEMCrypto_LoadDRMPrivateKey( s1.session_id(), OEMCrypto_RSA_Private_Key, wrapped_rsa_key_.data(), wrapped_rsa_key_.size())); Session s2; // Session s2 uses a different rsa key. encoded_rsa_key_.assign(kTestRSAPKCS8PrivateKeyInfo4_2048, kTestRSAPKCS8PrivateKeyInfo4_2048 + sizeof(kTestRSAPKCS8PrivateKeyInfo4_2048)); CreateWrappedRSAKey(); ASSERT_NO_FATAL_FAILURE(s2.open()); ASSERT_NO_FATAL_FAILURE( s2.PreparePublicKey(encoded_rsa_key_.data(), encoded_rsa_key_.size())); ASSERT_NO_FATAL_FAILURE(s2.InstallRSASessionTestKey(wrapped_rsa_key_)); LicenseRoundTrip license_messages2(&s2); ASSERT_NO_FATAL_FAILURE(s2.GenerateNonce()); ASSERT_NO_FATAL_FAILURE(license_messages2.SignAndVerifyRequest()); ASSERT_NO_FATAL_FAILURE(license_messages2.CreateDefaultResponse()); ASSERT_NO_FATAL_FAILURE(license_messages2.EncryptAndSignResponse()); ASSERT_EQ(OEMCrypto_SUCCESS, license_messages2.LoadResponse()); ASSERT_NO_FATAL_FAILURE(s2.TestDecryptCTR()); s2.close(); // After s2 has loaded its rsa key, we continue using s1's key. LicenseRoundTrip license_messages1(&s1); ASSERT_NO_FATAL_FAILURE(s1.GenerateNonce()); ASSERT_NO_FATAL_FAILURE(license_messages1.SignAndVerifyRequest()); ASSERT_NO_FATAL_FAILURE(license_messages1.CreateDefaultResponse()); ASSERT_NO_FATAL_FAILURE(license_messages1.EncryptAndSignResponse()); ASSERT_EQ(OEMCrypto_SUCCESS, license_messages1.LoadResponse()); ASSERT_NO_FATAL_FAILURE(s1.TestDecryptCTR()); } // Devices that load certificates, should at least support RSA 2048 keys. TEST_F(OEMCryptoLoadsCertificate, SupportsCertificatesAPI13) { ASSERT_NE(0u, OEMCrypto_Supports_RSA_2048bit & OEMCrypto_SupportedCertificates()) << "Supported certificates is only " << OEMCrypto_SupportedCertificates(); } // These tests are run by all L1 devices that load and use certificates. It is // also run by a few L3 devices that use a baked in certificate, but cannot load // a certificate. class OEMCryptoUsesCertificate : public OEMCryptoLoadsCertificate { protected: void SetUp() override { OEMCryptoLoadsCertificate::SetUp(); ASSERT_NO_FATAL_FAILURE(session_.open()); if (global_features.derive_key_method != DeviceFeatures::LOAD_TEST_RSA_KEY) { InstallTestRSAKey(&session_); } } void TearDown() override { ASSERT_NO_FATAL_FAILURE(session_.close()); OEMCryptoLoadsCertificate::TearDown(); } Session session_; }; // This test is not run by default, because it takes a long time and // is used to measure RSA performance, not test functionality. TEST_F(OEMCryptoLoadsCertificate, RSAPerformance) { const std::chrono::milliseconds kTestDuration(5000); OEMCryptoResult sts; std::chrono::steady_clock clock; wvcdm::TestSleep::Sleep(kShortSleep); // Make sure we are not nonce limited. auto start_time = clock.now(); int count = 15; for (int i = 0; i < count; i++) { // Only 20 nonce available. CreateWrappedRSAKey(); } auto delta_time = clock.now() - start_time; const double provision_time = delta_time / std::chrono::milliseconds(1) / count; Session session; CreateWrappedRSAKey(); start_time = clock.now(); count = 0; while (clock.now() - start_time < kTestDuration) { Session s; ASSERT_NO_FATAL_FAILURE(s.open()); sts = OEMCrypto_LoadDRMPrivateKey(s.session_id(), OEMCrypto_RSA_Private_Key, wrapped_rsa_key_.data(), wrapped_rsa_key_.size()); ASSERT_EQ(OEMCrypto_SUCCESS, sts); const size_t size = 50; vector licenseRequest(size); GetRandBytes(licenseRequest.data(), licenseRequest.size()); size_t signature_length = 0; sts = OEMCrypto_GenerateRSASignature(s.session_id(), licenseRequest.data(), licenseRequest.size(), nullptr, &signature_length, kSign_RSASSA_PSS); ASSERT_EQ(OEMCrypto_ERROR_SHORT_BUFFER, sts); ASSERT_NE(static_cast(0), signature_length); uint8_t* signature = new uint8_t[signature_length]; sts = OEMCrypto_GenerateRSASignature(s.session_id(), licenseRequest.data(), licenseRequest.size(), signature, &signature_length, kSign_RSASSA_PSS); delete[] signature; ASSERT_EQ(OEMCrypto_SUCCESS, sts); count++; } delta_time = clock.now() - start_time; const double license_request_time = delta_time / std::chrono::milliseconds(1) / count; Session s; ASSERT_NO_FATAL_FAILURE(s.open()); ASSERT_EQ(OEMCrypto_SUCCESS, OEMCrypto_LoadDRMPrivateKey( s.session_id(), OEMCrypto_RSA_Private_Key, wrapped_rsa_key_.data(), wrapped_rsa_key_.size())); vector session_key; vector enc_session_key; ASSERT_NO_FATAL_FAILURE( s.PreparePublicKey(encoded_rsa_key_.data(), encoded_rsa_key_.size())); ASSERT_TRUE(s.GenerateRSASessionKey(&session_key, &enc_session_key)); vector mac_context; vector enc_context; s.FillDefaultContext(&mac_context, &enc_context); enc_session_key = wvcdm::a2b_hex( "7789c619aa3b9fa3c0a53f57a4abc6" "02157c8aa57e3c6fb450b0bea22667fb" "0c3200f9d9d618e397837c720dc2dadf" "486f33590744b2a4e54ca134ae7dbf74" "434c2fcf6b525f3e132262f05ea3b3c1" "198595c0e52b573335b2e8a3debd0d0d" "d0306f8fcdde4e76476be71342957251" "e1688c9ca6c1c34ed056d3b989394160" "cf6937e5ce4d39cc73d11a2e93da21a2" "fa019d246c852fe960095b32f120c3c2" "7085f7b64aac344a68d607c0768676ce" "d4c5b2d057f7601921b453a451e1dea0" "843ebfef628d9af2784d68e86b730476" "e136dfe19989de4be30a4e7878efcde5" "ad2b1254f80c0c5dd3cf111b56572217" "b9f58fc1dacbf74b59d354a1e62cfa0e" "bf"); start_time = clock.now(); while (clock.now() - start_time < kTestDuration) { ASSERT_EQ(OEMCrypto_SUCCESS, OEMCrypto_DeriveKeysFromSessionKey( s.session_id(), enc_session_key.data(), enc_session_key.size(), mac_context.data(), mac_context.size(), enc_context.data(), enc_context.size())); count++; } delta_time = clock.now() - start_time; const double derive_keys_time = delta_time / std::chrono::milliseconds(1) / count; const char* level = OEMCrypto_SecurityLevel(); printf("PERF:head, security, provision (ms), lic req(ms), derive keys(ms)\n"); printf("PERF:stat, %s, %8.3f, %8.3f, %8.3f\n", level, provision_time, license_request_time, derive_keys_time); } // Test DeriveKeysFromSessionKey using the maximum size for the HMAC context. TEST_F(OEMCryptoUsesCertificate, GenerateDerivedKeysLargeBuffer) { vector session_key; vector enc_session_key; ASSERT_NO_FATAL_FAILURE(session_.PreparePublicKey(encoded_rsa_key_.data(), encoded_rsa_key_.size())); ASSERT_TRUE(session_.GenerateRSASessionKey(&session_key, &enc_session_key)); const size_t max_size = GetResourceValue(kLargeMessageSize); vector mac_context(max_size); vector enc_context(max_size); // Stripe the data so the two vectors are not identical, and not all zeroes. for (size_t i = 0; i < max_size; i++) { mac_context[i] = i % 0x100; enc_context[i] = (3 * i) % 0x100; } ASSERT_EQ(OEMCrypto_SUCCESS, OEMCrypto_DeriveKeysFromSessionKey( session_.session_id(), enc_session_key.data(), enc_session_key.size(), mac_context.data(), mac_context.size(), enc_context.data(), enc_context.size())); } // This test attempts to use alternate algorithms for loaded device certs. class OEMCryptoLoadsCertificateAlternates : public OEMCryptoLoadsCertificate { protected: void DisallowForbiddenPadding(RSA_Padding_Scheme scheme, size_t size) { OEMCryptoResult sts; Session s; ASSERT_NO_FATAL_FAILURE(s.open()); sts = OEMCrypto_LoadDRMPrivateKey(s.session_id(), OEMCrypto_RSA_Private_Key, wrapped_rsa_key_.data(), wrapped_rsa_key_.size()); ASSERT_EQ(OEMCrypto_SUCCESS, sts); // Sign a Message vector licenseRequest(size); GetRandBytes(licenseRequest.data(), licenseRequest.size()); size_t signature_length = 256; vector signature(signature_length); sts = OEMCrypto_GenerateRSASignature( s.session_id(), licenseRequest.data(), licenseRequest.size(), signature.data(), &signature_length, scheme); // Allow OEMCrypto to request a full buffer. if (sts == OEMCrypto_ERROR_SHORT_BUFFER) { ASSERT_NE(static_cast(0), signature_length); signature.assign(signature_length, 0); sts = OEMCrypto_GenerateRSASignature( s.session_id(), licenseRequest.data(), licenseRequest.size(), signature.data(), &signature_length, scheme); } EXPECT_NE(OEMCrypto_SUCCESS, sts) << "Signed with forbidden padding scheme=" << (int)scheme << ", size=" << (int)size; vector zero(signature_length, 0); ASSERT_EQ(zero, signature); // signature should not be computed. } void TestSignature(RSA_Padding_Scheme scheme, size_t size) { OEMCryptoResult sts; Session s; ASSERT_NO_FATAL_FAILURE(s.open()); sts = OEMCrypto_LoadDRMPrivateKey(s.session_id(), OEMCrypto_RSA_Private_Key, wrapped_rsa_key_.data(), wrapped_rsa_key_.size()); ASSERT_EQ(OEMCrypto_SUCCESS, sts); vector licenseRequest(size); GetRandBytes(licenseRequest.data(), licenseRequest.size()); size_t signature_length = 0; sts = OEMCrypto_GenerateRSASignature(s.session_id(), licenseRequest.data(), licenseRequest.size(), nullptr, &signature_length, scheme); ASSERT_EQ(OEMCrypto_ERROR_SHORT_BUFFER, sts); ASSERT_NE(static_cast(0), signature_length); uint8_t* signature = new uint8_t[signature_length]; sts = OEMCrypto_GenerateRSASignature(s.session_id(), licenseRequest.data(), licenseRequest.size(), signature, &signature_length, scheme); ASSERT_EQ(OEMCrypto_SUCCESS, sts) << "Failed to sign with padding scheme=" << (int)scheme << ", size=" << (int)size; ASSERT_NO_FATAL_FAILURE( s.PreparePublicKey(encoded_rsa_key_.data(), encoded_rsa_key_.size())); ASSERT_NO_FATAL_FAILURE(s.VerifyRSASignature(licenseRequest, signature, signature_length, scheme)); delete[] signature; } void DisallowDeriveKeys() { OEMCryptoResult sts; Session s; ASSERT_NO_FATAL_FAILURE(s.open()); sts = OEMCrypto_LoadDRMPrivateKey(s.session_id(), OEMCrypto_RSA_Private_Key, wrapped_rsa_key_.data(), wrapped_rsa_key_.size()); ASSERT_EQ(OEMCrypto_SUCCESS, sts); s.GenerateNonce(); vector session_key; vector enc_session_key; ASSERT_NO_FATAL_FAILURE( s.PreparePublicKey(encoded_rsa_key_.data(), encoded_rsa_key_.size())); ASSERT_TRUE(s.GenerateRSASessionKey(&session_key, &enc_session_key)); vector mac_context; vector enc_context; s.FillDefaultContext(&mac_context, &enc_context); ASSERT_NE(OEMCrypto_SUCCESS, OEMCrypto_DeriveKeysFromSessionKey( s.session_id(), enc_session_key.data(), enc_session_key.size(), mac_context.data(), mac_context.size(), enc_context.data(), enc_context.size())); } // If force is true, we assert that the key loads successfully. void LoadWithAllowedSchemes(uint32_t schemes, bool force) { Session s; ProvisioningRoundTrip provisioning_messages(&s, encoded_rsa_key_); provisioning_messages.set_allowed_schemes(schemes); provisioning_messages.PrepareSession(keybox_); ASSERT_NO_FATAL_FAILURE(provisioning_messages.SignAndVerifyRequest()); ASSERT_NO_FATAL_FAILURE(provisioning_messages.CreateDefaultResponse()); ASSERT_NO_FATAL_FAILURE(provisioning_messages.EncryptAndSignResponse()); OEMCryptoResult sts = provisioning_messages.LoadResponse(); encoded_rsa_key_ = provisioning_messages.encoded_rsa_key(); wrapped_rsa_key_ = provisioning_messages.wrapped_rsa_key(); key_loaded_ = (wrapped_rsa_key_.size() > 0); if (force) { EXPECT_EQ(OEMCrypto_SUCCESS, sts); EXPECT_EQ(nullptr, find(wrapped_rsa_key_, encoded_rsa_key_)); ASSERT_TRUE(key_loaded_); } } bool key_loaded_; }; // The alternate padding is only required for cast receivers, but all devices // should forbid the alternate padding for regular certificates. TEST_F(OEMCryptoLoadsCertificateAlternates, DisallowForbiddenPaddingAPI09) { LoadWithAllowedSchemes(kSign_RSASSA_PSS, true); // Use default padding scheme DisallowForbiddenPadding(kSign_PKCS1_Block1, 50); } // The alternate padding is only required for cast receivers, but if a device // does load an alternate certificate, it should NOT use it for generating // a license request signature. TEST_F(OEMCryptoLoadsCertificateAlternates, TestSignaturePKCS1_API16) { // Try to load an RSA key with alternative padding schemes. This signing // scheme is used by cast receivers. LoadWithAllowedSchemes(kSign_PKCS1_Block1, false); // If the device is a cast receiver, then this scheme is required. if (global_features.cast_receiver) ASSERT_TRUE(key_loaded_); // If the key loaded with no error, then we will verify that it is not used // for forbidden padding schemes. if (key_loaded_) { // The other padding scheme should fail. DisallowForbiddenPadding(kSign_RSASSA_PSS, 83); DisallowDeriveKeys(); if (global_features.cast_receiver) { // A signature with a valid size should succeed. TestSignature(kSign_PKCS1_Block1, 83); TestSignature(kSign_PKCS1_Block1, 50); } // A signature with padding that is too big should fail. DisallowForbiddenPadding(kSign_PKCS1_Block1, 84); // too big. } } // This test verifies RSA signing with the alternate padding scheme used by // Android cast receivers, PKCS1 Block 1. These tests are not required for // other devices, and should be filtered out by DeviceFeatures::Initialize for // those devices. class OEMCryptoCastReceiverTest : public OEMCryptoLoadsCertificateAlternates { protected: vector encode(uint8_t type, const vector& substring) { vector result; result.push_back(type); if (substring.size() < 0x80) { uint8_t length = substring.size(); result.push_back(length); } else if (substring.size() < 0x100) { result.push_back(0x81); uint8_t length = substring.size(); result.push_back(length); } else { result.push_back(0x82); uint16_t length = substring.size(); result.push_back(length >> 8); result.push_back(length & 0xFF); } result.insert(result.end(), substring.begin(), substring.end()); return result; } vector concat(const vector& a, const vector& b) { vector result = a; result.insert(result.end(), b.begin(), b.end()); return result; } // This encodes the RSA key used in the PKCS#1 signing tests below. void BuildRSAKey() { vector field_n = encode(0x02, wvcdm::a2b_hex("00" "df271fd25f8644496b0c81be4bd50297" "ef099b002a6fd67727eb449cea566ed6" "a3981a71312a141cabc9815c1209e320" "a25b32464e9999f18ca13a9fd3892558" "f9e0adefdd3650dd23a3f036d60fe398" "843706a40b0b8462c8bee3bce12f1f28" "60c2444cdc6a44476a75ff4aa24273cc" "be3bf80248465f8ff8c3a7f3367dfc0d" "f5b6509a4f82811cedd81cdaaa73c491" "da412170d544d4ba96b97f0afc806549" "8d3a49fd910992a1f0725be24f465cfe" "7e0eabf678996c50bc5e7524abf73f15" "e5bef7d518394e3138ce4944506aaaaf" "3f9b236dcab8fc00f87af596fdc3d9d6" "c75cd508362fae2cbeddcc4c7450b17b" "776c079ecca1f256351a43b97dbe2153")); vector field_e = encode(0x02, wvcdm::a2b_hex("010001")); vector field_d = encode(0x02, wvcdm::a2b_hex("5bd910257830dce17520b03441a51a8c" "ab94020ac6ecc252c808f3743c95b7c8" "3b8c8af1a5014346ebc4242cdfb5d718" "e30a733e71f291e4d473b61bfba6daca" "ed0a77bd1f0950ae3c91a8f901118825" "89e1d62765ee671e7baeea309f64d447" "bbcfa9ea12dce05e9ea8939bc5fe6108" "581279c982b308794b3448e7f7b95229" "2df88c80cb40142c4b5cf5f8ddaa0891" "678d610e582fcb880f0d707caf47d09a" "84e14ca65841e5a3abc5e9dba94075a9" "084341f0edad9b68e3b8e082b80b6e6e" "8a0547b44fb5061b6a9131603a5537dd" "abd01d8e863d8922e9aa3e4bfaea0b39" "d79283ad2cbc8a59cce7a6ecf4e4c81e" "d4c6591c807defd71ab06866bb5e7745")); vector field_p = encode(0x02, wvcdm::a2b_hex("00" "f44f5e4246391f482b2f5296e3602eb3" "4aa136427710f7c0416d403fd69d4b29" "130cfebef34e885abdb1a8a0a5f0e9b5" "c33e1fc3bfc285b1ae17e40cc67a1913" "dd563719815ebaf8514c2a7aa0018e63" "b6c631dc315a46235716423d11ff5803" "4e610645703606919f5c7ce2660cd148" "bd9efc123d9c54b6705590d006cfcf3f")); vector field_q = encode(0x02, wvcdm::a2b_hex("00" "e9d49841e0e0a6ad0d517857133e36dc" "72c1bdd90f9174b52e26570f373640f1" "c185e7ea8e2ed7f1e4ebb951f70a5802" "3633b0097aec67c6dcb800fc1a67f9bb" "0563610f08ebc8746ad129772136eb1d" "daf46436450d318332a84982fe5d28db" "e5b3e912407c3e0e03100d87d436ee40" "9eec1cf85e80aba079b2e6106b97bced")); vector field_exp1 = encode(0x02, wvcdm::a2b_hex("00" "ed102acdb26871534d1c414ecad9a4d7" "32fe95b10eea370da62f05de2c393b1a" "633303ea741b6b3269c97f704b352702" "c9ae79922f7be8d10db67f026a8145de" "41b30c0a42bf923bac5f7504c248604b" "9faa57ed6b3246c6ba158e36c644f8b9" "548fcf4f07e054a56f768674054440bc" "0dcbbc9b528f64a01706e05b0b91106f")); vector field_exp2 = encode(0x02, wvcdm::a2b_hex("6827924a85e88b55ba00f8219128bd37" "24c6b7d1dfe5629ef197925fecaff5ed" "b9cdf3a7befd8ea2e8dd3707138b3ff8" "7c3c39c57f439e562e2aa805a39d7cd7" "9966d2ece7845f1dbc16bee99999e4d0" "bf9eeca45fcda8a8500035fe6b5f03bc" "2f6d1bfc4d4d0a3723961af0cdce4a01" "eec82d7f5458ec19e71b90eeef7dff61")); vector field_invq = encode(0x02, wvcdm::a2b_hex("57b73888d183a99a6307422277551a3d" "9e18adf06a91e8b55ceffef9077c8496" "948ecb3b16b78155cb2a3a57c119d379" "951c010aa635edcf62d84c5a122a8d67" "ab5fa9e5a4a8772a1e943bafc70ae3a4" "c1f0f3a4ddffaefd1892c8cb33bb0d0b" "9590e963a69110fb34db7b906fc4ba28" "36995aac7e527490ac952a02268a4f18")); // Header of rsa key is constant. encoded_rsa_key_ = wvcdm::a2b_hex( // 0x02 0x01 0x00 == integer, size 1 byte, value = 0 (field=version) "020100" // 0x30, sequence, size = d = 13 (field=pkeyalg) AlgorithmIdentifier "300d" // 0x06 = object identifier. length = 9 // (this should be 1.2.840.113549.1.1.1) (field=algorithm) "0609" "2a" // 1*0x40 + 2 = 42 = 0x2a. "8648" // 840 = 0x348, 0x03 *2 + 0x80 + (0x48>>15) = 0x86. // 0x48 -> 0x48 "86f70d" // 113549 = 0x1668d -> (110 , 1110111, 0001101) // -> (0x80+0x06, 0x80+0x77, 0x0d) "01" // 1 "01" // 1 "01" // 1 "05" // null object. (field=parameter?) "00" // size of null object ); vector pkey = wvcdm::a2b_hex("020100"); // integer, version = 0. pkey = concat(pkey, field_n); pkey = concat(pkey, field_e); pkey = concat(pkey, field_d); pkey = concat(pkey, field_p); pkey = concat(pkey, field_q); pkey = concat(pkey, field_exp1); pkey = concat(pkey, field_exp2); pkey = concat(pkey, field_invq); pkey = encode(0x30, pkey); pkey = encode(0x04, pkey); encoded_rsa_key_ = concat(encoded_rsa_key_, pkey); encoded_rsa_key_ = encode(0x30, encoded_rsa_key_); // 0x30=sequence } // This is used to test a signature from the file pkcs1v15sign-vectors.txt. void TestSignature(RSA_Padding_Scheme scheme, const vector& message, const vector& correct_signature) { OEMCryptoResult sts; Session s; ASSERT_NO_FATAL_FAILURE(s.open()); sts = OEMCrypto_LoadDRMPrivateKey(s.session_id(), OEMCrypto_RSA_Private_Key, wrapped_rsa_key_.data(), wrapped_rsa_key_.size()); ASSERT_EQ(OEMCrypto_SUCCESS, sts); // The application will compute the SHA-1 Hash of the message, so this // test must do that also. uint8_t hash[SHA_DIGEST_LENGTH]; if (!SHA1(message.data(), message.size(), hash)) { dump_boringssl_error(); FAIL() << "boringssl error creating SHA1 hash."; } // The application will prepend the digest info to the hash. // SHA-1 digest info prefix = 0x30 0x21 0x30 ... vector digest = wvcdm::a2b_hex("3021300906052b0e03021a05000414"); digest.insert(digest.end(), hash, hash + SHA_DIGEST_LENGTH); // OEMCrypto will apply the padding, and encrypt to generate the signature. size_t signature_length = 0; sts = OEMCrypto_GenerateRSASignature(s.session_id(), digest.data(), digest.size(), nullptr, &signature_length, scheme); ASSERT_EQ(OEMCrypto_ERROR_SHORT_BUFFER, sts); ASSERT_NE(static_cast(0), signature_length); vector signature(signature_length); sts = OEMCrypto_GenerateRSASignature(s.session_id(), digest.data(), digest.size(), signature.data(), &signature_length, scheme); ASSERT_EQ(OEMCrypto_SUCCESS, sts) << "Failed to sign with padding scheme=" << (int)scheme << ", size=" << (int)message.size(); ASSERT_NO_FATAL_FAILURE( s.PreparePublicKey(encoded_rsa_key_.data(), encoded_rsa_key_.size())); // Verify that the signature matches the official test vector. ASSERT_EQ(correct_signature.size(), signature_length); signature.resize(signature_length); ASSERT_EQ(correct_signature, signature); // Also verify that our verification algorithm agrees. This is not needed // to test OEMCrypto, but it does verify that this test is valid. ASSERT_NO_FATAL_FAILURE(s.VerifyRSASignature(digest, signature.data(), signature_length, scheme)); ASSERT_NO_FATAL_FAILURE(s.VerifyRSASignature( digest, correct_signature.data(), correct_signature.size(), scheme)); } }; // CAST Receivers should report that they support cast certificates. TEST_F(OEMCryptoCastReceiverTest, SupportsCertificatesAPI13) { ASSERT_NE(0u, OEMCrypto_Supports_RSA_CAST & OEMCrypto_SupportedCertificates()); } // # PKCS#1 v1.5 Signature Example 15.1 TEST_F(OEMCryptoCastReceiverTest, TestSignaturePKCS1_15_1) { BuildRSAKey(); LoadWithAllowedSchemes(kSign_PKCS1_Block1, true); vector message = wvcdm::a2b_hex( "f45d55f35551e975d6a8dc7ea9f48859" "3940cc75694a278f27e578a163d839b3" "4040841808cf9c58c9b8728bf5f9ce8e" "e811ea91714f47bab92d0f6d5a26fcfe" "ea6cd93b910c0a2c963e64eb1823f102" "753d41f0335910ad3a977104f1aaf6c3" "742716a9755d11b8eed690477f445c5d" "27208b2e284330fa3d301423fa7f2d08" "6e0ad0b892b9db544e456d3f0dab85d9" "53c12d340aa873eda727c8a649db7fa6" "3740e25e9af1533b307e61329993110e" "95194e039399c3824d24c51f22b26bde" "1024cd395958a2dfeb4816a6e8adedb5" "0b1f6b56d0b3060ff0f1c4cb0d0e001d" "d59d73be12"); vector signature = wvcdm::a2b_hex( "b75a5466b65d0f300ef53833f2175c8a" "347a3804fc63451dc902f0b71f908345" "9ed37a5179a3b723a53f1051642d7737" "4c4c6c8dbb1ca20525f5c9f32db77695" "3556da31290e22197482ceb69906c46a" "758fb0e7409ba801077d2a0a20eae7d1" "d6d392ab4957e86b76f0652d68b83988" "a78f26e11172ea609bf849fbbd78ad7e" "dce21de662a081368c040607cee29db0" "627227f44963ad171d2293b633a392e3" "31dca54fe3082752f43f63c161b447a4" "c65a6875670d5f6600fcc860a1caeb0a" "88f8fdec4e564398a5c46c87f68ce070" "01f6213abe0ab5625f87d19025f08d81" "dac7bd4586bc9382191f6d2880f6227e" "5df3eed21e7792d249480487f3655261"); TestSignature(kSign_PKCS1_Block1, message, signature); } // # PKCS#1 v1.5 Signature Example 15.2 TEST_F(OEMCryptoCastReceiverTest, TestSignaturePKCS1_15_2) { BuildRSAKey(); LoadWithAllowedSchemes(kSign_PKCS1_Block1, true); vector message = wvcdm::a2b_hex( "c14b4c6075b2f9aad661def4ecfd3cb9" "33c623f4e63bf53410d2f016d1ab98e2" "729eccf8006cd8e08050737d95fdbf29" "6b66f5b9792a902936c4f7ac69f51453" "ce4369452dc22d96f037748114662000" "dd9cd3a5e179f4e0f81fa6a0311ca1ae" "e6519a0f63cec78d27bb726393fb7f1f" "88cde7c97f8a66cd66301281dac3f3a4" "33248c75d6c2dcd708b6a97b0a3f325e" "0b2964f8a5819e479b"); vector signature = wvcdm::a2b_hex( "afa7343462bea122cc149fca70abdae7" "9446677db5373666af7dc313015f4de7" "86e6e394946fad3cc0e2b02bedba5047" "fe9e2d7d099705e4a39f28683279cf0a" "c85c1530412242c0e918953be000e939" "cf3bf182525e199370fa7907eba69d5d" "b4631017c0e36df70379b5db8d4c695a" "979a8e6173224065d7dc15132ef28cd8" "22795163063b54c651141be86d36e367" "35bc61f31fca574e5309f3a3bbdf91ef" "f12b99e9cc1744f1ee9a1bd22c5bad96" "ad481929251f0343fd36bcf0acde7f11" "e5ad60977721202796fe061f9ada1fc4" "c8e00d6022a8357585ffe9fdd59331a2" "8c4aa3121588fb6cf68396d8ac054659" "9500c9708500a5972bd54f72cf8db0c8"); TestSignature(kSign_PKCS1_Block1, message, signature); } // # PKCS#1 v1.5 Signature Example 15.3 TEST_F(OEMCryptoCastReceiverTest, TestSignaturePKCS1_15_3) { BuildRSAKey(); LoadWithAllowedSchemes(kSign_PKCS1_Block1, true); vector message = wvcdm::a2b_hex( "d02371ad7ee48bbfdb2763de7a843b94" "08ce5eb5abf847ca3d735986df84e906" "0bdbcdd3a55ba55dde20d4761e1a21d2" "25c1a186f4ac4b3019d3adf78fe63346" "67f56f70c901a0a2700c6f0d56add719" "592dc88f6d2306c7009f6e7a635b4cb3" "a502dfe68ddc58d03be10a1170004fe7" "4dd3e46b82591ff75414f0c4a03e605e" "20524f2416f12eca589f111b75d639c6" "1baa80cafd05cf3500244a219ed9ced9" "f0b10297182b653b526f400f2953ba21" "4d5bcd47884132872ae90d4d6b1f4215" "39f9f34662a56dc0e7b4b923b6231e30" "d2676797817f7c337b5ac824ba93143b" "3381fa3dce0e6aebd38e67735187b1eb" "d95c02"); vector signature = wvcdm::a2b_hex( "3bac63f86e3b70271203106b9c79aabd" "9f477c56e4ee58a4fce5baf2cab4960f" "88391c9c23698be75c99aedf9e1abf17" "05be1dac33140adb48eb31f450bb9efe" "83b7b90db7f1576d33f40c1cba4b8d6b" "1d3323564b0f1774114fa7c08e6d1e20" "dd8fbba9b6ac7ad41e26b4568f4a8aac" "bfd178a8f8d2c9d5f5b88112935a8bc9" "ae32cda40b8d20375510735096536818" "ce2b2db71a9772c9b0dda09ae10152fa" "11466218d091b53d92543061b7294a55" "be82ff35d5c32fa233f05aaac7585030" "7ecf81383c111674397b1a1b9d3bf761" "2ccbe5bacd2b38f0a98397b24c83658f" "b6c0b4140ef11970c4630d44344e76ea" "ed74dcbee811dbf6575941f08a6523b8"); TestSignature(kSign_PKCS1_Block1, message, signature); }; // # PKCS#1 v1.5 Signature Example 15.4 TEST_F(OEMCryptoCastReceiverTest, TestSignaturePKCS1_15_4) { BuildRSAKey(); LoadWithAllowedSchemes(kSign_PKCS1_Block1, true); vector message = wvcdm::a2b_hex( "29035584ab7e0226a9ec4b02e8dcf127" "2dc9a41d73e2820007b0f6e21feccd5b" "d9dbb9ef88cd6758769ee1f956da7ad1" "8441de6fab8386dbc693"); vector signature = wvcdm::a2b_hex( "28d8e3fcd5dddb21ffbd8df1630d7377" "aa2651e14cad1c0e43ccc52f907f946d" "66de7254e27a6c190eb022ee89ecf622" "4b097b71068cd60728a1aed64b80e545" "7bd3106dd91706c937c9795f2b36367f" "f153dc2519a8db9bdf2c807430c451de" "17bbcd0ce782b3e8f1024d90624dea7f" "1eedc7420b7e7caa6577cef43141a726" "4206580e44a167df5e41eea0e69a8054" "54c40eefc13f48e423d7a32d02ed42c0" "ab03d0a7cf70c5860ac92e03ee005b60" "ff3503424b98cc894568c7c56a023355" "1cebe588cf8b0167b7df13adcad82867" "6810499c704da7ae23414d69e3c0d2db" "5dcbc2613bc120421f9e3653c5a87672" "97643c7e0740de016355453d6c95ae72"); TestSignature(kSign_PKCS1_Block1, message, signature); } // # PKCS#1 v1.5 Signature Example 15.5 TEST_F(OEMCryptoCastReceiverTest, TestSignaturePKCS1_15_5) { BuildRSAKey(); LoadWithAllowedSchemes(kSign_PKCS1_Block1, true); vector message = wvcdm::a2b_hex("bda3a1c79059eae598308d3df609"); vector signature = wvcdm::a2b_hex( "a156176cb96777c7fb96105dbd913bc4" "f74054f6807c6008a1a956ea92c1f81c" "b897dc4b92ef9f4e40668dc7c556901a" "cb6cf269fe615b0fb72b30a513386923" "14b0e5878a88c2c7774bd16939b5abd8" "2b4429d67bd7ac8e5ea7fe924e20a6ec" "662291f2548d734f6634868b039aa5f9" "d4d906b2d0cb8585bf428547afc91c6e" "2052ddcd001c3ef8c8eefc3b6b2a82b6" "f9c88c56f2e2c3cb0be4b80da95eba37" "1d8b5f60f92538743ddbb5da2972c71f" "e7b9f1b790268a0e770fc5eb4d5dd852" "47d48ae2ec3f26255a3985520206a1f2" "68e483e9dbb1d5cab190917606de31e7" "c5182d8f151bf41dfeccaed7cde690b2" "1647106b490c729d54a8fe2802a6d126"); TestSignature(kSign_PKCS1_Block1, message, signature); } // # PKCS#1 v1.5 Signature Example 15.6 TEST_F(OEMCryptoCastReceiverTest, TestSignaturePKCS1_15_6) { BuildRSAKey(); LoadWithAllowedSchemes(kSign_PKCS1_Block1, true); vector message = wvcdm::a2b_hex( "c187915e4e87da81c08ed4356a0cceac" "1c4fb5c046b45281b387ec28f1abfd56" "7e546b236b37d01ae71d3b2834365d3d" "f380b75061b736b0130b070be58ae8a4" "6d12166361b613dbc47dfaeb4ca74645" "6c2e888385525cca9dd1c3c7a9ada76d" "6c"); vector signature = wvcdm::a2b_hex( "9cab74163608669f7555a333cf196fe3" "a0e9e5eb1a32d34bb5c85ff689aaab0e" "3e65668ed3b1153f94eb3d8be379b8ee" "f007c4a02c7071ce30d8bb341e58c620" "f73d37b4ecbf48be294f6c9e0ecb5e63" "fec41f120e5553dfa0ebebbb72640a95" "37badcb451330229d9f710f62e3ed8ec" "784e50ee1d9262b42671340011d7d098" "c6f2557b2131fa9bd0254636597e88ec" "b35a240ef0fd85957124df8080fee1e1" "49af939989e86b26c85a5881fae8673d" "9fd40800dd134eb9bdb6410f420b0aa9" "7b20efcf2eb0c807faeb83a3ccd9b51d" "4553e41dfc0df6ca80a1e81dc234bb83" "89dd195a38b42de4edc49d346478b9f1" "1f0557205f5b0bd7ffe9c850f396d7c4"); TestSignature(kSign_PKCS1_Block1, message, signature); } // # PKCS#1 v1.5 Signature Example 15.7 TEST_F(OEMCryptoCastReceiverTest, TestSignaturePKCS1_15_7) { BuildRSAKey(); LoadWithAllowedSchemes(kSign_PKCS1_Block1, true); vector message = wvcdm::a2b_hex( "abfa2ecb7d29bd5bcb9931ce2bad2f74" "383e95683cee11022f08e8e7d0b8fa05" "8bf9eb7eb5f98868b5bb1fb5c31ceda3" "a64f1a12cdf20fcd0e5a246d7a1773d8" "dba0e3b277545babe58f2b96e3f4edc1" "8eabf5cd2a560fca75fe96e07d859def" "b2564f3a34f16f11e91b3a717b41af53" "f6605323001aa406c6"); vector signature = wvcdm::a2b_hex( "c4b437bcf703f352e1faf74eb9622039" "426b5672caf2a7b381c6c4f0191e7e4a" "98f0eebcd6f41784c2537ff0f99e7498" "2c87201bfbc65eae832db71d16dacadb" "0977e5c504679e40be0f9db06ffd848d" "d2e5c38a7ec021e7f68c47dfd38cc354" "493d5339b4595a5bf31e3f8f13816807" "373df6ad0dc7e731e51ad19eb4754b13" "4485842fe709d378444d8e36b1724a4f" "da21cafee653ab80747f7952ee804dea" "b1039d84139945bbf4be82008753f3c5" "4c7821a1d241f42179c794ef7042bbf9" "955656222e45c34369a384697b6ae742" "e18fa5ca7abad27d9fe71052e3310d0f" "52c8d12ea33bf053a300f4afc4f098df" "4e6d886779d64594d369158fdbc1f694"); TestSignature(kSign_PKCS1_Block1, message, signature); } // # PKCS#1 v1.5 Signature Example 15.8 TEST_F(OEMCryptoCastReceiverTest, TestSignaturePKCS1_15_8) { BuildRSAKey(); LoadWithAllowedSchemes(kSign_PKCS1_Block1, true); vector message = wvcdm::a2b_hex( "df4044a89a83e9fcbf1262540ae3038b" "bc90f2b2628bf2a4467ac67722d8546b" "3a71cb0ea41669d5b4d61859c1b4e47c" "ecc5933f757ec86db0644e311812d00f" "b802f03400639c0e364dae5aebc5791b" "c655762361bc43c53d3c7886768f7968" "c1c544c6f79f7be820c7e2bd2f9d73e6" "2ded6d2e937e6a6daef90ee37a1a52a5" "4f00e31addd64894cf4c02e16099e29f" "9eb7f1a7bb7f84c47a2b594813be02a1" "7b7fc43b34c22c91925264126c89f86b" "b4d87f3ef131296c53a308e0331dac8b" "af3b63422266ecef2b90781535dbda41" "cbd0cf22a8cbfb532ec68fc6afb2ac06"); vector signature = wvcdm::a2b_hex( "1414b38567ae6d973ede4a06842dcc0e" "0559b19e65a4889bdbabd0fd02806829" "13bacd5dc2f01b30bb19eb810b7d9ded" "32b284f147bbe771c930c6052aa73413" "90a849f81da9cd11e5eccf246dbae95f" "a95828e9ae0ca3550325326deef9f495" "30ba441bed4ac29c029c9a2736b1a419" "0b85084ad150426b46d7f85bd702f48d" "ac5f71330bc423a766c65cc1dcab20d3" "d3bba72b63b3ef8244d42f157cb7e3a8" "ba5c05272c64cc1ad21a13493c3911f6" "0b4e9f4ecc9900eb056ee59d6fe4b8ff" "6e8048ccc0f38f2836fd3dfe91bf4a38" "6e1ecc2c32839f0ca4d1b27a568fa940" "dd64ad16bd0125d0348e383085f08894" "861ca18987227d37b42b584a8357cb04"); TestSignature(kSign_PKCS1_Block1, message, signature); } // # PKCS#1 v1.5 Signature Example 15.9 TEST_F(OEMCryptoCastReceiverTest, TestSignaturePKCS1_15_9) { BuildRSAKey(); LoadWithAllowedSchemes(kSign_PKCS1_Block1, true); vector message = wvcdm::a2b_hex( "ea941ff06f86c226927fcf0e3b11b087" "2676170c1bfc33bda8e265c77771f9d0" "850164a5eecbcc5ce827fbfa07c85214" "796d8127e8caa81894ea61ceb1449e72" "fea0a4c943b2da6d9b105fe053b9039a" "9cc53d420b7539fab2239c6b51d17e69" "4c957d4b0f0984461879a0759c4401be" "ecd4c606a0afbd7a076f50a2dfc2807f" "24f1919baa7746d3a64e268ed3f5f8e6" "da83a2a5c9152f837cb07812bd5ba7d3" "a07985de88113c1796e9b466ec299c5a" "c1059e27f09415"); vector signature = wvcdm::a2b_hex( "ceeb84ccb4e9099265650721eea0e8ec" "89ca25bd354d4f64564967be9d4b08b3" "f1c018539c9d371cf8961f2291fbe0dc" "2f2f95fea47b639f1e12f4bc381cef0c" "2b7a7b95c3adf27605b7f63998c3cbad" "542808c3822e064d4ad14093679e6e01" "418a6d5c059684cd56e34ed65ab605b8" "de4fcfa640474a54a8251bbb7326a42d" "08585cfcfc956769b15b6d7fdf7da84f" "81976eaa41d692380ff10eaecfe0a579" "682909b5521fade854d797b8a0345b9a" "864e0588f6caddbf65f177998e180d1f" "102443e6dca53a94823caa9c3b35f322" "583c703af67476159ec7ec93d1769b30" "0af0e7157dc298c6cd2dee2262f8cddc" "10f11e01741471bbfd6518a175734575"); TestSignature(kSign_PKCS1_Block1, message, signature); } // # PKCS#1 v1.5 Signature Example 15.10 TEST_F(OEMCryptoCastReceiverTest, TestSignaturePKCS1_15_10) { BuildRSAKey(); LoadWithAllowedSchemes(kSign_PKCS1_Block1, true); vector message = wvcdm::a2b_hex( "d8b81645c13cd7ecf5d00ed2c91b9acd" "46c15568e5303c4a9775ede76b48403d" "6be56c05b6b1cf77c6e75de096c5cb35" "51cb6fa964f3c879cf589d28e1da2f9d" "ec"); vector signature = wvcdm::a2b_hex( "2745074ca97175d992e2b44791c323c5" "7167165cdd8da579cdef4686b9bb404b" "d36a56504eb1fd770f60bfa188a7b24b" "0c91e881c24e35b04dc4dd4ce38566bc" "c9ce54f49a175fc9d0b22522d9579047" "f9ed42eca83f764a10163997947e7d2b" "52ff08980e7e7c2257937b23f3d279d4" "cd17d6f495546373d983d536efd7d1b6" "7181ca2cb50ac616c5c7abfbb9260b91" "b1a38e47242001ff452f8de10ca6eaea" "dcaf9edc28956f28a711291fc9a80878" "b8ba4cfe25b8281cb80bc9cd6d2bd182" "5246eebe252d9957ef93707352084e6d" "36d423551bf266a85340fb4a6af37088" "0aab07153d01f48d086df0bfbec05e7b" "443b97e71718970e2f4bf62023e95b67"); TestSignature(kSign_PKCS1_Block1, message, signature); } // # PKCS#1 v1.5 Signature Example 15.11 TEST_F(OEMCryptoCastReceiverTest, TestSignaturePKCS1_15_11) { BuildRSAKey(); LoadWithAllowedSchemes(kSign_PKCS1_Block1, true); vector message = wvcdm::a2b_hex( "e5739b6c14c92d510d95b826933337ff" "0d24ef721ac4ef64c2bad264be8b44ef" "a1516e08a27eb6b611d3301df0062dae" "fc73a8c0d92e2c521facbc7b26473876" "7ea6fc97d588a0baf6ce50adf79e600b" "d29e345fcb1dba71ac5c0289023fe4a8" "2b46a5407719197d2e958e3531fd54ae" "f903aabb4355f88318994ed3c3dd62f4" "20a7"); vector signature = wvcdm::a2b_hex( "be40a5fb94f113e1b3eff6b6a33986f2" "02e363f07483b792e68dfa5554df0466" "cc32150950783b4d968b639a04fd2fb9" "7f6eb967021f5adccb9fca95acc8f2cd" "885a380b0a4e82bc760764dbab88c1e6" "c0255caa94f232199d6f597cc9145b00" "e3d4ba346b559a8833ad1516ad5163f0" "16af6a59831c82ea13c8224d84d0765a" "9d12384da460a8531b4c407e04f4f350" "709eb9f08f5b220ffb45abf6b75d1579" "fd3f1eb55fc75b00af8ba3b087827fe9" "ae9fb4f6c5fa63031fe582852fe2834f" "9c89bff53e2552216bc7c1d4a3d5dc2b" "a6955cd9b17d1363e7fee8ed7629753f" "f3125edd48521ae3b9b03217f4496d0d" "8ede57acbc5bd4deae74a56f86671de2"); TestSignature(kSign_PKCS1_Block1, message, signature); } // # PKCS#1 v1.5 Signature Example 15.12 TEST_F(OEMCryptoCastReceiverTest, TestSignaturePKCS1_15_12) { BuildRSAKey(); LoadWithAllowedSchemes(kSign_PKCS1_Block1, true); vector message = wvcdm::a2b_hex( "7af42835917a88d6b3c6716ba2f5b0d5" "b20bd4e2e6e574e06af1eef7c81131be" "22bf8128b9cbc6ec00275ba80294a5d1" "172d0824a79e8fdd830183e4c00b9678" "2867b1227fea249aad32ffc5fe007bc5" "1f21792f728deda8b5708aa99cabab20" "a4aa783ed86f0f27b5d563f42e07158c" "ea72d097aa6887ec411dd012912a5e03" "2bbfa678507144bcc95f39b58be7bfd1" "759adb9a91fa1d6d8226a8343a8b849d" "ae76f7b98224d59e28f781f13ece605f" "84f6c90bae5f8cf378816f4020a7dda1" "bed90c92a23634d203fac3fcd86d68d3" "182a7d9ccabe7b0795f5c655e9acc4e3" "ec185140d10cef053464ab175c83bd83" "935e3dabaf3462eebe63d15f573d269a"); vector signature = wvcdm::a2b_hex( "4e78c5902b807914d12fa537ae6871c8" "6db8021e55d1adb8eb0ccf1b8f36ab7d" "ad1f682e947a627072f03e627371781d" "33221d174abe460dbd88560c22f69011" "6e2fbbe6e964363a3e5283bb5d946ef1" "c0047eba038c756c40be7923055809b0" "e9f34a03a58815ebdde767931f018f6f" "1878f2ef4f47dd374051dd48685ded6e" "fb3ea8021f44be1d7d149398f98ea9c0" "8d62888ebb56192d17747b6b8e170954" "31f125a8a8e9962aa31c285264e08fb2" "1aac336ce6c38aa375e42bc92ab0ab91" "038431e1f92c39d2af5ded7e43bc151e" "6ebea4c3e2583af3437e82c43c5e3b5b" "07cf0359683d2298e35948ed806c063c" "606ea178150b1efc15856934c7255cfe"); TestSignature(kSign_PKCS1_Block1, message, signature); } // # PKCS#1 v1.5 Signature Example 15.13 TEST_F(OEMCryptoCastReceiverTest, TestSignaturePKCS1_15_13) { BuildRSAKey(); LoadWithAllowedSchemes(kSign_PKCS1_Block1, true); vector message = wvcdm::a2b_hex( "ebaef3f9f23bdfe5fa6b8af4c208c189" "f2251bf32f5f137b9de4406378686b3f" "0721f62d24cb8688d6fc41a27cbae21d" "30e429feacc7111941c277"); vector signature = wvcdm::a2b_hex( "c48dbef507114f03c95fafbeb4df1bfa" "88e0184a33cc4f8a9a1035ff7f822a5e" "38cda18723915ff078244429e0f6081c" "14fd83331fa65c6ba7bb9a12dbf66223" "74cd0ca57de3774e2bd7ae823677d061" "d53ae9c4040d2da7ef7014f3bbdc95a3" "61a43855c8ce9b97ecabce174d926285" "142b534a3087f9f4ef74511ec742b0d5" "685603faf403b5072b985df46adf2d25" "29a02d40711e2190917052371b79b749" "b83abf0ae29486c3f2f62477b2bd362b" "039c013c0c5076ef520dbb405f42cee9" "5425c373a975e1cdd032c49622c85079" "b09e88dab2b13969ef7a723973781040" "459f57d5013638483de2d91cb3c490da" "81c46de6cd76ea8a0c8f6fe331712d24"); TestSignature(kSign_PKCS1_Block1, message, signature); } // # PKCS#1 v1.5 Signature Example 15.14 TEST_F(OEMCryptoCastReceiverTest, TestSignaturePKCS1_15_14) { BuildRSAKey(); LoadWithAllowedSchemes(kSign_PKCS1_Block1, true); vector message = wvcdm::a2b_hex( "c5a2711278761dfcdd4f0c99e6f5619d" "6c48b5d4c1a80982faa6b4cf1cf7a60f" "f327abef93c801429efde08640858146" "1056acc33f3d04f5ada21216cacd5fd1" "f9ed83203e0e2fe6138e3eae8424e591" "5a083f3f7ab76052c8be55ae882d6ec1" "482b1e45c5dae9f41015405327022ec3" "2f0ea2429763b255043b1958ee3cf6d6" "3983596eb385844f8528cc9a9865835d" "c5113c02b80d0fca68aa25e72bcaaeb3" "cf9d79d84f984fd417"); vector signature = wvcdm::a2b_hex( "6bd5257aa06611fb4660087cb4bc4a9e" "449159d31652bd980844daf3b1c7b353" "f8e56142f7ea9857433b18573b4deede" "818a93b0290297783f1a2f23cbc72797" "a672537f01f62484cd4162c3214b9ac6" "28224c5de01f32bb9b76b27354f2b151" "d0e8c4213e4615ad0bc71f515e300d6a" "64c6743411fffde8e5ff190e54923043" "126ecfc4c4539022668fb675f25c07e2" "0099ee315b98d6afec4b1a9a93dc3349" "6a15bd6fde1663a7d49b9f1e639d3866" "4b37a010b1f35e658682d9cd63e57de0" "f15e8bdd096558f07ec0caa218a8c06f" "4788453940287c9d34b6d40a3f09bf77" "99fe98ae4eb49f3ff41c5040a50cefc9" "bdf2394b749cf164480df1ab6880273b"); TestSignature(kSign_PKCS1_Block1, message, signature); } // # PKCS#1 v1.5 Signature Example 15.15 TEST_F(OEMCryptoCastReceiverTest, TestSignaturePKCS1_15_15) { BuildRSAKey(); LoadWithAllowedSchemes(kSign_PKCS1_Block1, true); vector message = wvcdm::a2b_hex( "9bf8aa253b872ea77a7e23476be26b23" "29578cf6ac9ea2805b357f6fc3ad130d" "baeb3d869a13cce7a808bbbbc969857e" "03945c7bb61df1b5c2589b8e046c2a5d" "7e4057b1a74f24c711216364288529ec" "9570f25197213be1f5c2e596f8bf8b2c" "f3cb38aa56ffe5e31df7395820e94ecf" "3b1189a965dcf9a9cb4298d3c88b2923" "c19fc6bc34aacecad4e0931a7c4e5d73" "dc86dfa798a8476d82463eefaa90a8a9" "192ab08b23088dd58e1280f7d72e4548" "396baac112252dd5c5346adb2004a2f7" "101ccc899cc7fafae8bbe295738896a5" "b2012285014ef6"); vector signature = wvcdm::a2b_hex( "27f7f4da9bd610106ef57d32383a448a" "8a6245c83dc1309c6d770d357ba89e73" "f2ad0832062eb0fe0ac915575bcd6b8b" "cadb4e2ba6fa9da73a59175152b2d4fe" "72b070c9b7379e50000e55e6c269f665" "8c937972797d3add69f130e34b85bdec" "9f3a9b392202d6f3e430d09caca82277" "59ab825f7012d2ff4b5b62c8504dbad8" "55c05edd5cab5a4cccdc67f01dd6517c" "7d41c43e2a4957aff19db6f18b17859a" "f0bc84ab67146ec1a4a60a17d7e05f8b" "4f9ced6ad10908d8d78f7fc88b76adc8" "290f87daf2a7be10ae408521395d54ed" "2556fb7661854a730ce3d82c71a8d493" "ec49a378ac8a3c74439f7cc555ba13f8" "59070890ee18ff658fa4d741969d70a5"); TestSignature(kSign_PKCS1_Block1, message, signature); } // # PKCS#1 v1.5 Signature Example 15.16 TEST_F(OEMCryptoCastReceiverTest, TestSignaturePKCS1_15_16) { BuildRSAKey(); LoadWithAllowedSchemes(kSign_PKCS1_Block1, true); vector message = wvcdm::a2b_hex( "32474830e2203754c8bf0681dc4f842a" "fe360930378616c108e833656e5640c8" "6856885bb05d1eb9438efede679263de" "07cb39553f6a25e006b0a52311a063ca" "088266d2564ff6490c46b5609818548f" "88764dad34a25e3a85d575023f0b9e66" "5048a03c350579a9d32446c7bb96cc92" "e065ab94d3c8952e8df68ef0d9fa456b" "3a06bb80e3bbc4b28e6a94b6d0ff7696" "a64efe05e735fea025d7bdbc4139f3a3" "b546075cba7efa947374d3f0ac80a68d" "765f5df6210bca069a2d88647af7ea04" "2dac690cb57378ec0777614fb8b65ff4" "53ca6b7dce6098451a2f8c0da9bfecf1" "fdf391bbaa4e2a91ca18a1121a7523a2" "abd42514f489e8"); vector signature = wvcdm::a2b_hex( "6917437257c22ccb5403290c3dee82d9" "cf7550b31bd31c51bd57bfd35d452ab4" "db7c4be6b2e25ac9a59a1d2a7feb627f" "0afd4976b3003cc9cffd8896505ec382" "f265104d4cf8c932fa9fe86e00870795" "9912389da4b2d6b369b36a5e72e29d24" "c9a98c9d31a3ab44e643e6941266a47a" "45e3446ce8776abe241a8f5fc6423b24" "b1ff250dc2c3a8172353561077e850a7" "69b25f0325dac88965a3b9b472c494e9" "5f719b4eac332caa7a65c7dfe46d9aa7" "e6e00f525f303dd63ab7919218901868" "f9337f8cd26aafe6f33b7fb2c98810af" "19f7fcb282ba1577912c1d368975fd5d" "440b86e10c199715fa0b6f4250b53373" "2d0befe1545150fc47b876de09b00a94"); TestSignature(kSign_PKCS1_Block1, message, signature); } // # PKCS#1 v1.5 Signature Example 15.17 TEST_F(OEMCryptoCastReceiverTest, TestSignaturePKCS1_15_17) { BuildRSAKey(); LoadWithAllowedSchemes(kSign_PKCS1_Block1, true); vector message = wvcdm::a2b_hex( "008e59505eafb550aae5e845584cebb0" "0b6de1733e9f95d42c882a5bbeb5ce1c" "57e119e7c0d4daca9f1ff7870217f7cf" "d8a6b373977cac9cab8e71e420"); vector signature = wvcdm::a2b_hex( "922503b673ee5f3e691e1ca85e9ff417" "3cf72b05ac2c131da5603593e3bc259c" "94c1f7d3a06a5b9891bf113fa39e59ff" "7c1ed6465e908049cb89e4e125cd37d2" "ffd9227a41b4a0a19c0a44fbbf3de55b" "ab802087a3bb8d4ff668ee6bbb8ad89e" "6857a79a9c72781990dfcf92cd519404" "c950f13d1143c3184f1d250c90e17ac6" "ce36163b9895627ad6ffec1422441f55" "e4499dba9be89546ae8bc63cca01dd08" "463ae7f1fce3d893996938778c1812e6" "74ad9c309c5acca3fde44e7dd8695993" "e9c1fa87acda99ece5c8499e468957ad" "66359bf12a51adbe78d3a213b449bf0b" "5f8d4d496acf03d3033b7ccd196bc22f" "68fb7bef4f697c5ea2b35062f48a36dd"); TestSignature(kSign_PKCS1_Block1, message, signature); } // # PKCS#1 v1.5 Signature Example 15.18 TEST_F(OEMCryptoCastReceiverTest, TestSignaturePKCS1_15_18) { BuildRSAKey(); LoadWithAllowedSchemes(kSign_PKCS1_Block1, true); vector message = wvcdm::a2b_hex( "6abc54cf8d1dff1f53b17d8160368878" "a8788cc6d22fa5c2258c88e660b09a89" "33f9f2c0504ddadc21f6e75e0b833beb" "555229dee656b9047b92f62e76b8ffcc" "60dab06b80"); vector signature = wvcdm::a2b_hex( "0b6daf42f7a862147e417493c2c401ef" "ae32636ab4cbd44192bbf5f195b50ae0" "96a475a1614f0a9fa8f7a026cb46c650" "6e518e33d83e56477a875aca8c7e714c" "e1bdbd61ef5d535239b33f2bfdd61771" "bab62776d78171a1423cea8731f82e60" "766d6454265620b15f5c5a584f55f95b" "802fe78c574ed5dacfc831f3cf2b0502" "c0b298f25ccf11f973b31f85e4744219" "85f3cff702df3946ef0a6605682111b2" "f55b1f8ab0d2ea3a683c69985ead93ed" "449ea48f0358ddf70802cb41de2fd83f" "3c808082d84936948e0c84a131b49278" "27460527bb5cd24bfab7b48e071b2417" "1930f99763272f9797bcb76f1d248157" "5558fcf260b1f0e554ebb3df3cfcb958"); TestSignature(kSign_PKCS1_Block1, message, signature); } // # PKCS#1 v1.5 Signature Example 15.19 TEST_F(OEMCryptoCastReceiverTest, TestSignaturePKCS1_15_19) { BuildRSAKey(); LoadWithAllowedSchemes(kSign_PKCS1_Block1, true); vector message = wvcdm::a2b_hex( "af2d78152cf10efe01d274f217b177f6" "b01b5e749f1567715da324859cd3dd88" "db848ec79f48dbba7b6f1d33111ef31b" "64899e7391c2bffd69f49025cf201fc5" "85dbd1542c1c778a2ce7a7ee108a309f" "eca26d133a5ffedc4e869dcd7656596a" "c8427ea3ef6e3fd78fe99d8ddc71d839" "f6786e0da6e786bd62b3a4f19b891a56" "157a554ec2a2b39e25a1d7c7d37321c7" "a1d946cf4fbe758d9276f08563449d67" "414a2c030f4251cfe2213d04a5410637" "87"); vector signature = wvcdm::a2b_hex( "209c61157857387b71e24bf3dd564145" "50503bec180ff53bdd9bac062a2d4995" "09bf991281b79527df9136615b7a6d9d" "b3a103b535e0202a2caca197a7b74e53" "56f3dd595b49acfd9d30049a98ca88f6" "25bca1d5f22a392d8a749efb6eed9b78" "21d3110ac0d244199ecb4aa3d735a83a" "2e8893c6bf8581383ccaee834635b7fa" "1faffa45b13d15c1da33af71e89303d6" "8090ff62ee615fdf5a84d120711da53c" "2889198ab38317a9734ab27d67924cea" "74156ff99bef9876bb5c339e93745283" "e1b34e072226b88045e017e9f05b2a8c" "416740258e223b2690027491732273f3" "229d9ef2b1b3807e321018920ad3e53d" "ae47e6d9395c184b93a374c671faa2ce"); TestSignature(kSign_PKCS1_Block1, message, signature); } // # PKCS#1 v1.5 Signature Example 15.20 TEST_F(OEMCryptoCastReceiverTest, TestSignaturePKCS1_15_20) { BuildRSAKey(); LoadWithAllowedSchemes(kSign_PKCS1_Block1, true); vector message = wvcdm::a2b_hex( "40ee992458d6f61486d25676a96dd2cb" "93a37f04b178482f2b186cf88215270d" "ba29d786d774b0c5e78c7f6e56a956e7" "f73950a2b0c0c10a08dbcd67e5b210bb" "21c58e2767d44f7dd4014e3966143bf7" "e3d66ff0c09be4c55f93b39994b8518d" "9c1d76d5b47374dea08f157d57d70634" "978f3856e0e5b481afbbdb5a3ac48d48" "4be92c93de229178354c2de526e9c65a" "31ede1ef68cb6398d7911684fec0babc" "3a781a66660783506974d0e14825101c" "3bfaea"); vector signature = wvcdm::a2b_hex( "927502b824afc42513ca6570de338b8a" "64c3a85eb828d3193624f27e8b1029c5" "5c119c9733b18f5849b3500918bcc005" "51d9a8fdf53a97749fa8dc480d6fe974" "2a5871f973926528972a1af49e3925b0" "adf14a842719b4a5a2d89fa9c0b6605d" "212bed1e6723b93406ad30e86829a5c7" "19b890b389306dc5506486ee2f36a8df" "e0a96af678c9cbd6aff397ca200e3edc" "1e36bd2f08b31d540c0cb282a9559e4a" "dd4fc9e6492eed0ccbd3a6982e5faa2d" "dd17be47417c80b4e5452d31f72401a0" "42325109544d954c01939079d409a5c3" "78d7512dfc2d2a71efcc3432a765d1c6" "a52cfce899cd79b15b4fc3723641ef6b" "d00acc10407e5df58dd1c3c5c559a506"); TestSignature(kSign_PKCS1_Block1, message, signature); } // 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(); ASSERT_NO_FATAL_FAILURE(session_.GenerateNonce()); 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); } // 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); sts = OEMCrypto_SelectKey(session_.session_id(), session_.license().keys[key_index].key_id, session_.license().keys[key_index].key_id_length, OEMCrypto_CipherMode_CTR); ASSERT_EQ(OEMCrypto_SUCCESS, sts); vector encrypted(buffer_length); sts = OEMCrypto_Generic_Encrypt(session_.session_id(), 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); sts = OEMCrypto_SelectKey(session_.session_id(), session_.license().keys[key_index].key_id, session_.license().keys[key_index].key_id_length, OEMCrypto_CipherMode_CTR); ASSERT_EQ(OEMCrypto_SUCCESS, sts); vector resultant(encrypted.size()); sts = OEMCrypto_Generic_Decrypt(session_.session_id(), 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); sts = OEMCrypto_SelectKey(session_.session_id(), session_.license().keys[key_index].key_id, session_.license().keys[key_index].key_id_length, OEMCrypto_CipherMode_CTR); ASSERT_EQ(OEMCrypto_SUCCESS, sts); size_t signature_length = (size_t)SHA256_DIGEST_LENGTH; vector signature(SHA256_DIGEST_LENGTH); sts = OEMCrypto_Generic_Sign(session_.session_id(), 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; } sts = OEMCrypto_SelectKey(session_.session_id(), session_.license().keys[key_index].key_id, session_.license().keys[key_index].key_id_length, OEMCrypto_CipherMode_CTR); ASSERT_EQ(OEMCrypto_SUCCESS, sts); sts = OEMCrypto_Generic_Verify(session_.session_id(), 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]; }; TEST_P(OEMCryptoGenericCryptoTest, GenericKeyLoad) { EncryptAndLoadKeys(); } // Test that the Generic_Encrypt function works correctly. TEST_P(OEMCryptoGenericCryptoTest, GenericKeyEncrypt) { EncryptAndLoadKeys(); unsigned int key_index = 0; vector expected_encrypted; EncryptBuffer(key_index, clear_buffer_, &expected_encrypted); ASSERT_EQ( OEMCrypto_SUCCESS, OEMCrypto_SelectKey(session_.session_id(), session_.license().keys[key_index].key_id, session_.license().keys[key_index].key_id_length, OEMCrypto_CipherMode_CTR)); vector encrypted(clear_buffer_.size()); ASSERT_EQ( OEMCrypto_SUCCESS, OEMCrypto_Generic_Encrypt( session_.session_id(), clear_buffer_.data(), clear_buffer_.size(), iv_, OEMCrypto_AES_CBC_128_NO_PADDING, encrypted.data())); ASSERT_EQ(expected_encrypted, encrypted); } // Test that the Generic_Encrypt function fails when not allowed. TEST_P(OEMCryptoGenericCryptoTest, GenericKeyBadEncrypt) { EncryptAndLoadKeys(); BadEncrypt(0, OEMCrypto_HMAC_SHA256, buffer_size_); // The buffer size must be a multiple of 16, so subtracting 10 is bad. BadEncrypt(0, OEMCrypto_AES_CBC_128_NO_PADDING, buffer_size_ - 10); BadEncrypt(1, OEMCrypto_AES_CBC_128_NO_PADDING, buffer_size_); BadEncrypt(2, OEMCrypto_AES_CBC_128_NO_PADDING, buffer_size_); BadEncrypt(3, OEMCrypto_AES_CBC_128_NO_PADDING, buffer_size_); } // Test that the Generic_Encrypt works if the input and output buffers are the // same. TEST_P(OEMCryptoGenericCryptoTest, GenericKeyEncryptSameBufferAPI12) { EncryptAndLoadKeys(); unsigned int key_index = 0; vector expected_encrypted; EncryptBuffer(key_index, clear_buffer_, &expected_encrypted); ASSERT_EQ( OEMCrypto_SUCCESS, OEMCrypto_SelectKey(session_.session_id(), session_.license().keys[key_index].key_id, session_.license().keys[key_index].key_id_length, OEMCrypto_CipherMode_CTR)); // Input and output are same buffer: vector buffer = clear_buffer_; ASSERT_EQ(OEMCrypto_SUCCESS, OEMCrypto_Generic_Encrypt( session_.session_id(), buffer.data(), buffer.size(), iv_, OEMCrypto_AES_CBC_128_NO_PADDING, buffer.data())); ASSERT_EQ(expected_encrypted, buffer); } // Test Generic_Decrypt works correctly. TEST_P(OEMCryptoGenericCryptoTest, GenericKeyDecrypt) { EncryptAndLoadKeys(); unsigned int key_index = 1; vector encrypted; EncryptBuffer(key_index, clear_buffer_, &encrypted); ASSERT_EQ( OEMCrypto_SUCCESS, OEMCrypto_SelectKey(session_.session_id(), session_.license().keys[key_index].key_id, session_.license().keys[key_index].key_id_length, OEMCrypto_CipherMode_CTR)); vector resultant(encrypted.size()); ASSERT_EQ(OEMCrypto_SUCCESS, OEMCrypto_Generic_Decrypt( session_.session_id(), encrypted.data(), encrypted.size(), iv_, OEMCrypto_AES_CBC_128_NO_PADDING, resultant.data())); ASSERT_EQ(clear_buffer_, resultant); } // Test that Generic_Decrypt works correctly when the input and output buffers // are the same. TEST_P(OEMCryptoGenericCryptoTest, GenericKeyDecryptSameBufferAPI12) { EncryptAndLoadKeys(); unsigned int key_index = 1; vector encrypted; EncryptBuffer(key_index, clear_buffer_, &encrypted); ASSERT_EQ( OEMCrypto_SUCCESS, OEMCrypto_SelectKey(session_.session_id(), session_.license().keys[key_index].key_id, session_.license().keys[key_index].key_id_length, OEMCrypto_CipherMode_CTR)); vector buffer = encrypted; ASSERT_EQ(OEMCrypto_SUCCESS, OEMCrypto_Generic_Decrypt( session_.session_id(), buffer.data(), buffer.size(), iv_, OEMCrypto_AES_CBC_128_NO_PADDING, buffer.data())); ASSERT_EQ(clear_buffer_, buffer); } // Test that Generic_Decrypt fails to decrypt to an insecure buffer if the key // requires a secure data path. TEST_P(OEMCryptoGenericCryptoTest, GenericSecureToClear) { license_messages_.set_control(wvoec::kControlObserveDataPath | wvoec::kControlDataPathSecure); license_messages_.CreateResponseWithGenericCryptoKeys(); EncryptAndLoadKeys(); unsigned int key_index = 1; vector encrypted; EncryptBuffer(key_index, clear_buffer_, &encrypted); ASSERT_EQ( OEMCrypto_SUCCESS, OEMCrypto_SelectKey(session_.session_id(), session_.license().keys[key_index].key_id, session_.license().keys[key_index].key_id_length, OEMCrypto_CipherMode_CTR)); vector resultant(encrypted.size()); ASSERT_NE(OEMCrypto_SUCCESS, OEMCrypto_Generic_Decrypt( session_.session_id(), encrypted.data(), encrypted.size(), iv_, OEMCrypto_AES_CBC_128_NO_PADDING, resultant.data())); ASSERT_NE(clear_buffer_, resultant); } // Test that the Generic_Decrypt function fails when not allowed. TEST_P(OEMCryptoGenericCryptoTest, GenericKeyBadDecrypt) { EncryptAndLoadKeys(); BadDecrypt(1, OEMCrypto_HMAC_SHA256, buffer_size_); // The buffer size must be a multiple of 16, so subtracting 10 is bad. BadDecrypt(1, OEMCrypto_AES_CBC_128_NO_PADDING, buffer_size_ - 10); BadDecrypt(0, OEMCrypto_AES_CBC_128_NO_PADDING, buffer_size_); BadDecrypt(2, OEMCrypto_AES_CBC_128_NO_PADDING, buffer_size_); BadDecrypt(3, OEMCrypto_AES_CBC_128_NO_PADDING, buffer_size_); } TEST_P(OEMCryptoGenericCryptoTest, GenericKeySign) { EncryptAndLoadKeys(); unsigned int key_index = 2; vector expected_signature; SignBuffer(key_index, clear_buffer_, &expected_signature); ASSERT_EQ( OEMCrypto_SUCCESS, OEMCrypto_SelectKey(session_.session_id(), session_.license().keys[key_index].key_id, session_.license().keys[key_index].key_id_length, OEMCrypto_CipherMode_CTR)); size_t gen_signature_length = 0; ASSERT_EQ(OEMCrypto_ERROR_SHORT_BUFFER, OEMCrypto_Generic_Sign(session_.session_id(), clear_buffer_.data(), clear_buffer_.size(), OEMCrypto_HMAC_SHA256, nullptr, &gen_signature_length)); ASSERT_EQ(static_cast(SHA256_DIGEST_LENGTH), gen_signature_length); vector signature(SHA256_DIGEST_LENGTH); ASSERT_EQ(OEMCrypto_SUCCESS, OEMCrypto_Generic_Sign(session_.session_id(), clear_buffer_.data(), clear_buffer_.size(), OEMCrypto_HMAC_SHA256, signature.data(), &gen_signature_length)); ASSERT_EQ(expected_signature, signature); } // Test that the Generic_Sign function fails when not allowed. TEST_P(OEMCryptoGenericCryptoTest, GenericKeyBadSign) { EncryptAndLoadKeys(); BadSign(0, OEMCrypto_HMAC_SHA256); // Can't sign with encrypt key. BadSign(1, OEMCrypto_HMAC_SHA256); // Can't sign with decrypt key. BadSign(3, OEMCrypto_HMAC_SHA256); // Can't sign with verify key. BadSign(2, OEMCrypto_AES_CBC_128_NO_PADDING); // Bad signing algorithm. } TEST_P(OEMCryptoGenericCryptoTest, GenericKeyVerify) { EncryptAndLoadKeys(); unsigned int key_index = 3; vector signature; SignBuffer(key_index, clear_buffer_, &signature); ASSERT_EQ( OEMCrypto_SUCCESS, OEMCrypto_SelectKey(session_.session_id(), session_.license().keys[key_index].key_id, session_.license().keys[key_index].key_id_length, OEMCrypto_CipherMode_CTR)); ASSERT_EQ(OEMCrypto_SUCCESS, OEMCrypto_Generic_Verify( session_.session_id(), clear_buffer_.data(), clear_buffer_.size(), OEMCrypto_HMAC_SHA256, signature.data(), signature.size())); } // Test that the Generic_Verify function fails when not allowed. TEST_P(OEMCryptoGenericCryptoTest, GenericKeyBadVerify) { EncryptAndLoadKeys(); BadVerify(0, OEMCrypto_HMAC_SHA256, SHA256_DIGEST_LENGTH, false); BadVerify(1, OEMCrypto_HMAC_SHA256, SHA256_DIGEST_LENGTH, false); BadVerify(2, OEMCrypto_HMAC_SHA256, SHA256_DIGEST_LENGTH, false); BadVerify(3, OEMCrypto_HMAC_SHA256, SHA256_DIGEST_LENGTH, true); BadVerify(3, OEMCrypto_HMAC_SHA256, SHA256_DIGEST_LENGTH - 1, false); BadVerify(3, OEMCrypto_HMAC_SHA256, SHA256_DIGEST_LENGTH + 1, false); BadVerify(3, OEMCrypto_AES_CBC_128_NO_PADDING, SHA256_DIGEST_LENGTH, false); } // Test Generic_Encrypt with the maximum buffer size. TEST_P(OEMCryptoGenericCryptoTest, GenericKeyEncryptLargeBuffer) { ResizeBuffer(GetResourceValue(kMaxGenericBuffer)); EncryptAndLoadKeys(); unsigned int key_index = 0; vector expected_encrypted; EncryptBuffer(key_index, clear_buffer_, &expected_encrypted); ASSERT_EQ( OEMCrypto_SUCCESS, OEMCrypto_SelectKey(session_.session_id(), session_.license().keys[key_index].key_id, session_.license().keys[key_index].key_id_length, OEMCrypto_CipherMode_CTR)); vector encrypted(clear_buffer_.size()); ASSERT_EQ( OEMCrypto_SUCCESS, OEMCrypto_Generic_Encrypt( session_.session_id(), clear_buffer_.data(), clear_buffer_.size(), iv_, OEMCrypto_AES_CBC_128_NO_PADDING, encrypted.data())); ASSERT_EQ(expected_encrypted, encrypted); } // Test Generic_Decrypt with the maximum buffer size. TEST_P(OEMCryptoGenericCryptoTest, GenericKeyDecryptLargeBuffer) { // Some applications are known to pass in a block that is almost 400k. ResizeBuffer(GetResourceValue(kMaxGenericBuffer)); EncryptAndLoadKeys(); unsigned int key_index = 1; vector encrypted; EncryptBuffer(key_index, clear_buffer_, &encrypted); ASSERT_EQ( OEMCrypto_SUCCESS, OEMCrypto_SelectKey(session_.session_id(), session_.license().keys[key_index].key_id, session_.license().keys[key_index].key_id_length, OEMCrypto_CipherMode_CTR)); vector resultant(encrypted.size()); ASSERT_EQ(OEMCrypto_SUCCESS, OEMCrypto_Generic_Decrypt( session_.session_id(), encrypted.data(), encrypted.size(), iv_, OEMCrypto_AES_CBC_128_NO_PADDING, resultant.data())); ASSERT_EQ(clear_buffer_, resultant); } // Test Generic_Sign with the maximum buffer size. TEST_P(OEMCryptoGenericCryptoTest, GenericKeySignLargeBuffer) { ResizeBuffer(GetResourceValue(kMaxGenericBuffer)); EncryptAndLoadKeys(); unsigned int key_index = 2; vector expected_signature; SignBuffer(key_index, clear_buffer_, &expected_signature); ASSERT_EQ( OEMCrypto_SUCCESS, OEMCrypto_SelectKey(session_.session_id(), session_.license().keys[key_index].key_id, session_.license().keys[key_index].key_id_length, OEMCrypto_CipherMode_CTR)); size_t gen_signature_length = 0; ASSERT_EQ(OEMCrypto_ERROR_SHORT_BUFFER, OEMCrypto_Generic_Sign(session_.session_id(), clear_buffer_.data(), clear_buffer_.size(), OEMCrypto_HMAC_SHA256, nullptr, &gen_signature_length)); ASSERT_EQ(static_cast(SHA256_DIGEST_LENGTH), gen_signature_length); vector signature(SHA256_DIGEST_LENGTH); ASSERT_EQ(OEMCrypto_SUCCESS, OEMCrypto_Generic_Sign(session_.session_id(), clear_buffer_.data(), clear_buffer_.size(), OEMCrypto_HMAC_SHA256, signature.data(), &gen_signature_length)); ASSERT_EQ(expected_signature, signature); } // Test Generic_Verify with the maximum buffer size. TEST_P(OEMCryptoGenericCryptoTest, GenericKeyVerifyLargeBuffer) { ResizeBuffer(GetResourceValue(kMaxGenericBuffer)); EncryptAndLoadKeys(); unsigned int key_index = 3; vector signature; SignBuffer(key_index, clear_buffer_, &signature); ASSERT_EQ( OEMCrypto_SUCCESS, OEMCrypto_SelectKey(session_.session_id(), session_.license().keys[key_index].key_id, session_.license().keys[key_index].key_id_length, OEMCrypto_CipherMode_CTR)); ASSERT_EQ(OEMCrypto_SUCCESS, OEMCrypto_Generic_Verify( session_.session_id(), clear_buffer_.data(), clear_buffer_.size(), OEMCrypto_HMAC_SHA256, signature.data(), signature.size())); } // Test Generic_Encrypt when the key duration has expired. TEST_P(OEMCryptoGenericCryptoTest, KeyDurationEncrypt) { license_messages_.core_response() .timer_limits.total_playback_duration_seconds = kDuration; license_messages_.CreateResponseWithGenericCryptoKeys(); EncryptAndLoadKeys(); vector expected_encrypted; EncryptBuffer(0, clear_buffer_, &expected_encrypted); unsigned int key_index = 0; vector encrypted(clear_buffer_.size()); // Should be valid key at the start. ASSERT_EQ( OEMCrypto_SUCCESS, OEMCrypto_SelectKey(session_.session_id(), session_.license().keys[key_index].key_id, session_.license().keys[key_index].key_id_length, OEMCrypto_CipherMode_CTR)); ASSERT_EQ( OEMCrypto_SUCCESS, OEMCrypto_Generic_Encrypt( session_.session_id(), clear_buffer_.data(), clear_buffer_.size(), iv_, OEMCrypto_AES_CBC_128_NO_PADDING, encrypted.data())); ASSERT_EQ(expected_encrypted, encrypted); wvcdm::TestSleep::Sleep(kLongSleep + kShortSleep); // Should be expired key. encrypted.assign(clear_buffer_.size(), 0); OEMCryptoResult status = OEMCrypto_Generic_Encrypt( session_.session_id(), clear_buffer_.data(), clear_buffer_.size(), iv_, OEMCrypto_AES_CBC_128_NO_PADDING, encrypted.data()); ASSERT_EQ(OEMCrypto_ERROR_KEY_EXPIRED, status); ASSERT_NE(encrypted, expected_encrypted); ASSERT_NO_FATAL_FAILURE(session_.TestSelectExpired(key_index)); } // Test Generic_Decrypt when the key duration has expired. TEST_P(OEMCryptoGenericCryptoTest, KeyDurationDecrypt) { license_messages_.core_response() .timer_limits.total_playback_duration_seconds = kDuration; license_messages_.CreateResponseWithGenericCryptoKeys(); EncryptAndLoadKeys(); // Should be valid key at the start. unsigned int key_index = 1; vector encrypted; EncryptBuffer(key_index, clear_buffer_, &encrypted); ASSERT_EQ( OEMCrypto_SUCCESS, OEMCrypto_SelectKey(session_.session_id(), session_.license().keys[key_index].key_id, session_.license().keys[key_index].key_id_length, OEMCrypto_CipherMode_CTR)); vector resultant(encrypted.size()); ASSERT_EQ(OEMCrypto_SUCCESS, OEMCrypto_Generic_Decrypt( session_.session_id(), encrypted.data(), encrypted.size(), iv_, OEMCrypto_AES_CBC_128_NO_PADDING, resultant.data())); ASSERT_EQ(clear_buffer_, resultant); wvcdm::TestSleep::Sleep(kLongSleep + kShortSleep); // Should be expired key. resultant.assign(encrypted.size(), 0); OEMCryptoResult status = OEMCrypto_Generic_Decrypt( session_.session_id(), encrypted.data(), encrypted.size(), iv_, OEMCrypto_AES_CBC_128_NO_PADDING, resultant.data()); ASSERT_EQ(OEMCrypto_ERROR_KEY_EXPIRED, status); ASSERT_NE(clear_buffer_, resultant); ASSERT_NO_FATAL_FAILURE(session_.TestSelectExpired(key_index)); } // Test Generic_Sign when the key duration has expired. TEST_P(OEMCryptoGenericCryptoTest, KeyDurationSign) { license_messages_.core_response() .timer_limits.total_playback_duration_seconds = kDuration; license_messages_.CreateResponseWithGenericCryptoKeys(); EncryptAndLoadKeys(); unsigned int key_index = 2; vector expected_signature; vector signature(SHA256_DIGEST_LENGTH); size_t signature_length = signature.size(); SignBuffer(key_index, clear_buffer_, &expected_signature); // Should be valid key at the start. ASSERT_EQ( OEMCrypto_SUCCESS, OEMCrypto_SelectKey(session_.session_id(), session_.license().keys[key_index].key_id, session_.license().keys[key_index].key_id_length, OEMCrypto_CipherMode_CTR)); ASSERT_EQ(OEMCrypto_SUCCESS, OEMCrypto_Generic_Sign(session_.session_id(), clear_buffer_.data(), clear_buffer_.size(), OEMCrypto_HMAC_SHA256, signature.data(), &signature_length)); ASSERT_EQ(expected_signature, signature); wvcdm::TestSleep::Sleep(kLongSleep + kShortSleep); // Should be expired key. signature.assign(SHA256_DIGEST_LENGTH, 0); OEMCryptoResult status = OEMCrypto_Generic_Sign( session_.session_id(), clear_buffer_.data(), clear_buffer_.size(), OEMCrypto_HMAC_SHA256, signature.data(), &signature_length); ASSERT_EQ(OEMCrypto_ERROR_KEY_EXPIRED, status); ASSERT_NE(expected_signature, signature); ASSERT_NO_FATAL_FAILURE(session_.TestSelectExpired(key_index)); } // Test Generic_Verify when the key duration has expired. TEST_P(OEMCryptoGenericCryptoTest, KeyDurationVerify) { license_messages_.core_response() .timer_limits.total_playback_duration_seconds = kDuration; license_messages_.CreateResponseWithGenericCryptoKeys(); EncryptAndLoadKeys(); unsigned int key_index = 3; vector signature; SignBuffer(key_index, clear_buffer_, &signature); // Should be valid key at the start. ASSERT_EQ( OEMCrypto_SUCCESS, OEMCrypto_SelectKey(session_.session_id(), session_.license().keys[key_index].key_id, session_.license().keys[key_index].key_id_length, OEMCrypto_CipherMode_CTR)); ASSERT_EQ(OEMCrypto_SUCCESS, OEMCrypto_Generic_Verify( session_.session_id(), clear_buffer_.data(), clear_buffer_.size(), OEMCrypto_HMAC_SHA256, signature.data(), signature.size())); wvcdm::TestSleep::Sleep(kLongSleep + kShortSleep); // Should be expired key. OEMCryptoResult status = OEMCrypto_Generic_Verify( session_.session_id(), clear_buffer_.data(), clear_buffer_.size(), OEMCrypto_HMAC_SHA256, signature.data(), signature.size()); ASSERT_EQ(OEMCrypto_ERROR_KEY_EXPIRED, status); ASSERT_NO_FATAL_FAILURE(session_.TestSelectExpired(key_index)); } const unsigned int kLongKeyId = 2; // Test that short key ids are allowed. class OEMCryptoGenericCryptoKeyIdLengthTest : public OEMCryptoGenericCryptoTest { protected: void SetUp() override { OEMCryptoGenericCryptoTest::SetUp(); license_messages_.set_num_keys(5); license_messages_.set_control(wvoec::kControlAllowDecrypt); license_messages_.core_response() .timer_limits.total_playback_duration_seconds = kDuration; ASSERT_NO_FATAL_FAILURE(license_messages_.CreateDefaultResponse()); SetUniformKeyIdLength(16); // Start with all key ids being 16 bytes. // But, we are testing that the key ids do not have to have the same length. // 12 bytes (common key id length). license_messages_.SetKeyId(0, "123456789012"); license_messages_.SetKeyId(1, "12345"); // short key id. // 16 byte key id. (default) license_messages_.SetKeyId(2, "1234567890123456"); license_messages_.SetKeyId(3, "12345678901234"); // 14 byte. (uncommon) license_messages_.SetKeyId(4, "1"); // very short key id. ASSERT_EQ(2u, kLongKeyId); ASSERT_NO_FATAL_FAILURE(license_messages_.FillCoreResponseSubstrings()); } // Make all four keys have the same length. void SetUniformKeyIdLength(size_t key_id_length) { for (size_t i = 0; i < license_messages_.num_keys(); i++) { string key_id; key_id.resize(key_id_length, i + 'a'); license_messages_.SetKeyId(i, key_id); } ASSERT_NO_FATAL_FAILURE(license_messages_.FillCoreResponseSubstrings()); } void TestWithKey(unsigned int key_index) { ASSERT_LT(key_index, license_messages_.num_keys()); EncryptAndLoadKeys(); vector encrypted; // To make sure OEMCrypto is not expecting the key_id to be zero padded, we // will create a buffer that is padded with 'Z'. // Then, we use fill the buffer with the longer of the three keys. If // OEMCrypto is paying attention to the key id length, it should pick out // the correct key. vector key_id_buffer( session_.license().keys[kLongKeyId].key_id_length + 5, 'Z'); // Fill a bigger buffer with letter 'Z'. memcpy(key_id_buffer.data(), session_.license().keys[kLongKeyId].key_id, session_.license().keys[kLongKeyId].key_id_length); EncryptBuffer(key_index, clear_buffer_, &encrypted); ASSERT_EQ( OEMCrypto_SUCCESS, OEMCrypto_SelectKey(session_.session_id(), key_id_buffer.data(), session_.license().keys[key_index].key_id_length, OEMCrypto_CipherMode_CTR)); vector resultant(encrypted.size()); ASSERT_EQ(OEMCrypto_SUCCESS, OEMCrypto_Generic_Decrypt( session_.session_id(), encrypted.data(), encrypted.size(), iv_, OEMCrypto_AES_CBC_128_NO_PADDING, resultant.data())); ASSERT_EQ(clear_buffer_, resultant); } }; TEST_P(OEMCryptoGenericCryptoKeyIdLengthTest, MediumKeyId) { TestWithKey(0); } TEST_P(OEMCryptoGenericCryptoKeyIdLengthTest, ShortKeyId) { TestWithKey(1); } TEST_P(OEMCryptoGenericCryptoKeyIdLengthTest, LongKeyId) { TestWithKey(2); } TEST_P(OEMCryptoGenericCryptoKeyIdLengthTest, FourteenByteKeyId) { TestWithKey(3); } TEST_P(OEMCryptoGenericCryptoKeyIdLengthTest, VeryShortKeyId) { TestWithKey(4); } TEST_P(OEMCryptoGenericCryptoKeyIdLengthTest, UniformShortKeyId) { SetUniformKeyIdLength(5); TestWithKey(2); } TEST_P(OEMCryptoGenericCryptoKeyIdLengthTest, UniformLongKeyId) { SetUniformKeyIdLength(kTestKeyIdMaxLength); TestWithKey(2); } INSTANTIATE_TEST_CASE_P(TestAll, OEMCryptoGenericCryptoTest, Range(kCurrentAPI - 1, kCurrentAPI + 1)); INSTANTIATE_TEST_CASE_P(TestAll, OEMCryptoGenericCryptoKeyIdLengthTest, Range(kCurrentAPI - 1, kCurrentAPI + 1)); // Test usage table functionality. class LicenseWithUsageEntry { public: LicenseWithUsageEntry(const std::string& pst = "my_pst") : session_(), license_messages_(&session_), generic_crypto_(false), time_license_received_(0), time_first_decrypt_(0), time_last_decrypt_(0), active_(true) { license_messages_.set_pst(pst); } void MakeAndLoadOnline(OEMCryptoSessionTests* test) { MakeAndLoad(test, wvoec::kControlNonceEnabled | wvoec::kControlNonceRequired); } // If status in not a nullptr, then creating a new entry is allowed to fail, // and its error code is stored in status. void MakeOfflineAndClose(OEMCryptoSessionTests* test, OEMCryptoResult* status = nullptr) { MakeAndLoad(test, wvoec::kControlNonceOrEntry, status); if (status != nullptr && *status != OEMCrypto_SUCCESS) { ASSERT_NO_FATAL_FAILURE(session_.close()); return; } ASSERT_NO_FATAL_FAILURE( session_.UpdateUsageEntry(&(test->encrypted_usage_header_))); ASSERT_NO_FATAL_FAILURE(GenerateVerifyReport(kUnused)); ASSERT_NO_FATAL_FAILURE(session_.close()); } // If status in not a nullptr, then creating a new entry is allowed to fail, // and its error code is stored in status. void MakeAndLoad(SessionUtil* util, uint32_t control, OEMCryptoResult* status = nullptr) { license_messages_.set_control(control); ASSERT_NO_FATAL_FAILURE(session_.open()); ASSERT_NO_FATAL_FAILURE(util->InstallTestRSAKey(&session_)); ASSERT_NO_FATAL_FAILURE(session_.GenerateNonce()); ASSERT_NO_FATAL_FAILURE(license_messages_.SignAndVerifyRequest()); if (generic_crypto_) { ASSERT_NO_FATAL_FAILURE( license_messages_.CreateResponseWithGenericCryptoKeys()); } else { ASSERT_NO_FATAL_FAILURE(license_messages_.CreateDefaultResponse()); } ASSERT_NO_FATAL_FAILURE(license_messages_.EncryptAndSignResponse()); ASSERT_NO_FATAL_FAILURE(session_.CreateNewUsageEntry(status)); if (status != nullptr && *status != OEMCrypto_SUCCESS) return; ASSERT_EQ(OEMCrypto_SUCCESS, license_messages_.LoadResponse()); time_license_received_ = wvcdm::Clock().GetCurrentTime(); } void OpenAndReload(SessionUtil* util) { ASSERT_NO_FATAL_FAILURE(session_.open()); ASSERT_NO_FATAL_FAILURE(session_.ReloadUsageEntry()); ASSERT_NO_FATAL_FAILURE(util->InstallTestRSAKey(&session_)); ASSERT_NO_FATAL_FAILURE(session_.GenerateDerivedKeysFromSessionKey()); ASSERT_EQ(OEMCrypto_SUCCESS, license_messages_.LoadResponse()); } // Test decrypt, and update the decrypt times for the pst report. void TestDecryptCTR(bool select_key_first = true, OEMCryptoResult expected_result = OEMCrypto_SUCCESS) { session_.TestDecryptCTR(select_key_first, expected_result); time_last_decrypt_ = wvcdm::Clock().GetCurrentTime(); if (time_first_decrypt_ == 0) time_first_decrypt_ = time_last_decrypt_; } void DeactivateUsageEntry() { active_ = false; ASSERT_EQ( OEMCrypto_SUCCESS, OEMCrypto_DeactivateUsageEntry( session_.session_id(), reinterpret_cast(pst().c_str()), pst().length())); } void GenerateVerifyReport(OEMCrypto_Usage_Entry_Status status) { ASSERT_NO_FATAL_FAILURE(session_.GenerateReport(pst())); Test_PST_Report expected(pst(), status); ASSERT_NO_FATAL_FAILURE( session_.VerifyReport(expected, time_license_received_, time_first_decrypt_, time_last_decrypt_)); // The PST report was signed above. Below we verify that the entire message // that is sent to the server will be signed by the right mac keys. RenewalRoundTrip renewal_messages(&license_messages_); renewal_messages.set_is_release(!active_); ASSERT_NO_FATAL_FAILURE(renewal_messages.SignAndVerifyRequest()); } void ReloadUsageEntry() { session_.ReloadUsageEntry(); session_.set_mac_keys(license_messages_.response_data().mac_keys); } const std::string& pst() const { return license_messages_.pst(); } void set_pst(const std::string& pst) { license_messages_.set_pst(pst); } LicenseRoundTrip& license_messages() { return license_messages_; } Session& session() { return session_; } void set_generic_crypto(bool generic_crypto) { generic_crypto_ = generic_crypto; } private: Session session_; LicenseRoundTrip license_messages_; bool generic_crypto_; int64_t time_license_received_; int64_t time_first_decrypt_; int64_t time_last_decrypt_; bool active_; }; class OEMCryptoUsageTableTest : public OEMCryptoGenericCryptoTest { public: 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()); EnsureTestKeys(); 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 = wvcdm::Clock().GetCurrentTime(); do { wvcdm::TestSleep::Sleep(1); elapsed_time = wvcdm::Clock().GetCurrentTime() - start_time; if (elapsed_time >= dot_time) { cout << "."; cout.flush(); dot_time += interval_seconds; } } while (elapsed_time < total_seconds); cout << endl; } }; // Test an online or streaming license with PST. This license requires a valid // nonce and can only be loaded once. TEST_P(OEMCryptoUsageTableTest, OnlineLicense) { LicenseWithUsageEntry entry; entry.license_messages().set_api_version(license_api_version_); entry.MakeAndLoadOnline(this); Session& s = entry.session(); ASSERT_NO_FATAL_FAILURE(s.UpdateUsageEntry(&encrypted_usage_header_)); // test repeated report generation ASSERT_NO_FATAL_FAILURE(entry.GenerateVerifyReport(kUnused)); ASSERT_NO_FATAL_FAILURE(entry.GenerateVerifyReport(kUnused)); ASSERT_NO_FATAL_FAILURE(entry.GenerateVerifyReport(kUnused)); ASSERT_NO_FATAL_FAILURE(entry.GenerateVerifyReport(kUnused)); ASSERT_NO_FATAL_FAILURE(entry.TestDecryptCTR()); ASSERT_NO_FATAL_FAILURE(s.UpdateUsageEntry(&encrypted_usage_header_)); ASSERT_NO_FATAL_FAILURE(entry.GenerateVerifyReport(kActive)); // Flag the entry as inactive. ASSERT_NO_FATAL_FAILURE(entry.DeactivateUsageEntry()); ASSERT_NO_FATAL_FAILURE(s.UpdateUsageEntry(&encrypted_usage_header_)); // It should report as inactive. ASSERT_NO_FATAL_FAILURE(entry.GenerateVerifyReport(kInactiveUsed)); // Decrypt should fail. ASSERT_NO_FATAL_FAILURE( entry.TestDecryptCTR(false, OEMCrypto_ERROR_UNKNOWN_FAILURE)); // We could call DeactivateUsageEntry multiple times. The state should not // change. ASSERT_NO_FATAL_FAILURE(entry.DeactivateUsageEntry()); ASSERT_NO_FATAL_FAILURE(s.UpdateUsageEntry(&encrypted_usage_header_)); // It should report as inactive. ASSERT_NO_FATAL_FAILURE(entry.GenerateVerifyReport(kInactiveUsed)); } // Test the usage report when the license is loaded but the keys are never used // for decryption. TEST_P(OEMCryptoUsageTableTest, OnlineLicenseUnused) { LicenseWithUsageEntry entry; entry.license_messages().set_api_version(license_api_version_); entry.MakeAndLoadOnline(this); Session& s = entry.session(); ASSERT_NO_FATAL_FAILURE(s.UpdateUsageEntry(&encrypted_usage_header_)); // No decrypt. We do not use this license. ASSERT_NO_FATAL_FAILURE(entry.GenerateVerifyReport(kUnused)); // Flag the entry as inactive. ASSERT_NO_FATAL_FAILURE(entry.DeactivateUsageEntry()); ASSERT_NO_FATAL_FAILURE(s.UpdateUsageEntry(&encrypted_usage_header_)); // It should report as inactive. ASSERT_NO_FATAL_FAILURE(entry.GenerateVerifyReport(kInactiveUnused)); // Decrypt should fail. ASSERT_NO_FATAL_FAILURE( entry.TestDecryptCTR(false, OEMCrypto_ERROR_UNKNOWN_FAILURE)); // We could call DeactivateUsageEntry multiple times. The state should not // change. ASSERT_NO_FATAL_FAILURE(entry.DeactivateUsageEntry()); ASSERT_NO_FATAL_FAILURE(s.UpdateUsageEntry(&encrypted_usage_header_)); // It should report as inactive. ASSERT_NO_FATAL_FAILURE(entry.GenerateVerifyReport(kInactiveUnused)); } // Test that the usage table has been updated and saved before a report can be // generated. TEST_P(OEMCryptoUsageTableTest, ForbidReportWithNoUpdate) { LicenseWithUsageEntry entry; entry.license_messages().set_api_version(license_api_version_); entry.MakeAndLoadOnline(this); Session& s = entry.session(); ASSERT_NO_FATAL_FAILURE(s.UpdateUsageEntry(&encrypted_usage_header_)); ASSERT_NO_FATAL_FAILURE(entry.GenerateVerifyReport(kUnused)); ASSERT_NO_FATAL_FAILURE(entry.TestDecryptCTR()); // Cannot generate a report without first updating the file. ASSERT_NO_FATAL_FAILURE( s.GenerateReport(entry.pst(), OEMCrypto_ERROR_ENTRY_NEEDS_UPDATE)); ASSERT_NO_FATAL_FAILURE(s.UpdateUsageEntry(&encrypted_usage_header_)); // Now it's OK. ASSERT_NO_FATAL_FAILURE(entry.GenerateVerifyReport(kActive)); // Flag the entry as inactive. ASSERT_NO_FATAL_FAILURE(entry.DeactivateUsageEntry()); // Cannot generate a report without first updating the file. ASSERT_NO_FATAL_FAILURE( s.GenerateReport(entry.pst(), OEMCrypto_ERROR_ENTRY_NEEDS_UPDATE)); // Decrypt should fail. ASSERT_NO_FATAL_FAILURE( entry.TestDecryptCTR(false, OEMCrypto_ERROR_UNKNOWN_FAILURE)); } // Test an online license with a license renewal. TEST_P(OEMCryptoUsageTableTest, OnlineLicenseWithRefreshAPI16) { LicenseWithUsageEntry entry; entry.license_messages().set_api_version(license_api_version_); entry.MakeAndLoadOnline(this); Session& s = entry.session(); ASSERT_NO_FATAL_FAILURE(entry.TestDecryptCTR()); RenewalRoundTrip renewal_messages(&entry.license_messages()); MakeRenewalRequest(&renewal_messages); LoadRenewal(&renewal_messages, OEMCrypto_SUCCESS); ASSERT_NO_FATAL_FAILURE(s.UpdateUsageEntry(&encrypted_usage_header_)); ASSERT_NO_FATAL_FAILURE(entry.GenerateVerifyReport(kActive)); } // Verify that a streaming license cannot be reloaded. TEST_P(OEMCryptoUsageTableTest, RepeatOnlineLicense) { LicenseWithUsageEntry entry; entry.license_messages().set_api_version(license_api_version_); entry.MakeAndLoadOnline(this); Session& s = entry.session(); ASSERT_NO_FATAL_FAILURE(s.UpdateUsageEntry(&encrypted_usage_header_)); ASSERT_NO_FATAL_FAILURE(s.close()); Session s2; ASSERT_NO_FATAL_FAILURE(s2.open()); ASSERT_NO_FATAL_FAILURE(InstallTestRSAKey(&s2)); s2.LoadUsageEntry(s); // Use the same entry. ASSERT_NE(OEMCrypto_SUCCESS, entry.license_messages().LoadResponse(&s2)); } // An offline license should not load on the first call if the nonce is bad. TEST_P(OEMCryptoUsageTableTest, OnlineBadNonce) { Session s; LicenseRoundTrip license_messages(&s); license_messages.set_api_version(license_api_version_); license_messages.set_control(wvoec::kControlNonceEnabled | wvoec::kControlNonceRequired); license_messages.set_pst("my-pst"); ASSERT_NO_FATAL_FAILURE(s.open()); ASSERT_NO_FATAL_FAILURE(InstallTestRSAKey(&s)); ASSERT_NO_FATAL_FAILURE(s.CreateNewUsageEntry()); ASSERT_NO_FATAL_FAILURE(s.GenerateNonce()); ASSERT_NO_FATAL_FAILURE(license_messages.SignAndVerifyRequest()); ASSERT_NO_FATAL_FAILURE(license_messages.CreateDefaultResponse()); for (uint32_t i = 0; i < license_messages.num_keys(); i++) license_messages.response_data().keys[i].control.nonce ^= 42; ASSERT_NO_FATAL_FAILURE(license_messages.EncryptAndSignResponse()); ASSERT_EQ(OEMCrypto_ERROR_INVALID_NONCE, license_messages.LoadResponse()); } // A license with non-zero replay control bits needs a valid pst. TEST_P(OEMCryptoUsageTableTest, OnlineEmptyPST) { Session s; ASSERT_NO_FATAL_FAILURE(s.open()); ASSERT_NO_FATAL_FAILURE(InstallTestRSAKey(&s)); ASSERT_NO_FATAL_FAILURE(s.GenerateNonce()); LicenseRoundTrip license_messages(&s); license_messages.set_api_version(license_api_version_); license_messages.set_control(wvoec::kControlNonceEnabled | wvoec::kControlNonceRequired); // DO NOT SET PST: license_messages.set_pst(pst); ASSERT_NO_FATAL_FAILURE(license_messages.SignAndVerifyRequest()); ASSERT_NO_FATAL_FAILURE(license_messages.CreateDefaultResponse()); ASSERT_NO_FATAL_FAILURE(license_messages.EncryptAndSignResponse()); ASSERT_NO_FATAL_FAILURE(s.CreateNewUsageEntry()); ASSERT_NE(OEMCrypto_SUCCESS, license_messages.LoadResponse()); } // A license with non-zero replay control bits needs a valid pst. TEST_P(OEMCryptoUsageTableTest, OnlineMissingEntry) { Session s; ASSERT_NO_FATAL_FAILURE(s.open()); ASSERT_NO_FATAL_FAILURE(InstallTestRSAKey(&s)); ASSERT_NO_FATAL_FAILURE(s.GenerateNonce()); LicenseRoundTrip license_messages(&s); license_messages.set_api_version(license_api_version_); license_messages.set_control(wvoec::kControlNonceEnabled | wvoec::kControlNonceRequired); license_messages.set_pst("my-pst"); ASSERT_NO_FATAL_FAILURE(license_messages.SignAndVerifyRequest()); ASSERT_NO_FATAL_FAILURE(license_messages.CreateDefaultResponse()); ASSERT_NO_FATAL_FAILURE(license_messages.EncryptAndSignResponse()); // ENTRY NOT CREATED: ASSERT_NO_FATAL_FAILURE(s.CreateNewUsageEntry()); ASSERT_NE(OEMCrypto_SUCCESS, license_messages.LoadResponse()); } // Sessions should have at most one entry at a time. This tests different // orderings of CreateNewUsageEntry and LoadUsageEntry calls. TEST_P(OEMCryptoUsageTableTest, CreateAndLoadMultipleEntriesAPI16) { // Entry Count: we start each test with an empty header. uint32_t usage_entry_number; LicenseWithUsageEntry entry; entry.license_messages().set_api_version(license_api_version_); Session& s = entry.session(); // Make first entry 0. ASSERT_NO_FATAL_FAILURE(entry.MakeOfflineAndClose(this)); // Load an entry, then try to create a second. ASSERT_NO_FATAL_FAILURE(s.open()); // Reload entry 0. ASSERT_NO_FATAL_FAILURE(s.ReloadUsageEntry()); // Create new entry 1 should fail. ASSERT_EQ(OEMCrypto_ERROR_MULTIPLE_USAGE_ENTRIES, OEMCrypto_CreateNewUsageEntry(entry.session().session_id(), &usage_entry_number)); ASSERT_NO_FATAL_FAILURE(s.close()); // Create an entry, then try to load a second. Session s2; ASSERT_NO_FATAL_FAILURE(s2.open()); // Create entry 1. ASSERT_NO_FATAL_FAILURE(s2.CreateNewUsageEntry()); // Try to reload entry 0. ASSERT_EQ(OEMCrypto_ERROR_MULTIPLE_USAGE_ENTRIES, OEMCrypto_LoadUsageEntry(s2.session_id(), s.usage_entry_number(), s.encrypted_usage_entry().data(), s.encrypted_usage_entry().size())); ASSERT_NO_FATAL_FAILURE(s2.close()); // Reload an entry and a license, then try to load the same entry again. // This reloads entry 0. ASSERT_NO_FATAL_FAILURE(s.open()); ASSERT_NO_FATAL_FAILURE(s.ReloadUsageEntry()); ASSERT_EQ(OEMCrypto_ERROR_MULTIPLE_USAGE_ENTRIES, OEMCrypto_LoadUsageEntry(s.session_id(), s.usage_entry_number(), s.encrypted_usage_entry().data(), s.encrypted_usage_entry().size())); ASSERT_NO_FATAL_FAILURE(s.close()); // Create an entry, then try to create a second entry. ASSERT_NO_FATAL_FAILURE(s2.open()); ASSERT_NO_FATAL_FAILURE(s2.CreateNewUsageEntry()); ASSERT_EQ( OEMCrypto_ERROR_MULTIPLE_USAGE_ENTRIES, OEMCrypto_CreateNewUsageEntry(s2.session_id(), &usage_entry_number)); } // An entry can be loaded in only one session at a time. TEST_P(OEMCryptoUsageTableTest, LoadEntryInMultipleSessions) { // Entry Count: we start each test with an empty header. LicenseWithUsageEntry entry; entry.license_messages().set_api_version(license_api_version_); Session& s = entry.session(); // Make first entry 0. ASSERT_NO_FATAL_FAILURE(entry.MakeOfflineAndClose(this)); const uint32_t usage_entry_number = s.usage_entry_number(); EXPECT_EQ(usage_entry_number, 0u); // Should be only entry in this test. // Load an entry, then try to create a second. ASSERT_NO_FATAL_FAILURE(s.open()); // Reload entry 0. ASSERT_NO_FATAL_FAILURE(s.ReloadUsageEntry()); // Create an entry, then try to load a second. Session s2; ASSERT_NO_FATAL_FAILURE(s2.open()); // Try to load entry 0 into session 2. ASSERT_EQ(OEMCrypto_ERROR_INVALID_SESSION, OEMCrypto_LoadUsageEntry(s2.session_id(), usage_entry_number, s.encrypted_usage_entry().data(), s.encrypted_usage_entry().size())); } // Test generic encrypt when the license uses a PST. TEST_P(OEMCryptoUsageTableTest, GenericCryptoEncrypt) { LicenseWithUsageEntry entry; entry.license_messages().set_api_version(license_api_version_); entry.set_generic_crypto(true); entry.MakeAndLoadOnline(this); Session& s = entry.session(); OEMCryptoResult sts; unsigned int key_index = 0; vector expected_encrypted; EncryptBufferWithKey(s.license().keys[key_index].key_data, clear_buffer_, &expected_encrypted); sts = OEMCrypto_SelectKey(s.session_id(), s.license().keys[key_index].key_id, s.license().keys[key_index].key_id_length, OEMCrypto_CipherMode_CTR); ASSERT_EQ(OEMCrypto_SUCCESS, sts); vector encrypted(clear_buffer_.size()); sts = OEMCrypto_Generic_Encrypt( s.session_id(), clear_buffer_.data(), clear_buffer_.size(), iv_, OEMCrypto_AES_CBC_128_NO_PADDING, encrypted.data()); ASSERT_EQ(OEMCrypto_SUCCESS, sts); EXPECT_EQ(expected_encrypted, encrypted); ASSERT_NO_FATAL_FAILURE(s.UpdateUsageEntry(&encrypted_usage_header_)); ASSERT_NO_FATAL_FAILURE(entry.GenerateVerifyReport(kActive)); ASSERT_NO_FATAL_FAILURE(entry.DeactivateUsageEntry()); ASSERT_NO_FATAL_FAILURE(s.UpdateUsageEntry(&encrypted_usage_header_)); ASSERT_NO_FATAL_FAILURE(entry.GenerateVerifyReport(kInactiveUsed)); encrypted.assign(clear_buffer_.size(), 0); sts = OEMCrypto_Generic_Encrypt( s.session_id(), clear_buffer_.data(), clear_buffer_.size(), iv_, OEMCrypto_AES_CBC_128_NO_PADDING, encrypted.data()); ASSERT_NE(OEMCrypto_SUCCESS, sts); EXPECT_NE(encrypted, expected_encrypted); } // Test generic decrypt when the license uses a PST. TEST_P(OEMCryptoUsageTableTest, GenericCryptoDecrypt) { LicenseWithUsageEntry entry; entry.license_messages().set_api_version(license_api_version_); entry.set_generic_crypto(true); entry.MakeAndLoadOnline(this); Session& s = entry.session(); OEMCryptoResult sts; unsigned int key_index = 1; vector encrypted; EncryptBufferWithKey(s.license().keys[key_index].key_data, clear_buffer_, &encrypted); sts = OEMCrypto_SelectKey(s.session_id(), s.license().keys[key_index].key_id, s.license().keys[key_index].key_id_length, OEMCrypto_CipherMode_CTR); ASSERT_EQ(OEMCrypto_SUCCESS, sts); vector resultant(encrypted.size()); sts = OEMCrypto_Generic_Decrypt( s.session_id(), encrypted.data(), encrypted.size(), iv_, OEMCrypto_AES_CBC_128_NO_PADDING, resultant.data()); ASSERT_EQ(OEMCrypto_SUCCESS, sts); EXPECT_EQ(clear_buffer_, resultant); ASSERT_NO_FATAL_FAILURE(s.UpdateUsageEntry(&encrypted_usage_header_)); ASSERT_NO_FATAL_FAILURE(entry.GenerateVerifyReport(kActive)); ASSERT_NO_FATAL_FAILURE(entry.DeactivateUsageEntry()); ASSERT_NO_FATAL_FAILURE(s.UpdateUsageEntry(&encrypted_usage_header_)); ASSERT_NO_FATAL_FAILURE(entry.GenerateVerifyReport(kInactiveUsed)); resultant.assign(encrypted.size(), 0); sts = OEMCrypto_Generic_Decrypt( s.session_id(), encrypted.data(), encrypted.size(), iv_, OEMCrypto_AES_CBC_128_NO_PADDING, resultant.data()); ASSERT_NE(OEMCrypto_SUCCESS, sts); EXPECT_NE(clear_buffer_, resultant); } // Test generic sign when the license uses a PST. TEST_P(OEMCryptoUsageTableTest, GenericCryptoSign) { LicenseWithUsageEntry entry; entry.license_messages().set_api_version(license_api_version_); entry.set_generic_crypto(true); entry.MakeAndLoadOnline(this); Session& s = entry.session(); OEMCryptoResult sts; unsigned int key_index = 2; vector expected_signature; SignBufferWithKey(s.license().keys[key_index].key_data, clear_buffer_, &expected_signature); sts = OEMCrypto_SelectKey(s.session_id(), s.license().keys[key_index].key_id, s.license().keys[key_index].key_id_length, OEMCrypto_CipherMode_CTR); ASSERT_EQ(OEMCrypto_SUCCESS, sts); size_t gen_signature_length = 0; sts = OEMCrypto_Generic_Sign(s.session_id(), clear_buffer_.data(), clear_buffer_.size(), OEMCrypto_HMAC_SHA256, nullptr, &gen_signature_length); ASSERT_EQ(OEMCrypto_ERROR_SHORT_BUFFER, sts); ASSERT_EQ(static_cast(SHA256_DIGEST_LENGTH), gen_signature_length); vector signature(SHA256_DIGEST_LENGTH); sts = OEMCrypto_Generic_Sign(s.session_id(), clear_buffer_.data(), clear_buffer_.size(), OEMCrypto_HMAC_SHA256, signature.data(), &gen_signature_length); ASSERT_EQ(OEMCrypto_SUCCESS, sts); ASSERT_EQ(expected_signature, signature); ASSERT_NO_FATAL_FAILURE(s.UpdateUsageEntry(&encrypted_usage_header_)); ASSERT_NO_FATAL_FAILURE(entry.GenerateVerifyReport(kActive)); ASSERT_NO_FATAL_FAILURE(entry.DeactivateUsageEntry()); ASSERT_NO_FATAL_FAILURE(s.UpdateUsageEntry(&encrypted_usage_header_)); ASSERT_NO_FATAL_FAILURE(entry.GenerateVerifyReport(kInactiveUsed)); signature.assign(SHA256_DIGEST_LENGTH, 0); gen_signature_length = SHA256_DIGEST_LENGTH; sts = OEMCrypto_Generic_Sign(s.session_id(), clear_buffer_.data(), clear_buffer_.size(), OEMCrypto_HMAC_SHA256, signature.data(), &gen_signature_length); ASSERT_NE(OEMCrypto_SUCCESS, sts); ASSERT_NE(signature, expected_signature); } // Test generic verify when the license uses a PST. TEST_P(OEMCryptoUsageTableTest, GenericCryptoVerify) { LicenseWithUsageEntry entry; entry.license_messages().set_api_version(license_api_version_); entry.set_generic_crypto(true); entry.MakeAndLoadOnline(this); Session& s = entry.session(); OEMCryptoResult sts; unsigned int key_index = 3; vector signature; SignBufferWithKey(s.license().keys[key_index].key_data, clear_buffer_, &signature); sts = OEMCrypto_SelectKey(s.session_id(), s.license().keys[key_index].key_id, s.license().keys[key_index].key_id_length, OEMCrypto_CipherMode_CTR); ASSERT_EQ(OEMCrypto_SUCCESS, sts); sts = OEMCrypto_Generic_Verify(s.session_id(), clear_buffer_.data(), clear_buffer_.size(), OEMCrypto_HMAC_SHA256, signature.data(), signature.size()); ASSERT_EQ(OEMCrypto_SUCCESS, sts); ASSERT_NO_FATAL_FAILURE(s.UpdateUsageEntry(&encrypted_usage_header_)); ASSERT_NO_FATAL_FAILURE(entry.GenerateVerifyReport(kActive)); ASSERT_NO_FATAL_FAILURE(entry.DeactivateUsageEntry()); ASSERT_NO_FATAL_FAILURE(s.UpdateUsageEntry(&encrypted_usage_header_)); ASSERT_NO_FATAL_FAILURE(entry.GenerateVerifyReport(kInactiveUsed)); sts = OEMCrypto_Generic_Verify(s.session_id(), clear_buffer_.data(), clear_buffer_.size(), OEMCrypto_HMAC_SHA256, signature.data(), signature.size()); ASSERT_NE(OEMCrypto_SUCCESS, sts); } // Test that an offline license can be loaded. TEST_P(OEMCryptoUsageTableTest, OfflineLicense) { LicenseWithUsageEntry entry; entry.license_messages().set_api_version(license_api_version_); ASSERT_NO_FATAL_FAILURE(entry.MakeOfflineAndClose(this)); } // Test that an offline license can be loaded and that the license can be // renewed. TEST_P(OEMCryptoUsageTableTest, OfflineLicenseRefresh) { LicenseWithUsageEntry entry; entry.license_messages().set_api_version(license_api_version_); entry.MakeAndLoad(this, wvoec::kControlNonceOrEntry); Session& s = entry.session(); ASSERT_NO_FATAL_FAILURE(entry.TestDecryptCTR()); // License renewal message is signed by client and verified by the server. RenewalRoundTrip renewal_messages(&entry.license_messages()); MakeRenewalRequest(&renewal_messages); LoadRenewal(&renewal_messages, OEMCrypto_SUCCESS); ASSERT_NO_FATAL_FAILURE(entry.TestDecryptCTR()); ASSERT_NO_FATAL_FAILURE(s.UpdateUsageEntry(&encrypted_usage_header_)); ASSERT_NO_FATAL_FAILURE(entry.GenerateVerifyReport(kActive)); } // Test that an offline license can be reloaded in a new session. TEST_P(OEMCryptoUsageTableTest, ReloadOfflineLicense) { LicenseWithUsageEntry entry; entry.license_messages().set_api_version(license_api_version_); entry.MakeOfflineAndClose(this); Session& s = entry.session(); ASSERT_NO_FATAL_FAILURE(entry.OpenAndReload(this)); ASSERT_NO_FATAL_FAILURE(s.UpdateUsageEntry(&encrypted_usage_header_)); ASSERT_NO_FATAL_FAILURE(entry.GenerateVerifyReport(kUnused)); ASSERT_NO_FATAL_FAILURE(entry.TestDecryptCTR()); ASSERT_NO_FATAL_FAILURE(s.UpdateUsageEntry(&encrypted_usage_header_)); ASSERT_NO_FATAL_FAILURE(entry.GenerateVerifyReport(kActive)); } // Test that an offline license can be reloaded in a new session, and then // refreshed. TEST_P(OEMCryptoUsageTableTest, ReloadOfflineLicenseWithRefresh) { LicenseWithUsageEntry entry; entry.license_messages().set_api_version(license_api_version_); entry.MakeOfflineAndClose(this); Session& s = entry.session(); ASSERT_NO_FATAL_FAILURE(entry.OpenAndReload(this)); ASSERT_NO_FATAL_FAILURE(s.UpdateUsageEntry(&encrypted_usage_header_)); ASSERT_NO_FATAL_FAILURE(entry.GenerateVerifyReport(kUnused)); ASSERT_NO_FATAL_FAILURE(entry.TestDecryptCTR()); ASSERT_NO_FATAL_FAILURE(s.UpdateUsageEntry(&encrypted_usage_header_)); RenewalRoundTrip renewal_messages(&entry.license_messages()); MakeRenewalRequest(&renewal_messages); LoadRenewal(&renewal_messages, OEMCrypto_SUCCESS); ASSERT_NO_FATAL_FAILURE(entry.TestDecryptCTR()); ASSERT_NO_FATAL_FAILURE(s.UpdateUsageEntry(&encrypted_usage_header_)); ASSERT_NO_FATAL_FAILURE(entry.GenerateVerifyReport(kActive)); } // Verify that we can still reload an offline license after OEMCrypto_Terminate // and Initialize are called. This is as close to a reboot as we can do in a // unit test. TEST_P(OEMCryptoUsageTableTest, ReloadOfflineLicenseWithTerminate) { LicenseWithUsageEntry entry; entry.license_messages().set_api_version(license_api_version_); entry.MakeOfflineAndClose(this); Session& s = entry.session(); ShutDown(); // This calls OEMCrypto_Terminate. Restart(); // This calls OEMCrypto_Initialize. ASSERT_EQ(OEMCrypto_SUCCESS, OEMCrypto_LoadUsageTableHeader(encrypted_usage_header_.data(), encrypted_usage_header_.size())); ASSERT_NO_FATAL_FAILURE(entry.OpenAndReload(this)); ASSERT_NO_FATAL_FAILURE(s.UpdateUsageEntry(&encrypted_usage_header_)); ASSERT_NO_FATAL_FAILURE(entry.GenerateVerifyReport(kUnused)); ASSERT_NO_FATAL_FAILURE(entry.TestDecryptCTR()); ASSERT_NO_FATAL_FAILURE(s.UpdateUsageEntry(&encrypted_usage_header_)); ASSERT_NO_FATAL_FAILURE(entry.GenerateVerifyReport(kActive)); } // If we attempt to load a second license with the same usage entry as the // first, but it has different mac keys, then the attempt should fail. This is // how we verify that we are reloading the same license. TEST_P(OEMCryptoUsageTableTest, BadReloadOfflineLicense) { LicenseWithUsageEntry entry; entry.license_messages().set_api_version(license_api_version_); entry.MakeOfflineAndClose(this); Session& s = entry.session(); // Offline license with new mac keys should fail. Session s2; LicenseRoundTrip license_messages2(&s2); // Copy the response, and then change the mac keys. license_messages2.response_data() = entry.license_messages().response_data(); license_messages2.core_response() = entry.license_messages().core_response(); license_messages2.response_data().mac_keys[7] ^= 42; ASSERT_NO_FATAL_FAILURE(s2.open()); ASSERT_NO_FATAL_FAILURE(InstallTestRSAKey(&s2)); // This is a valid license: it is correctly signed. license_messages2.EncryptAndSignResponse(); // Load the usage entry should be OK. ASSERT_NO_FATAL_FAILURE(s2.LoadUsageEntry(s)); ASSERT_NE(OEMCrypto_SUCCESS, license_messages2.LoadResponse()); ASSERT_NO_FATAL_FAILURE(s2.close()); // Now we go back to the original license response. It should load OK. ASSERT_NO_FATAL_FAILURE(entry.OpenAndReload(this)); ASSERT_NO_FATAL_FAILURE(s.UpdateUsageEntry(&encrypted_usage_header_)); ASSERT_NO_FATAL_FAILURE(entry.GenerateVerifyReport(kUnused)); } // An offline license should not load on the first call if the nonce is bad. TEST_P(OEMCryptoUsageTableTest, OfflineBadNonce) { Session s; LicenseRoundTrip license_messages(&s); license_messages.set_api_version(license_api_version_); license_messages.set_control(wvoec::kControlNonceOrEntry); license_messages.set_pst("my-pst"); ASSERT_NO_FATAL_FAILURE(s.open()); ASSERT_NO_FATAL_FAILURE(InstallTestRSAKey(&s)); ASSERT_NO_FATAL_FAILURE(s.CreateNewUsageEntry()); ASSERT_NO_FATAL_FAILURE(s.GenerateNonce()); ASSERT_NO_FATAL_FAILURE(license_messages.SignAndVerifyRequest()); ASSERT_NO_FATAL_FAILURE(license_messages.CreateDefaultResponse()); for (size_t i = 0; i < license_messages.num_keys(); i++) license_messages.response_data().keys[i].control.nonce ^= 42; ASSERT_NO_FATAL_FAILURE(license_messages.EncryptAndSignResponse()); ASSERT_EQ(OEMCrypto_ERROR_INVALID_NONCE, license_messages.LoadResponse()); } // An offline license needs a valid pst. TEST_P(OEMCryptoUsageTableTest, OfflineEmptyPST) { Session s; ASSERT_NO_FATAL_FAILURE(s.open()); ASSERT_NO_FATAL_FAILURE(InstallTestRSAKey(&s)); ASSERT_NO_FATAL_FAILURE(s.GenerateNonce()); LicenseRoundTrip license_messages(&s); license_messages.set_api_version(license_api_version_); license_messages.set_control(wvoec::kControlNonceOrEntry); // DO NOT SET PST: license_messages.set_pst(pst); ASSERT_NO_FATAL_FAILURE(license_messages.SignAndVerifyRequest()); ASSERT_NO_FATAL_FAILURE(license_messages.CreateDefaultResponse()); ASSERT_NO_FATAL_FAILURE(license_messages.EncryptAndSignResponse()); ASSERT_NO_FATAL_FAILURE(s.CreateNewUsageEntry()); ASSERT_NE(OEMCrypto_SUCCESS, license_messages.LoadResponse()); } // If we try to reload a license with a different PST, the attempt should fail. TEST_P(OEMCryptoUsageTableTest, ReloadOfflineWrongPST) { LicenseWithUsageEntry entry; entry.license_messages().set_api_version(license_api_version_); entry.MakeOfflineAndClose(this); Session& s = entry.session(); Session s2; LicenseRoundTrip license_messages2(&s2); license_messages2.response_data() = entry.license_messages().response_data(); license_messages2.core_response() = entry.license_messages().core_response(); // Change the middle of the pst. license_messages2.response_data().pst[3] ^= 'Z'; ASSERT_NO_FATAL_FAILURE(s2.open()); ASSERT_NO_FATAL_FAILURE(InstallTestRSAKey(&s2)); // This is a valid license: it is correctly signed. license_messages2.EncryptAndSignResponse(); // Load the usage entry should be OK. ASSERT_NO_FATAL_FAILURE(s2.LoadUsageEntry(s)); ASSERT_NE(OEMCrypto_SUCCESS, license_messages2.LoadResponse()); } // Once a license has been deactivated, the keys can no longer be used for // decryption. However, we can still generate a usage report. TEST_P(OEMCryptoUsageTableTest, DeactivateOfflineLicense) { LicenseWithUsageEntry entry; entry.license_messages().set_api_version(license_api_version_); entry.MakeOfflineAndClose(this); Session& s = entry.session(); // Reload the offline license. ASSERT_NO_FATAL_FAILURE(entry.OpenAndReload(this)); ASSERT_NO_FATAL_FAILURE( entry.TestDecryptCTR()); // Should be able to decrypt. ASSERT_NO_FATAL_FAILURE(entry.DeactivateUsageEntry()); // Then deactivate. // After deactivate, should not be able to decrypt. ASSERT_NO_FATAL_FAILURE( entry.TestDecryptCTR(false, OEMCrypto_ERROR_UNKNOWN_FAILURE)); ASSERT_NO_FATAL_FAILURE(s.UpdateUsageEntry(&encrypted_usage_header_)); ASSERT_NO_FATAL_FAILURE(entry.GenerateVerifyReport(kInactiveUsed)); ASSERT_NO_FATAL_FAILURE(s.close()); // Offline license can not be reused if it has been deactivated. ASSERT_NO_FATAL_FAILURE(s.open()); ASSERT_NO_FATAL_FAILURE(InstallTestRSAKey(&s)); ASSERT_NE(OEMCrypto_SUCCESS, entry.license_messages().LoadResponse(&s)); s.close(); // But we can still generate a report. ASSERT_NO_FATAL_FAILURE(s.open()); ASSERT_NO_FATAL_FAILURE(entry.ReloadUsageEntry()); ASSERT_NO_FATAL_FAILURE(s.UpdateUsageEntry(&encrypted_usage_header_)); // Sending a release from an offline license that has been deactivate will // only work if the license server can handle v16 licenses. This is a rare // condition, so it is OK to break it during the transition months. entry.license_messages().set_api_version(global_features.api_version); ASSERT_NO_FATAL_FAILURE(entry.GenerateVerifyReport(kInactiveUsed)); // We could call DeactivateUsageEntry multiple times. The state should not // change. ASSERT_NO_FATAL_FAILURE(entry.DeactivateUsageEntry()); ASSERT_NO_FATAL_FAILURE(s.UpdateUsageEntry(&encrypted_usage_header_)); ASSERT_NO_FATAL_FAILURE(entry.GenerateVerifyReport(kInactiveUsed)); } // The usage report should indicate that the keys were never used for // decryption. TEST_P(OEMCryptoUsageTableTest, DeactivateOfflineLicenseUnused) { LicenseWithUsageEntry entry; entry.license_messages().set_api_version(license_api_version_); entry.MakeOfflineAndClose(this); Session& s = entry.session(); // No Decrypt. This license is unused. ASSERT_NO_FATAL_FAILURE(entry.OpenAndReload(this)); ASSERT_NO_FATAL_FAILURE(entry.DeactivateUsageEntry()); // Then deactivate. // After deactivate, should not be able to decrypt. ASSERT_NO_FATAL_FAILURE( entry.TestDecryptCTR(false, OEMCrypto_ERROR_UNKNOWN_FAILURE)); ASSERT_NO_FATAL_FAILURE(s.UpdateUsageEntry(&encrypted_usage_header_)); ASSERT_NO_FATAL_FAILURE(entry.GenerateVerifyReport(kInactiveUnused)); ASSERT_NO_FATAL_FAILURE(s.close()); // Offline license can not be reused if it has been deactivated. ASSERT_NO_FATAL_FAILURE(s.open()); ASSERT_NO_FATAL_FAILURE(InstallTestRSAKey(&s)); ASSERT_NE(OEMCrypto_SUCCESS, entry.license_messages().LoadResponse(&s)); s.close(); // But we can still generate a report. ASSERT_NO_FATAL_FAILURE(s.open()); ASSERT_NO_FATAL_FAILURE(entry.ReloadUsageEntry()); ASSERT_NO_FATAL_FAILURE(s.UpdateUsageEntry(&encrypted_usage_header_)); // Sending a release from an offline license that has been deactivate will // only work if the license server can handle v16 licenses. This is a rare // condition, so it is OK to break it during the transition months. entry.license_messages().set_api_version(global_features.api_version); ASSERT_NO_FATAL_FAILURE(entry.GenerateVerifyReport(kInactiveUnused)); // We could call DeactivateUsageEntry multiple times. The state should not // change. ASSERT_NO_FATAL_FAILURE(entry.DeactivateUsageEntry()); ASSERT_NO_FATAL_FAILURE(s.UpdateUsageEntry(&encrypted_usage_header_)); ASSERT_NO_FATAL_FAILURE(entry.GenerateVerifyReport(kInactiveUnused)); } TEST_P(OEMCryptoUsageTableTest, SecureStop) { LicenseWithUsageEntry entry; entry.license_messages().set_api_version(license_api_version_); entry.MakeAndLoadOnline(this); Session& s = entry.session(); ASSERT_NO_FATAL_FAILURE(s.UpdateUsageEntry(&encrypted_usage_header_)); ASSERT_NO_FATAL_FAILURE(entry.TestDecryptCTR()); ASSERT_NO_FATAL_FAILURE(s.UpdateUsageEntry(&encrypted_usage_header_)); ASSERT_NO_FATAL_FAILURE(s.close()); // When we generate a secure stop without loading the license first, it // should assume the server does not support core messages. ASSERT_NO_FATAL_FAILURE(s.open()); ASSERT_NO_FATAL_FAILURE(entry.ReloadUsageEntry()); ASSERT_NO_FATAL_FAILURE(entry.DeactivateUsageEntry()); ASSERT_NO_FATAL_FAILURE(s.UpdateUsageEntry(&encrypted_usage_header_)); // It should report as inactive. ASSERT_NO_FATAL_FAILURE(entry.GenerateVerifyReport(kInactiveUsed)); } // Test update usage table fails when passed a null pointer. TEST_P(OEMCryptoUsageTableTest, UpdateFailsWithNullPtr) { LicenseWithUsageEntry entry; entry.license_messages().set_api_version(license_api_version_); entry.MakeAndLoadOnline(this); Session& s = entry.session(); ASSERT_NO_FATAL_FAILURE(s.UpdateUsageEntry(&encrypted_usage_header_)); size_t header_buffer_length = encrypted_usage_header_.size(); size_t entry_buffer_length = s.encrypted_usage_entry().size(); vector buffer(entry_buffer_length); // Now try to pass in null pointers for the buffers. This should fail. ASSERT_NE( OEMCrypto_SUCCESS, OEMCrypto_UpdateUsageEntry(s.session_id(), nullptr, &header_buffer_length, buffer.data(), &entry_buffer_length)); ASSERT_NE(OEMCrypto_SUCCESS, OEMCrypto_UpdateUsageEntry( s.session_id(), encrypted_usage_header_.data(), &header_buffer_length, nullptr, &entry_buffer_length)); } // Class used to test usage table defragmentation. class OEMCryptoUsageTableDefragTest : public OEMCryptoUsageTableTest { protected: void ReloadLicense(LicenseWithUsageEntry* entry) { Session& s = entry->session(); ASSERT_NO_FATAL_FAILURE(entry->OpenAndReload(this)); ASSERT_NO_FATAL_FAILURE(s.UpdateUsageEntry(&encrypted_usage_header_)); ASSERT_NO_FATAL_FAILURE(entry->TestDecryptCTR()); ASSERT_NO_FATAL_FAILURE(s.UpdateUsageEntry(&encrypted_usage_header_)); ASSERT_NO_FATAL_FAILURE(entry->GenerateVerifyReport(kActive)); ASSERT_NO_FATAL_FAILURE(s.close()); } void FailReloadLicense(LicenseWithUsageEntry* entry, OEMCryptoResult expected_result) { Session& s = entry->session(); ASSERT_NO_FATAL_FAILURE(s.open()); ASSERT_NO_FATAL_FAILURE(InstallTestRSAKey(&s)); ASSERT_EQ(expected_result, OEMCrypto_LoadUsageEntry(s.session_id(), s.usage_entry_number(), s.encrypted_usage_entry().data(), s.encrypted_usage_entry().size())); ASSERT_NE(OEMCrypto_SUCCESS, entry->license_messages().LoadResponse()); ASSERT_NO_FATAL_FAILURE(s.close()); } void ShrinkHeader(uint32_t new_size, OEMCryptoResult expected_result = OEMCrypto_SUCCESS) { // We call OEMCrypto_ShrinkUsageTableHeader once with a zero length buffer, // so that OEMCrypto can tell us how big the buffer should be. size_t header_buffer_length = 0; OEMCryptoResult sts = OEMCrypto_ShrinkUsageTableHeader( new_size, nullptr, &header_buffer_length); // If we are expecting success, then the first call shall return // SHORT_BUFFER. However, if we are not expecting success, this first call // may return either SHORT_BUFFER or the expect error. if (expected_result == OEMCrypto_SUCCESS) { ASSERT_EQ(OEMCrypto_ERROR_SHORT_BUFFER, sts); } else if (sts != OEMCrypto_ERROR_SHORT_BUFFER) { // If we got any thing from the first call, it should be the expected // error, and we don't need to call a second time. ASSERT_EQ(expected_result, sts); return; } // If the first call resulted in SHORT_BUFFER, we should resize the buffer // and try again. ASSERT_LT(0u, header_buffer_length); encrypted_usage_header_.resize(header_buffer_length); sts = OEMCrypto_ShrinkUsageTableHeader( new_size, encrypted_usage_header_.data(), &header_buffer_length); // For the second call, we always demand the expected result. ASSERT_EQ(expected_result, sts); } }; // Verify that usage table entries can be moved around in the table. TEST_P(OEMCryptoUsageTableDefragTest, MoveUsageEntries) { const size_t ENTRY_COUNT = 10; vector entries(ENTRY_COUNT); for (size_t i = 0; i < ENTRY_COUNT; i++) { entries[i].set_pst("pst " + std::to_string(i)); ASSERT_NO_FATAL_FAILURE(entries[i].MakeOfflineAndClose(this)) << "On license " << i << " pst=" << entries[i].pst(); wvcdm::TestSleep::SyncFakeClock(); } for (size_t i = 0; i < ENTRY_COUNT; i++) { ASSERT_NO_FATAL_FAILURE(entries[i].OpenAndReload(this)) << "On license " << i << " pst=" << entries[i].pst(); ASSERT_NO_FATAL_FAILURE(entries[i].session().close()) << "On license " << i << " pst=" << entries[i].pst(); } // Move 4 to 1. ASSERT_NO_FATAL_FAILURE( entries[4].session().MoveUsageEntry(1, &encrypted_usage_header_)); // Shrink header to 3 entries 0, 1 was 4, 2. ASSERT_NO_FATAL_FAILURE(ShrinkHeader(3)); ShutDown(); Restart(); ASSERT_EQ(OEMCrypto_SUCCESS, OEMCrypto_LoadUsageTableHeader(encrypted_usage_header_.data(), encrypted_usage_header_.size())); wvcdm::TestSleep::SyncFakeClock(); ASSERT_NO_FATAL_FAILURE(ReloadLicense(&entries[0])); // Now has index 1. ASSERT_NO_FATAL_FAILURE(ReloadLicense(&entries[4])); ASSERT_NO_FATAL_FAILURE(ReloadLicense(&entries[2])); // When 4 was moved to 1, it increased the gen. number in the header. ASSERT_NO_FATAL_FAILURE( FailReloadLicense(&entries[1], OEMCrypto_ERROR_GENERATION_SKEW)); // Index 3 is beyond the end of the table. ASSERT_NO_FATAL_FAILURE( FailReloadLicense(&entries[3], OEMCrypto_ERROR_UNKNOWN_FAILURE)); } // A usage table entry cannot be moved into an entry where an open session is // currently using the entry. TEST_P(OEMCryptoUsageTableDefragTest, MoveUsageEntriesToOpenSession) { LicenseWithUsageEntry entry0; entry0.set_pst("pst 0"); entry0.MakeOfflineAndClose(this); LicenseWithUsageEntry entry1; entry1.set_pst("pst 1"); entry1.MakeOfflineAndClose(this); entry0.session().open(); ASSERT_NO_FATAL_FAILURE(entry0.ReloadUsageEntry()); // s0 currently open on index 0. Expect this to fail: ASSERT_NO_FATAL_FAILURE(entry1.session().MoveUsageEntry( 0, &encrypted_usage_header_, OEMCrypto_ERROR_ENTRY_IN_USE)); } // The usage table cannot be shrunk if any session is using an entry that would // be deleted. TEST_P(OEMCryptoUsageTableDefragTest, ShrinkOverOpenSessions) { LicenseWithUsageEntry entry0; entry0.set_pst("pst 0"); entry0.MakeOfflineAndClose(this); LicenseWithUsageEntry entry1; entry1.set_pst("pst 1"); entry1.MakeOfflineAndClose(this); entry0.session().open(); ASSERT_NO_FATAL_FAILURE(entry0.ReloadUsageEntry()); entry1.session().open(); ASSERT_NO_FATAL_FAILURE(entry1.ReloadUsageEntry()); // Since s0 and s1 are open, we can't shrink. ASSERT_NO_FATAL_FAILURE(ShrinkHeader(1, OEMCrypto_ERROR_ENTRY_IN_USE)); entry1.session().close(); // Can shrink after closing s1, even if s0 is open. ASSERT_NO_FATAL_FAILURE(ShrinkHeader(1, OEMCrypto_SUCCESS)); } // Verify the usage table size can be increased. TEST_P(OEMCryptoUsageTableDefragTest, EnlargeHeader) { LicenseWithUsageEntry entry0; entry0.set_pst("pst 0"); entry0.MakeOfflineAndClose(this); LicenseWithUsageEntry entry1; entry1.set_pst("pst 1"); entry1.MakeOfflineAndClose(this); // Can only shrink the header -- not make it bigger. ASSERT_NO_FATAL_FAILURE(ShrinkHeader(4, OEMCrypto_ERROR_UNKNOWN_FAILURE)); } // A new header can only be created while no entries are in use. TEST_P(OEMCryptoUsageTableDefragTest, CreateNewHeaderWhileUsingOldOne) { LicenseWithUsageEntry entry0; entry0.set_pst("pst 0"); entry0.MakeOfflineAndClose(this); LicenseWithUsageEntry entry1; entry1.set_pst("pst 1"); entry1.MakeOfflineAndClose(this); entry0.session().open(); ASSERT_NO_FATAL_FAILURE(entry0.ReloadUsageEntry()); const bool kExpectFailure = false; ASSERT_NO_FATAL_FAILURE(CreateUsageTableHeader(kExpectFailure)); } // Verify that a usage table entry can only be loaded into the correct index of // the table. TEST_P(OEMCryptoUsageTableDefragTest, ReloadUsageEntryWrongIndex) { LicenseWithUsageEntry entry0; entry0.set_pst("pst 0"); entry0.MakeOfflineAndClose(this); LicenseWithUsageEntry entry1; entry1.set_pst("pst 1"); entry1.MakeOfflineAndClose(this); entry0.session().set_usage_entry_number(1); ASSERT_NO_FATAL_FAILURE( FailReloadLicense(&entry0, OEMCrypto_ERROR_INVALID_SESSION)); } // Verify that a usage table entry cannot be loaded if it has been altered. TEST_P(OEMCryptoUsageTableDefragTest, ReloadUsageEntryBadData) { LicenseWithUsageEntry entry; entry.MakeOfflineAndClose(this); Session& s = entry.session(); ASSERT_NO_FATAL_FAILURE(s.open()); ASSERT_NO_FATAL_FAILURE(InstallTestRSAKey(&s)); vector data = s.encrypted_usage_entry(); ASSERT_LT(0UL, data.size()); data[0] ^= 42; // Error could be signature or verification error. ASSERT_NE(OEMCrypto_SUCCESS, OEMCrypto_LoadUsageEntry(s.session_id(), s.usage_entry_number(), data.data(), data.size())); } // This verifies we can actually create the required number of usage table // entries. TEST_P(OEMCryptoUsageTableDefragTest, ManyUsageEntries) { // OEMCrypto is required to store at least 300 entries in the usage table // header, but it is allowed to store more. This test verifies that if we keep // adding entries, the error indicates a resource limit. It then verifies // that all of the successful entries are still valid after we throw out the // last invalid entry. // After API 16, we require 300 entries in the usage table. Before API 16, we // required 200. const size_t required_capacity = RequiredUsageSize(); // We try to make a much large header, and assume there is an error at some // point. const size_t attempt_count = required_capacity * 5; // Count of how many entries we successfully create. size_t successful_count = 0; // These entries have licenses tied to them. std::vector> entries; // Store the status of the last attempt to create an entry. OEMCryptoResult status = OEMCrypto_SUCCESS; while (successful_count < attempt_count && status == OEMCrypto_SUCCESS) { wvcdm::TestSleep::SyncFakeClock(); LOGD("Creating license for entry %zd", successful_count); entries.push_back( std::unique_ptr(new LicenseWithUsageEntry())); entries.back()->set_pst("pst " + std::to_string(successful_count)); ASSERT_NO_FATAL_FAILURE(entries.back()->MakeOfflineAndClose(this, &status)) << "Failed creating license for entry " << successful_count; if (status != OEMCrypto_SUCCESS) { // Remove the failed session. entries.resize(entries.size() - 1); break; } EXPECT_EQ(entries.back()->session().usage_entry_number(), successful_count); successful_count++; // We don't create a license for each entry. For every license, we'll // create 10 empty entries. constexpr size_t filler_count = 10; for (size_t i = 0; i < filler_count; i++) { Session s; ASSERT_NO_FATAL_FAILURE(s.open()); ASSERT_NO_FATAL_FAILURE(s.CreateNewUsageEntry(&status)) << "Failed creating entry " << successful_count; if (status != OEMCrypto_SUCCESS) break; EXPECT_EQ(s.usage_entry_number(), successful_count); successful_count++; } } LOGD("successful_count = %d", successful_count); if (status != OEMCrypto_SUCCESS) { // If we failed to create this many entries because of limited resources, // then the error returned should be insufficient resources. EXPECT_EQ(OEMCrypto_ERROR_INSUFFICIENT_RESOURCES, status) << "Failed to create license " << successful_count << ", with wrong error code."; } EXPECT_GE(successful_count, required_capacity); wvcdm::TestSleep::SyncFakeClock(); // Shrink the table a little. constexpr size_t small_number = 5; size_t smaller_size = successful_count - small_number; ASSERT_NO_FATAL_FAILURE(ShrinkHeader(smaller_size)); // Throw out the last license if it was in the part of the table that was // shrunk. if (entries.back()->session().usage_entry_number() >= smaller_size) { entries.pop_back(); } // Create a few more license for (size_t i = 0; i < small_number; i++) { wvcdm::TestSleep::SyncFakeClock(); entries.push_back( std::unique_ptr(new LicenseWithUsageEntry())); entries.back()->set_pst("new pst " + std::to_string(smaller_size + i)); entries.back()->MakeOfflineAndClose(this); } // Make sure that all of the licenses can be reloaded. for (size_t i = 0; i < entries.size(); i++) { wvcdm::TestSleep::SyncFakeClock(); Session& s = entries[i]->session(); ASSERT_NO_FATAL_FAILURE(entries[i]->OpenAndReload(this)); ASSERT_NO_FATAL_FAILURE(s.UpdateUsageEntry(&encrypted_usage_header_)); ASSERT_NO_FATAL_FAILURE(entries[i]->GenerateVerifyReport(kUnused)); ASSERT_NO_FATAL_FAILURE(entries[i]->TestDecryptCTR()); ASSERT_NO_FATAL_FAILURE(s.UpdateUsageEntry(&encrypted_usage_header_)); ASSERT_NO_FATAL_FAILURE(entries[i]->GenerateVerifyReport(kActive)); ASSERT_NO_FATAL_FAILURE(s.close()); } } // This verifies that the usage table header can be loaded if the generation // number is off by one, but not off by two. TEST_P(OEMCryptoUsageTableTest, ReloadUsageTableWithSkew) { // This also tests a few other error conditions with usage table headers. LicenseWithUsageEntry entry; entry.MakeOfflineAndClose(this); Session& s = entry.session(); // Reload the license, and save the header. ASSERT_NO_FATAL_FAILURE(entry.OpenAndReload(this)); ASSERT_NO_FATAL_FAILURE(s.UpdateUsageEntry(&encrypted_usage_header_)); vector old_usage_header_2_ = encrypted_usage_header_; ASSERT_NO_FATAL_FAILURE(s.UpdateUsageEntry(&encrypted_usage_header_)); vector old_usage_header_1_ = encrypted_usage_header_; vector old_usage_entry_1 = s.encrypted_usage_entry(); ASSERT_NO_FATAL_FAILURE(s.UpdateUsageEntry(&encrypted_usage_header_)); ASSERT_NO_FATAL_FAILURE(s.close()); ShutDown(); Restart(); // Null pointer generates error. ASSERT_NE(OEMCrypto_SUCCESS, OEMCrypto_LoadUsageTableHeader( nullptr, old_usage_header_2_.size())); ASSERT_NO_FATAL_FAILURE(s.open()); // Cannot load an entry if header didn't load. ASSERT_EQ(OEMCrypto_ERROR_UNKNOWN_FAILURE, OEMCrypto_LoadUsageEntry(s.session_id(), s.usage_entry_number(), s.encrypted_usage_entry().data(), s.encrypted_usage_entry().size())); ASSERT_NO_FATAL_FAILURE(s.close()); // Modified header generates error. vector bad_header = encrypted_usage_header_; bad_header[3] ^= 42; ASSERT_NE(OEMCrypto_SUCCESS, OEMCrypto_LoadUsageTableHeader( bad_header.data(), bad_header.size())); ASSERT_NO_FATAL_FAILURE(s.open()); // Cannot load an entry if header didn't load. ASSERT_EQ(OEMCrypto_ERROR_UNKNOWN_FAILURE, OEMCrypto_LoadUsageEntry(s.session_id(), s.usage_entry_number(), s.encrypted_usage_entry().data(), s.encrypted_usage_entry().size())); ASSERT_NO_FATAL_FAILURE(s.close()); // Old by 2 generation numbers is error. ASSERT_EQ(OEMCrypto_ERROR_GENERATION_SKEW, OEMCrypto_LoadUsageTableHeader(old_usage_header_2_.data(), old_usage_header_2_.size())); ASSERT_NO_FATAL_FAILURE(s.open()); // Cannot load an entry if header didn't load. ASSERT_NE(OEMCrypto_SUCCESS, OEMCrypto_LoadUsageEntry(s.session_id(), s.usage_entry_number(), s.encrypted_usage_entry().data(), s.encrypted_usage_entry().size())); ASSERT_NO_FATAL_FAILURE(s.close()); // Old by 1 generation numbers is just warning. ASSERT_EQ(OEMCrypto_WARNING_GENERATION_SKEW, OEMCrypto_LoadUsageTableHeader(old_usage_header_1_.data(), old_usage_header_1_.size())); // Everything else should still work. The old entry goes with the old header. ASSERT_NO_FATAL_FAILURE(s.open()); ASSERT_EQ(OEMCrypto_SUCCESS, OEMCrypto_LoadUsageEntry(s.session_id(), s.usage_entry_number(), old_usage_entry_1.data(), old_usage_entry_1.size())); ASSERT_NO_FATAL_FAILURE(InstallTestRSAKey(&s)); ASSERT_NO_FATAL_FAILURE(s.GenerateDerivedKeysFromSessionKey()); ASSERT_EQ(OEMCrypto_SUCCESS, entry.license_messages().LoadResponse()); } // A usage report with the wrong pst should fail. TEST_P(OEMCryptoUsageTableTest, GenerateReportWrongPST) { LicenseWithUsageEntry entry; entry.license_messages().set_api_version(license_api_version_); entry.MakeAndLoadOnline(this); Session& s = entry.session(); ASSERT_NO_FATAL_FAILURE(s.UpdateUsageEntry(&encrypted_usage_header_)); ASSERT_NO_FATAL_FAILURE( s.GenerateReport("wrong_pst", OEMCrypto_ERROR_WRONG_PST)); } // Test usage table timing. TEST_P(OEMCryptoUsageTableTest, TimingTest) { LicenseWithUsageEntry entry1; entry1.license_messages().set_api_version(license_api_version_); Session& s1 = entry1.session(); entry1.set_pst("my_pst_1"); ASSERT_NO_FATAL_FAILURE(entry1.MakeOfflineAndClose(this)); LicenseWithUsageEntry entry2; entry2.license_messages().set_api_version(license_api_version_); Session& s2 = entry2.session(); entry2.set_pst("my_pst_2"); ASSERT_NO_FATAL_FAILURE(entry2.MakeOfflineAndClose(this)); LicenseWithUsageEntry entry3; entry3.license_messages().set_api_version(license_api_version_); Session& s3 = entry3.session(); entry3.set_pst("my_pst_3"); ASSERT_NO_FATAL_FAILURE(entry3.MakeOfflineAndClose(this)); ASSERT_NO_FATAL_FAILURE(entry1.MakeOfflineAndClose(this)); ASSERT_NO_FATAL_FAILURE(entry2.MakeOfflineAndClose(this)); ASSERT_NO_FATAL_FAILURE(entry3.MakeOfflineAndClose(this)); wvcdm::TestSleep::Sleep(kLongSleep); ASSERT_NO_FATAL_FAILURE(entry1.OpenAndReload(this)); ASSERT_NO_FATAL_FAILURE(entry1.TestDecryptCTR()); ASSERT_NO_FATAL_FAILURE(entry2.OpenAndReload(this)); ASSERT_NO_FATAL_FAILURE(entry2.TestDecryptCTR()); wvcdm::TestSleep::Sleep(kLongSleep); ASSERT_NO_FATAL_FAILURE(entry1.TestDecryptCTR()); ASSERT_NO_FATAL_FAILURE(entry2.TestDecryptCTR()); wvcdm::TestSleep::Sleep(kLongSleep); ASSERT_NO_FATAL_FAILURE(entry1.DeactivateUsageEntry()); ASSERT_NO_FATAL_FAILURE(s1.UpdateUsageEntry(&encrypted_usage_header_)); ASSERT_NO_FATAL_FAILURE(s2.UpdateUsageEntry(&encrypted_usage_header_)); ASSERT_NO_FATAL_FAILURE(s1.close()); ASSERT_NO_FATAL_FAILURE(s2.close()); wvcdm::TestSleep::Sleep(kLongSleep); // This is as close to reboot as we can simulate in code. ShutDown(); wvcdm::TestSleep::Sleep(kShortSleep); Restart(); ASSERT_EQ(OEMCrypto_SUCCESS, OEMCrypto_LoadUsageTableHeader(encrypted_usage_header_.data(), encrypted_usage_header_.size())); // After a reboot, we should be able to reload keys, and generate reports. wvcdm::TestSleep::Sleep(kLongSleep); ASSERT_NO_FATAL_FAILURE(entry2.OpenAndReload(this)); ASSERT_NO_FATAL_FAILURE(entry2.TestDecryptCTR()); ASSERT_NO_FATAL_FAILURE(s2.UpdateUsageEntry(&encrypted_usage_header_)); ASSERT_NO_FATAL_FAILURE(s2.close()); ASSERT_NO_FATAL_FAILURE(s1.open()); ASSERT_NO_FATAL_FAILURE(entry1.ReloadUsageEntry()); ASSERT_NO_FATAL_FAILURE(entry2.OpenAndReload(this)); ASSERT_NO_FATAL_FAILURE(entry3.OpenAndReload(this)); wvcdm::TestSleep::Sleep(kLongSleep); ASSERT_NO_FATAL_FAILURE(s1.UpdateUsageEntry(&encrypted_usage_header_)); ASSERT_NO_FATAL_FAILURE(entry1.GenerateVerifyReport(kInactiveUsed)); ASSERT_NO_FATAL_FAILURE(s2.UpdateUsageEntry(&encrypted_usage_header_)); ASSERT_NO_FATAL_FAILURE(entry2.GenerateVerifyReport(kActive)); ASSERT_NO_FATAL_FAILURE(s3.UpdateUsageEntry(&encrypted_usage_header_)); ASSERT_NO_FATAL_FAILURE(entry3.GenerateVerifyReport(kUnused)); } // Verify the times in the usage report. For performance reasons, we allow the // times in the usage report to be off by as much as kUsageTimeTolerance, which // is 10 seconds. This acceptable error is called slop. This test needs to run // long enough that the reported values are distinct, even after accounting for // this slop. TEST_P(OEMCryptoUsageTableTest, VerifyUsageTimes) { LicenseWithUsageEntry entry; entry.license_messages().set_api_version(license_api_version_); entry.MakeAndLoadOnline(this); Session& s = entry.session(); ASSERT_NO_FATAL_FAILURE(s.UpdateUsageEntry(&encrypted_usage_header_)); ASSERT_NO_FATAL_FAILURE(entry.GenerateVerifyReport(kUnused)); const int64_t kDotIntervalInSeconds = 5; const int64_t kIdleInSeconds = 20; const int64_t kPlaybackLoopInSeconds = 2 * 60; cout << "This test verifies the elapsed time reported in the usage table " "for a 2 minute simulated playback." << endl; cout << "The total time for this test is about " << kPlaybackLoopInSeconds + 2 * kIdleInSeconds << " seconds." << endl; cout << "Wait " << kIdleInSeconds << " seconds to verify usage table time before playback." << endl; PrintDotsWhileSleep(kIdleInSeconds, kDotIntervalInSeconds); ASSERT_NO_FATAL_FAILURE(s.UpdateUsageEntry(&encrypted_usage_header_)); ASSERT_NO_FATAL_FAILURE(entry.GenerateVerifyReport(kUnused)); cout << "Start simulated playback..." << endl; int64_t dot_time = kDotIntervalInSeconds; int64_t playback_time = 0; const int64_t start_time = wvcdm::Clock().GetCurrentTime(); do { ASSERT_NO_FATAL_FAILURE(entry.TestDecryptCTR()); ASSERT_NO_FATAL_FAILURE(s.UpdateUsageEntry(&encrypted_usage_header_)); ASSERT_NO_FATAL_FAILURE(entry.GenerateVerifyReport(kActive)); wvcdm::TestSleep::Sleep(kShortSleep); playback_time = wvcdm::Clock().GetCurrentTime() - start_time; ASSERT_LE(0, playback_time); if (playback_time >= dot_time) { cout << "."; cout.flush(); dot_time += kDotIntervalInSeconds; } } while (playback_time < kPlaybackLoopInSeconds); cout << "\nSimulated playback time = " << playback_time << " seconds.\n"; ASSERT_NO_FATAL_FAILURE(s.UpdateUsageEntry(&encrypted_usage_header_)); ASSERT_NO_FATAL_FAILURE(entry.GenerateVerifyReport(kActive)); EXPECT_NEAR(s.pst_report().seconds_since_first_decrypt() - s.pst_report().seconds_since_last_decrypt(), playback_time, kUsageTableTimeTolerance); cout << "Wait another " << kIdleInSeconds << " seconds " "to verify usage table time since playback ended." << endl; PrintDotsWhileSleep(kIdleInSeconds, kDotIntervalInSeconds); // At this point, this is what we expect: // idle playback loop idle // |-----|-------------------------|-----| // |<--->| = seconds_since_last_decrypt // |<----------------------------->| = seconds_since_first_decrypt // |<------------------------------------| = seconds_since_license_received ASSERT_NO_FATAL_FAILURE(s.UpdateUsageEntry(&encrypted_usage_header_)); ASSERT_NO_FATAL_FAILURE(entry.GenerateVerifyReport(kActive)); ASSERT_NO_FATAL_FAILURE(entry.DeactivateUsageEntry()); ASSERT_NO_FATAL_FAILURE(s.UpdateUsageEntry(&encrypted_usage_header_)); ASSERT_NO_FATAL_FAILURE(entry.GenerateVerifyReport(kInactiveUsed)); ASSERT_NO_FATAL_FAILURE( entry.TestDecryptCTR(false, OEMCrypto_ERROR_UNKNOWN_FAILURE)); } // This test class is only used to roll back the wall clock. It is used to // verify that OEMCrypto's system clock is monotonic. It is should only be run // on a device that allows an application to set the clock. class OEMCryptoUsageTableTestWallClock : public OEMCryptoUsageTableTest { public: void SetUp() override { OEMCryptoUsageTableTest::SetUp(); } void TearDown() override { wvcdm::TestSleep::ResetRollback(); OEMCryptoUsageTableTest::TearDown(); } }; // NOTE: This test needs root access since clock_settime messes with the system // time in order to verify that OEMCrypto protects against rollbacks in usage // entries. Therefore, this test is filtered if not run as root. // We don't test roll-forward protection or instances where the user rolls back // the time to the last decrypt call since this requires hardware-secure clocks // to guarantee. // // This test overlaps two tests in parallel because they each have several // seconds of sleeping, then we roll the system clock back, and then we sleep // some more. // For the first test, we use entry1. The playback duration is 6 short // intervals. We play for 3, roll the clock back 2, and then play for 3 more. // We then sleep until after the allowed playback duration and try to play. If // OEMCrypto allows the rollback, then there is only 5 intervals, which is // legal. But if OEMCrypto forbids the rollback, then there is 8 intervals of // playback, which is not legal. // // For the second test, we use entry2. The rental duration is 6 short // intervals. The times are the same as for entry1, except we do not start // playback for entry2 until the end. // clang-format off // [--][--][--][--][--][--][--] -- playback or rental limit. // // Here's what the system clock sees with rollback: // [--][--][--] 3 short intervals of playback or sleep // <------> Rollback 2 short intervals. // [--][--][--] 3 short intervals of playback or sleep // [--] 1 short intervals of sleep. // // Here's what the system clock sees without rollback: // [--][--][--] 3 short intervals of playback or sleep // [--][--][--] 3 short intervals of playback or sleep // [--][--]X 2 short intervals of sleep. // // |<---------------------------->| 8 short intervals from license received // until pst reports generated. // clang-format on TEST_P(OEMCryptoUsageTableTestWallClock, TimeRollbackPrevention) { cout << "This test temporarily rolls back the system time in order to verify " << "that the usage report accounts for the change. After the test, it " << "rolls the clock back forward." << endl; constexpr int kRollBackTime = kShortSleep * 2; constexpr int kPlaybackCount = 3; constexpr int kTotalTime = kShortSleep * 8; LicenseWithUsageEntry entry1; entry1.license_messages() .core_response() .timer_limits.total_playback_duration_seconds = 7 * kShortSleep; entry1.MakeOfflineAndClose(this); Session& s1 = entry1.session(); ASSERT_NO_FATAL_FAILURE(entry1.OpenAndReload(this)); LicenseWithUsageEntry entry2; entry2.license_messages() .core_response() .timer_limits.rental_duration_seconds = 7 * kShortSleep; entry2.MakeOfflineAndClose(this); Session& s2 = entry2.session(); ASSERT_NO_FATAL_FAILURE(entry2.OpenAndReload(this)); // Start with three short intervals of playback for entry1. for (int i = 0; i < kPlaybackCount; i++) { ASSERT_NO_FATAL_FAILURE(entry1.TestDecryptCTR()); wvcdm::TestSleep::Sleep(kShortSleep); ASSERT_NO_FATAL_FAILURE(entry1.TestDecryptCTR()); } cout << "Rolling the system time back..." << endl; ASSERT_TRUE(wvcdm::TestSleep::RollbackSystemTime(kRollBackTime)); // Three more short intervals of playback after the rollback. for (int i = 0; i < kPlaybackCount; i++) { ASSERT_NO_FATAL_FAILURE(entry1.TestDecryptCTR()); wvcdm::TestSleep::Sleep(kShortSleep); ASSERT_NO_FATAL_FAILURE(entry1.TestDecryptCTR()); } // One short interval of sleep to push us past the 6 interval duration. wvcdm::TestSleep::Sleep(2 * kShortSleep); // Should not be able to continue playback in entry1. ASSERT_NO_FATAL_FAILURE( entry1.TestDecryptCTR(false, OEMCrypto_ERROR_KEY_EXPIRED)); // Should not be able to start playback in entry2. ASSERT_NO_FATAL_FAILURE( entry2.TestDecryptCTR(true, OEMCrypto_ERROR_KEY_EXPIRED)); // Now we look at the usage reports: ASSERT_NO_FATAL_FAILURE(s1.UpdateUsageEntry(&encrypted_usage_header_)); ASSERT_NO_FATAL_FAILURE(s2.UpdateUsageEntry(&encrypted_usage_header_)); ASSERT_NO_FATAL_FAILURE(s1.GenerateReport(entry1.pst())); wvcdm::Unpacked_PST_Report report1 = s1.pst_report(); EXPECT_EQ(report1.status(), kActive); EXPECT_GE(report1.seconds_since_license_received(), kTotalTime); EXPECT_GE(report1.seconds_since_first_decrypt(), kTotalTime); ASSERT_NO_FATAL_FAILURE(s2.GenerateReport(entry2.pst())); wvcdm::Unpacked_PST_Report report2 = s2.pst_report(); EXPECT_EQ(report2.status(), kUnused); EXPECT_GE(report2.seconds_since_license_received(), kTotalTime); } // Verify that a large PST can be used with usage table entries. TEST_P(OEMCryptoUsageTableTest, PSTLargeBuffer) { std::string pst(kMaxPSTLength, 'a'); // A large PST. LicenseWithUsageEntry entry(pst); entry.MakeOfflineAndClose(this); Session& s = entry.session(); ASSERT_NO_FATAL_FAILURE(entry.OpenAndReload(this)); ASSERT_NO_FATAL_FAILURE( entry.TestDecryptCTR()); // Should be able to decrypt. ASSERT_NO_FATAL_FAILURE(entry.DeactivateUsageEntry()); // Then deactivate. // After deactivate, should not be able to decrypt. ASSERT_NO_FATAL_FAILURE( entry.TestDecryptCTR(false, OEMCrypto_ERROR_UNKNOWN_FAILURE)); ASSERT_NO_FATAL_FAILURE(s.UpdateUsageEntry(&encrypted_usage_header_)); ASSERT_NO_FATAL_FAILURE(entry.GenerateVerifyReport(kInactiveUsed)); ASSERT_NO_FATAL_FAILURE(s.close()); } INSTANTIATE_TEST_CASE_P(TestAll, OEMCryptoUsageTableTest, Range(kCurrentAPI - 1, kCurrentAPI + 1)); // These tests only work when the license has a core message. INSTANTIATE_TEST_CASE_P(TestAPI16, OEMCryptoUsageTableDefragTest, Values(kCurrentAPI)); // These tests only work when the license has a core message. INSTANTIATE_TEST_CASE_P(TestAPI16, OEMCryptoUsageTableTestWallClock, Values(kCurrentAPI)); } // namespace wvoec