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
This commit is contained in:
Vicky Min
2023-03-27 19:42:06 -07:00
committed by Fred Gylys-Colwell
parent 225a3e50ed
commit f83698a164
6 changed files with 404 additions and 410 deletions

View File

@@ -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<uint8_t> 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<uint8_t> 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<uint32_t>(kCurrentAPI - 1, kCurrentAPI + 1));
// These tests only work when the license has a core message.
INSTANTIATE_TEST_SUITE_P(TestAPI16, OEMCryptoRefreshTestAPI16,
Range<uint32_t>(kCoreMessagesAPI, kCurrentAPI + 1));
/// @}
} // namespace wvoec

View File

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

View File

@@ -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<std::unique_ptr<Session>> sessions;
std::vector<std::unique_ptr<LicenseRoundTrip>> 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<Session>(new Session()));
licenses.push_back(std::unique_ptr<LicenseRoundTrip>(
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<uint8_t> public_key(buffer_size);
size_t public_key_size = buffer_size;
std::vector<uint8_t> public_key_signature(buffer_size);
size_t public_key_signature_size = buffer_size;
std::vector<uint8_t> 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

View File

@@ -10,6 +10,7 @@
#include <gtest/gtest.h>
#include "OEMCryptoCENC.h"
#include "oec_extra_test_keys.h"
#include "oemcrypto_basic_test.h"
#include "oemcrypto_license_test.h"
#include "oemcrypto_resource_test.h"

View File

@@ -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<uint8_t> 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<uint8_t> 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<uint32_t>(kCurrentAPI - 1, kCurrentAPI + 1));
// These tests only work when the license has a core message.
INSTANTIATE_TEST_SUITE_P(TestAPI16, OEMCryptoRefreshTestAPI16,
Range<uint32_t>(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<std::unique_ptr<Session>> sessions;
std::vector<std::unique_ptr<LicenseRoundTrip>> 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<Session>(new Session()));
licenses.push_back(std::unique_ptr<LicenseRoundTrip>(
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<uint8_t> public_key(buffer_size);
size_t public_key_size = buffer_size;
std::vector<uint8_t> public_key_signature(buffer_size);
size_t public_key_signature_size = buffer_size;
std::vector<uint8_t> 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
/// @{

View File

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