From dc346cf70a056277e012828f2986d5045da69735 Mon Sep 17 00:00:00 2001 From: Fred Gylys-Colwell Date: Sun, 15 Dec 2019 17:01:22 -0800 Subject: [PATCH] RenewLicense test updates. --- oemcrypto/odk/include/odk.h | 2 +- oemcrypto/odk/src/odk.c | 58 +- oemcrypto/odk/src/odk_timer.c | 7 + oemcrypto/odk/test/odk_test.cpp | 1 - oemcrypto/test/oec_device_features.cpp | 2 +- oemcrypto/test/oec_key_deriver.cpp | 1 - oemcrypto/test/oec_session_util.cpp | 58 +- oemcrypto/test/oec_session_util.h | 38 +- oemcrypto/test/oemcrypto_test.cpp | 1889 +++++++++++------------- 9 files changed, 1020 insertions(+), 1036 deletions(-) diff --git a/oemcrypto/odk/include/odk.h b/oemcrypto/odk/include/odk.h index b89c2b2..391fef6 100644 --- a/oemcrypto/odk/include/odk.h +++ b/oemcrypto/odk/include/odk.h @@ -319,7 +319,7 @@ OEMCryptoResult ODK_PrepareCoreLicenseRequest( */ OEMCryptoResult ODK_PrepareCoreRenewalRequest( uint8_t* message, size_t message_length, size_t* core_message_size, - const ODK_NonceValues* nonce_values, const ODK_ClockValues* clock_values, + const ODK_NonceValues* nonce_values, ODK_ClockValues* clock_values, uint64_t system_time_seconds); /* diff --git a/oemcrypto/odk/src/odk.c b/oemcrypto/odk/src/odk.c index 92cb8ca..80471bf 100644 --- a/oemcrypto/odk/src/odk.c +++ b/oemcrypto/odk/src/odk.c @@ -130,15 +130,24 @@ OEMCryptoResult ODK_PrepareCoreLicenseRequest( OEMCryptoResult ODK_PrepareCoreRenewalRequest( uint8_t* message, size_t message_length, size_t* core_message_length, const ODK_NonceValues* nonce_values, - const ODK_ClockValues* clock_values, uint64_t system_time_seconds) { + ODK_ClockValues* clock_values, uint64_t system_time_seconds) { ODK_RenewalMessage renewal_request = { {0}, }; - if (odk_sub_overflow_u64(system_time_seconds, - clock_values->time_of_first_decrypt, - &renewal_request.playback_time)) { - return ODK_ERROR_CORE_MESSAGE; + if (clock_values->time_of_first_decrypt == 0) { + /* It is OK to preemptively request a renewal before playback starts. + * We'll treat this as asking for a renewal at playback time 0. */ + renewal_request.playback_time = 0; + } else { + /* Otherwise, playback_time is relative to the first decrypt. */ + if (odk_sub_overflow_u64(system_time_seconds, + clock_values->time_of_first_decrypt, + &renewal_request.playback_time)) { + return ODK_ERROR_CORE_MESSAGE; + } } + /* Save time for this request so that we can verify the response. */ + clock_values->time_of_renewal_request = renewal_request.playback_time; return ODK_PrepareRequest(message, message_length, core_message_length, ODK_Renewal_Request_Type, nonce_values, &renewal_request.core_message); @@ -187,32 +196,41 @@ OEMCryptoResult ODK_ParseLicense(const uint8_t* message, size_t message_length, if (err != OEMCrypto_SUCCESS) { return err; } - + /* This function should not be used for legacy licenses. */ if (license_response.core_message.nonce_values.api_version != 16) { return ODK_UNSUPPORTED_API; } if (parsed_license->nonce_required) { if (initial_license_load) { - if (nonce_values->nonce != license_response.core_message.nonce_values.nonce || - nonce_values->session_id != license_response.core_message.nonce_values.session_id) { + if (nonce_values->nonce != + license_response.core_message.nonce_values.nonce || + nonce_values->session_id != + license_response.core_message.nonce_values.session_id) { return OEMCrypto_ERROR_INVALID_NONCE; } } else { /* !initial_license_load */ nonce_values->nonce = license_response.core_message.nonce_values.nonce; - nonce_values->session_id = license_response.core_message.nonce_values.session_id; + nonce_values->session_id = + license_response.core_message.nonce_values.session_id; } } - - if (initial_license_load && - memcmp(request_hash, parsed_license->request_hash, ODK_SHA256_HASH_SIZE)) { + /* For v16, in order to be backwards compatible with a v15 license server, + * OEMCrypto stores a hash of the core license request and only signs the + * message body. Here, when we process the license response, we verify that + * the server has the same hash of the core request. */ + if (initial_license_load && memcmp(request_hash, parsed_license->request_hash, + ODK_SHA256_HASH_SIZE)) { return ODK_ERROR_CORE_MESSAGE; } - + /* If the license has a provider session token (pst), then OEMCrypto should + * have a usage entry loaded. */ if (usage_entry_present && parsed_license->pst.length == 0) { return ODK_ERROR_CORE_MESSAGE; } - + /* If the license loaded OK, then we should save off the timer limits. */ + if (!timer_limits) return OEMCrypto_ERROR_UNKNOWN_FAILURE; + *timer_limits = parsed_license->timer_limits; return err; } @@ -223,7 +241,7 @@ OEMCryptoResult ODK_ParseRenewal(const uint8_t* message, size_t message_length, const ODK_TimerLimits* timer_limits, ODK_ClockValues* clock_values, uint64_t* timer_value) { - if (!nonce_values || !timer_limits || !clock_values || !timer_value) { + if (!nonce_values || !timer_limits || !clock_values) { return ODK_ERROR_CORE_MESSAGE; } @@ -243,6 +261,13 @@ OEMCryptoResult ODK_ParseRenewal(const uint8_t* message, size_t message_length, * Section: Renewal Message */ + /* If a renewal request is lost in transit, we should throw it out and create + * a new one. We use the timestamp to make sure we have the latest request. + */ + if (clock_values->time_of_renewal_request < renewal_response.playback_time) { + return ODK_STALE_RENEWAL; + } + uint64_t playback_timer = 0; if (odk_sub_overflow_u64(clock_values->time_when_timer_expires, system_time, &playback_timer)) { @@ -268,7 +293,8 @@ OEMCryptoResult ODK_ParseRenewal(const uint8_t* message, size_t message_length, } /* todo: when to return ODK_DISABLE_TIMER */ - *timer_value = timer_limits->renewal_playback_duration_seconds; + if (timer_value) + *timer_value = timer_limits->renewal_playback_duration_seconds; return ODK_SET_TIMER; } diff --git a/oemcrypto/odk/src/odk_timer.c b/oemcrypto/odk/src/odk_timer.c index ebe829f..38da1ef 100644 --- a/oemcrypto/odk/src/odk_timer.c +++ b/oemcrypto/odk/src/odk_timer.c @@ -188,6 +188,13 @@ OEMCryptoResult ODK_UpdateLastPlaybackTime(uint64_t system_time_seconds, if (clock_values->timer_status == ODK_TIMER_EXPIRED) { return ODK_TIMER_EXPIRED; } + /* If the clock status is already marked as inactive, then playback is + * not allowed. */ + /* TODO(b/142415188): add helper function. */ + if (clock_values->status > kActive) { + clock_values->timer_status = ODK_TIMER_EXPIRED; + return ODK_TIMER_EXPIRED; + } if (clock_values->time_when_timer_expires > 0 && system_time_seconds > clock_values->time_when_timer_expires) { clock_values->timer_status = ODK_TIMER_EXPIRED; diff --git a/oemcrypto/odk/test/odk_test.cpp b/oemcrypto/odk/test/odk_test.cpp index 4b9c5fa..f515d79 100644 --- a/oemcrypto/odk/test/odk_test.cpp +++ b/oemcrypto/odk/test/odk_test.cpp @@ -191,7 +191,6 @@ void expect_eq_buf(const void* s1, const void* s2, size_t n) { std::fstream out(tmp, std::ios::out | std::ios::binary); out.write((char*)buffers[i], n); out.close(); - std: std::cerr << "buffer " << i << " dumped to " << tmp << std::endl; } FAIL(); diff --git a/oemcrypto/test/oec_device_features.cpp b/oemcrypto/test/oec_device_features.cpp index 8430782..dfd4475 100644 --- a/oemcrypto/test/oec_device_features.cpp +++ b/oemcrypto/test/oec_device_features.cpp @@ -195,7 +195,7 @@ std::string DeviceFeatures::RestrictFilter(const std::string& initial_filter) { // Some tests may require root access. If user is not root, filter these tests // out. if (!CanChangeTime()) { - FilterOut(&filter, "OEMCryptoUsageTableTest.TimeRollbackPrevention"); + FilterOut(&filter, "*TimeRollbackPrevention*"); } // Performance tests take a long time. Filter them out if they are not // specifically requested. diff --git a/oemcrypto/test/oec_key_deriver.cpp b/oemcrypto/test/oec_key_deriver.cpp index d39e836..34fb0fe 100644 --- a/oemcrypto/test/oec_key_deriver.cpp +++ b/oemcrypto/test/oec_key_deriver.cpp @@ -138,7 +138,6 @@ void KeyDeriver::set_mac_keys(const uint8_t* mac_keys) { void KeyDeriver::ServerSignBuffer(const uint8_t* data, size_t data_length, std::vector* signature) const { - ASSERT_LE(data_length, kMaxMessageSize); ASSERT_EQ(mac_key_server_.size(), MAC_KEY_SIZE); signature->assign(SHA256_DIGEST_LENGTH, 0); unsigned int sig_len = SHA256_DIGEST_LENGTH; diff --git a/oemcrypto/test/oec_session_util.cpp b/oemcrypto/test/oec_session_util.cpp index 03efff2..376cdea 100644 --- a/oemcrypto/test/oec_session_util.cpp +++ b/oemcrypto/test/oec_session_util.cpp @@ -317,10 +317,10 @@ void ProvisioningRoundTrip::EncryptAndSignResponse() { &response_signature_); } -OEMCryptoResult ProvisioningRoundTrip::LoadResponse() { +OEMCryptoResult ProvisioningRoundTrip::LoadResponse(Session* session) { size_t wrapped_key_length = 0; const OEMCryptoResult sts = OEMCrypto_LoadProvisioning( - session_->session_id(), encrypted_response_.data(), + session->session_id(), encrypted_response_.data(), encrypted_response_.size(), serialized_core_message_.size(), response_signature_.data(), response_signature_.size(), nullptr, &wrapped_key_length); @@ -328,7 +328,7 @@ OEMCryptoResult ProvisioningRoundTrip::LoadResponse() { wrapped_rsa_key_.clear(); wrapped_rsa_key_.assign(wrapped_key_length, 0); return OEMCrypto_LoadProvisioning( - session_->session_id(), encrypted_response_.data(), + session->session_id(), encrypted_response_.data(), encrypted_response_.size(), serialized_core_message_.size(), response_signature_.data(), response_signature_.size(), wrapped_rsa_key_.data(), &wrapped_key_length); @@ -397,11 +397,30 @@ void LicenseRoundTrip::CreateDefaultResponse() { // Fill in the default core_response_ fields, except the substrings, which are // filled in the next function. core_response_.nonce_required = - (wvoec::kControlNonceEnabled & control_) ? 1 : 0; + ((wvoec::kControlNonceEnabled | wvoec::kControlNonceOrEntry | + wvoec::kControlNonceRequired) & + control_) + ? 1 + : 0; core_response_.license_type = license_type_; FillCoreResponseSubstrings(); } +void LicenseRoundTrip::CreateResponseWithGenericCryptoKeys() { + CreateDefaultResponse(); + response_data_.keys[0].control.control_bits |= + htonl(wvoec::kControlAllowEncrypt); + response_data_.keys[1].control.control_bits |= + htonl(wvoec::kControlAllowDecrypt); + response_data_.keys[2].control.control_bits |= + htonl(wvoec::kControlAllowSign); + response_data_.keys[3].control.control_bits |= + htonl(wvoec::kControlAllowVerify); + response_data_.keys[2].key_data_length = wvoec::MAC_KEY_SIZE; + response_data_.keys[3].key_data_length = wvoec::MAC_KEY_SIZE; + FillCoreResponseSubstrings(); +} + void LicenseRoundTrip::FillCoreResponseSubstrings() { if (update_mac_keys_) { core_response_.enc_mac_keys_iv = FindSubstring( @@ -496,7 +515,7 @@ void LicenseRoundTrip::EncryptAndSignResponse() { &response_signature_); } -OEMCryptoResult LicenseRoundTrip::LoadResponse() { +OEMCryptoResult LicenseRoundTrip::LoadResponse(Session* session) { // Some tests adjust the offset to be beyond the length of the message. Here, // we create a duplicate of the message buffer so that these offsets do not // point to garbage data. The goal is to make sure OEMCrypto is verifying that @@ -510,7 +529,7 @@ OEMCryptoResult LicenseRoundTrip::LoadResponse() { OEMCryptoResult result; if (api_version_ < kCoreMessagesAPI) { result = OEMCrypto_LoadKeys( - session_->session_id(), double_message.data(), + session->session_id(), double_message.data(), encrypted_response_.size(), response_signature_.data(), response_signature_.size(), core_response_.enc_mac_keys_iv, core_response_.enc_mac_keys, core_response_.key_array_length, @@ -519,7 +538,7 @@ OEMCryptoResult LicenseRoundTrip::LoadResponse() { static_cast(core_response_.license_type)); } else { result = OEMCrypto_LoadLicense( - session_->session_id(), double_message.data(), + session->session_id(), double_message.data(), encrypted_response_.size(), serialized_core_message_.size(), response_signature_.data(), response_signature_.size()); } @@ -527,10 +546,10 @@ OEMCryptoResult LicenseRoundTrip::LoadResponse() { // Give the session object a copy of the license truth data so that it can // call SelectKey, use key control information, and so that it has key data // to verify decrypt operations. - session_->set_license(response_data_); + session->set_license(response_data_); // Also, if the license has new mac keys, then install them now. if (core_response_.enc_mac_keys.length > 0) { - session_->set_mac_keys(response_data_.mac_keys); + session->set_mac_keys(response_data_.mac_keys); } // Note: we verify content licenses here. For entitlement license, we verify @@ -540,6 +559,11 @@ OEMCryptoResult LicenseRoundTrip::LoadResponse() { return result; } +OEMCryptoResult LicenseRoundTrip::ReloadResponse(Session* session) { + session->GenerateDerivedKeysFromSessionKey(); + return LoadResponse(session); +} + // This function verifies that the key control block reported by OEMCrypto agree // with the truth key control block. Failures in this function probably // indicate the OEMCrypto_LoadLicense/LoadKeys did not correctly process the key @@ -729,9 +753,13 @@ void RenewalRoundTrip::FillAndVerifyCoreRequest( oec_util::ParseRenewalRequest(core_message_string, &core_request_)); EXPECT_EQ(license_messages_->core_request().api_version, core_request_.api_version); - EXPECT_EQ(license_messages_->core_request().nonce, core_request_.nonce); - EXPECT_EQ(license_messages_->core_request().session_id, - core_request_.session_id); + if (!is_release_) { + // For a license release, we don not expect the nonce to be correct. That + // is because a release might be sent without loading the license first. + EXPECT_EQ(license_messages_->core_request().nonce, core_request_.nonce); + EXPECT_EQ(license_messages_->core_request().session_id, + core_request_.session_id); + } } } @@ -800,15 +828,15 @@ void RenewalRoundTrip::EncryptAndSignResponse() { &response_signature_); } -OEMCryptoResult RenewalRoundTrip::LoadResponse() { +OEMCryptoResult RenewalRoundTrip::LoadResponse(Session* session) { if (license_messages_->api_version() < kCoreMessagesAPI) { return OEMCrypto_RefreshKeys( - session_->session_id(), encrypted_response_.data(), + session->session_id(), encrypted_response_.data(), encrypted_response_.size(), response_signature_.data(), response_signature_.size(), 1, &refresh_object_); } else { return OEMCrypto_LoadRenewal( - session_->session_id(), encrypted_response_.data(), + session->session_id(), encrypted_response_.data(), encrypted_response_.size(), serialized_core_message_.size(), response_signature_.data(), response_signature_.size()); } diff --git a/oemcrypto/test/oec_session_util.h b/oemcrypto/test/oec_session_util.h index 031a8a4..f31db7c 100644 --- a/oemcrypto/test/oec_session_util.h +++ b/oemcrypto/test/oec_session_util.h @@ -33,7 +33,7 @@ void PrintTo(const vector& value, ostream* os); namespace wvoec { // Make sure this is larger than kMaxKeysPerSession, in oemcrypto_test.cpp -constexpr size_t kMaxNumKeys = 35; +constexpr size_t kMaxNumKeys = 30; namespace { #if defined(TEST_SPEED_MULTIPLIER) // Can slow test time limits when @@ -66,7 +66,7 @@ constexpr int kDefaultKeyIdLength = 16; constexpr size_t kMaxPSTLength = 255; // In specification. constexpr size_t kMaxMessageSize = 8 * 1024; // In specification. -constexpr size_t kMaxCoreMessage = 20 * kMaxNumKeys + 150; // Rough estimate. +constexpr size_t kMaxCoreMessage = 200 * kMaxNumKeys + 200; // Rough estimate. typedef struct { uint8_t key_id[kTestKeyIdMaxLength]; @@ -177,7 +177,9 @@ class RoundTrip { // Attempt to load the response and return the error. Short buffer errors are // handled by LoadResponse, not the caller. All other errors should be // handled by the caller. - virtual OEMCryptoResult LoadResponse() = 0; + virtual OEMCryptoResult LoadResponse() { return LoadResponse(session_); } + // As with LoadResponse, but load into a different session. + virtual OEMCryptoResult LoadResponse(Session* session) = 0; // Accessors are all read/write because tests modify default values. Session* session() { return session_; } @@ -188,14 +190,10 @@ class RoundTrip { std::vector& encrypted_response_buffer() { return encrypted_response_; } + void set_session(Session* session) { session_ = session; } // Set the size of the buffer used the encrypted license. - // Must be between sizeof(MessageData) and kMaxMessageSize. - void set_message_size(size_t size) { - message_size_ = size; - ASSERT_GE(message_size_, sizeof(ResponseData) + kMaxCoreMessage); - ASSERT_LE(message_size_, kMaxMessageSize); - } + void set_message_size(size_t size) { message_size_ = size; } // The size of the encrypted message. size_t message_size() { return message_size_; } std::vector& response_signature() { return response_signature_; } @@ -245,7 +243,8 @@ class ProvisioningRoundTrip virtual void PrepareSession(const wvoec::WidevineKeybox& keybox); void CreateDefaultResponse() override; void EncryptAndSignResponse() override; - OEMCryptoResult LoadResponse() override; + OEMCryptoResult LoadResponse() override { return LoadResponse(session_); } + OEMCryptoResult LoadResponse(Session* session) override; void VerifyLoadFailed(); const std::vector& encoded_rsa_key() { return encoded_rsa_key_; } const std::vector& wrapped_rsa_key() { return wrapped_rsa_key_; } @@ -282,14 +281,21 @@ class LicenseRoundTrip pst_(""), minimum_srm_version_(0), update_mac_keys_(true), - api_version_(0), + api_version_(kCurrentAPI), expect_request_has_correct_nonce_(true), license_type_(OEMCrypto_ContentLicense) {} void CreateDefaultResponse() override; + // Create a license with four keys, one each that is allowed to do generic + // encrypt, decrypt, sign and verify. + void CreateResponseWithGenericCryptoKeys(); // Fill the |core_response| substrings. virtual void FillCoreResponseSubstrings(); void EncryptAndSignResponse() override; - OEMCryptoResult LoadResponse() override; + OEMCryptoResult LoadResponse() override { return LoadResponse(session_); } + OEMCryptoResult LoadResponse(Session* session) override; + // Reload an offline license into a different session. This derives new mac + // keys and then calls LoadResponse. + OEMCryptoResult ReloadResponse(Session* session); void VerifyTestKeys(); // Set the default key control block for all keys. This is used in // CreateDefaultResponse. The key control block determines the restrictions @@ -368,10 +374,13 @@ class RenewalRoundTrip : public RoundTrip< RenewalRoundTrip(LicenseRoundTrip* license_messages) : RoundTrip(license_messages->session()), license_messages_(license_messages), - refresh_object_() {} + refresh_object_(), + is_release_(false) {} void CreateDefaultResponse() override; void EncryptAndSignResponse() override; - OEMCryptoResult LoadResponse() override; + OEMCryptoResult LoadResponse() override { return LoadResponse(session_); } + OEMCryptoResult LoadResponse(Session* session) override; + void set_is_release(bool is_release) { is_release_ = is_release; } protected: void VerifyRequestSignature(const vector& data, @@ -382,6 +391,7 @@ class RenewalRoundTrip : public RoundTrip< const std::string& core_message_string) override; LicenseRoundTrip* license_messages_; OEMCrypto_KeyRefreshObject refresh_object_; + bool is_release_; // If this is a license release, and not a real renewal. }; class EntitledMessage { diff --git a/oemcrypto/test/oemcrypto_test.cpp b/oemcrypto/test/oemcrypto_test.cpp index 3504d85..96496d1 100644 --- a/oemcrypto/test/oemcrypto_test.cpp +++ b/oemcrypto/test/oemcrypto_test.cpp @@ -103,6 +103,7 @@ const size_t kMaxSubsampleSize[] = { 100*KiB, 500*KiB, 1*MiB}; const size_t kMaxGenericBuffer[] = { 10*KiB, 100*KiB, 500*KiB}; const size_t kMaxConcurrentSession[] = { 10, 20, 20}; const size_t kMaxKeysPerSession[] = { 4, 20, 20}; +const size_t kLargeMessageSize[] = { 8 * KiB, 8 * KiB, 16 * KiB, 32 * KiB}; // clang-format on // Note: Frame rate and simultaneous playback are specified by resource rating, @@ -188,6 +189,8 @@ TEST_F(OEMCryptoClientTest, VersionNumber) { cout << " OEMCrypto does not support usage tables." << endl; } if (version >= 15) { + cout << " Resource Rating Tier: " + << OEMCrypto_ResourceRatingTier() << endl; const char* build_info = OEMCrypto_BuildInformation(); ASSERT_NE(nullptr, build_info); ASSERT_TRUE(strnlen(build_info, 256) <= 256) @@ -605,10 +608,11 @@ TEST_F(OEMCryptoKeyboxTest, ProductionKeyboxValid) { TEST_F(OEMCryptoKeyboxTest, GenerateDerivedKeysFromKeyboxLargeBuffer) { Session s; ASSERT_NO_FATAL_FAILURE(s.open()); - vector mac_context(kMaxMessageSize); - vector enc_context(kMaxMessageSize); + 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 < kMaxMessageSize; i++) { + for (size_t i = 0; i < max_size; i++) { mac_context[i] = i % 0x100; enc_context[i] = (3 * i) % 0x100; } @@ -729,6 +733,9 @@ TEST_F(OEMCryptoProv30Test, GetCertOnlyAPI16) { // 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() {} @@ -759,8 +766,6 @@ class OEMCryptoSessionTests : public OEMCryptoClientTest { ASSERT_NE(OEMCrypto_SUCCESS, sts); } } - - vector encrypted_usage_header_; }; class OEMCryptoSessionTestKeyboxTest : public OEMCryptoSessionTests {}; @@ -835,7 +840,8 @@ TEST_P(OEMCryptoLicenseTest, SignLicenseRequestNoNonce) { // Verify that a large license request may be signed. TEST_P(OEMCryptoLicenseTest, SignLargeLicenseRequest) { - license_messages_.set_message_size(kMaxMessageSize); + const size_t max_size = GetResourceValue(kLargeMessageSize); + license_messages_.set_message_size(max_size); ASSERT_NO_FATAL_FAILURE(license_messages_.SignAndVerifyRequest()); } @@ -931,7 +937,8 @@ TEST_P(OEMCryptoLicenseTest, LoadEntitlementKeysWrongEntitlementKeysAPI14) { TEST_P(OEMCryptoLicenseTest, LoadKeyLargeBuffer) { ASSERT_NO_FATAL_FAILURE(session_.GenerateNonce()); ASSERT_NO_FATAL_FAILURE(license_messages_.SignAndVerifyRequest()); - license_messages_.set_message_size(kMaxMessageSize); + 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()); @@ -1051,6 +1058,22 @@ TEST_P(OEMCryptoLicenseTest, LoadKeyWithBadRange_key_control_iv) { 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 += + license_messages_.message_size(); + 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()); +} //---------------------------------------------------------------------------// //---------------------------------------------------------------------------// @@ -1649,7 +1672,9 @@ class OEMCryptoRefreshTest : public OEMCryptoLicenseTest { void LoadLicense() { // If we require a nonce, then generate one. - if (license_messages_.control() & wvoec::kControlNonceEnabled) { + if (license_messages_.control() & + (wvoec::kControlNonceEnabled | wvoec::kControlNonceOrEntry | + wvoec::kControlNonceRequired)) { ASSERT_NO_FATAL_FAILURE(session_.GenerateNonce()); } license_messages_.core_response().timer_limits = timer_limits_; @@ -1673,6 +1698,10 @@ class OEMCryptoRefreshTest : public OEMCryptoLicenseTest { 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(); @@ -1691,100 +1720,90 @@ TEST_P(OEMCryptoRefreshTest, RefreshNoNonce) { } // Refresh keys should NOT work if a license has not been loaded. -TEST_P(OEMCryptoRefreshTest, RefreshNoLicense) { - // We do not call LoadLicense(); - RenewalRoundTrip renewal_messages(&license_messages_); - MakeRenewalRequest(&renewal_messages); - LoadRenewal(&renewal_messages, OEMCrypto_ERROR_UNKNOWN_FAILURE); +TEST_P(OEMCryptoRefreshTestAPI16, RefreshNoLicense) { + Session s; + s.open(); + constexpr size_t message_size = kMaxCoreMessage; + 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); + 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); } -#if 0 // These tests will be updated in a future CL. - -// TODO(b/136281032): Nonce should be same as one in license. // Refresh keys should fail if the nonce is not in the session. -TEST_P(OEMCryptoSessionTestRefreshKeyTestAPI16, RefreshBadNonceAPI11) { - Session s; - ASSERT_NO_FATAL_FAILURE(s.open()); - ASSERT_NO_FATAL_FAILURE(InstallTestRSAKey(&s)); - ASSERT_NO_FATAL_FAILURE(s.GenerateNonce()); - ASSERT_NO_FATAL_FAILURE( - s.FillSimpleMessage(kDuration, wvoec::kControlNonceEnabled, s.nonce())); - ASSERT_NO_FATAL_FAILURE(s.EncryptAndSign()); - ASSERT_NO_FATAL_FAILURE(s.LoadTestKeys("", new_mac_keys_)); - // License renewal message is signed by client and verified by the server. - ASSERT_NO_FATAL_FAILURE(s.VerifyRenewalRequestSignature()); - uint32_t nonce = s.nonce() ^ 42; - ASSERT_NO_FATAL_FAILURE( - s.RefreshTestKeys(num_keys_, wvoec::kControlNonceEnabled, nonce, - OEMCrypto_ERROR_INVALID_NONCE)); +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(OEMCryptoSessionTestRefreshKeyTestAPI16, RefreshLargeBuffer) { - Session s; - s.set_message_size(kMaxMessageSize); - ASSERT_NO_FATAL_FAILURE(s.open()); - ASSERT_NO_FATAL_FAILURE(InstallTestRSAKey(&s)); - ASSERT_NO_FATAL_FAILURE(s.GenerateNonce()); - ASSERT_NO_FATAL_FAILURE( - s.FillSimpleMessage(kDuration, wvoec::kControlNonceEnabled, s.nonce())); - ASSERT_NO_FATAL_FAILURE(s.EncryptAndSign()); - ASSERT_NO_FATAL_FAILURE(s.LoadTestKeys("", new_mac_keys_)); - // License renewal message is signed by client and verified by the server. - // This uses a large buffer for the renewal message. - ASSERT_NO_FATAL_FAILURE(s.VerifyRenewalRequestSignature(kMaxMessageSize)); - ASSERT_NO_FATAL_FAILURE(s.RefreshTestKeys( - num_keys_, wvoec::kControlNonceEnabled, s.nonce(), OEMCrypto_SUCCESS)); +TEST_P(OEMCryptoRefreshTest, RefreshLargeBuffer) { + LoadLicense(); + RenewalRoundTrip renewal_messages(&license_messages_); + const size_t max_size = GetResourceValue(kLargeMessageSize); + license_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(OEMCryptoSessionTestRefreshKeyTestAPI16, RefreshWithNoSelectKey) { - Session s; - ASSERT_NO_FATAL_FAILURE(s.open()); - ASSERT_NO_FATAL_FAILURE(InstallTestRSAKey(&s)); - ASSERT_NO_FATAL_FAILURE(s.GenerateNonce()); - ASSERT_NO_FATAL_FAILURE( - s.FillSimpleMessage(kDuration, wvoec::kControlNonceEnabled, s.nonce())); - ASSERT_NO_FATAL_FAILURE(s.EncryptAndSign()); - ASSERT_NO_FATAL_FAILURE(s.LoadTestKeys("", new_mac_keys_)); +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(s.TestDecryptCTR(true)); + ASSERT_NO_FATAL_FAILURE(session_.TestDecryptCTR(true)); - // License renewal message is signed by client and verified by the server. - ASSERT_NO_FATAL_FAILURE(s.VerifyRenewalRequestSignature()); - // Note: we store the message in encrypted_license_, but the refresh key - // message is not actually encrypted. It is, however, signed. - // FillRefreshMessage fills the message with a duration of kLongDuration. - ASSERT_NO_FATAL_FAILURE( - s.FillRefreshMessage(num_keys_, wvoec::kControlNonceEnabled, s.nonce())); - s.key_deriver().ServerSignBuffer( - reinterpret_cast(&s.encrypted_license()), - s.message_size(), &s.signature()); - std::vector key_array(num_keys_); - s.FillRefreshArray(key_array.data(), num_keys_); - ASSERT_EQ( - OEMCrypto_SUCCESS, - OEMCrypto_RefreshKeys(s.session_id(), s.message_ptr(), s.message_size(), - s.signature().data(), s.signature().size(), - num_keys_, key_array.data())); - ASSERT_NO_FATAL_FAILURE(s.TestDecryptCTR(false)); // 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(s.TestDecryptCTR(false)); + 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 succeed if and only if the - // refresh succeeded. + // expiration of the refresh message. This should fail until we have loaded + // the renewal. wvcdm::TestSleep::Sleep(kShortSleep + kLongSleep); - ASSERT_NO_FATAL_FAILURE(s.TestDecryptCTR(false)); + 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)); } -#endif // XXX These tests will be updated in a future CL. INSTANTIATE_TEST_CASE_P(TestAll, OEMCryptoRefreshTest, - Range(kCurrentAPI - 1, kCurrentAPI + 1)); + 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) { @@ -1837,32 +1856,6 @@ TEST_P(OEMCryptoLicenseTest, DecryptZeroDuration) { ASSERT_NO_FATAL_FAILURE(session_.TestDecryptCTR()); } -// Verify that several sessions may each load a license and then each may -// decrypt. -TEST_F(OEMCryptoSessionTests, SimultaneousDecrypt) { - vector s(8); - for (int i = 0; i < 8; i++) { - ASSERT_NO_FATAL_FAILURE(s[i].open()); - ASSERT_NO_FATAL_FAILURE(InstallTestRSAKey(&s[i])); - } - for (int i = 0; i < 8; i++) { - ASSERT_NO_FATAL_FAILURE(s[i].GenerateNonce()); - ASSERT_NO_FATAL_FAILURE( - s[i].FillSimpleMessage(kLongDuration, 0, s[i].nonce())); - ASSERT_NO_FATAL_FAILURE(s[i].EncryptAndSign()); - } - for (int i = 0; i < 8; i++) { - ASSERT_NO_FATAL_FAILURE(s[i].LoadTestKeys()); - } - for (int i = 0; i < 8; i++) { - ASSERT_NO_FATAL_FAILURE(s[i].TestDecryptCTR()); - } - // Second call to decrypt for each session. - for (int i = 0; i < 8; i++) { - ASSERT_NO_FATAL_FAILURE(s[i].TestDecryptCTR()); - } -} - struct SubsampleSize { size_t clear_size; size_t encrypted_size; @@ -2565,7 +2558,8 @@ TEST_F(OEMCryptoLoadsCertificate, SignLargeProvisioningRequest) { } s.GenerateNonce(); ProvisioningRoundTrip provisioning_messages(&s, encoded_rsa_key_); - provisioning_messages.set_message_size(kMaxMessageSize); + const size_t max_size = GetResourceValue(kLargeMessageSize); + provisioning_messages.set_message_size(max_size); ASSERT_NO_FATAL_FAILURE(provisioning_messages.SignAndVerifyRequest()); } @@ -2733,7 +2727,8 @@ TEST_F(OEMCryptoLoadsCertificate, CertificateProvisionBadRSAKeyKeyboxTest) { TEST_F(OEMCryptoLoadsCertificate, CertificateProvisionLargeBuffer) { Session s; ProvisioningRoundTrip provisioning_messages(&s, encoded_rsa_key_); - provisioning_messages.set_message_size(kMaxMessageSize); + 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()); @@ -2975,10 +2970,11 @@ TEST_F(OEMCryptoUsesCertificate, GenerateDerivedKeysLargeBuffer) { ASSERT_NO_FATAL_FAILURE(session_.PreparePublicKey(encoded_rsa_key_.data(), encoded_rsa_key_.size())); ASSERT_TRUE(session_.GenerateRSASessionKey(&session_key, &enc_session_key)); - vector mac_context(kMaxMessageSize); - vector enc_context(kMaxMessageSize); + 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 < kMaxMessageSize; i++) { + for (size_t i = 0; i < max_size; i++) { mac_context[i] = i % 0x100; enc_context[i] = (3 * i) % 0x100; } @@ -4048,47 +4044,22 @@ TEST_F(OEMCryptoCastReceiverTest, TestSignaturePKCS1_15_20) { } // This class is for testing the generic crypto functionality. -class OEMCryptoGenericCryptoTest : public OEMCryptoSessionTests { +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), license_messages_(&session_) {} + OEMCryptoGenericCryptoTest() : buffer_size_(160) {} void SetUp() override { - OEMCryptoSessionTests::SetUp(); - ASSERT_NO_FATAL_FAILURE(session_.open()); - ASSERT_NO_FATAL_FAILURE(InstallTestRSAKey(&session_)); + OEMCryptoRefreshTest::SetUp(); + ASSERT_NO_FATAL_FAILURE(session_.GenerateNonce()); ASSERT_NO_FATAL_FAILURE(license_messages_.SignAndVerifyRequest()); - ASSERT_NO_FATAL_FAILURE(MakeFourKeys()); + ASSERT_NO_FATAL_FAILURE( + license_messages_.CreateResponseWithGenericCryptoKeys()); + InitializeClearBuffer(); } - void TearDown() override { - ASSERT_NO_FATAL_FAILURE(session_.close()); - OEMCryptoSessionTests::TearDown(); - } - - // This makes four keys, one for each of the generic operations that we want - // to test. - void MakeFourKeys(uint32_t duration = kDuration, uint32_t control = 0, - uint32_t nonce = 0, const std::string& pst = "") { - license_messages_.set_control(control); - license_messages_.set_pst(pst); - license_messages_.core_response().timer_limits.license_duration_seconds = - kDuration; - ASSERT_NO_FATAL_FAILURE(license_messages_.CreateDefaultResponse()); - license_messages_.response_data().keys[0].control.control_bits |= - htonl(wvoec::kControlAllowEncrypt); - license_messages_.response_data().keys[1].control.control_bits |= - htonl(wvoec::kControlAllowDecrypt); - license_messages_.response_data().keys[2].control.control_bits |= - htonl(wvoec::kControlAllowSign); - license_messages_.response_data().keys[3].control.control_bits |= - htonl(wvoec::kControlAllowVerify); - license_messages_.response_data().keys[2].key_data_length = - wvoec::MAC_KEY_SIZE; - license_messages_.response_data().keys[3].key_data_length = - wvoec::MAC_KEY_SIZE; + void InitializeClearBuffer() { clear_buffer_.assign(buffer_size_, 0); for (size_t i = 0; i < clear_buffer_.size(); i++) { clear_buffer_[i] = 1 + i % 250; @@ -4096,7 +4067,6 @@ class OEMCryptoGenericCryptoTest : public OEMCryptoSessionTests { for (size_t i = 0; i < wvoec::KEY_IV_SIZE; i++) { iv_[i] = i; } - ASSERT_NO_FATAL_FAILURE(license_messages_.FillCoreResponseSubstrings()); } void EncryptAndLoadKeys() { @@ -4104,13 +4074,19 @@ class OEMCryptoGenericCryptoTest : public OEMCryptoSessionTests { ASSERT_EQ(OEMCrypto_SUCCESS, license_messages_.LoadResponse()); } - // Encrypt the buffer with the specified key made in MakeFourKeys. + // 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(session_.license().keys[key_index].key_data, - AES_BLOCK_SIZE * 8, &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()); @@ -4120,14 +4096,22 @@ class OEMCryptoGenericCryptoTest : public OEMCryptoSessionTests { &aes_key, iv_buffer, AES_ENCRYPT); } - // Sign the buffer with the specified key. + // 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(), session_.license().keys[key_index].key_data, - wvoec::MAC_KEY_SIZE, in_buffer.data(), in_buffer.size(), - signature->data(), &md_len); + 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 @@ -4221,14 +4205,12 @@ class OEMCryptoGenericCryptoTest : public OEMCryptoSessionTests { vector clear_buffer_; vector encrypted_buffer_; uint8_t iv_[wvoec::KEY_IV_SIZE]; - Session session_; - LicenseRoundTrip license_messages_; }; -TEST_F(OEMCryptoGenericCryptoTest, GenericKeyLoad) { EncryptAndLoadKeys(); } +TEST_P(OEMCryptoGenericCryptoTest, GenericKeyLoad) { EncryptAndLoadKeys(); } // Test that the Generic_Encrypt function works correctly. -TEST_F(OEMCryptoGenericCryptoTest, GenericKeyEncrypt) { +TEST_P(OEMCryptoGenericCryptoTest, GenericKeyEncrypt) { EncryptAndLoadKeys(); unsigned int key_index = 0; vector expected_encrypted; @@ -4249,7 +4231,7 @@ TEST_F(OEMCryptoGenericCryptoTest, GenericKeyEncrypt) { } // Test that the Generic_Encrypt function fails when not allowed. -TEST_F(OEMCryptoGenericCryptoTest, GenericKeyBadEncrypt) { +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. @@ -4261,7 +4243,7 @@ TEST_F(OEMCryptoGenericCryptoTest, GenericKeyBadEncrypt) { // Test that the Generic_Encrypt works if the input and output buffers are the // same. -TEST_F(OEMCryptoGenericCryptoTest, GenericKeyEncryptSameBufferAPI12) { +TEST_P(OEMCryptoGenericCryptoTest, GenericKeyEncryptSameBufferAPI12) { EncryptAndLoadKeys(); unsigned int key_index = 0; vector expected_encrypted; @@ -4282,7 +4264,7 @@ TEST_F(OEMCryptoGenericCryptoTest, GenericKeyEncryptSameBufferAPI12) { } // Test Generic_Decrypt works correctly. -TEST_F(OEMCryptoGenericCryptoTest, GenericKeyDecrypt) { +TEST_P(OEMCryptoGenericCryptoTest, GenericKeyDecrypt) { EncryptAndLoadKeys(); unsigned int key_index = 1; vector encrypted; @@ -4303,7 +4285,7 @@ TEST_F(OEMCryptoGenericCryptoTest, GenericKeyDecrypt) { // Test that Generic_Decrypt works correctly when the input and output buffers // are the same. -TEST_F(OEMCryptoGenericCryptoTest, GenericKeyDecryptSameBufferAPI12) { +TEST_P(OEMCryptoGenericCryptoTest, GenericKeyDecryptSameBufferAPI12) { EncryptAndLoadKeys(); unsigned int key_index = 1; vector encrypted; @@ -4324,9 +4306,10 @@ TEST_F(OEMCryptoGenericCryptoTest, GenericKeyDecryptSameBufferAPI12) { // Test that Generic_Decrypt fails to decrypt to an insecure buffer if the key // requires a secure data path. -TEST_F(OEMCryptoGenericCryptoTest, GenericSecureToClear) { - MakeFourKeys(0, - wvoec::kControlObserveDataPath | wvoec::kControlDataPathSecure); +TEST_P(OEMCryptoGenericCryptoTest, GenericSecureToClear) { + license_messages_.set_control(wvoec::kControlObserveDataPath | + wvoec::kControlDataPathSecure); + license_messages_.CreateResponseWithGenericCryptoKeys(); EncryptAndLoadKeys(); unsigned int key_index = 1; vector encrypted; @@ -4346,7 +4329,7 @@ TEST_F(OEMCryptoGenericCryptoTest, GenericSecureToClear) { } // Test that the Generic_Decrypt function fails when not allowed. -TEST_F(OEMCryptoGenericCryptoTest, GenericKeyBadDecrypt) { +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. @@ -4356,7 +4339,7 @@ TEST_F(OEMCryptoGenericCryptoTest, GenericKeyBadDecrypt) { BadDecrypt(3, OEMCrypto_AES_CBC_128_NO_PADDING, buffer_size_); } -TEST_F(OEMCryptoGenericCryptoTest, GenericKeySign) { +TEST_P(OEMCryptoGenericCryptoTest, GenericKeySign) { EncryptAndLoadKeys(); unsigned int key_index = 2; vector expected_signature; @@ -4383,7 +4366,7 @@ TEST_F(OEMCryptoGenericCryptoTest, GenericKeySign) { } // Test that the Generic_Sign function fails when not allowed. -TEST_F(OEMCryptoGenericCryptoTest, GenericKeyBadSign) { +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. @@ -4391,7 +4374,7 @@ TEST_F(OEMCryptoGenericCryptoTest, GenericKeyBadSign) { BadSign(2, OEMCrypto_AES_CBC_128_NO_PADDING); // Bad signing algorithm. } -TEST_F(OEMCryptoGenericCryptoTest, GenericKeyVerify) { +TEST_P(OEMCryptoGenericCryptoTest, GenericKeyVerify) { EncryptAndLoadKeys(); unsigned int key_index = 3; vector signature; @@ -4411,7 +4394,7 @@ TEST_F(OEMCryptoGenericCryptoTest, GenericKeyVerify) { } // Test that the Generic_Verify function fails when not allowed. -TEST_F(OEMCryptoGenericCryptoTest, GenericKeyBadVerify) { +TEST_P(OEMCryptoGenericCryptoTest, GenericKeyBadVerify) { EncryptAndLoadKeys(); BadVerify(0, OEMCrypto_HMAC_SHA256, SHA256_DIGEST_LENGTH, false); BadVerify(1, OEMCrypto_HMAC_SHA256, SHA256_DIGEST_LENGTH, false); @@ -4423,7 +4406,7 @@ TEST_F(OEMCryptoGenericCryptoTest, GenericKeyBadVerify) { } // Test Generic_Encrypt with the maximum buffer size. -TEST_F(OEMCryptoGenericCryptoTest, GenericKeyEncryptLargeBuffer) { +TEST_P(OEMCryptoGenericCryptoTest, GenericKeyEncryptLargeBuffer) { buffer_size_ = GetResourceValue(kMaxGenericBuffer); EncryptAndLoadKeys(); unsigned int key_index = 0; @@ -4445,7 +4428,7 @@ TEST_F(OEMCryptoGenericCryptoTest, GenericKeyEncryptLargeBuffer) { } // Test Generic_Decrypt with the maximum buffer size. -TEST_F(OEMCryptoGenericCryptoTest, GenericKeyDecryptLargeBuffer) { +TEST_P(OEMCryptoGenericCryptoTest, GenericKeyDecryptLargeBuffer) { // Some applications are known to pass in a block that is almost 400k. buffer_size_ = GetResourceValue(kMaxGenericBuffer); EncryptAndLoadKeys(); @@ -4467,7 +4450,7 @@ TEST_F(OEMCryptoGenericCryptoTest, GenericKeyDecryptLargeBuffer) { } // Test Generic_Sign with the maximum buffer size. -TEST_F(OEMCryptoGenericCryptoTest, GenericKeySignLargeBuffer) { +TEST_P(OEMCryptoGenericCryptoTest, GenericKeySignLargeBuffer) { buffer_size_ = GetResourceValue(kMaxGenericBuffer); EncryptAndLoadKeys(); unsigned int key_index = 2; @@ -4495,7 +4478,7 @@ TEST_F(OEMCryptoGenericCryptoTest, GenericKeySignLargeBuffer) { } // Test Generic_Verify with the maximum buffer size. -TEST_F(OEMCryptoGenericCryptoTest, GenericKeyVerifyLargeBuffer) { +TEST_P(OEMCryptoGenericCryptoTest, GenericKeyVerifyLargeBuffer) { buffer_size_ = GetResourceValue(kMaxGenericBuffer); EncryptAndLoadKeys(); unsigned int key_index = 3; @@ -4516,15 +4499,17 @@ TEST_F(OEMCryptoGenericCryptoTest, GenericKeyVerifyLargeBuffer) { } // Test Generic_Encrypt when the key duration has expired. -TEST_F(OEMCryptoGenericCryptoTest, KeyDurationEncrypt) { +TEST_P(OEMCryptoGenericCryptoTest, KeyDurationEncrypt) { + license_messages_.core_response().timer_limits.license_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()); - wvcdm::TestSleep::Sleep(kShortSleep); // Should still be valid key. - + // Should be valid key at the start. ASSERT_EQ( OEMCrypto_SUCCESS, OEMCrypto_SelectKey(session_.session_id(), @@ -4538,8 +4523,7 @@ TEST_F(OEMCryptoGenericCryptoTest, KeyDurationEncrypt) { encrypted.data())); ASSERT_EQ(expected_encrypted, encrypted); - wvcdm::TestSleep::Sleep(kLongSleep); // Should be expired key. - + 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_, @@ -4551,9 +4535,13 @@ TEST_F(OEMCryptoGenericCryptoTest, KeyDurationEncrypt) { } // Test Generic_Decrypt when the key duration has expired. -TEST_F(OEMCryptoGenericCryptoTest, KeyDurationDecrypt) { +TEST_P(OEMCryptoGenericCryptoTest, KeyDurationDecrypt) { + license_messages_.core_response().timer_limits.license_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); @@ -4563,9 +4551,6 @@ TEST_F(OEMCryptoGenericCryptoTest, KeyDurationDecrypt) { session_.license().keys[key_index].key_id, session_.license().keys[key_index].key_id_length, OEMCrypto_CipherMode_CTR)); - - wvcdm::TestSleep::Sleep(kShortSleep); // Should still be valid key. - vector resultant(encrypted.size()); ASSERT_EQ(OEMCrypto_SUCCESS, OEMCrypto_Generic_Decrypt( @@ -4573,8 +4558,7 @@ TEST_F(OEMCryptoGenericCryptoTest, KeyDurationDecrypt) { OEMCrypto_AES_CBC_128_NO_PADDING, resultant.data())); ASSERT_EQ(clear_buffer_, resultant); - wvcdm::TestSleep::Sleep(kLongSleep); // Should be expired key. - + 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_, @@ -4586,7 +4570,10 @@ TEST_F(OEMCryptoGenericCryptoTest, KeyDurationDecrypt) { } // Test Generic_Sign when the key duration has expired. -TEST_F(OEMCryptoGenericCryptoTest, KeyDurationSign) { +TEST_P(OEMCryptoGenericCryptoTest, KeyDurationSign) { + license_messages_.core_response().timer_limits.license_duration_seconds = + kDuration; + license_messages_.CreateResponseWithGenericCryptoKeys(); EncryptAndLoadKeys(); unsigned int key_index = 2; @@ -4595,23 +4582,20 @@ TEST_F(OEMCryptoGenericCryptoTest, KeyDurationSign) { 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)); - - wvcdm::TestSleep::Sleep(kShortSleep); // Should still be valid key. - 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); // Should be expired key. - + 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(), @@ -4623,22 +4607,23 @@ TEST_F(OEMCryptoGenericCryptoTest, KeyDurationSign) { } // Test Generic_Verify when the key duration has expired. -TEST_F(OEMCryptoGenericCryptoTest, KeyDurationVerify) { +TEST_P(OEMCryptoGenericCryptoTest, KeyDurationVerify) { + license_messages_.core_response().timer_limits.license_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)); - - wvcdm::TestSleep::Sleep(kShortSleep); // Should still be valid key. - ASSERT_EQ(OEMCrypto_SUCCESS, OEMCrypto_Generic_Verify( session_.session_id(), clear_buffer_.data(), @@ -4720,54 +4705,126 @@ class OEMCryptoGenericCryptoKeyIdLengthTest } }; -TEST_F(OEMCryptoGenericCryptoKeyIdLengthTest, MediumKeyId) { TestWithKey(0); } +TEST_P(OEMCryptoGenericCryptoKeyIdLengthTest, MediumKeyId) { TestWithKey(0); } -TEST_F(OEMCryptoGenericCryptoKeyIdLengthTest, ShortKeyId) { TestWithKey(1); } +TEST_P(OEMCryptoGenericCryptoKeyIdLengthTest, ShortKeyId) { TestWithKey(1); } -TEST_F(OEMCryptoGenericCryptoKeyIdLengthTest, LongKeyId) { TestWithKey(2); } +TEST_P(OEMCryptoGenericCryptoKeyIdLengthTest, LongKeyId) { TestWithKey(2); } -TEST_F(OEMCryptoGenericCryptoKeyIdLengthTest, FourteenByteKeyId) { +TEST_P(OEMCryptoGenericCryptoKeyIdLengthTest, FourteenByteKeyId) { TestWithKey(3); } -TEST_F(OEMCryptoGenericCryptoKeyIdLengthTest, VeryShortKeyId) { +TEST_P(OEMCryptoGenericCryptoKeyIdLengthTest, VeryShortKeyId) { TestWithKey(4); } -TEST_F(OEMCryptoGenericCryptoKeyIdLengthTest, UniformShortKeyId) { +TEST_P(OEMCryptoGenericCryptoKeyIdLengthTest, UniformShortKeyId) { SetUniformKeyIdLength(5); TestWithKey(2); } -TEST_F(OEMCryptoGenericCryptoKeyIdLengthTest, UniformLongKeyId) { +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 OEMCryptoUsageTableTest : public OEMCryptoGenericCryptoTest { - protected: - void SetUp() override { - OEMCryptoGenericCryptoTest::SetUp(); - new_mac_keys_ = true; - did_change_system_time_ = false; - test_start_steady_ = steady_clock_.now(); -#ifdef _WIN32 - GetSystemTime(&test_start_wall_); -#else - ASSERT_EQ(0, gettimeofday(&test_start_wall_, nullptr)); -#endif + +class LicenseWithUsageEntry { + public: + LicenseWithUsageEntry() + : pst_("my_pst"), + session_(), + license_messages_(&session_), + generic_crypto_(false) { + license_messages_.set_pst(pst_); } - void TearDown() override { - if (did_change_system_time_) { - const auto delta = steady_clock_.now() - test_start_steady_; - const int64_t delta_sec = delta / std::chrono::seconds(1); - ASSERT_NO_FATAL_FAILURE(SetWallTimeDelta(delta_sec)); + void MakeAndLoadOnline(OEMCryptoSessionTests* test) { + MakeAndLoad(test, + wvoec::kControlNonceEnabled | wvoec::kControlNonceRequired); + } + + void MakeOfflineAndClose(OEMCryptoSessionTests* test) { + MakeAndLoad(test, wvoec::kControlNonceOrEntry); + ASSERT_NO_FATAL_FAILURE( + session_.UpdateUsageEntry(&(test->encrypted_usage_header_))); + ASSERT_NO_FATAL_FAILURE(GenerateVerifyReport(kUnused)); + ASSERT_NO_FATAL_FAILURE(session_.close()); + } + + void MakeAndLoad(SessionUtil* util, uint32_t control) { + 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()); } - OEMCryptoGenericCryptoTest::TearDown(); + ASSERT_NO_FATAL_FAILURE(license_messages_.EncryptAndSignResponse()); + ASSERT_NO_FATAL_FAILURE(session_.CreateNewUsageEntry()); + ASSERT_EQ(OEMCrypto_SUCCESS, license_messages_.LoadResponse()); } + void OpenAndReload(SessionUtil* util) { + ASSERT_NO_FATAL_FAILURE(session_.open()); + ASSERT_NO_FATAL_FAILURE(session_.ReloadUsageEntry()); + ASSERT_NO_FATAL_FAILURE(util->InstallTestRSAKey(&session_)); + ASSERT_EQ(OEMCrypto_SUCCESS, license_messages_.LoadResponse()); + } + + void GenerateVerifyReport(OEMCrypto_Usage_Entry_Status status, + int64_t time_license_received = 0, + int64_t time_first_decrypt = 0, + int64_t time_last_decrypt = 0) { + 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(true); + 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 pst_; } + void set_pst(const std::string& pst) { + pst_ = 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: + std::string pst_; + Session session_; + LicenseRoundTrip license_messages_; + bool generic_crypto_; +}; + +class OEMCryptoUsageTableTest : public OEMCryptoGenericCryptoTest { + public: virtual void ShutDown() { ASSERT_NO_FATAL_FAILURE(session_.close()); ASSERT_EQ(OEMCrypto_SUCCESS, OEMCrypto_Terminate()); @@ -4780,20 +4837,6 @@ class OEMCryptoUsageTableTest : public OEMCryptoGenericCryptoTest { ASSERT_NO_FATAL_FAILURE(session_.open()); } - void LoadOfflineLicense(Session& s, const std::string& pst) { - ASSERT_NO_FATAL_FAILURE(s.open()); - ASSERT_NO_FATAL_FAILURE(InstallTestRSAKey(&s)); - ASSERT_NO_FATAL_FAILURE(s.GenerateNonce()); - ASSERT_NO_FATAL_FAILURE( - s.FillSimpleMessage(0, wvoec::kControlNonceOrEntry, s.nonce(), pst)); - ASSERT_NO_FATAL_FAILURE(s.EncryptAndSign()); - ASSERT_NO_FATAL_FAILURE(s.CreateNewUsageEntry()); - ASSERT_NO_FATAL_FAILURE(s.LoadTestKeys(pst, new_mac_keys_)); - ASSERT_NO_FATAL_FAILURE(s.UpdateUsageEntry(&encrypted_usage_header_)); - ASSERT_NO_FATAL_FAILURE(s.GenerateVerifyReport(pst, kUnused)); - ASSERT_NO_FATAL_FAILURE(s.close()); - } - void PrintDotsWhileSleep(int64_t total_seconds, int64_t interval_seconds) { int64_t dot_time = interval_seconds; int64_t elapsed_time = 0; @@ -4809,207 +4852,148 @@ class OEMCryptoUsageTableTest : public OEMCryptoGenericCryptoTest { } while (elapsed_time < total_seconds); cout << endl; } - - protected: - /** - * Sets the current wall-clock time to a delta based on the start of the - * test. - */ - void SetWallTimeDelta(int64_t delta_seconds) { - did_change_system_time_ = true; - NativeTime time = test_start_wall_; - ASSERT_NO_FATAL_FAILURE(AddNativeTime(delta_seconds, &time)); -#ifdef _WIN32 - ASSERT_TRUE(SetSystemTime(&time)); -#else - ASSERT_EQ(0, settimeofday(&time, nullptr)); -#endif - } - - bool new_mac_keys_; - bool did_change_system_time_; - std::chrono::steady_clock steady_clock_; - std::chrono::time_point test_start_steady_; - NativeTime test_start_wall_; -}; - -// Some usage tables we want to check a license either with or without a -// new pair of mac keys in the license response. This affects signatures after -// the license is loaded. -// This test is parameterized by a boolean which determines if the license -// installs new mac keys in LoadKeys. -class OEMCryptoUsageTableTestWithMAC : public OEMCryptoUsageTableTest, - public WithParamInterface { - protected: - void SetUp() override { - OEMCryptoUsageTableTest::SetUp(); - new_mac_keys_ = GetParam(); - } }; // Test an online or streaming license with PST. This license requires a valid // nonce and can only be loaded once. -TEST_P(OEMCryptoUsageTableTestWithMAC, OnlineLicense) { - std::string pst = "my_pst"; - Session s; - ASSERT_NO_FATAL_FAILURE(s.open()); - ASSERT_NO_FATAL_FAILURE(InstallTestRSAKey(&s)); - ASSERT_NO_FATAL_FAILURE(s.GenerateNonce()); - ASSERT_NO_FATAL_FAILURE(s.FillSimpleMessage( - 0, wvoec::kControlNonceEnabled | wvoec::kControlNonceRequired, s.nonce(), - pst)); - ASSERT_NO_FATAL_FAILURE(s.EncryptAndSign()); - ASSERT_NO_FATAL_FAILURE(s.CreateNewUsageEntry()); - ASSERT_NO_FATAL_FAILURE(s.LoadTestKeys(pst, new_mac_keys_)); +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(s.GenerateVerifyReport(pst, kUnused)); - ASSERT_NO_FATAL_FAILURE(s.GenerateVerifyReport(pst, kUnused)); - ASSERT_NO_FATAL_FAILURE(s.GenerateVerifyReport(pst, kUnused)); - ASSERT_NO_FATAL_FAILURE(s.GenerateVerifyReport(pst, 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.GenerateVerifyReport(kUnused)); ASSERT_NO_FATAL_FAILURE(s.TestDecryptCTR()); ASSERT_NO_FATAL_FAILURE(s.UpdateUsageEntry(&encrypted_usage_header_)); - ASSERT_NO_FATAL_FAILURE(s.GenerateVerifyReport(pst, kActive)); + ASSERT_NO_FATAL_FAILURE(entry.GenerateVerifyReport(kActive)); // Flag the entry as inactive. - ASSERT_NO_FATAL_FAILURE(s.DeactivateUsageEntry(pst)); + ASSERT_NO_FATAL_FAILURE(s.DeactivateUsageEntry(entry.pst())); ASSERT_NO_FATAL_FAILURE(s.UpdateUsageEntry(&encrypted_usage_header_)); // It should report as inactive. - ASSERT_NO_FATAL_FAILURE(s.GenerateVerifyReport(pst, kInactiveUsed)); + ASSERT_NO_FATAL_FAILURE(entry.GenerateVerifyReport(kInactiveUsed)); // Decrypt should fail. ASSERT_NO_FATAL_FAILURE( s.TestDecryptCTR(false, OEMCrypto_ERROR_UNKNOWN_FAILURE)); // We could call DeactivateUsageEntry multiple times. The state should not // change. - ASSERT_NO_FATAL_FAILURE(s.DeactivateUsageEntry(pst)); + ASSERT_NO_FATAL_FAILURE(s.DeactivateUsageEntry(entry.pst())); ASSERT_NO_FATAL_FAILURE(s.UpdateUsageEntry(&encrypted_usage_header_)); // It should report as inactive. - ASSERT_NO_FATAL_FAILURE(s.GenerateVerifyReport(pst, kInactiveUsed)); + 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(OEMCryptoUsageTableTestWithMAC, OnlineLicenseUnused) { - std::string pst = "my_pst"; - Session s; - ASSERT_NO_FATAL_FAILURE(s.open()); - ASSERT_NO_FATAL_FAILURE(InstallTestRSAKey(&s)); - ASSERT_NO_FATAL_FAILURE(s.GenerateNonce()); - ASSERT_NO_FATAL_FAILURE(s.FillSimpleMessage( - 0, wvoec::kControlNonceEnabled | wvoec::kControlNonceRequired, s.nonce(), - pst)); - ASSERT_NO_FATAL_FAILURE(s.EncryptAndSign()); - ASSERT_NO_FATAL_FAILURE(s.CreateNewUsageEntry()); - ASSERT_NO_FATAL_FAILURE(s.LoadTestKeys(pst, new_mac_keys_)); +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(s.GenerateVerifyReport(pst, kUnused)); + ASSERT_NO_FATAL_FAILURE(entry.GenerateVerifyReport(kUnused)); // Flag the entry as inactive. - ASSERT_NO_FATAL_FAILURE(s.DeactivateUsageEntry(pst)); + ASSERT_NO_FATAL_FAILURE(s.DeactivateUsageEntry(entry.pst())); ASSERT_NO_FATAL_FAILURE(s.UpdateUsageEntry(&encrypted_usage_header_)); // It should report as inactive. - ASSERT_NO_FATAL_FAILURE(s.GenerateVerifyReport(pst, kInactiveUnused)); + ASSERT_NO_FATAL_FAILURE(entry.GenerateVerifyReport(kInactiveUnused)); // Decrypt should fail. ASSERT_NO_FATAL_FAILURE( s.TestDecryptCTR(false, OEMCrypto_ERROR_UNKNOWN_FAILURE)); // We could call DeactivateUsageEntry multiple times. The state should not // change. - ASSERT_NO_FATAL_FAILURE(s.DeactivateUsageEntry(pst)); + ASSERT_NO_FATAL_FAILURE(s.DeactivateUsageEntry(entry.pst())); ASSERT_NO_FATAL_FAILURE(s.UpdateUsageEntry(&encrypted_usage_header_)); // It should report as inactive. - ASSERT_NO_FATAL_FAILURE(s.GenerateVerifyReport(pst, kInactiveUnused)); + 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(OEMCryptoUsageTableTestWithMAC, ForbidReportWithNoUpdate) { - std::string pst = "my_pst"; - Session s; - ASSERT_NO_FATAL_FAILURE(s.open()); - ASSERT_NO_FATAL_FAILURE(InstallTestRSAKey(&s)); - ASSERT_NO_FATAL_FAILURE(s.GenerateNonce()); - ASSERT_NO_FATAL_FAILURE(s.FillSimpleMessage( - 0, wvoec::kControlNonceEnabled | wvoec::kControlNonceRequired, s.nonce(), - pst)); - ASSERT_NO_FATAL_FAILURE(s.EncryptAndSign()); - ASSERT_NO_FATAL_FAILURE(s.CreateNewUsageEntry()); - ASSERT_NO_FATAL_FAILURE(s.LoadTestKeys(pst, new_mac_keys_)); +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(s.GenerateVerifyReport(pst, kUnused)); + ASSERT_NO_FATAL_FAILURE(entry.GenerateVerifyReport(kUnused)); ASSERT_NO_FATAL_FAILURE(s.TestDecryptCTR()); // Cannot generate a report without first updating the file. ASSERT_NO_FATAL_FAILURE( - s.GenerateReport(pst, OEMCrypto_ERROR_ENTRY_NEEDS_UPDATE)); + 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(s.GenerateVerifyReport(pst, kActive)); + ASSERT_NO_FATAL_FAILURE(entry.GenerateVerifyReport(kActive)); // Flag the entry as inactive. - ASSERT_NO_FATAL_FAILURE(s.DeactivateUsageEntry(pst)); + ASSERT_NO_FATAL_FAILURE(s.DeactivateUsageEntry(entry.pst())); // Cannot generate a report without first updating the file. ASSERT_NO_FATAL_FAILURE( - s.GenerateReport(pst, OEMCrypto_ERROR_ENTRY_NEEDS_UPDATE)); + s.GenerateReport(entry.pst(), OEMCrypto_ERROR_ENTRY_NEEDS_UPDATE)); // Decrypt should fail. ASSERT_NO_FATAL_FAILURE( s.TestDecryptCTR(false, OEMCrypto_ERROR_UNKNOWN_FAILURE)); } // Test an online license with a license renewal. -TEST_P(OEMCryptoUsageTableTestWithMAC, OnlineLicenseWithRefreshAPI16) { - std::string pst = "my_pst"; - Session s; - ASSERT_NO_FATAL_FAILURE(s.open()); - ASSERT_NO_FATAL_FAILURE(InstallTestRSAKey(&s)); - ASSERT_NO_FATAL_FAILURE(s.GenerateNonce()); - ASSERT_NO_FATAL_FAILURE(s.FillSimpleMessage( - 0, wvoec::kControlNonceEnabled | wvoec::kControlNonceRequired, s.nonce(), - pst)); - ASSERT_NO_FATAL_FAILURE(s.EncryptAndSign()); - ASSERT_NO_FATAL_FAILURE(s.CreateNewUsageEntry()); - ASSERT_NO_FATAL_FAILURE(s.LoadTestKeys(pst, new_mac_keys_)); +TEST_P(OEMCryptoUsageTableTest, OnlineLicenseWithRefreshAPI16) { + LicenseWithUsageEntry entry; + entry.license_messages().set_api_version(license_api_version_); + entry.MakeAndLoadOnline(this); + Session& s = entry.session(); const int64_t loaded = wvcdm::Clock().GetCurrentTime(); ASSERT_NO_FATAL_FAILURE(s.TestDecryptCTR()); - // License renewal message is signed by client and verified by the server. - ASSERT_NO_FATAL_FAILURE(s.VerifyRenewalRequestSignature()); - size_t kAllKeys = 1; - ASSERT_NO_FATAL_FAILURE(s.RefreshTestKeys( - kAllKeys, wvoec::kControlNonceEnabled | wvoec::kControlNonceRequired, - s.nonce(), OEMCrypto_SUCCESS)); + + 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( - s.GenerateVerifyReport(pst, kActive, - loaded, // when license loaded. (not refreshed) - loaded, // first decrypt. - 0)); // last decrypt is now. + ASSERT_NO_FATAL_FAILURE(entry.GenerateVerifyReport( + kActive, + loaded, // when license loaded. (not refreshed) + loaded, // first decrypt. + 0)); // last decrypt is now. } // Verify that a streaming license cannot be reloaded. -TEST_F(OEMCryptoUsageTableTest, RepeatOnlineLicense) { - std::string pst = "my_pst"; - Session s; - ASSERT_NO_FATAL_FAILURE(s.open()); - ASSERT_NO_FATAL_FAILURE(InstallTestRSAKey(&s)); - ASSERT_NO_FATAL_FAILURE(s.GenerateNonce()); - ASSERT_NO_FATAL_FAILURE(s.FillSimpleMessage( - 0, wvoec::kControlNonceEnabled | wvoec::kControlNonceRequired, s.nonce(), - pst)); - ASSERT_NO_FATAL_FAILURE(s.EncryptAndSign()); - ASSERT_NO_FATAL_FAILURE(s.CreateNewUsageEntry()); - ASSERT_NO_FATAL_FAILURE(s.LoadTestKeys(pst, new_mac_keys_)); +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. - // Trying to reuse a PST is bad. We use session ID for s2, everything else - // reused from s. - ASSERT_NE( - OEMCrypto_SUCCESS, - OEMCrypto_LoadKeys(s2.session_id(), s.message_ptr(), s.message_size(), - s.signature().data(), s.signature().size(), - s.enc_mac_keys_iv_substr(), s.enc_mac_keys_substr(), - s.num_keys(), s.key_array(), s.pst_substr(), - GetSubstring(), OEMCrypto_ContentLicense)); - ASSERT_NO_FATAL_FAILURE(s2.close()); + // key expired error. + 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 (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()); } // A license with non-zero replay control bits needs a valid pst. @@ -5018,70 +5002,71 @@ TEST_F(OEMCryptoUsageTableTest, OnlineEmptyPST) { ASSERT_NO_FATAL_FAILURE(s.open()); ASSERT_NO_FATAL_FAILURE(InstallTestRSAKey(&s)); ASSERT_NO_FATAL_FAILURE(s.GenerateNonce()); - ASSERT_NO_FATAL_FAILURE(s.FillSimpleMessage( - 0, wvoec::kControlNonceEnabled | wvoec::kControlNonceRequired, - s.nonce())); - ASSERT_NO_FATAL_FAILURE(s.EncryptAndSign()); + 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()); - OEMCryptoResult sts = OEMCrypto_LoadKeys( - s.session_id(), s.message_ptr(), s.message_size(), s.signature().data(), - s.signature().size(), s.enc_mac_keys_iv_substr(), s.enc_mac_keys_substr(), - s.num_keys(), s.key_array(), GetSubstring(), GetSubstring(), - OEMCrypto_ContentLicense); - ASSERT_NE(OEMCrypto_SUCCESS, sts); - ASSERT_NO_FATAL_FAILURE(s.close()); + ASSERT_NE(OEMCrypto_SUCCESS, license_messages.LoadResponse()); } // A license with non-zero replay control bits needs a valid pst. -TEST_F(OEMCryptoUsageTableTest, OnlineMissingEntry) { - std::string pst = "my_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()); - ASSERT_NO_FATAL_FAILURE(s.FillSimpleMessage( - 0, wvoec::kControlNonceEnabled | wvoec::kControlNonceRequired, s.nonce(), - pst)); - ASSERT_NO_FATAL_FAILURE(s.EncryptAndSign()); + 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()); - OEMCryptoResult sts = OEMCrypto_LoadKeys( - s.session_id(), s.message_ptr(), s.message_size(), s.signature().data(), - s.signature().size(), s.enc_mac_keys_iv_substr(), s.enc_mac_keys_substr(), - s.num_keys(), s.key_array(), s.pst_substr(), GetSubstring(), - OEMCrypto_ContentLicense); - ASSERT_NE(OEMCrypto_SUCCESS, sts); - ASSERT_NO_FATAL_FAILURE(s.close()); + 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_F(OEMCryptoUsageTableTest, CreateAndLoadMultipleEntriesAPI16) { - std::string pst = "my_pst"; - Session s; - - ASSERT_NO_FATAL_FAILURE(s.open()); - ASSERT_NO_FATAL_FAILURE(s.CreateNewUsageEntry()); +TEST_P(OEMCryptoUsageTableTest, CreateAndLoadMultipleEntries) { + // Entry Count: we start each test with an empty header. uint32_t usage_entry_number; - ASSERT_EQ(OEMCrypto_ERROR_MULTIPLE_USAGE_ENTRIES, - OEMCrypto_CreateNewUsageEntry(s.session_id(), &usage_entry_number)); - ASSERT_NO_FATAL_FAILURE(s.close()); + 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)); - ASSERT_NO_FATAL_FAILURE(LoadOfflineLicense(s, pst)); + // 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(s.session_id(), &usage_entry_number)); + OEMCrypto_CreateNewUsageEntry(entry.session().session_id(), + &usage_entry_number)); ASSERT_NO_FATAL_FAILURE(s.close()); - ASSERT_NO_FATAL_FAILURE(s.open()); - ASSERT_NO_FATAL_FAILURE(s.CreateNewUsageEntry()); + // 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(s.session_id(), s.usage_entry_number(), + OEMCrypto_LoadUsageEntry(s2.session_id(), s.usage_entry_number(), s.encrypted_usage_entry().data(), s.encrypted_usage_entry().size())); - ASSERT_NO_FATAL_FAILURE(s.close()); + ASSERT_NO_FATAL_FAILURE(s2.close()); - ASSERT_NO_FATAL_FAILURE(LoadOfflineLicense(s, pst)); + // 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, @@ -5089,129 +5074,124 @@ TEST_F(OEMCryptoUsageTableTest, CreateAndLoadMultipleEntriesAPI16) { 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)); } +// XXX -- maybe this should be a generic test that also does usage table stuff? // Test generic encrypt when the license uses a PST. -TEST_P(OEMCryptoUsageTableTestWithMAC, GenericCryptoEncrypt) { - std::string pst = "A PST"; - ASSERT_NO_FATAL_FAILURE(session_.GenerateNonce()); - uint32_t nonce = session_.nonce(); - MakeFourKeys(0, wvoec::kControlNonceEnabled | wvoec::kControlNonceRequired, - nonce, pst); - ASSERT_NO_FATAL_FAILURE(license_messages_.EncryptAndSignResponse()); - ASSERT_NO_FATAL_FAILURE(session_.CreateNewUsageEntry()); - ASSERT_EQ(OEMCrypto_SUCCESS, license_messages_.LoadResponse()); +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; - 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, + 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( - session_.session_id(), clear_buffer_.data(), clear_buffer_.size(), iv_, + 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(session_.UpdateUsageEntry(&encrypted_usage_header_)); - ASSERT_NO_FATAL_FAILURE(session_.GenerateVerifyReport(pst, kActive)); - ASSERT_NO_FATAL_FAILURE(session_.DeactivateUsageEntry(pst)); - ASSERT_NO_FATAL_FAILURE(session_.UpdateUsageEntry(&encrypted_usage_header_)); - ASSERT_NO_FATAL_FAILURE(session_.GenerateVerifyReport(pst, kInactiveUsed)); + ASSERT_NO_FATAL_FAILURE(s.UpdateUsageEntry(&encrypted_usage_header_)); + ASSERT_NO_FATAL_FAILURE(entry.GenerateVerifyReport(kActive)); + ASSERT_NO_FATAL_FAILURE(s.DeactivateUsageEntry(entry.pst())); + 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( - session_.session_id(), clear_buffer_.data(), clear_buffer_.size(), iv_, + 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(OEMCryptoUsageTableTestWithMAC, GenericCryptoDecrypt) { - std::string pst = "my_pst"; - ASSERT_NO_FATAL_FAILURE(session_.GenerateNonce()); - uint32_t nonce = session_.nonce(); - MakeFourKeys( - 0, wvoec::kControlNonceEnabled | wvoec::kControlNonceRequired, - nonce, pst); - ASSERT_NO_FATAL_FAILURE(license_messages_.EncryptAndSignResponse()); - ASSERT_NO_FATAL_FAILURE(session_.CreateNewUsageEntry()); - ASSERT_EQ(OEMCrypto_SUCCESS, license_messages_.LoadResponse()); +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; - 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, + 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( - session_.session_id(), encrypted.data(), encrypted.size(), iv_, - OEMCrypto_AES_CBC_128_NO_PADDING,resultant.data()); + 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(session_.UpdateUsageEntry(&encrypted_usage_header_)); - ASSERT_NO_FATAL_FAILURE(session_.GenerateVerifyReport(pst, kActive)); - ASSERT_NO_FATAL_FAILURE(session_.DeactivateUsageEntry(pst)); - ASSERT_NO_FATAL_FAILURE(session_.UpdateUsageEntry(&encrypted_usage_header_)); - ASSERT_NO_FATAL_FAILURE(session_.GenerateVerifyReport(pst, kInactiveUsed)); + ASSERT_NO_FATAL_FAILURE(s.UpdateUsageEntry(&encrypted_usage_header_)); + ASSERT_NO_FATAL_FAILURE(entry.GenerateVerifyReport(kActive)); + ASSERT_NO_FATAL_FAILURE(s.DeactivateUsageEntry(entry.pst())); + 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( - session_.session_id(), encrypted.data(), encrypted.size(), iv_, + 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(OEMCryptoUsageTableTestWithMAC, GenericCryptoSign) { - std::string pst = "my_pst"; - ASSERT_NO_FATAL_FAILURE(session_.GenerateNonce()); - uint32_t nonce = session_.nonce(); - MakeFourKeys( - 0, wvoec::kControlNonceEnabled | wvoec::kControlNonceRequired, - nonce, pst); - ASSERT_NO_FATAL_FAILURE(license_messages_.EncryptAndSignResponse()); - ASSERT_NO_FATAL_FAILURE(session_.CreateNewUsageEntry()); - ASSERT_EQ(OEMCrypto_SUCCESS, license_messages_.LoadResponse()); - +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; - ASSERT_NO_FATAL_FAILURE( - SignBuffer(key_index, clear_buffer_, &expected_signature)); + SignBufferWithKey(s.license().keys[key_index].key_data, 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, + 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(session_.session_id(), clear_buffer_.data(), + 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(session_.session_id(), clear_buffer_.data(), + 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(session_.UpdateUsageEntry(&encrypted_usage_header_)); - ASSERT_NO_FATAL_FAILURE(session_.GenerateVerifyReport(pst, kActive)); - ASSERT_NO_FATAL_FAILURE(session_.DeactivateUsageEntry(pst)); - ASSERT_NO_FATAL_FAILURE(session_.UpdateUsageEntry(&encrypted_usage_header_)); - ASSERT_NO_FATAL_FAILURE(session_.GenerateVerifyReport(pst, kInactiveUsed)); + ASSERT_NO_FATAL_FAILURE(s.UpdateUsageEntry(&encrypted_usage_header_)); + ASSERT_NO_FATAL_FAILURE(entry.GenerateVerifyReport(kActive)); + ASSERT_NO_FATAL_FAILURE(s.DeactivateUsageEntry(entry.pst())); + 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(session_.session_id(), clear_buffer_.data(), + 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); @@ -5219,395 +5199,311 @@ TEST_P(OEMCryptoUsageTableTestWithMAC, GenericCryptoSign) { } // Test generic verify when the license uses a PST. -TEST_P(OEMCryptoUsageTableTestWithMAC, GenericCryptoVerify) { - std::string pst = "my_pst"; - ASSERT_NO_FATAL_FAILURE(session_.GenerateNonce()); - uint32_t nonce = session_.nonce(); - MakeFourKeys( - 0, wvoec::kControlNonceEnabled | wvoec::kControlNonceRequired, - nonce, pst); - ASSERT_NO_FATAL_FAILURE(license_messages_.EncryptAndSignResponse()); - ASSERT_NO_FATAL_FAILURE(session_.CreateNewUsageEntry()); - ASSERT_EQ(OEMCrypto_SUCCESS, license_messages_.LoadResponse()); - +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; - SignBuffer(key_index, clear_buffer_, &signature); + SignBufferWithKey(s.license().keys[key_index].key_data, clear_buffer_, + &signature); - sts = OEMCrypto_SelectKey(session_.session_id(), - session_.license().keys[key_index].key_id, - session_.license().keys[key_index].key_id_length, + 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(session_.session_id(), clear_buffer_.data(), + 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(session_.UpdateUsageEntry(&encrypted_usage_header_)); - ASSERT_NO_FATAL_FAILURE(session_.GenerateVerifyReport(pst, kActive)); - ASSERT_NO_FATAL_FAILURE(session_.DeactivateUsageEntry(pst)); - ASSERT_NO_FATAL_FAILURE(session_.UpdateUsageEntry(&encrypted_usage_header_)); - ASSERT_NO_FATAL_FAILURE(session_.GenerateVerifyReport(pst, kInactiveUsed)); - sts = OEMCrypto_Generic_Verify(session_.session_id(), clear_buffer_.data(), + ASSERT_NO_FATAL_FAILURE(s.UpdateUsageEntry(&encrypted_usage_header_)); + ASSERT_NO_FATAL_FAILURE(entry.GenerateVerifyReport(kActive)); + ASSERT_NO_FATAL_FAILURE(s.DeactivateUsageEntry(entry.pst())); + 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(OEMCryptoUsageTableTestWithMAC, OfflineLicense) { - std::string pst = "my_pst"; - Session s; - ASSERT_NO_FATAL_FAILURE(LoadOfflineLicense(s, pst)); +TEST_P(OEMCryptoUsageTableTest, OfflineLicense) { + LicenseWithUsageEntry entry; + entry.license_messages().set_api_version(license_api_version_); + entry.MakeOfflineAndClose(this); } // Test that an offline license can be loaded and that the license can be // renewed. -TEST_P(OEMCryptoUsageTableTestWithMAC, OfflineLicenseRefresh) { - std::string pst = "my_pst"; - Session s; - ASSERT_NO_FATAL_FAILURE(s.open()); - ASSERT_NO_FATAL_FAILURE(InstallTestRSAKey(&s)); - ASSERT_NO_FATAL_FAILURE(s.GenerateNonce()); - ASSERT_NO_FATAL_FAILURE( - s.FillSimpleMessage(0, wvoec::kControlNonceOrEntry, s.nonce(), pst)); - ASSERT_NO_FATAL_FAILURE(s.EncryptAndSign()); - ASSERT_NO_FATAL_FAILURE(s.CreateNewUsageEntry()); - ASSERT_NO_FATAL_FAILURE(s.LoadTestKeys(pst, new_mac_keys_)); +TEST_P(OEMCryptoUsageTableTest, OfflineLicenseRefresh) { + LicenseWithUsageEntry entry; + entry.license_messages().set_api_version(license_api_version_); + entry.MakeAndLoad(this, wvoec::kControlNonceOrEntry); + Session& s = entry.session(); + const int64_t loaded = wvcdm::Clock().GetCurrentTime(); ASSERT_NO_FATAL_FAILURE(s.TestDecryptCTR()); // License renewal message is signed by client and verified by the server. - ASSERT_NO_FATAL_FAILURE(s.VerifyRenewalRequestSignature()); - size_t kAllKeys = 1; - ASSERT_NO_FATAL_FAILURE(s.RefreshTestKeys( - kAllKeys, wvoec::kControlNonceOrEntry, 0, OEMCrypto_SUCCESS)); + RenewalRoundTrip renewal_messages(&entry.license_messages()); + MakeRenewalRequest(&renewal_messages); + LoadRenewal(&renewal_messages, OEMCrypto_SUCCESS); ASSERT_NO_FATAL_FAILURE(s.TestDecryptCTR()); ASSERT_NO_FATAL_FAILURE(s.UpdateUsageEntry(&encrypted_usage_header_)); - ASSERT_NO_FATAL_FAILURE( - s.GenerateVerifyReport(pst, kActive, - loaded, // license received. - loaded, // First decrypt when loaded, not refresh. - 0)); // last decrypt now. + ASSERT_NO_FATAL_FAILURE(entry.GenerateVerifyReport( + kActive, + loaded, // license received. + loaded, // First decrypt when loaded, not refresh. + 0)); // last decrypt now. } // Test that an offline license can be reloaded in a new session. -TEST_P(OEMCryptoUsageTableTestWithMAC, ReloadOfflineLicense) { - std::string pst = "my_pst"; - Session s; - ASSERT_NO_FATAL_FAILURE(LoadOfflineLicense(s, pst)); - - ASSERT_NO_FATAL_FAILURE(s.open()); - // We will reuse the encrypted and signed message, so we don't call - // FillSimpleMessage again. - ASSERT_NO_FATAL_FAILURE(s.ReloadUsageEntry()); - ASSERT_NO_FATAL_FAILURE(InstallTestRSAKey(&s)); - ASSERT_NO_FATAL_FAILURE(s.LoadTestKeys(pst, new_mac_keys_)); +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(s.GenerateVerifyReport(pst, kUnused)); + ASSERT_NO_FATAL_FAILURE(entry.GenerateVerifyReport(kUnused)); ASSERT_NO_FATAL_FAILURE(s.TestDecryptCTR()); ASSERT_NO_FATAL_FAILURE(s.UpdateUsageEntry(&encrypted_usage_header_)); - ASSERT_NO_FATAL_FAILURE(s.GenerateVerifyReport(pst, kActive)); - ASSERT_NO_FATAL_FAILURE(s.close()); + ASSERT_NO_FATAL_FAILURE(entry.GenerateVerifyReport(kActive)); } // Test that an offline license can be reloaded in a new session, and then // refreshed. -TEST_P(OEMCryptoUsageTableTestWithMAC, ReloadOfflineLicenseWithRefresh) { - std::string pst = "my_pst"; - Session s; - ASSERT_NO_FATAL_FAILURE(LoadOfflineLicense(s, pst)); +TEST_P(OEMCryptoUsageTableTest, ReloadOfflineLicenseWithRefresh) { + LicenseWithUsageEntry entry; + entry.license_messages().set_api_version(license_api_version_); + entry.MakeOfflineAndClose(this); + Session& s = entry.session(); const int64_t loaded = wvcdm::Clock().GetCurrentTime(); - ASSERT_NO_FATAL_FAILURE(s.open()); - // We will reuse the encrypted and signed message, so we don't call - // FillSimpleMessage again. - ASSERT_NO_FATAL_FAILURE(s.ReloadUsageEntry()); - ASSERT_NO_FATAL_FAILURE(InstallTestRSAKey(&s)); - ASSERT_NO_FATAL_FAILURE(s.LoadTestKeys(pst, new_mac_keys_)); + ASSERT_NO_FATAL_FAILURE(entry.OpenAndReload(this)); ASSERT_NO_FATAL_FAILURE(s.UpdateUsageEntry(&encrypted_usage_header_)); - ASSERT_NO_FATAL_FAILURE(s.GenerateVerifyReport(pst, kUnused, loaded, 0, 0)); + ASSERT_NO_FATAL_FAILURE(entry.GenerateVerifyReport(kUnused, loaded, 0, 0)); ASSERT_NO_FATAL_FAILURE(s.TestDecryptCTR()); const int64_t decrypt_time = wvcdm::Clock().GetCurrentTime(); ASSERT_NO_FATAL_FAILURE(s.UpdateUsageEntry(&encrypted_usage_header_)); - ASSERT_NO_FATAL_FAILURE( - s.GenerateVerifyReport(pst, kActive, - loaded, // license received. - decrypt_time, // first decrypt - decrypt_time)); // last decrypt - size_t kAllKeys = 1; - ASSERT_NO_FATAL_FAILURE(s.RefreshTestKeys( - kAllKeys, wvoec::kControlNonceOrEntry, 0, OEMCrypto_SUCCESS)); + RenewalRoundTrip renewal_messages(&entry.license_messages()); + MakeRenewalRequest(&renewal_messages); + LoadRenewal(&renewal_messages, OEMCrypto_SUCCESS); ASSERT_NO_FATAL_FAILURE(s.TestDecryptCTR()); ASSERT_NO_FATAL_FAILURE(s.UpdateUsageEntry(&encrypted_usage_header_)); - ASSERT_NO_FATAL_FAILURE(s.GenerateVerifyReport(pst, kActive, - loaded, // license loaded. - decrypt_time, // first decrypt - 0)); // last decrypt - ASSERT_NO_FATAL_FAILURE(s.close()); + ASSERT_NO_FATAL_FAILURE( + entry.GenerateVerifyReport(kActive, + loaded, // license loaded. + decrypt_time, // first decrypt + 0)); // last decrypt } // 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(OEMCryptoUsageTableTestWithMAC, ReloadOfflineLicenseWithTerminate) { - std::string pst = "my_pst"; - Session s; - ASSERT_NO_FATAL_FAILURE(LoadOfflineLicense(s, pst)); +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(s.open()); - // We will reuse the encrypted and signed message, so we don't call - // FillSimpleMessage again. - ASSERT_NO_FATAL_FAILURE(s.ReloadUsageEntry()); - ASSERT_NO_FATAL_FAILURE(InstallTestRSAKey(&s)); - ASSERT_NO_FATAL_FAILURE(s.LoadTestKeys(pst, new_mac_keys_)); + ASSERT_NO_FATAL_FAILURE(entry.OpenAndReload(this)); ASSERT_NO_FATAL_FAILURE(s.UpdateUsageEntry(&encrypted_usage_header_)); - ASSERT_NO_FATAL_FAILURE(s.GenerateVerifyReport(pst, kUnused)); + ASSERT_NO_FATAL_FAILURE(entry.GenerateVerifyReport(kUnused)); ASSERT_NO_FATAL_FAILURE(s.TestDecryptCTR()); ASSERT_NO_FATAL_FAILURE(s.UpdateUsageEntry(&encrypted_usage_header_)); - ASSERT_NO_FATAL_FAILURE(s.GenerateVerifyReport(pst, kActive)); - ASSERT_NO_FATAL_FAILURE(s.close()); + 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(OEMCryptoUsageTableTestWithMAC, BadReloadOfflineLicense) { - std::string pst = "my_pst"; - Session s; - ASSERT_NO_FATAL_FAILURE(LoadOfflineLicense(s, pst)); +TEST_P(OEMCryptoUsageTableTest, BadReloadOfflineLicense) { + LicenseWithUsageEntry entry; + entry.license_messages().set_api_version(license_api_version_); + entry.MakeOfflineAndClose(this); + Session& s = entry.session(); const int64_t loaded = wvcdm::Clock().GetCurrentTime(); // 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)); - ASSERT_NO_FATAL_FAILURE(s2.GenerateNonce()); - ASSERT_NO_FATAL_FAILURE( - s2.FillSimpleMessage(0, wvoec::kControlNonceOrEntry, s2.nonce(), pst)); - ASSERT_NO_FATAL_FAILURE(s2.EncryptAndSign()); + // 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, - OEMCrypto_LoadKeys(s2.session_id(), s2.message_ptr(), s2.message_size(), - s2.signature().data(), s2.signature().size(), - s2.enc_mac_keys_iv_substr(), s2.enc_mac_keys_substr(), - s.num_keys(), s2.key_array(), s2.pst_substr(), - GetSubstring(), OEMCrypto_ContentLicense)); + ASSERT_NE(OEMCrypto_SUCCESS, license_messages2.LoadResponse()); ASSERT_NO_FATAL_FAILURE(s2.close()); - // Offline license with same mac keys should still be OK. - ASSERT_NO_FATAL_FAILURE(s.open()); - ASSERT_NO_FATAL_FAILURE(s.ReloadUsageEntry()); - ASSERT_NO_FATAL_FAILURE(InstallTestRSAKey(&s)); - ASSERT_NO_FATAL_FAILURE(s.LoadTestKeys(pst, new_mac_keys_)); + // 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(s.GenerateVerifyReport(pst, kUnused, - loaded, // license loaded. - 0, - 0)); // first and last decrypt + ASSERT_NO_FATAL_FAILURE( + entry.GenerateVerifyReport(kUnused, + loaded, // license loaded. + 0, + 0)); // first and last decrypt } // An offline license should not load on the first call if the nonce is bad. -TEST_P(OEMCryptoUsageTableTestWithMAC, OfflineBadNonce) { - std::string pst = "my_pst"; +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( - s.FillSimpleMessage(0, wvoec::kControlNonceOrEntry, 42, pst)); - ASSERT_NO_FATAL_FAILURE(s.EncryptAndSign()); - OEMCryptoResult sts = OEMCrypto_LoadKeys( - s.session_id(), s.message_ptr(), s.message_size(), s.signature().data(), - s.signature().size(), s.enc_mac_keys_iv_substr(), s.enc_mac_keys_substr(), - s.num_keys(), s.key_array(), s.pst_substr(), GetSubstring(), - OEMCrypto_ContentLicense); - ASSERT_NE(OEMCrypto_SUCCESS, sts); - ASSERT_NO_FATAL_FAILURE(s.close()); + 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()); } // An offline license needs a valid pst. -TEST_P(OEMCryptoUsageTableTestWithMAC, OfflineEmptyPST) { +TEST_P(OEMCryptoUsageTableTest, OfflineEmptyPST) { Session s; 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( - s.FillSimpleMessage(0, wvoec::kControlNonceOrEntry, s.nonce())); - ASSERT_NO_FATAL_FAILURE(s.EncryptAndSign()); - OEMCryptoResult sts = OEMCrypto_LoadKeys( - s.session_id(), s.message_ptr(), s.message_size(), s.signature().data(), - s.signature().size(), s.enc_mac_keys_iv_substr(), s.enc_mac_keys_substr(), - s.num_keys(), s.key_array(), GetSubstring(), GetSubstring(), - OEMCrypto_ContentLicense); - ASSERT_NE(OEMCrypto_SUCCESS, sts); - ASSERT_NO_FATAL_FAILURE(s.close()); + 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(OEMCryptoUsageTableTestWithMAC, ReloadOfflineWrongPST) { - std::string pst = "my_pst1"; - Session s; - ASSERT_NO_FATAL_FAILURE(LoadOfflineLicense(s, pst)); +TEST_P(OEMCryptoUsageTableTest, ReloadOfflineWrongPST) { + LicenseWithUsageEntry entry; + entry.license_messages().set_api_version(license_api_version_); + entry.MakeOfflineAndClose(this); + Session& s = entry.session(); - std::string bad_pst = "my_pst2"; - ASSERT_NO_FATAL_FAILURE(s.open()); - ASSERT_NO_FATAL_FAILURE(InstallTestRSAKey(&s)); - ASSERT_NO_FATAL_FAILURE(s.ReloadUsageEntry()); - memcpy(s.license().pst, bad_pst.c_str(), bad_pst.length()); - ASSERT_NO_FATAL_FAILURE(s.EncryptAndSign()); - ASSERT_NE(OEMCrypto_SUCCESS, - OEMCrypto_LoadKeys( - s.session_id(), s.message_ptr(), s.message_size(), - s.signature().data(), s.signature().size(), GetSubstring(), - GetSubstring(), s.num_keys(), s.key_array(), s.pst_substr(), - GetSubstring(), OEMCrypto_ContentLicense)); + 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(OEMCryptoUsageTableTestWithMAC, DeactivateOfflineLicense) { - std::string pst = "my_pst"; - Session s; - ASSERT_NO_FATAL_FAILURE(LoadOfflineLicense(s, pst)); - - ASSERT_NO_FATAL_FAILURE(s.open()); - ASSERT_NO_FATAL_FAILURE(s.ReloadUsageEntry()); - ASSERT_NO_FATAL_FAILURE(InstallTestRSAKey(&s)); - ASSERT_NO_FATAL_FAILURE( - s.LoadTestKeys(pst, new_mac_keys_)); // Reload the license +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(s.TestDecryptCTR()); // Should be able to decrypt. - ASSERT_NO_FATAL_FAILURE(s.DeactivateUsageEntry(pst)); // Then deactivate. + ASSERT_NO_FATAL_FAILURE( + s.DeactivateUsageEntry(entry.pst())); // Then deactivate. // After deactivate, should not be able to decrypt. ASSERT_NO_FATAL_FAILURE( s.TestDecryptCTR(false, OEMCrypto_ERROR_UNKNOWN_FAILURE)); ASSERT_NO_FATAL_FAILURE(s.UpdateUsageEntry(&encrypted_usage_header_)); - ASSERT_NO_FATAL_FAILURE(s.GenerateVerifyReport(pst, kInactiveUsed)); + ASSERT_NO_FATAL_FAILURE(entry.GenerateVerifyReport(kInactiveUsed)); ASSERT_NO_FATAL_FAILURE(s.close()); - Session s2; - ASSERT_NO_FATAL_FAILURE(s2.open()); - ASSERT_NO_FATAL_FAILURE(InstallTestRSAKey(&s2)); - ASSERT_NO_FATAL_FAILURE(s2.LoadUsageEntry(s)); // Offile license can not be reused if it has been deactivated. - EXPECT_NE( - OEMCrypto_SUCCESS, - OEMCrypto_LoadKeys(s2.session_id(), s.message_ptr(), s.message_size(), - s.signature().data(), s.signature().size(), - s.enc_mac_keys_iv_substr(), s.enc_mac_keys_substr(), - s.num_keys(), s.key_array(), s.pst_substr(), - GetSubstring(), OEMCrypto_ContentLicense)); - s2.close(); + 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. - Session s3; - ASSERT_NO_FATAL_FAILURE(s3.open()); - ASSERT_NO_FATAL_FAILURE(s3.LoadUsageEntry(s)); - ASSERT_NO_FATAL_FAILURE(s3.UpdateUsageEntry(&encrypted_usage_header_)); - ASSERT_NO_FATAL_FAILURE(s3.GenerateReport(pst, OEMCrypto_SUCCESS, &s)); - EXPECT_EQ(kInactiveUsed, s3.pst_report().status()); + 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(s3.DeactivateUsageEntry(pst)); - ASSERT_NO_FATAL_FAILURE(s3.UpdateUsageEntry(&encrypted_usage_header_)); - ASSERT_NO_FATAL_FAILURE(s3.GenerateReport(pst, OEMCrypto_SUCCESS, &s)); - EXPECT_EQ(kInactiveUsed, s3.pst_report().status()); + ASSERT_NO_FATAL_FAILURE(s.DeactivateUsageEntry(entry.pst())); + 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(OEMCryptoUsageTableTestWithMAC, DeactivateOfflineLicenseUnused) { - std::string pst = "my_pst"; - Session s1; - ASSERT_NO_FATAL_FAILURE(LoadOfflineLicense(s1, pst)); - - ASSERT_NO_FATAL_FAILURE(s1.open()); - ASSERT_NO_FATAL_FAILURE(s1.ReloadUsageEntry()); - ASSERT_NO_FATAL_FAILURE(InstallTestRSAKey(&s1)); - ASSERT_NO_FATAL_FAILURE( - s1.LoadTestKeys(pst, new_mac_keys_)); // Reload the license +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(s1.DeactivateUsageEntry(pst)); // Then deactivate. + ASSERT_NO_FATAL_FAILURE(entry.OpenAndReload(this)); + ASSERT_NO_FATAL_FAILURE( + s.DeactivateUsageEntry(entry.pst())); // Then deactivate. // After deactivate, should not be able to decrypt. ASSERT_NO_FATAL_FAILURE( - s1.TestDecryptCTR(false, OEMCrypto_ERROR_UNKNOWN_FAILURE)); - ASSERT_NO_FATAL_FAILURE(s1.UpdateUsageEntry(&encrypted_usage_header_)); - ASSERT_NO_FATAL_FAILURE(s1.GenerateVerifyReport(pst, kInactiveUnused)); - ASSERT_NO_FATAL_FAILURE(s1.close()); + s.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()); - Session s2; - ASSERT_NO_FATAL_FAILURE(s2.open()); - ASSERT_NO_FATAL_FAILURE(InstallTestRSAKey(&s2)); - ASSERT_NO_FATAL_FAILURE(s2.LoadUsageEntry(s1)); - // Offline license can not be reused if it has been deactivated. - EXPECT_NE( - OEMCrypto_SUCCESS, - OEMCrypto_LoadKeys(s2.session_id(), s1.message_ptr(), s1.message_size(), - s1.signature().data(), s1.signature().size(), - s1.enc_mac_keys_iv_substr(), s1.enc_mac_keys_substr(), - s1.num_keys(), s1.key_array(), s1.pst_substr(), - GetSubstring(), OEMCrypto_ContentLicense)); - s2.close(); - // But we can still generate a report. - Session s3; - ASSERT_NO_FATAL_FAILURE(s3.open()); - ASSERT_NO_FATAL_FAILURE(s3.LoadUsageEntry(s1)); - ASSERT_NO_FATAL_FAILURE(s3.UpdateUsageEntry(&encrypted_usage_header_)); - ASSERT_NO_FATAL_FAILURE(s3.GenerateReport(pst, OEMCrypto_SUCCESS, &s1)); - EXPECT_EQ(kInactiveUnused, s3.pst_report().status()); - // We could call DeactivateUsageEntry multiple times. The state should not - // change. - ASSERT_NO_FATAL_FAILURE(s3.DeactivateUsageEntry(pst)); - ASSERT_NO_FATAL_FAILURE(s3.UpdateUsageEntry(&encrypted_usage_header_)); - ASSERT_NO_FATAL_FAILURE(s3.GenerateReport(pst, OEMCrypto_SUCCESS, &s1)); - EXPECT_EQ(kInactiveUnused, s3.pst_report().status()); -} - -// If the PST pointers are not contained in the message, then LoadKeys should -// reject the attempt. -TEST_P(OEMCryptoUsageTableTestWithMAC, BadRange) { - std::string pst = "my_pst"; - Session s; + // Offile license can not be reused if it has been deactivated. ASSERT_NO_FATAL_FAILURE(s.open()); ASSERT_NO_FATAL_FAILURE(InstallTestRSAKey(&s)); - ASSERT_NO_FATAL_FAILURE(session_.CreateNewUsageEntry()); - ASSERT_NO_FATAL_FAILURE(s.GenerateNonce()); - ASSERT_NO_FATAL_FAILURE( - s.FillSimpleMessage(0, wvoec::kControlNonceOrEntry, s.nonce(), pst)); - ASSERT_NO_FATAL_FAILURE(s.EncryptAndSign()); - std::string double_message = DuplicateMessage(s.encrypted_license()); - OEMCrypto_Substring wrong_pst = s.pst_substr(); - wrong_pst.offset += s.message_size(); - ASSERT_NE( - OEMCrypto_SUCCESS, - OEMCrypto_LoadKeys( - s.session_id(), - reinterpret_cast(double_message.data()), - s.message_size(), s.signature().data(), s.signature().size(), - s.enc_mac_keys_iv_substr(), s.enc_mac_keys_substr(), s.num_keys(), - s.key_array(), wrong_pst, GetSubstring(), OEMCrypto_ContentLicense)); + 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(s.DeactivateUsageEntry(entry.pst())); + ASSERT_NO_FATAL_FAILURE(s.UpdateUsageEntry(&encrypted_usage_header_)); + ASSERT_NO_FATAL_FAILURE(entry.GenerateVerifyReport(kInactiveUnused)); } // Test update usage table fails when passed a null pointer. -TEST_F(OEMCryptoUsageTableTest, UpdateFailsWithNullPtr) { - std::string pst = "my_pst"; - Session s; - ASSERT_NO_FATAL_FAILURE(s.open()); - ASSERT_NO_FATAL_FAILURE(InstallTestRSAKey(&s)); - ASSERT_NO_FATAL_FAILURE(s.GenerateNonce()); - ASSERT_NO_FATAL_FAILURE(s.FillSimpleMessage( - 0, wvoec::kControlNonceEnabled | wvoec::kControlNonceRequired, s.nonce(), - pst)); - ASSERT_NO_FATAL_FAILURE(s.EncryptAndSign()); - ASSERT_NO_FATAL_FAILURE(s.CreateNewUsageEntry()); - ASSERT_NO_FATAL_FAILURE(s.LoadTestKeys(pst, new_mac_keys_)); +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(); @@ -5623,10 +5519,13 @@ TEST_F(OEMCryptoUsageTableTest, UpdateFailsWithNullPtr) { &header_buffer_length, nullptr, &entry_buffer_length)); } +// XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX +// XXX separate these tests out. // Class used to test usage table defragmentation. class OEMCryptoUsageTableDefragTest : public OEMCryptoUsageTableTest { protected: - void LoadFirstLicense(Session* s, uint32_t index) { +#if 0 + void LoadFirstLicense(Session* s, uint32_t index) { make offline. ASSERT_NO_FATAL_FAILURE(s->open()); ASSERT_NO_FATAL_FAILURE(InstallTestRSAKey(s)); std::string pst = "pst " + std::to_string(index); @@ -5641,36 +5540,31 @@ class OEMCryptoUsageTableDefragTest : public OEMCryptoUsageTableTest { ASSERT_NO_FATAL_FAILURE(s->UpdateUsageEntry(&encrypted_usage_header_)); ASSERT_NO_FATAL_FAILURE(s->close()); } +#endif - void ReloadLicense(Session* s, int64_t start) { - ASSERT_NO_FATAL_FAILURE(s->open()); - ASSERT_NO_FATAL_FAILURE(s->ReloadUsageEntry()); - ASSERT_NO_FATAL_FAILURE(InstallTestRSAKey(s)); - ASSERT_NO_FATAL_FAILURE(s->LoadTestKeys(s->pst(), new_mac_keys_)); - ASSERT_NO_FATAL_FAILURE(s->UpdateUsageEntry(&encrypted_usage_header_)); - ASSERT_NO_FATAL_FAILURE(s->TestDecryptCTR()); - ASSERT_NO_FATAL_FAILURE(s->UpdateUsageEntry(&encrypted_usage_header_)); - ASSERT_NO_FATAL_FAILURE(s->GenerateVerifyReport(s->pst(), kActive, - start, start, 0)); - ASSERT_NO_FATAL_FAILURE(s->close()); + void ReloadLicense(LicenseWithUsageEntry* entry, int64_t start) { + Session& s = entry->session(); + ASSERT_NO_FATAL_FAILURE(entry->OpenAndReload(this)); + ASSERT_NO_FATAL_FAILURE(s.UpdateUsageEntry(&encrypted_usage_header_)); + ASSERT_NO_FATAL_FAILURE(s.TestDecryptCTR()); + ASSERT_NO_FATAL_FAILURE(s.UpdateUsageEntry(&encrypted_usage_header_)); + ASSERT_NO_FATAL_FAILURE( + entry->GenerateVerifyReport(kActive, start, start, 0)); + ASSERT_NO_FATAL_FAILURE(s.close()); } - void FailReload(Session* s, OEMCryptoResult expected_result) { - ASSERT_NO_FATAL_FAILURE(s->open()); - ASSERT_NO_FATAL_FAILURE(InstallTestRSAKey(s)); + 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())); + OEMCrypto_LoadUsageEntry(s.session_id(), s.usage_entry_number(), + s.encrypted_usage_entry().data(), + s.encrypted_usage_entry().size())); - ASSERT_NE(OEMCrypto_SUCCESS, - OEMCrypto_LoadKeys( - s->session_id(), s->message_ptr(), s->message_size(), - s->signature().data(), s->signature().size(), - s->enc_mac_keys_iv_substr(), s->enc_mac_keys_substr(), - s->num_keys(), s->key_array(), s->pst_substr(), - GetSubstring(), OEMCrypto_ContentLicense)); - ASSERT_NO_FATAL_FAILURE(s->close()); + ASSERT_NE(OEMCrypto_SUCCESS, entry->license_messages().LoadResponse()); + ASSERT_NO_FATAL_FAILURE(s.close()); } void ShrinkHeader(uint32_t new_size, @@ -5694,23 +5588,26 @@ class OEMCryptoUsageTableDefragTest : public OEMCryptoUsageTableTest { }; // Verify that usage table entries can be moved around in the table. -TEST_F(OEMCryptoUsageTableDefragTest, MoveUsageEntries) { +TEST_P(OEMCryptoUsageTableDefragTest, MoveUsageEntries) { const size_t ENTRY_COUNT = 10; - vector sessions(ENTRY_COUNT); + vector entries(ENTRY_COUNT); vector start(ENTRY_COUNT); for (size_t i = 0; i < ENTRY_COUNT; i++) { - ASSERT_NO_FATAL_FAILURE(LoadFirstLicense(&sessions[i], i)) - << "On license " << i << " pst=" << sessions[i].pst(); + 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(); start[i] = wvcdm::Clock().GetCurrentTime(); } for (size_t i = 0; i < ENTRY_COUNT; i++) { - ASSERT_NO_FATAL_FAILURE(ReloadLicense(&sessions[i], start[i])) - << "On license " << i << " pst=" << sessions[i].pst(); + 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( - sessions[4].MoveUsageEntry(1, &encrypted_usage_header_)); + 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(); @@ -5719,87 +5616,103 @@ TEST_F(OEMCryptoUsageTableDefragTest, MoveUsageEntries) { OEMCrypto_LoadUsageTableHeader(encrypted_usage_header_.data(), encrypted_usage_header_.size())); wvcdm::TestSleep::SyncFakeClock(); - ASSERT_NO_FATAL_FAILURE(ReloadLicense(&sessions[0], start[0])); + ASSERT_NO_FATAL_FAILURE(ReloadLicense(&entries[0], start[0])); // Now has index 1. - ASSERT_NO_FATAL_FAILURE(ReloadLicense(&sessions[4], start[4])); - ASSERT_NO_FATAL_FAILURE(ReloadLicense(&sessions[2], start[2])); + ASSERT_NO_FATAL_FAILURE(ReloadLicense(&entries[4], start[4])); + ASSERT_NO_FATAL_FAILURE(ReloadLicense(&entries[2], start[2])); // When 4 was moved to 1, it increased the gen. number in the header. ASSERT_NO_FATAL_FAILURE( - FailReload(&sessions[1], OEMCrypto_ERROR_GENERATION_SKEW)); + FailReloadLicense(&entries[1], OEMCrypto_ERROR_GENERATION_SKEW)); // Index 3 is beyond the end of the table. ASSERT_NO_FATAL_FAILURE( - FailReload(&sessions[3], OEMCrypto_ERROR_UNKNOWN_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_F(OEMCryptoUsageTableDefragTest, MoveUsageEntriesToOpenSession) { - Session s0; - Session s1; - LoadFirstLicense(&s0, 0); - LoadFirstLicense(&s1, 1); - s0.open(); - ASSERT_NO_FATAL_FAILURE(s0.ReloadUsageEntry()); +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(s1.MoveUsageEntry(0, &encrypted_usage_header_, - OEMCrypto_ERROR_ENTRY_IN_USE)); + 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_F(OEMCryptoUsageTableDefragTest, ShrinkOverOpenSessions) { - Session s0; - Session s1; - LoadFirstLicense(&s0, 0); - LoadFirstLicense(&s1, 1); - s0.open(); - ASSERT_NO_FATAL_FAILURE(s0.ReloadUsageEntry()); - s1.open(); - ASSERT_NO_FATAL_FAILURE(s1.ReloadUsageEntry()); +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)); - s1.close(); // Can shrink after closing s1, even if s0 is open. + 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_F(OEMCryptoUsageTableDefragTest, EnlargeHeader) { - Session s0; - Session s1; - LoadFirstLicense(&s0, 0); - LoadFirstLicense(&s1, 1); +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_F(OEMCryptoUsageTableDefragTest, CreateNewHeaderWhileUsingOldOne) { - Session s0; - Session s1; - LoadFirstLicense(&s0, 0); - LoadFirstLicense(&s1, 1); - s0.open(); - ASSERT_NO_FATAL_FAILURE(s0.ReloadUsageEntry()); +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_F(OEMCryptoUsageTableDefragTest, ReloadUsageEntryWrongIndex) { - Session s0; - Session s1; - LoadFirstLicense(&s0, 0); - LoadFirstLicense(&s1, 1); - s0.set_usage_entry_number(1); +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( - FailReload(&s0, OEMCrypto_ERROR_INVALID_SESSION)); + FailReloadLicense(&entry0, OEMCrypto_ERROR_INVALID_SESSION)); } // Verify that a usage table entry cannot be loaded if it has been altered. -TEST_F(OEMCryptoUsageTableDefragTest, ReloadUsageEntryBadData) { - Session s; - LoadFirstLicense(&s, 0); +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(); @@ -5811,12 +5724,11 @@ TEST_F(OEMCryptoUsageTableDefragTest, ReloadUsageEntryBadData) { data.data(), data.size())); } -static std::string MakePST(size_t n) { - return "pst-" + std::to_string(n); -} - +// XXX --- This is ugly. It takes too long. Fix it. +#if 0 // This verifies we can actually create two hundered usage table entries. -TEST_F(OEMCryptoUsageTableDefragTest, TwoHundredEntries) { +TEST_P(OEMCryptoUsageTableDefragTest, TwoHundredEntries) { + ASSERT_TRUE(false); // OEMCrypto is required to store at least 200 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 @@ -5902,22 +5814,18 @@ TEST_F(OEMCryptoUsageTableDefragTest, TwoHundredEntries) { ASSERT_NO_FATAL_FAILURE(sessions[i].close()); } } +#endif // This verifies that the usage table header can be loaded if the generation // number is off by one, but not off by two. -TEST_F(OEMCryptoUsageTableTest, ReloadUsageTableWithSkew) { +TEST_P(OEMCryptoUsageTableTest, ReloadUsageTableWithSkew) { // This also tests a few other error conditions with usage table headers. - std::string pst = "my_pst"; - Session s; - ASSERT_NO_FATAL_FAILURE(LoadOfflineLicense(s, pst)); + LicenseWithUsageEntry entry; + entry.MakeOfflineAndClose(this); + Session& s = entry.session(); // Reload the license, and save the header. - ASSERT_NO_FATAL_FAILURE(s.open()); - // We will reuse the encrypted and signed message, so we don't call - // FillSimpleMessage again. - ASSERT_NO_FATAL_FAILURE(s.ReloadUsageEntry()); - ASSERT_NO_FATAL_FAILURE(InstallTestRSAKey(&s)); - ASSERT_NO_FATAL_FAILURE(s.LoadTestKeys(pst, new_mac_keys_)); + 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_)); @@ -5937,7 +5845,6 @@ TEST_F(OEMCryptoUsageTableTest, ReloadUsageTableWithSkew) { OEMCrypto_LoadUsageEntry(s.session_id(), s.usage_entry_number(), s.encrypted_usage_entry().data(), s.encrypted_usage_entry().size())); - ASSERT_NO_FATAL_FAILURE(InstallTestRSAKey(&s)); ASSERT_NO_FATAL_FAILURE(s.close()); // Modified header generates error. @@ -5953,7 +5860,6 @@ TEST_F(OEMCryptoUsageTableTest, ReloadUsageTableWithSkew) { OEMCrypto_LoadUsageEntry(s.session_id(), s.usage_entry_number(), s.encrypted_usage_entry().data(), s.encrypted_usage_entry().size())); - ASSERT_NO_FATAL_FAILURE(InstallTestRSAKey(&s)); ASSERT_NO_FATAL_FAILURE(s.close()); // Old by 2 generation numbers is error. @@ -5967,7 +5873,6 @@ TEST_F(OEMCryptoUsageTableTest, ReloadUsageTableWithSkew) { OEMCrypto_LoadUsageEntry(s.session_id(), s.usage_entry_number(), s.encrypted_usage_entry().data(), s.encrypted_usage_entry().size())); - ASSERT_NO_FATAL_FAILURE(InstallTestRSAKey(&s)); ASSERT_NO_FATAL_FAILURE(s.close()); // Old by 1 generation numbers is just warning. @@ -5982,55 +5887,54 @@ TEST_F(OEMCryptoUsageTableTest, ReloadUsageTableWithSkew) { s.encrypted_usage_entry().data(), s.encrypted_usage_entry().size())); ASSERT_NO_FATAL_FAILURE(InstallTestRSAKey(&s)); - ASSERT_NO_FATAL_FAILURE(s.LoadTestKeys(pst, new_mac_keys_)); - ASSERT_NO_FATAL_FAILURE(s.UpdateUsageEntry(&encrypted_usage_header_)); - ASSERT_NO_FATAL_FAILURE(s.close()); + ASSERT_EQ(OEMCrypto_SUCCESS, entry.license_messages().LoadResponse()); } // A usage report with the wrong pst should fail. -TEST_F(OEMCryptoUsageTableTest, GenerateReportWrongPST) { - std::string pst = "my_pst"; - Session s; - ASSERT_NO_FATAL_FAILURE(s.open()); - ASSERT_NO_FATAL_FAILURE(InstallTestRSAKey(&s)); - ASSERT_NO_FATAL_FAILURE(s.GenerateNonce()); - ASSERT_NO_FATAL_FAILURE( - s.FillSimpleMessage(0, wvoec::kControlNonceOrEntry, s.nonce(), pst)); - ASSERT_NO_FATAL_FAILURE(s.EncryptAndSign()); - ASSERT_NO_FATAL_FAILURE(s.CreateNewUsageEntry()); - ASSERT_NO_FATAL_FAILURE(s.LoadTestKeys(pst, new_mac_keys_)); +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_F(OEMCryptoUsageTableTest, TimingTest) { - std::string pst1 = "my_pst_1"; - std::string pst2 = "my_pst_2"; - std::string pst3 = "my_pst_3"; - Session s1; - Session s2; - Session s3; - ASSERT_NO_FATAL_FAILURE(LoadOfflineLicense(s1, pst1)); +// XXX broken. +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)); const int64_t loaded1 = wvcdm::Clock().GetCurrentTime(); - ASSERT_NO_FATAL_FAILURE(LoadOfflineLicense(s2, pst2)); + ASSERT_NO_FATAL_FAILURE(entry2.MakeOfflineAndClose(this)); const int64_t loaded2 = wvcdm::Clock().GetCurrentTime(); - ASSERT_NO_FATAL_FAILURE(LoadOfflineLicense(s3, pst3)); + ASSERT_NO_FATAL_FAILURE(entry3.MakeOfflineAndClose(this)); const int64_t loaded3 = wvcdm::Clock().GetCurrentTime(); wvcdm::TestSleep::Sleep(kLongSleep); - ASSERT_NO_FATAL_FAILURE(s1.open()); - ASSERT_NO_FATAL_FAILURE(s1.ReloadUsageEntry()); - ASSERT_NO_FATAL_FAILURE(InstallTestRSAKey(&s1)); - ASSERT_NO_FATAL_FAILURE(s1.LoadTestKeys(pst1, new_mac_keys_)); + ASSERT_NO_FATAL_FAILURE(entry1.OpenAndReload(this)); const int64_t first_decrypt1 = wvcdm::Clock().GetCurrentTime(); ASSERT_NO_FATAL_FAILURE(s1.TestDecryptCTR()); - ASSERT_NO_FATAL_FAILURE(s2.open()); - ASSERT_NO_FATAL_FAILURE(s2.ReloadUsageEntry()); - ASSERT_NO_FATAL_FAILURE(InstallTestRSAKey(&s2)); - ASSERT_NO_FATAL_FAILURE(s2.LoadTestKeys(pst2, new_mac_keys_)); + ASSERT_NO_FATAL_FAILURE(entry2.OpenAndReload(this)); const int64_t first_decrypt2 = wvcdm::Clock().GetCurrentTime(); ASSERT_NO_FATAL_FAILURE(s2.TestDecryptCTR()); @@ -6040,7 +5944,7 @@ TEST_F(OEMCryptoUsageTableTest, TimingTest) { ASSERT_NO_FATAL_FAILURE(s2.TestDecryptCTR()); wvcdm::TestSleep::Sleep(kLongSleep); - ASSERT_NO_FATAL_FAILURE(s1.DeactivateUsageEntry(pst1)); + ASSERT_NO_FATAL_FAILURE(s1.DeactivateUsageEntry(entry1.pst())); ASSERT_NO_FATAL_FAILURE(s1.UpdateUsageEntry(&encrypted_usage_header_)); ASSERT_NO_FATAL_FAILURE(s2.UpdateUsageEntry(&encrypted_usage_header_)); ASSERT_NO_FATAL_FAILURE(s1.close()); @@ -6058,10 +5962,7 @@ TEST_F(OEMCryptoUsageTableTest, TimingTest) { // After a reboot, we should be able to reload keys, and generate reports. wvcdm::TestSleep::Sleep(kLongSleep); const int64_t third_decrypt = wvcdm::Clock().GetCurrentTime(); - ASSERT_NO_FATAL_FAILURE(s2.open()); - ASSERT_NO_FATAL_FAILURE(s2.ReloadUsageEntry()); - ASSERT_NO_FATAL_FAILURE(InstallTestRSAKey(&s2)); - ASSERT_NO_FATAL_FAILURE(s2.LoadTestKeys(pst2, new_mac_keys_)); + ASSERT_NO_FATAL_FAILURE(entry2.OpenAndReload(this)); ASSERT_NO_FATAL_FAILURE(s2.TestDecryptCTR()); ASSERT_NO_FATAL_FAILURE(s2.UpdateUsageEntry(&encrypted_usage_header_)); ASSERT_NO_FATAL_FAILURE(s2.close()); @@ -6069,45 +5970,41 @@ TEST_F(OEMCryptoUsageTableTest, TimingTest) { ASSERT_NO_FATAL_FAILURE(s1.open()); ASSERT_NO_FATAL_FAILURE(s2.open()); ASSERT_NO_FATAL_FAILURE(s3.open()); - ASSERT_NO_FATAL_FAILURE(s1.ReloadUsageEntry()); - ASSERT_NO_FATAL_FAILURE(s2.ReloadUsageEntry()); - ASSERT_NO_FATAL_FAILURE(s3.ReloadUsageEntry()); + ASSERT_NO_FATAL_FAILURE(entry1.ReloadUsageEntry()); + ASSERT_NO_FATAL_FAILURE(entry2.ReloadUsageEntry()); + ASSERT_NO_FATAL_FAILURE(entry3.ReloadUsageEntry()); + // 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. + entry1.license_messages().set_api_version(global_features.api_version); + entry2.license_messages().set_api_version(global_features.api_version); + entry3.license_messages().set_api_version(global_features.api_version); wvcdm::TestSleep::Sleep(kLongSleep); ASSERT_NO_FATAL_FAILURE(s1.UpdateUsageEntry(&encrypted_usage_header_)); - ASSERT_NO_FATAL_FAILURE(s1.GenerateVerifyReport(pst1, kInactiveUsed, - loaded1, - first_decrypt1, - second_decrypt)); + ASSERT_NO_FATAL_FAILURE(entry1.GenerateVerifyReport( + kInactiveUsed, loaded1, first_decrypt1, second_decrypt)); ASSERT_NO_FATAL_FAILURE(s2.UpdateUsageEntry(&encrypted_usage_header_)); - ASSERT_NO_FATAL_FAILURE(s2.GenerateVerifyReport(pst2, kActive, - loaded2, - first_decrypt2, - third_decrypt)); + ASSERT_NO_FATAL_FAILURE(entry2.GenerateVerifyReport( + kActive, loaded2, first_decrypt2, third_decrypt)); ASSERT_NO_FATAL_FAILURE(s3.UpdateUsageEntry(&encrypted_usage_header_)); - ASSERT_NO_FATAL_FAILURE(s3.GenerateVerifyReport(pst3, kUnused, loaded3)); + ASSERT_NO_FATAL_FAILURE(entry3.GenerateVerifyReport(kUnused, loaded3)); } +// XXX -- review this with the renewal stuff. // 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_F(OEMCryptoUsageTableTest, VerifyUsageTimes) { - std::string pst = "my_pst"; - Session s; - ASSERT_NO_FATAL_FAILURE(s.open()); - ASSERT_NO_FATAL_FAILURE(InstallTestRSAKey(&s)); - ASSERT_NO_FATAL_FAILURE(s.GenerateNonce()); - ASSERT_NO_FATAL_FAILURE(s.FillSimpleMessage( - 0, wvoec::kControlNonceEnabled | wvoec::kControlNonceRequired, s.nonce(), - pst)); - ASSERT_NO_FATAL_FAILURE(s.EncryptAndSign()); - ASSERT_NO_FATAL_FAILURE(s.CreateNewUsageEntry()); - ASSERT_NO_FATAL_FAILURE(s.LoadTestKeys(pst, new_mac_keys_)); +TEST_P(OEMCryptoUsageTableTest, VerifyUsageTimes) { + LicenseWithUsageEntry entry; + entry.license_messages().set_api_version(license_api_version_); + entry.MakeAndLoadOnline(this); + Session& s = entry.session(); const int64_t load_time = wvcdm::Clock().GetCurrentTime(); ASSERT_NO_FATAL_FAILURE(s.UpdateUsageEntry(&encrypted_usage_header_)); - ASSERT_NO_FATAL_FAILURE(s.GenerateVerifyReport(pst, kUnused)); + ASSERT_NO_FATAL_FAILURE(entry.GenerateVerifyReport(kUnused)); const int64_t kDotIntervalInSeconds = 5; const int64_t kIdleInSeconds = 20; @@ -6124,7 +6021,7 @@ TEST_F(OEMCryptoUsageTableTest, VerifyUsageTimes) { PrintDotsWhileSleep(kIdleInSeconds, kDotIntervalInSeconds); ASSERT_NO_FATAL_FAILURE(s.UpdateUsageEntry(&encrypted_usage_header_)); - ASSERT_NO_FATAL_FAILURE(s.GenerateVerifyReport(pst, kUnused, load_time)); + ASSERT_NO_FATAL_FAILURE(entry.GenerateVerifyReport(kUnused, load_time)); cout << "Start simulated playback..." << endl; int64_t dot_time = kDotIntervalInSeconds; @@ -6133,10 +6030,9 @@ TEST_F(OEMCryptoUsageTableTest, VerifyUsageTimes) { do { ASSERT_NO_FATAL_FAILURE(s.TestDecryptCTR()); ASSERT_NO_FATAL_FAILURE(s.UpdateUsageEntry(&encrypted_usage_header_)); - ASSERT_NO_FATAL_FAILURE(s.GenerateVerifyReport(pst, kActive, - load_time, - start_time, - 0)); // last decrypt = now. + ASSERT_NO_FATAL_FAILURE( + entry.GenerateVerifyReport(kActive, load_time, start_time, + 0)); // last decrypt = now. wvcdm::TestSleep::Sleep(kShortSleep); playback_time = wvcdm::Clock().GetCurrentTime() - start_time; ASSERT_LE(0, playback_time); @@ -6149,10 +6045,9 @@ TEST_F(OEMCryptoUsageTableTest, VerifyUsageTimes) { cout << "\nSimulated playback time = " << playback_time << " seconds.\n"; ASSERT_NO_FATAL_FAILURE(s.UpdateUsageEntry(&encrypted_usage_header_)); - ASSERT_NO_FATAL_FAILURE(s.GenerateVerifyReport(pst, kActive, - load_time, - start_time, - 0)); // last decrypt = now. + ASSERT_NO_FATAL_FAILURE( + entry.GenerateVerifyReport(kActive, load_time, start_time, + 0)); // last decrypt = now. EXPECT_NEAR(s.pst_report().seconds_since_first_decrypt() - s.pst_report().seconds_since_last_decrypt(), playback_time, kUsageTableTimeTolerance); @@ -6170,59 +6065,97 @@ TEST_F(OEMCryptoUsageTableTest, VerifyUsageTimes) { // |<----------------------------->| = seconds_since_first_decrypt // |<------------------------------------| = seconds_since_license_received ASSERT_NO_FATAL_FAILURE(s.UpdateUsageEntry(&encrypted_usage_header_)); - ASSERT_NO_FATAL_FAILURE(s.GenerateVerifyReport(pst, kActive, - load_time, - start_time, - kIdleInSeconds)); - ASSERT_NO_FATAL_FAILURE(s.DeactivateUsageEntry(pst)); + ASSERT_NO_FATAL_FAILURE(entry.GenerateVerifyReport( + kActive, load_time, start_time, kIdleInSeconds)); + ASSERT_NO_FATAL_FAILURE(s.DeactivateUsageEntry(entry.pst())); ASSERT_NO_FATAL_FAILURE(s.UpdateUsageEntry(&encrypted_usage_header_)); - ASSERT_NO_FATAL_FAILURE(s.GenerateVerifyReport(pst, kInactiveUsed, - load_time, - start_time, - kIdleInSeconds)); + ASSERT_NO_FATAL_FAILURE(entry.GenerateVerifyReport( + kInactiveUsed, load_time, start_time, kIdleInSeconds)); ASSERT_NO_FATAL_FAILURE( s.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(); + did_change_system_time_ = false; + test_start_steady_ = steady_clock_.now(); +#ifdef _WIN32 + GetSystemTime(&test_start_wall_); +#else + ASSERT_EQ(0, gettimeofday(&test_start_wall_, nullptr)); +#endif + } + + void TearDown() override { + if (did_change_system_time_) { + const auto delta = steady_clock_.now() - test_start_steady_; + const int64_t delta_sec = delta / std::chrono::seconds(1); + ASSERT_NO_FATAL_FAILURE(SetWallTimeDelta(delta_sec)); + } + OEMCryptoUsageTableTest::TearDown(); + } + + protected: + /** + * Sets the current wall-clock time to a delta based on the start of the + * test. + */ + void SetWallTimeDelta(int64_t delta_seconds) { + did_change_system_time_ = true; + NativeTime time = test_start_wall_; + ASSERT_NO_FATAL_FAILURE(AddNativeTime(delta_seconds, &time)); +#ifdef _WIN32 + ASSERT_TRUE(SetSystemTime(&time)); +#else + ASSERT_EQ(0, settimeofday(&time, nullptr)); +#endif + } + + std::chrono::steady_clock steady_clock_; + bool did_change_system_time_; + NativeTime test_start_wall_; + std::chrono::time_point test_start_steady_; +}; + // 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. -TEST_F(OEMCryptoUsageTableTest, TimeRollbackPrevention) { - std::string pst = "my_pst"; - Session s1; +// XXX broken. +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. It then rolls " << "the time back forward to the absolute time." << endl; + LicenseWithUsageEntry entry; + entry.MakeOfflineAndClose(this); + Session& s = entry.session(); std::chrono::system_clock wall_clock; std::chrono::steady_clock monotonic_clock; const auto loaded = wall_clock.now(); - ASSERT_NO_FATAL_FAILURE(LoadOfflineLicense(s1, pst)); - ASSERT_NO_FATAL_FAILURE(s1.open()); - ASSERT_NO_FATAL_FAILURE(s1.ReloadUsageEntry()); - ASSERT_NO_FATAL_FAILURE(InstallTestRSAKey(&s1)); - ASSERT_NO_FATAL_FAILURE(s1.LoadTestKeys(pst, new_mac_keys_)); + ASSERT_NO_FATAL_FAILURE(entry.OpenAndReload(this)); const auto first_decrypt = wall_clock.now(); // Monotonic clock can't be changed. We use this since system clock will be // unreliable. const auto first_decrypt_monotonic = monotonic_clock.now(); - ASSERT_NO_FATAL_FAILURE(s1.TestDecryptCTR()); - ASSERT_NO_FATAL_FAILURE(s1.UpdateUsageEntry(&encrypted_usage_header_)); - ASSERT_NO_FATAL_FAILURE(s1.close()); + ASSERT_NO_FATAL_FAILURE(s.TestDecryptCTR()); + ASSERT_NO_FATAL_FAILURE(s.UpdateUsageEntry(&encrypted_usage_header_)); + ASSERT_NO_FATAL_FAILURE(s.close()); // Imitate playback. wvcdm::TestSleep::Sleep(kLongDuration * 2); - ASSERT_NO_FATAL_FAILURE(s1.open()); - ASSERT_NO_FATAL_FAILURE(s1.ReloadUsageEntry()); - ASSERT_NO_FATAL_FAILURE(InstallTestRSAKey(&s1)); - ASSERT_NO_FATAL_FAILURE(s1.LoadTestKeys(pst, new_mac_keys_)); - ASSERT_NO_FATAL_FAILURE(s1.TestDecryptCTR()); - ASSERT_NO_FATAL_FAILURE(s1.UpdateUsageEntry(&encrypted_usage_header_)); - ASSERT_NO_FATAL_FAILURE(s1.close()); + ASSERT_NO_FATAL_FAILURE(entry.OpenAndReload(this)); + ASSERT_NO_FATAL_FAILURE(s.TestDecryptCTR()); + ASSERT_NO_FATAL_FAILURE(s.UpdateUsageEntry(&encrypted_usage_header_)); + ASSERT_NO_FATAL_FAILURE(s.close()); // Rollback the wall clock time. cout << "Rolling the system time back..." << endl; @@ -6230,15 +6163,12 @@ TEST_F(OEMCryptoUsageTableTest, TimeRollbackPrevention) { -static_cast(kLongDuration) * 10)); // Try to playback again. - ASSERT_NO_FATAL_FAILURE(s1.open()); - ASSERT_NO_FATAL_FAILURE(s1.ReloadUsageEntry()); - ASSERT_NO_FATAL_FAILURE(InstallTestRSAKey(&s1)); - ASSERT_NO_FATAL_FAILURE(s1.LoadTestKeys(pst, new_mac_keys_)); + ASSERT_NO_FATAL_FAILURE(entry.OpenAndReload(this)); const auto third_decrypt_monotonic = monotonic_clock.now(); - ASSERT_NO_FATAL_FAILURE(s1.TestDecryptCTR()); - ASSERT_NO_FATAL_FAILURE(s1.UpdateUsageEntry(&encrypted_usage_header_)); - ASSERT_NO_FATAL_FAILURE(s1.GenerateReport(pst)); - Test_PST_Report expected(pst, kActive); + ASSERT_NO_FATAL_FAILURE(s.TestDecryptCTR()); + ASSERT_NO_FATAL_FAILURE(s.UpdateUsageEntry(&encrypted_usage_header_)); + ASSERT_NO_FATAL_FAILURE(s.close()); + Test_PST_Report expected(entry.pst(), kActive); // Restore wall clock to its original position to verify that OEMCrypto does // not report negative times. @@ -6251,55 +6181,40 @@ TEST_F(OEMCryptoUsageTableTest, TimeRollbackPrevention) { expected.time_created = UnixTime(wall_clock.now()); const auto end_time = first_decrypt + test_duration; - ASSERT_NO_FATAL_FAILURE( - s1.VerifyReport(expected, UnixTime(loaded), UnixTime(first_decrypt), - UnixTime(end_time))); - ASSERT_NO_FATAL_FAILURE(s1.close()); + ASSERT_NO_FATAL_FAILURE(s.VerifyReport( + expected, UnixTime(loaded), UnixTime(first_decrypt), UnixTime(end_time))); + ASSERT_NO_FATAL_FAILURE(s.close()); } // Verify that a large PST can be used with usage table entries. -TEST_F(OEMCryptoUsageTableTest, PSTLargeBuffer) { +TEST_P(OEMCryptoUsageTableTest, PSTLargeBuffer) { + LicenseWithUsageEntry entry; std::string pst(kMaxPSTLength, 'a'); // A large PST. - Session s; - ASSERT_NO_FATAL_FAILURE(LoadOfflineLicense(s, pst)); + entry.set_pst(pst); + entry.MakeOfflineAndClose(this); + Session& s = entry.session(); - ASSERT_NO_FATAL_FAILURE(s.open()); - ASSERT_NO_FATAL_FAILURE(s.ReloadUsageEntry()); - ASSERT_NO_FATAL_FAILURE(InstallTestRSAKey(&s)); - ASSERT_NO_FATAL_FAILURE( - s.LoadTestKeys(pst, new_mac_keys_)); // Reload the license + ASSERT_NO_FATAL_FAILURE(entry.OpenAndReload(this)); ASSERT_NO_FATAL_FAILURE(s.TestDecryptCTR()); // Should be able to decrypt. - ASSERT_NO_FATAL_FAILURE(s.DeactivateUsageEntry(pst)); // Then deactivate. + ASSERT_NO_FATAL_FAILURE( + s.DeactivateUsageEntry(entry.pst())); // Then deactivate. // After deactivate, should not be able to decrypt. ASSERT_NO_FATAL_FAILURE( s.TestDecryptCTR(false, OEMCrypto_ERROR_UNKNOWN_FAILURE)); ASSERT_NO_FATAL_FAILURE(s.UpdateUsageEntry(&encrypted_usage_header_)); - ASSERT_NO_FATAL_FAILURE(s.GenerateVerifyReport(pst, kInactiveUsed)); + ASSERT_NO_FATAL_FAILURE(entry.GenerateVerifyReport(kInactiveUsed)); ASSERT_NO_FATAL_FAILURE(s.close()); - - Session s2; - ASSERT_NO_FATAL_FAILURE(s2.open()); - ASSERT_NO_FATAL_FAILURE(InstallTestRSAKey(&s2)); - // Offile license can not be reused if it has been deactivated. - ASSERT_NO_FATAL_FAILURE(s2.LoadUsageEntry(s)); - EXPECT_NE( - OEMCrypto_SUCCESS, - OEMCrypto_LoadKeys(s2.session_id(), s.message_ptr(), s.message_size(), - s.signature().data(), s.signature().size(), - s.enc_mac_keys_iv_substr(), s.enc_mac_keys_substr(), - s.num_keys(), s.key_array(), s.pst_substr(), - GetSubstring(), OEMCrypto_ContentLicense)); - s2.close(); - // But we can still generate a report. - Session s3; - ASSERT_NO_FATAL_FAILURE(s3.open()); - ASSERT_NO_FATAL_FAILURE(s3.LoadUsageEntry(s)); - ASSERT_NO_FATAL_FAILURE(s3.UpdateUsageEntry(&encrypted_usage_header_)); - ASSERT_NO_FATAL_FAILURE(s3.GenerateReport(pst, OEMCrypto_SUCCESS, &s)); - EXPECT_EQ(kInactiveUsed, s3.pst_report().status()); } -INSTANTIATE_TEST_CASE_P(OEMCryptoTestUsageTables, - OEMCryptoUsageTableTestWithMAC, - Values(true, false)); // With and without new_mac_keys. +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