From f83698a164db2e6000c3957ad81a51a222ea09e0 Mon Sep 17 00:00:00 2001 From: Vicky Min Date: Mon, 27 Mar 2023 19:42:06 -0700 Subject: [PATCH] Refactor missed provisioning and renewal tests Merge from Widevine repo of http://go/wvgerrit/169079 Bug: 253779846 Merged from https://widevine-internal-review.googlesource.com/167738 Change-Id: If8fc484f02fc1544977f1fb3a5fe1fa42d7367d7 --- .../oemcrypto/test/oemcrypto_license_test.cpp | 167 ++++++++ .../oemcrypto/test/oemcrypto_license_test.h | 46 +++ .../test/oemcrypto_provisioning_test.cpp | 190 +++++++++ .../test/oemcrypto_provisioning_test.h | 1 + .../oemcrypto/test/oemcrypto_test.cpp | 368 ------------------ .../test/oemcrypto_usage_table_test.h | 42 -- 6 files changed, 404 insertions(+), 410 deletions(-) diff --git a/libwvdrmengine/oemcrypto/test/oemcrypto_license_test.cpp b/libwvdrmengine/oemcrypto/test/oemcrypto_license_test.cpp index 25cf89be..1031cf9a 100644 --- a/libwvdrmengine/oemcrypto/test/oemcrypto_license_test.cpp +++ b/libwvdrmengine/oemcrypto/test/oemcrypto_license_test.cpp @@ -6,6 +6,9 @@ #include "oemcrypto_license_test.h" #include "platform.h" +#include "test_sleep.h" + +using ::testing::Range; namespace wvoec { @@ -800,5 +803,169 @@ TEST_F(OEMCryptoSessionTests, CheckMinimumPatchLevel) { } } +// +// Load, Refresh Keys Test +// + +// 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. + wvutil::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. + wvutil::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)); +} + +// Test that playback clock is correctly started and that the license can be +// renewed. +TEST_P(OEMCryptoRefreshTest, RenewLicenseLoadSuccess) { + license_messages_.core_response().renewal_delay_base = OEMCrypto_License_Load; + timer_limits_.rental_duration_seconds = kDuration; // 2 seconds. + timer_limits_.initial_renewal_duration_seconds = kLongDuration; // 5 seconds. + // First version to support Renew on Load. + constexpr uint32_t kFeatureVersion = 18; + + // Loading the license should start the playback clock. + LoadLicense(); + // Sleep until just after rental window is over. + wvutil::TestSleep::Sleep(kDuration + kShortSleep); + if (license_api_version_ < kFeatureVersion || + global_features.api_version < kFeatureVersion) { + // If the feature is not supported, then we expect failure because the + // playback clock was not started and we are outside the rental window. + ASSERT_NO_FATAL_FAILURE( + session_.TestDecryptCTR(true, OEMCrypto_ERROR_KEY_EXPIRED)); + return; + } else { + // If the feature is supported, we expect decrypt to work because we are + // still within the initial renewal window, and the playback clock should + // have started. + ASSERT_NO_FATAL_FAILURE(session_.TestDecryptCTR(true, OEMCrypto_SUCCESS)); + } + // This is after the initial renewal duration, so we expect failure before + // loading the renewal. + wvutil::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)); +} + +TEST_P(OEMCryptoRefreshTest, RenewLicenseLoadOutsideRentalDuration) { + license_messages_.core_response().renewal_delay_base = OEMCrypto_License_Load; + timer_limits_.rental_duration_seconds = kDuration; // 2 seconds. + timer_limits_.initial_renewal_duration_seconds = kLongDuration; // 5 seconds. + + // Sleep until just after rental window is over. + wvutil::TestSleep::Sleep(kDuration + kShortSleep); + // Loading the license should start the playback clock. + LoadLicense(); + // If the license is loaded after the rental duration window, we expect + // failure. + ASSERT_NO_FATAL_FAILURE( + session_.TestDecryptCTR(false, OEMCrypto_ERROR_UNKNOWN_FAILURE)); + return; +} + +INSTANTIATE_TEST_SUITE_P(TestAll, OEMCryptoRefreshTest, + Range(kCurrentAPI - 1, kCurrentAPI + 1)); + +// These tests only work when the license has a core message. +INSTANTIATE_TEST_SUITE_P(TestAPI16, OEMCryptoRefreshTestAPI16, + Range(kCoreMessagesAPI, kCurrentAPI + 1)); + /// @} } // namespace wvoec \ No newline at end of file diff --git a/libwvdrmengine/oemcrypto/test/oemcrypto_license_test.h b/libwvdrmengine/oemcrypto/test/oemcrypto_license_test.h index fd59d153..0d88c6e6 100644 --- a/libwvdrmengine/oemcrypto/test/oemcrypto_license_test.h +++ b/libwvdrmengine/oemcrypto/test/oemcrypto_license_test.h @@ -359,6 +359,52 @@ class LicenseWithUsageEntry { bool active_; }; +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() { + 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 {}; + } // namespace wvoec #endif // CDM_OEMCRYPTO_LICENSE_TEST_ \ No newline at end of file diff --git a/libwvdrmengine/oemcrypto/test/oemcrypto_provisioning_test.cpp b/libwvdrmengine/oemcrypto/test/oemcrypto_provisioning_test.cpp index 1c0ddd27..a091abbc 100644 --- a/libwvdrmengine/oemcrypto/test/oemcrypto_provisioning_test.cpp +++ b/libwvdrmengine/oemcrypto/test/oemcrypto_provisioning_test.cpp @@ -868,5 +868,195 @@ TEST_F(OEMCryptoLoadsCertificate, CertificateProvisionLargeBuffer) { provisioning_messages.encoded_rsa_key())); } +// Test that a wrapped RSA key can be loaded. +TEST_F(OEMCryptoLoadsCertificate, LoadWrappedRSAKey) { + ASSERT_NO_FATAL_FAILURE(CreateWrappedDRMKey()); + Session s; + ASSERT_NO_FATAL_FAILURE(s.open()); + ASSERT_NO_FATAL_FAILURE(s.LoadWrappedRsaDrmKey(wrapped_drm_key_)); +} + +class OEMCryptoLoadsCertVariousKeys : public OEMCryptoLoadsCertificate { + public: + void TestKey(const uint8_t* key, size_t key_length) { + encoded_rsa_key_.assign(key, key + key_length); + ASSERT_NO_FATAL_FAILURE(CreateWrappedDRMKey()); + Session s; + ASSERT_NO_FATAL_FAILURE(s.open()); + ASSERT_NO_FATAL_FAILURE(s.SetRsaPublicKeyFromPrivateKeyInfo( + encoded_rsa_key_.data(), encoded_rsa_key_.size())); + ASSERT_NO_FATAL_FAILURE(s.LoadWrappedRsaDrmKey(wrapped_drm_key_)); + + LicenseRoundTrip license_messages(&s); + 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 a 3072 bit RSA key certificate. +TEST_F(OEMCryptoLoadsCertVariousKeys, TestLargeRSAKey3072) { + TestKey(kTestRSAPKCS8PrivateKeyInfo3_3072, + sizeof(kTestRSAPKCS8PrivateKeyInfo3_3072)); +} + +// Test an RSA key certificate which has a private key generated using the +// Carmichael totient. +TEST_F(OEMCryptoLoadsCertVariousKeys, TestCarmichaelRSAKey) { + TestKey(kTestKeyRSACarmichael_2048, sizeof(kTestKeyRSACarmichael_2048)); +} + +TEST_F(OEMCryptoLoadsCertVariousKeys, TestCarmichaelNonZeroNormalDer) { + TestKey(kCarmichaelNonZeroNormalDer, kCarmichaelNonZeroNormalDerLen); +} + +TEST_F(OEMCryptoLoadsCertVariousKeys, TestCarmichaelNonZeroShortDer) { + TestKey(kCarmichaelNonZeroShortDer, kCarmichaelNonZeroShortDerLen); +} + +TEST_F(OEMCryptoLoadsCertVariousKeys, TestCarmichaelZeroNormalDer) { + TestKey(kCarmichaelZeroNormalDer, kCarmichaelZeroNormalDerLen); +} + +TEST_F(OEMCryptoLoadsCertVariousKeys, TestCarmichaelZeroShortDer) { + TestKey(kCarmichaelZeroShortDer, kCarmichaelZeroShortDerLen); +} + +TEST_F(OEMCryptoLoadsCertVariousKeys, TestDualNonZeroNormalDer) { + TestKey(kDualNonZeroNormalDer, kDualNonZeroNormalDerLen); +} + +TEST_F(OEMCryptoLoadsCertVariousKeys, TestDualNonZeroShortDer) { + TestKey(kDualNonZeroShortDer, kDualNonZeroShortDerLen); +} + +TEST_F(OEMCryptoLoadsCertVariousKeys, TestDualZeroNormalDer) { + TestKey(kDualZeroNormalDer, kDualZeroNormalDerLen); +} + +TEST_F(OEMCryptoLoadsCertVariousKeys, TestDualZeroShortDer) { + TestKey(kDualZeroShortDer, kDualZeroShortDerLen); +} + +TEST_F(OEMCryptoLoadsCertVariousKeys, TestEulerNonZeroNormalDer) { + TestKey(kEulerNonZeroNormalDer, kEulerNonZeroNormalDerLen); +} + +TEST_F(OEMCryptoLoadsCertVariousKeys, TestEulerZeroNormalDer) { + TestKey(kEulerZeroNormalDer, kEulerZeroNormalDerLen); +} + +// This tests that two sessions can use different RSA keys simultaneously. +TEST_F(OEMCryptoLoadsCertificate, TestMultipleRSAKeys) { + ASSERT_NO_FATAL_FAILURE(CreateWrappedDRMKey()); + 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.SetRsaPublicKeyFromPrivateKeyInfo( + encoded_rsa_key_.data(), encoded_rsa_key_.size())); + ASSERT_NO_FATAL_FAILURE(s1.LoadWrappedRsaDrmKey(wrapped_drm_key_)); + + Session s2; // Session s2 uses a different rsa key. + encoded_rsa_key_.assign(kTestRSAPKCS8PrivateKeyInfo4_2048, + kTestRSAPKCS8PrivateKeyInfo4_2048 + + sizeof(kTestRSAPKCS8PrivateKeyInfo4_2048)); + ASSERT_NO_FATAL_FAILURE(CreateWrappedDRMKey()); + ASSERT_NO_FATAL_FAILURE(s2.open()); + ASSERT_NO_FATAL_FAILURE(s2.SetRsaPublicKeyFromPrivateKeyInfo( + encoded_rsa_key_.data(), encoded_rsa_key_.size())); + ASSERT_NO_FATAL_FAILURE(s2.LoadWrappedRsaDrmKey(wrapped_drm_key_)); + LicenseRoundTrip license_messages2(&s2); + 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(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()); +} + +// This tests the maximum number of DRM private keys that OEMCrypto can load +TEST_F(OEMCryptoLoadsCertificate, TestMaxDRMKeys) { + const size_t max_total_keys = GetResourceValue(kMaxTotalDRMPrivateKeys); + std::vector> sessions; + std::vector> licenses; + + // It should be able to load up to kMaxTotalDRMPrivateKeys keys + for (size_t i = 0; i < 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 key_index = i % kTestRSAPKCS8PrivateKeys_2048.size(); + encoded_rsa_key_.assign(kTestRSAPKCS8PrivateKeys_2048[key_index].begin(), + kTestRSAPKCS8PrivateKeys_2048[key_index].end()); + ASSERT_NO_FATAL_FAILURE(CreateWrappedDRMKey()); + ASSERT_NO_FATAL_FAILURE(sessions[i]->open()); + ASSERT_NO_FATAL_FAILURE(InstallTestDrmKey(sessions[i].get())); + } + + // Attempts to load one more key than the kMaxTotalDRMPrivateKeys + if (global_features.provisioning_method == OEMCrypto_BootCertificateChain) { + Session s; + const size_t buffer_size = 5000; // Make sure it is large enough. + std::vector public_key(buffer_size); + size_t public_key_size = buffer_size; + std::vector public_key_signature(buffer_size); + size_t public_key_signature_size = buffer_size; + std::vector wrapped_private_key(buffer_size); + size_t wrapped_private_key_size = buffer_size; + OEMCrypto_PrivateKeyType key_type; + OEMCryptoResult result = OEMCrypto_GenerateCertificateKeyPair( + s.session_id(), public_key.data(), &public_key_size, + public_key_signature.data(), &public_key_signature_size, + wrapped_private_key.data(), &wrapped_private_key_size, &key_type); + // Key creation is allowed to fail due to resource restriction + if (result != OEMCrypto_SUCCESS) { + ASSERT_TRUE(result == OEMCrypto_ERROR_INSUFFICIENT_RESOURCES || + result == OEMCrypto_ERROR_TOO_MANY_KEYS); + } + } else { + Session s; + encoded_rsa_key_.assign(kTestRSAPKCS8PrivateKeyInfo2_2048, + kTestRSAPKCS8PrivateKeyInfo2_2048 + + sizeof(kTestRSAPKCS8PrivateKeyInfo2_2048)); + Session ps; + ProvisioningRoundTrip provisioning_messages(&ps, 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()); + OEMCryptoResult result = provisioning_messages.LoadResponse(); + // Key loading is allowed to fail due to resource restriction + if (result != OEMCrypto_SUCCESS) { + ASSERT_TRUE(result == OEMCrypto_ERROR_INSUFFICIENT_RESOURCES || + result == OEMCrypto_ERROR_TOO_MANY_KEYS); + } + } + // Verifies that the DRM keys which are already loaded should still function + for (size_t i = 0; i < licenses.size(); i++) { + ASSERT_NO_FATAL_FAILURE(licenses[i]->SignAndVerifyRequest()); + ASSERT_NO_FATAL_FAILURE(licenses[i]->CreateDefaultResponse()); + ASSERT_NO_FATAL_FAILURE(licenses[i]->EncryptAndSignResponse()); + ASSERT_EQ(OEMCrypto_SUCCESS, licenses[i]->LoadResponse()); + ASSERT_NO_FATAL_FAILURE(sessions[i]->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(); +} + /// @} } // namespace wvoec \ No newline at end of file diff --git a/libwvdrmengine/oemcrypto/test/oemcrypto_provisioning_test.h b/libwvdrmengine/oemcrypto/test/oemcrypto_provisioning_test.h index ad47cbaa..03942a01 100644 --- a/libwvdrmengine/oemcrypto/test/oemcrypto_provisioning_test.h +++ b/libwvdrmengine/oemcrypto/test/oemcrypto_provisioning_test.h @@ -10,6 +10,7 @@ #include #include "OEMCryptoCENC.h" +#include "oec_extra_test_keys.h" #include "oemcrypto_basic_test.h" #include "oemcrypto_license_test.h" #include "oemcrypto_resource_test.h" diff --git a/libwvdrmengine/oemcrypto/test/oemcrypto_test.cpp b/libwvdrmengine/oemcrypto/test/oemcrypto_test.cpp index c8604f14..8304fe2f 100644 --- a/libwvdrmengine/oemcrypto/test/oemcrypto_test.cpp +++ b/libwvdrmengine/oemcrypto/test/oemcrypto_test.cpp @@ -1432,179 +1432,6 @@ INSTANTIATE_TEST_SUITE_P(TestHDCP, OEMCryptoSessionTestLoadCasKeysWithHDCP, Range(1, 6)); /// @} -/// @addtogroup renewal -/// @{ - -// -// Load, Refresh Keys Test -// - -// 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. - wvutil::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. - wvutil::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)); -} - -// Test that playback clock is correctly started and that the license can be -// renewed. -TEST_P(OEMCryptoRefreshTest, RenewLicenseLoadSuccess) { - license_messages_.core_response().renewal_delay_base = OEMCrypto_License_Load; - timer_limits_.rental_duration_seconds = kDuration; // 2 seconds. - timer_limits_.initial_renewal_duration_seconds = kLongDuration; // 5 seconds. - // First version to support Renew on Load. - constexpr uint32_t kFeatureVersion = 18; - - // Loading the license should start the playback clock. - LoadLicense(); - // Sleep until just after rental window is over. - wvutil::TestSleep::Sleep(kDuration + kShortSleep); - if (license_api_version_ < kFeatureVersion || - global_features.api_version < kFeatureVersion) { - // If the feature is not supported, then we expect failure because the - // playback clock was not started and we are outside the rental window. - ASSERT_NO_FATAL_FAILURE( - session_.TestDecryptCTR(true, OEMCrypto_ERROR_KEY_EXPIRED)); - return; - } else { - // If the feature is supported, we expect decrypt to work because we are - // still within the initial renewal window, and the playback clock should - // have started. - ASSERT_NO_FATAL_FAILURE(session_.TestDecryptCTR(true, OEMCrypto_SUCCESS)); - } - // This is after the initial renewal duration, so we expect failure before - // loading the renewal. - wvutil::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)); -} - -TEST_P(OEMCryptoRefreshTest, RenewLicenseLoadOutsideRentalDuration) { - license_messages_.core_response().renewal_delay_base = OEMCrypto_License_Load; - timer_limits_.rental_duration_seconds = kDuration; // 2 seconds. - timer_limits_.initial_renewal_duration_seconds = kLongDuration; // 5 seconds. - - // Sleep until just after rental window is over. - wvutil::TestSleep::Sleep(kDuration + kShortSleep); - // Loading the license should start the playback clock. - LoadLicense(); - // If the license is loaded after the rental duration window, we expect - // failure. - ASSERT_NO_FATAL_FAILURE( - session_.TestDecryptCTR(false, OEMCrypto_ERROR_UNKNOWN_FAILURE)); - return; -} - -INSTANTIATE_TEST_SUITE_P(TestAll, OEMCryptoRefreshTest, - Range(kCurrentAPI - 1, kCurrentAPI + 1)); - -// These tests only work when the license has a core message. -INSTANTIATE_TEST_SUITE_P(TestAPI16, OEMCryptoRefreshTestAPI16, - Range(kCoreMessagesAPI, kCurrentAPI + 1)); - -/// @} - /// @addtogroup security /// @{ @@ -1770,201 +1597,6 @@ TEST_F( /// @} -/// @addtogroup provision -/// @{ - -// Test that a wrapped RSA key can be loaded. -TEST_F(OEMCryptoLoadsCertificate, LoadWrappedRSAKey) { - ASSERT_NO_FATAL_FAILURE(CreateWrappedDRMKey()); - Session s; - ASSERT_NO_FATAL_FAILURE(s.open()); - ASSERT_NO_FATAL_FAILURE(s.LoadWrappedRsaDrmKey(wrapped_drm_key_)); -} - -class OEMCryptoLoadsCertVariousKeys : public OEMCryptoLoadsCertificate { - public: - void TestKey(const uint8_t* key, size_t key_length) { - encoded_rsa_key_.assign(key, key + key_length); - ASSERT_NO_FATAL_FAILURE(CreateWrappedDRMKey()); - Session s; - ASSERT_NO_FATAL_FAILURE(s.open()); - ASSERT_NO_FATAL_FAILURE(s.SetRsaPublicKeyFromPrivateKeyInfo( - encoded_rsa_key_.data(), encoded_rsa_key_.size())); - ASSERT_NO_FATAL_FAILURE(s.LoadWrappedRsaDrmKey(wrapped_drm_key_)); - - LicenseRoundTrip license_messages(&s); - 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 a 3072 bit RSA key certificate. -TEST_F(OEMCryptoLoadsCertVariousKeys, TestLargeRSAKey3072) { - TestKey(kTestRSAPKCS8PrivateKeyInfo3_3072, - sizeof(kTestRSAPKCS8PrivateKeyInfo3_3072)); -} - -// Test an RSA key certificate which has a private key generated using the -// Carmichael totient. -TEST_F(OEMCryptoLoadsCertVariousKeys, TestCarmichaelRSAKey) { - TestKey(kTestKeyRSACarmichael_2048, sizeof(kTestKeyRSACarmichael_2048)); -} - -TEST_F(OEMCryptoLoadsCertVariousKeys, TestCarmichaelNonZeroNormalDer) { - TestKey(kCarmichaelNonZeroNormalDer, kCarmichaelNonZeroNormalDerLen); -} - -TEST_F(OEMCryptoLoadsCertVariousKeys, TestCarmichaelNonZeroShortDer) { - TestKey(kCarmichaelNonZeroShortDer, kCarmichaelNonZeroShortDerLen); -} - -TEST_F(OEMCryptoLoadsCertVariousKeys, TestCarmichaelZeroNormalDer) { - TestKey(kCarmichaelZeroNormalDer, kCarmichaelZeroNormalDerLen); -} - -TEST_F(OEMCryptoLoadsCertVariousKeys, TestCarmichaelZeroShortDer) { - TestKey(kCarmichaelZeroShortDer, kCarmichaelZeroShortDerLen); -} - -TEST_F(OEMCryptoLoadsCertVariousKeys, TestDualNonZeroNormalDer) { - TestKey(kDualNonZeroNormalDer, kDualNonZeroNormalDerLen); -} - -TEST_F(OEMCryptoLoadsCertVariousKeys, TestDualNonZeroShortDer) { - TestKey(kDualNonZeroShortDer, kDualNonZeroShortDerLen); -} - -TEST_F(OEMCryptoLoadsCertVariousKeys, TestDualZeroNormalDer) { - TestKey(kDualZeroNormalDer, kDualZeroNormalDerLen); -} - -TEST_F(OEMCryptoLoadsCertVariousKeys, TestDualZeroShortDer) { - TestKey(kDualZeroShortDer, kDualZeroShortDerLen); -} - -TEST_F(OEMCryptoLoadsCertVariousKeys, TestEulerNonZeroNormalDer) { - TestKey(kEulerNonZeroNormalDer, kEulerNonZeroNormalDerLen); -} - -TEST_F(OEMCryptoLoadsCertVariousKeys, TestEulerZeroNormalDer) { - TestKey(kEulerZeroNormalDer, kEulerZeroNormalDerLen); -} - -// This tests that two sessions can use different RSA keys simultaneously. -TEST_F(OEMCryptoLoadsCertificate, TestMultipleRSAKeys) { - ASSERT_NO_FATAL_FAILURE(CreateWrappedDRMKey()); - 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.SetRsaPublicKeyFromPrivateKeyInfo( - encoded_rsa_key_.data(), encoded_rsa_key_.size())); - ASSERT_NO_FATAL_FAILURE(s1.LoadWrappedRsaDrmKey(wrapped_drm_key_)); - - Session s2; // Session s2 uses a different rsa key. - encoded_rsa_key_.assign(kTestRSAPKCS8PrivateKeyInfo4_2048, - kTestRSAPKCS8PrivateKeyInfo4_2048 + - sizeof(kTestRSAPKCS8PrivateKeyInfo4_2048)); - ASSERT_NO_FATAL_FAILURE(CreateWrappedDRMKey()); - ASSERT_NO_FATAL_FAILURE(s2.open()); - ASSERT_NO_FATAL_FAILURE(s2.SetRsaPublicKeyFromPrivateKeyInfo( - encoded_rsa_key_.data(), encoded_rsa_key_.size())); - ASSERT_NO_FATAL_FAILURE(s2.LoadWrappedRsaDrmKey(wrapped_drm_key_)); - LicenseRoundTrip license_messages2(&s2); - 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(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()); -} - -// This tests the maximum number of DRM private keys that OEMCrypto can load -TEST_F(OEMCryptoLoadsCertificate, TestMaxDRMKeys) { - const size_t max_total_keys = GetResourceValue(kMaxTotalDRMPrivateKeys); - std::vector> sessions; - std::vector> licenses; - - // It should be able to load up to kMaxTotalDRMPrivateKeys keys - for (size_t i = 0; i < 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 key_index = i % kTestRSAPKCS8PrivateKeys_2048.size(); - encoded_rsa_key_.assign(kTestRSAPKCS8PrivateKeys_2048[key_index].begin(), - kTestRSAPKCS8PrivateKeys_2048[key_index].end()); - ASSERT_NO_FATAL_FAILURE(CreateWrappedDRMKey()); - ASSERT_NO_FATAL_FAILURE(sessions[i]->open()); - ASSERT_NO_FATAL_FAILURE(InstallTestDrmKey(sessions[i].get())); - } - - // Attempts to load one more key than the kMaxTotalDRMPrivateKeys - if (global_features.provisioning_method == OEMCrypto_BootCertificateChain) { - Session s; - const size_t buffer_size = 5000; // Make sure it is large enough. - std::vector public_key(buffer_size); - size_t public_key_size = buffer_size; - std::vector public_key_signature(buffer_size); - size_t public_key_signature_size = buffer_size; - std::vector wrapped_private_key(buffer_size); - size_t wrapped_private_key_size = buffer_size; - OEMCrypto_PrivateKeyType key_type; - OEMCryptoResult result = OEMCrypto_GenerateCertificateKeyPair( - s.session_id(), public_key.data(), &public_key_size, - public_key_signature.data(), &public_key_signature_size, - wrapped_private_key.data(), &wrapped_private_key_size, &key_type); - // Key creation is allowed to fail due to resource restriction - if (result != OEMCrypto_SUCCESS) { - ASSERT_TRUE(result == OEMCrypto_ERROR_INSUFFICIENT_RESOURCES || - result == OEMCrypto_ERROR_TOO_MANY_KEYS); - } - } else { - Session s; - encoded_rsa_key_.assign(kTestRSAPKCS8PrivateKeyInfo2_2048, - kTestRSAPKCS8PrivateKeyInfo2_2048 + - sizeof(kTestRSAPKCS8PrivateKeyInfo2_2048)); - Session ps; - ProvisioningRoundTrip provisioning_messages(&ps, 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()); - OEMCryptoResult result = provisioning_messages.LoadResponse(); - // Key loading is allowed to fail due to resource restriction - if (result != OEMCrypto_SUCCESS) { - ASSERT_TRUE(result == OEMCrypto_ERROR_INSUFFICIENT_RESOURCES || - result == OEMCrypto_ERROR_TOO_MANY_KEYS); - } - } - // Verifies that the DRM keys which are already loaded should still function - for (size_t i = 0; i < licenses.size(); i++) { - ASSERT_NO_FATAL_FAILURE(licenses[i]->SignAndVerifyRequest()); - ASSERT_NO_FATAL_FAILURE(licenses[i]->CreateDefaultResponse()); - ASSERT_NO_FATAL_FAILURE(licenses[i]->EncryptAndSignResponse()); - ASSERT_EQ(OEMCrypto_SUCCESS, licenses[i]->LoadResponse()); - ASSERT_NO_FATAL_FAILURE(sessions[i]->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(); -} - -/// @} - /// @addtogroup security /// @{ diff --git a/libwvdrmengine/oemcrypto/test/oemcrypto_usage_table_test.h b/libwvdrmengine/oemcrypto/test/oemcrypto_usage_table_test.h index 9ffd3fc9..ce5d7ab9 100644 --- a/libwvdrmengine/oemcrypto/test/oemcrypto_usage_table_test.h +++ b/libwvdrmengine/oemcrypto/test/oemcrypto_usage_table_test.h @@ -19,48 +19,6 @@ namespace wvoec { -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() { - 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 testing the generic crypto functionality. class OEMCryptoGenericCryptoTest : public OEMCryptoRefreshTest { protected: