981 lines
44 KiB
C++
981 lines
44 KiB
C++
// Copyright 2023 Google LLC. All Rights Reserved. This file and proprietary
|
|
// source code may only be used and distributed under the Widevine
|
|
// License Agreement.
|
|
//
|
|
|
|
#include "oemcrypto_license_test.h"
|
|
|
|
#include <string>
|
|
|
|
#include "platform.h"
|
|
#include "test_sleep.h"
|
|
|
|
using ::testing::Range;
|
|
|
|
namespace wvoec {
|
|
|
|
/// @addtogroup license
|
|
/// @{
|
|
|
|
// Function to test APIs that expect a buffer length as input
|
|
// by passing huge buffer lengths up to end_buffer_length and test that the API
|
|
// doesn't crash.
|
|
void TestHugeLengthDoesNotCrashAPI(oemcrypto_function f,
|
|
size_t start_buffer_length,
|
|
size_t end_buffer_length,
|
|
bool check_status) {
|
|
OEMCryptoResult sts = OEMCrypto_SUCCESS;
|
|
for (size_t buffer_length = start_buffer_length;
|
|
buffer_length < end_buffer_length &&
|
|
(sts == OEMCrypto_SUCCESS || sts == OEMCrypto_ERROR_SHORT_BUFFER ||
|
|
!check_status);
|
|
buffer_length *= 2) {
|
|
sts = f(buffer_length);
|
|
if (check_status && sts != OEMCrypto_SUCCESS &&
|
|
sts != OEMCrypto_ERROR_SHORT_BUFFER) {
|
|
LOGI("Test exits huge buffer loop for length:%zu, status:%d",
|
|
buffer_length, sts);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Function to test APIs that expect a buffer length as input
|
|
// by passing huge buffer lengths up to kHugeInputBufferLength and test that
|
|
// the API doesn't crash.
|
|
void TestHugeLengthDoesNotCrashAPI(oemcrypto_function f, bool check_status) {
|
|
TestHugeLengthDoesNotCrashAPI(f, 1, kHugeInputBufferLength, check_status);
|
|
}
|
|
|
|
// This test verifies that OEMCrypto can load the total number of keys required
|
|
// for the reported resource level.
|
|
void TestMaxKeys(SessionUtil* util, size_t num_keys_per_session) {
|
|
const size_t max_total_keys = GetResourceValue(kMaxTotalKeys);
|
|
ASSERT_LE(num_keys_per_session, kMaxNumKeys) << "Update test constants.";
|
|
std::vector<std::unique_ptr<Session>> sessions;
|
|
std::vector<std::unique_ptr<LicenseRoundTrip>> licenses;
|
|
size_t total_keys = 0;
|
|
for (size_t i = 0; total_keys < max_total_keys; i++) {
|
|
sessions.push_back(std::unique_ptr<Session>(new Session()));
|
|
licenses.push_back(std::unique_ptr<LicenseRoundTrip>(
|
|
new LicenseRoundTrip(sessions[i].get())));
|
|
const size_t num_keys =
|
|
std::min(max_total_keys - total_keys, num_keys_per_session);
|
|
licenses[i]->set_num_keys(static_cast<uint32_t>(num_keys));
|
|
total_keys += num_keys;
|
|
ASSERT_NO_FATAL_FAILURE(sessions[i]->open());
|
|
ASSERT_NO_FATAL_FAILURE(util->InstallTestDrmKey(sessions[i].get()));
|
|
ASSERT_NO_FATAL_FAILURE(licenses[i]->SignAndVerifyRequest());
|
|
}
|
|
for (size_t i = 0; i < licenses.size(); i++) {
|
|
ASSERT_NO_FATAL_FAILURE(licenses[i]->CreateDefaultResponse());
|
|
ASSERT_NO_FATAL_FAILURE(licenses[i]->EncryptAndSignResponse());
|
|
ASSERT_EQ(OEMCrypto_SUCCESS, licenses[i]->LoadResponse());
|
|
}
|
|
constexpr bool kSelectKeyFirst = true;
|
|
for (size_t i = 0; i < licenses.size(); i++) {
|
|
for (size_t key_index = 0; key_index < licenses[i]->num_keys();
|
|
key_index++) {
|
|
ASSERT_NO_FATAL_FAILURE(sessions[i]->TestDecryptCTR(
|
|
kSelectKeyFirst, OEMCrypto_SUCCESS, key_index));
|
|
}
|
|
}
|
|
// Second call to decrypt for each session.
|
|
for (size_t i = 0; i < licenses.size(); i++) {
|
|
for (size_t key_index = 0; key_index < licenses[i]->num_keys();
|
|
key_index++) {
|
|
ASSERT_NO_FATAL_FAILURE(sessions[i]->TestDecryptCTR(
|
|
kSelectKeyFirst, OEMCrypto_SUCCESS, key_index));
|
|
}
|
|
}
|
|
}
|
|
|
|
TEST_F(OEMCryptoSessionTests,
|
|
OEMCryptoMemoryPrepareLicenseRequestForHugeRequestMessageLength) {
|
|
TestPrepareLicenseRequestForHugeBufferLengths(
|
|
[](size_t message_size, LicenseRoundTrip* license_messages) {
|
|
license_messages->set_message_size(message_size);
|
|
},
|
|
kCheckStatus);
|
|
}
|
|
|
|
TEST_F(OEMCryptoSessionTests,
|
|
OEMCryptoMemoryPrepareLicenseRequestForHugeCoreMessageLength) {
|
|
TestPrepareLicenseRequestForHugeBufferLengths(
|
|
[](size_t core_message_size, LicenseRoundTrip* license_messages) {
|
|
license_messages->set_core_message_size(core_message_size);
|
|
},
|
|
kCheckStatus);
|
|
}
|
|
|
|
TEST_F(OEMCryptoSessionTests,
|
|
OEMCryptoMemoryPrepareLicenseRequestForHugeSignatureLength) {
|
|
// There is a limit of signature length that gets validated. Hence not
|
|
// checking status as we would like to test it for all possible signature
|
|
// lengths.
|
|
TestPrepareLicenseRequestForHugeBufferLengths(
|
|
[](size_t length, LicenseRoundTrip* license_messages) {
|
|
license_messages->set_request_signature_size(length);
|
|
},
|
|
!kCheckStatus);
|
|
}
|
|
|
|
// Verify that a license may be signed.
|
|
TEST_P(OEMCryptoLicenseTest, SignLicenseRequest) {
|
|
ASSERT_NO_FATAL_FAILURE(license_messages_.SignAndVerifyRequest());
|
|
}
|
|
|
|
// Verify that a large license request may be signed.
|
|
TEST_P(OEMCryptoLicenseTest, SignLargeLicenseRequest) {
|
|
const size_t max_size = GetResourceValue(kLargeMessageSize);
|
|
license_messages_.set_message_size(max_size);
|
|
ASSERT_NO_FATAL_FAILURE(license_messages_.SignAndVerifyRequest());
|
|
}
|
|
|
|
// Verify that a license may be loaded without a nonce.
|
|
TEST_P(OEMCryptoLicenseTest, LoadKeyNoNonce) {
|
|
ASSERT_NO_FATAL_FAILURE(license_messages_.SignAndVerifyRequest());
|
|
license_messages_.set_control(0);
|
|
ASSERT_NO_FATAL_FAILURE(license_messages_.CreateDefaultResponse());
|
|
ASSERT_NO_FATAL_FAILURE(license_messages_.EncryptAndSignResponse());
|
|
ASSERT_EQ(OEMCrypto_SUCCESS, license_messages_.LoadResponse());
|
|
ASSERT_NO_FATAL_FAILURE(session_.TestDecryptCTR(true, OEMCrypto_SUCCESS));
|
|
}
|
|
|
|
// Verify that a preloaded license may be loaded without first signing the
|
|
// request. This test is important for the preloaded licenses used by ATSC and
|
|
// CAS.
|
|
TEST_P(OEMCryptoLicenseTest, LoadKeyWithNoRequest) {
|
|
if (license_api_version_ > global_features.api_version) {
|
|
// We should not attempt to preload a license with an API higher than that
|
|
// of OEMCrypto.
|
|
license_api_version_ = global_features.api_version;
|
|
license_messages_.set_api_version(license_api_version_);
|
|
}
|
|
license_messages_.set_control(0);
|
|
// Notice that we do not call SignAndVerifyRequest -- we do not need a request
|
|
// in order to generate a response for a preloaded license.
|
|
// The test code uses the core request to create the core response.
|
|
license_messages_.core_request().api_major_version =
|
|
global_features.api_version;
|
|
license_messages_.core_request().api_minor_version = 0;
|
|
ASSERT_NO_FATAL_FAILURE(license_messages_.CreateDefaultResponse());
|
|
ASSERT_NO_FATAL_FAILURE(license_messages_.EncryptAndSignResponse());
|
|
|
|
// Load license in a different session, which did not create the request.
|
|
Session session2;
|
|
ASSERT_NO_FATAL_FAILURE(session2.open());
|
|
ASSERT_NO_FATAL_FAILURE(InstallTestDrmKey(&session2));
|
|
ASSERT_NO_FATAL_FAILURE(session2.GenerateDerivedKeysFromSessionKey());
|
|
ASSERT_EQ(OEMCrypto_SUCCESS, license_messages_.LoadResponse(&session2));
|
|
ASSERT_NO_FATAL_FAILURE(session2.TestDecryptCTR(true, OEMCrypto_SUCCESS));
|
|
}
|
|
|
|
// Verify that a license may be reloaded without a nonce, but with a nonzero
|
|
// rental duration. In order to start the rental clock, we sign a placeholder
|
|
// license instead.
|
|
TEST_P(OEMCryptoLicenseTest, LoadKeyWithNoRequestRentalDuration) {
|
|
ASSERT_NO_FATAL_FAILURE(license_messages_.SignAndVerifyRequest());
|
|
license_messages_.set_control(0);
|
|
ASSERT_NO_FATAL_FAILURE(license_messages_.CreateDefaultResponse());
|
|
// It is not recommended for a license without a nonce to have a nonzero
|
|
// rental duration. But there are content providers that have licenses with
|
|
// this policy.
|
|
license_messages_.core_response().timer_limits.rental_duration_seconds =
|
|
kDuration;
|
|
ASSERT_NO_FATAL_FAILURE(license_messages_.EncryptAndSignResponse());
|
|
|
|
// Load license in a different session, which did not create the request.
|
|
Session session2;
|
|
ASSERT_NO_FATAL_FAILURE(session2.open());
|
|
ASSERT_NO_FATAL_FAILURE(InstallTestDrmKey(&session2));
|
|
// However, in order to start the rental clock, we have to sign something. So
|
|
// we will sign a placeholder license request.
|
|
LicenseRoundTrip dummy_license(&session2);
|
|
ASSERT_NO_FATAL_FAILURE(dummy_license.SignAndVerifyRequest());
|
|
ASSERT_NO_FATAL_FAILURE(session2.GenerateDerivedKeysFromSessionKey());
|
|
ASSERT_EQ(OEMCrypto_SUCCESS, license_messages_.LoadResponse(&session2));
|
|
ASSERT_NO_FATAL_FAILURE(session2.TestDecryptCTR(true, OEMCrypto_SUCCESS));
|
|
}
|
|
|
|
// Verify that a license may be loaded with a nonce.
|
|
TEST_P(OEMCryptoLicenseTest, LoadKeyWithNonce) {
|
|
ASSERT_NO_FATAL_FAILURE(license_messages_.SignAndVerifyRequest());
|
|
ASSERT_NO_FATAL_FAILURE(license_messages_.CreateDefaultResponse());
|
|
ASSERT_NO_FATAL_FAILURE(license_messages_.EncryptAndSignResponse());
|
|
ASSERT_EQ(OEMCrypto_SUCCESS, license_messages_.LoadResponse());
|
|
ASSERT_NO_FATAL_FAILURE(session_.TestDecryptCTR(true, OEMCrypto_SUCCESS));
|
|
}
|
|
|
|
// Verify that a second license may not be loaded in a session.
|
|
TEST_P(OEMCryptoLicenseTest, LoadKeyNoNonceTwiceAPI16) {
|
|
ASSERT_NO_FATAL_FAILURE(license_messages_.SignAndVerifyRequest());
|
|
license_messages_.set_control(0);
|
|
license_messages_.skip_nonce_check();
|
|
ASSERT_NO_FATAL_FAILURE(license_messages_.CreateDefaultResponse());
|
|
ASSERT_NO_FATAL_FAILURE(license_messages_.EncryptAndSignResponse());
|
|
ASSERT_EQ(OEMCrypto_SUCCESS, license_messages_.LoadResponse());
|
|
// A second load, should NOT succeed.
|
|
ASSERT_NE(OEMCrypto_SUCCESS, license_messages_.LoadResponse());
|
|
}
|
|
|
|
// Verify that a second license may not be loaded in a session.
|
|
TEST_P(OEMCryptoLicenseTest, LoadKeyWithNonceTwiceAPI16) {
|
|
ASSERT_NO_FATAL_FAILURE(license_messages_.SignAndVerifyRequest());
|
|
ASSERT_NO_FATAL_FAILURE(license_messages_.CreateDefaultResponse());
|
|
ASSERT_NO_FATAL_FAILURE(license_messages_.EncryptAndSignResponse());
|
|
ASSERT_EQ(OEMCrypto_SUCCESS, license_messages_.LoadResponse());
|
|
// A second load, should NOT succeed.
|
|
ASSERT_NE(OEMCrypto_SUCCESS, license_messages_.LoadResponse());
|
|
}
|
|
|
|
// This tests load license with an 8k license response.
|
|
TEST_P(OEMCryptoLicenseTest, LoadKeyLargeBuffer) {
|
|
ASSERT_NO_FATAL_FAILURE(license_messages_.SignAndVerifyRequest());
|
|
const size_t max_size = GetResourceValue(kLargeMessageSize);
|
|
license_messages_.set_message_size(max_size);
|
|
ASSERT_NO_FATAL_FAILURE(license_messages_.CreateDefaultResponse());
|
|
ASSERT_NO_FATAL_FAILURE(license_messages_.EncryptAndSignResponse());
|
|
ASSERT_EQ(OEMCrypto_SUCCESS, license_messages_.LoadResponse());
|
|
}
|
|
|
|
//---------------------------------------------------------------------------//
|
|
//---------------------------------------------------------------------------//
|
|
// Each of the following LoadKeyWithBadRange_* tests is similar. They verify
|
|
// that OEMCrypto_LoadLicense checks the range of all the pointers. It should
|
|
// reject a message if the pointer does not point into the message buffer.
|
|
//---------------------------------------------------------------------------//
|
|
//---------------------------------------------------------------------------//
|
|
TEST_P(OEMCryptoLicenseTest, LoadKeyWithBadRange_enc_mac_keys) {
|
|
ASSERT_NO_FATAL_FAILURE(license_messages_.SignAndVerifyRequest());
|
|
ASSERT_NO_FATAL_FAILURE(license_messages_.CreateDefaultResponse());
|
|
// See the comment in LicenseRoundTrip::LoadResponse for why we increment by
|
|
// the message size.
|
|
license_messages_.core_response().enc_mac_keys.offset +=
|
|
sizeof(license_messages_.response_data());
|
|
ASSERT_NO_FATAL_FAILURE(license_messages_.EncryptAndSignResponse());
|
|
ASSERT_NE(OEMCrypto_SUCCESS, license_messages_.LoadResponse());
|
|
}
|
|
|
|
TEST_P(OEMCryptoLicenseTest, LoadKeyWithBadRange_enc_mac_keys_iv) {
|
|
ASSERT_NO_FATAL_FAILURE(license_messages_.SignAndVerifyRequest());
|
|
ASSERT_NO_FATAL_FAILURE(license_messages_.CreateDefaultResponse());
|
|
// See the comment in LicenseRoundTrip::LoadResponse for why we increment by
|
|
// the message size.
|
|
license_messages_.core_response().enc_mac_keys_iv.offset +=
|
|
sizeof(license_messages_.response_data());
|
|
ASSERT_NO_FATAL_FAILURE(license_messages_.EncryptAndSignResponse());
|
|
ASSERT_NE(OEMCrypto_SUCCESS, license_messages_.LoadResponse());
|
|
}
|
|
|
|
TEST_P(OEMCryptoLicenseTest, LoadKeyWithBadRange_key_id) {
|
|
ASSERT_NO_FATAL_FAILURE(license_messages_.SignAndVerifyRequest());
|
|
ASSERT_NO_FATAL_FAILURE(license_messages_.CreateDefaultResponse());
|
|
// See the comment in LicenseRoundTrip::LoadResponse for why we increment by
|
|
// the message size.
|
|
license_messages_.core_response().key_array[0].key_id.offset +=
|
|
sizeof(license_messages_.response_data());
|
|
ASSERT_NO_FATAL_FAILURE(license_messages_.EncryptAndSignResponse());
|
|
ASSERT_NE(OEMCrypto_SUCCESS, license_messages_.LoadResponse());
|
|
}
|
|
|
|
TEST_P(OEMCryptoLicenseTest, LoadKeyWithBadRange_key_data) {
|
|
ASSERT_NO_FATAL_FAILURE(license_messages_.SignAndVerifyRequest());
|
|
ASSERT_NO_FATAL_FAILURE(license_messages_.CreateDefaultResponse());
|
|
// See the comment in LicenseRoundTrip::LoadResponse for why we increment by
|
|
// the message size.
|
|
license_messages_.core_response().key_array[1].key_data.offset +=
|
|
sizeof(license_messages_.response_data());
|
|
ASSERT_NO_FATAL_FAILURE(license_messages_.EncryptAndSignResponse());
|
|
ASSERT_NE(OEMCrypto_SUCCESS, license_messages_.LoadResponse());
|
|
}
|
|
|
|
TEST_P(OEMCryptoLicenseTest, LoadKeyWithBadRange_key_data_iv) {
|
|
ASSERT_NO_FATAL_FAILURE(license_messages_.SignAndVerifyRequest());
|
|
ASSERT_NO_FATAL_FAILURE(license_messages_.CreateDefaultResponse());
|
|
// See the comment in LicenseRoundTrip::LoadResponse for why we increment by
|
|
// the message size.
|
|
license_messages_.core_response().key_array[1].key_data_iv.offset +=
|
|
sizeof(license_messages_.response_data());
|
|
ASSERT_NO_FATAL_FAILURE(license_messages_.EncryptAndSignResponse());
|
|
ASSERT_NE(OEMCrypto_SUCCESS, license_messages_.LoadResponse());
|
|
}
|
|
|
|
TEST_P(OEMCryptoLicenseTest, LoadKeyWithBadRange_key_control) {
|
|
ASSERT_NO_FATAL_FAILURE(license_messages_.SignAndVerifyRequest());
|
|
ASSERT_NO_FATAL_FAILURE(license_messages_.CreateDefaultResponse());
|
|
// See the comment in LicenseRoundTrip::LoadResponse for why we increment by
|
|
// the message size.
|
|
license_messages_.core_response().key_array[2].key_control.offset +=
|
|
sizeof(license_messages_.response_data());
|
|
ASSERT_NO_FATAL_FAILURE(license_messages_.EncryptAndSignResponse());
|
|
ASSERT_NE(OEMCrypto_SUCCESS, license_messages_.LoadResponse());
|
|
}
|
|
|
|
TEST_P(OEMCryptoLicenseTest, LoadKeyWithBadRange_key_control_iv) {
|
|
ASSERT_NO_FATAL_FAILURE(license_messages_.SignAndVerifyRequest());
|
|
ASSERT_NO_FATAL_FAILURE(license_messages_.CreateDefaultResponse());
|
|
// See the comment in LicenseRoundTrip::LoadResponse for why we increment by
|
|
// the message size.
|
|
license_messages_.core_response().key_array[2].key_control_iv.offset +=
|
|
sizeof(license_messages_.response_data());
|
|
ASSERT_NO_FATAL_FAILURE(license_messages_.EncryptAndSignResponse());
|
|
ASSERT_NE(OEMCrypto_SUCCESS, license_messages_.LoadResponse());
|
|
}
|
|
|
|
TEST_P(OEMCryptoLicenseTest, LoadKeyWithBadRange_pst) {
|
|
license_messages_.set_control(wvoec::kControlNonceOrEntry);
|
|
license_messages_.set_pst("my_pst");
|
|
ASSERT_NO_FATAL_FAILURE(license_messages_.SignAndVerifyRequest());
|
|
ASSERT_NO_FATAL_FAILURE(license_messages_.CreateDefaultResponse());
|
|
// See the comment in LicenseRoundTrip::LoadResponse for why we increment by
|
|
// the message size.
|
|
license_messages_.core_response().pst.offset +=
|
|
sizeof(license_messages_.response_data());
|
|
ASSERT_NO_FATAL_FAILURE(license_messages_.EncryptAndSignResponse());
|
|
// If we have a pst, then we need a usage entry.
|
|
ASSERT_NO_FATAL_FAILURE(session_.CreateNewUsageEntry());
|
|
ASSERT_NE(OEMCrypto_SUCCESS, license_messages_.LoadResponse());
|
|
}
|
|
//---------------------------------------------------------------------------//
|
|
//---------------------------------------------------------------------------//
|
|
|
|
// Test that LoadKeys fails when a key is loaded with no key control block.
|
|
TEST_P(OEMCryptoLicenseTest, LoadKeyWithNullKeyControl) {
|
|
ASSERT_NO_FATAL_FAILURE(license_messages_.SignAndVerifyRequest());
|
|
ASSERT_NO_FATAL_FAILURE(license_messages_.CreateDefaultResponse());
|
|
license_messages_.core_response().key_array[2].key_control.offset = 0;
|
|
license_messages_.core_response().key_array[2].key_control.length = 0;
|
|
ASSERT_NO_FATAL_FAILURE(license_messages_.EncryptAndSignResponse());
|
|
ASSERT_NE(OEMCrypto_SUCCESS, license_messages_.LoadResponse());
|
|
}
|
|
|
|
// Verify that LoadKeys fails when a key's nonce is wrong.
|
|
TEST_P(OEMCryptoLicenseTest, LoadKeyWithBadNonce) {
|
|
ASSERT_NO_FATAL_FAILURE(license_messages_.SignAndVerifyRequest());
|
|
ASSERT_NO_FATAL_FAILURE(license_messages_.CreateDefaultResponse());
|
|
for (unsigned int i = 0; i < license_messages_.num_keys(); i++)
|
|
license_messages_.response_data().keys[i].control.nonce ^= 42;
|
|
ASSERT_NO_FATAL_FAILURE(license_messages_.EncryptAndSignResponse());
|
|
ASSERT_EQ(OEMCrypto_ERROR_INVALID_NONCE, license_messages_.LoadResponse());
|
|
}
|
|
|
|
// Verify that LoadKeys fails when the core message's nonce is wrong.
|
|
TEST_F(OEMCryptoLicenseTestAPI16, LoadKeyWithBadNonce2) {
|
|
ASSERT_NO_FATAL_FAILURE(license_messages_.SignAndVerifyRequest());
|
|
ASSERT_NO_FATAL_FAILURE(license_messages_.CreateDefaultResponse());
|
|
license_messages_.core_request().nonce ^= 42;
|
|
ASSERT_NO_FATAL_FAILURE(license_messages_.EncryptAndSignResponse());
|
|
ASSERT_EQ(OEMCrypto_ERROR_INVALID_NONCE, license_messages_.LoadResponse());
|
|
}
|
|
|
|
// Verify that LoadKeys fails when the core message's session is wrong.
|
|
TEST_F(OEMCryptoLicenseTestAPI16, LoadKeyWithBadNonce3) {
|
|
ASSERT_NO_FATAL_FAILURE(license_messages_.SignAndVerifyRequest());
|
|
ASSERT_NO_FATAL_FAILURE(license_messages_.CreateDefaultResponse());
|
|
license_messages_.core_request().session_id++;
|
|
ASSERT_NO_FATAL_FAILURE(license_messages_.EncryptAndSignResponse());
|
|
ASSERT_EQ(OEMCrypto_ERROR_INVALID_NONCE, license_messages_.LoadResponse());
|
|
}
|
|
|
|
// Verify that LoadKeys fails when an attempt is made to use a nonce twice.
|
|
TEST_P(OEMCryptoLicenseTest, LoadKeyWithRepeatNonce) {
|
|
ASSERT_NO_FATAL_FAILURE(license_messages_.SignAndVerifyRequest());
|
|
const uint32_t nonce = session_.nonce();
|
|
ASSERT_NO_FATAL_FAILURE(license_messages_.CreateDefaultResponse());
|
|
ASSERT_NO_FATAL_FAILURE(license_messages_.EncryptAndSignResponse());
|
|
// This is the first attempt. It should succeed.
|
|
ASSERT_EQ(OEMCrypto_SUCCESS, license_messages_.LoadResponse());
|
|
|
|
// Now, open a new session and try to load a license with the same nonce.
|
|
session_.close();
|
|
ASSERT_NO_FATAL_FAILURE(session_.open());
|
|
ASSERT_NO_FATAL_FAILURE(InstallTestDrmKey(&session_));
|
|
license_messages_.skip_nonce_check();
|
|
ASSERT_NO_FATAL_FAILURE(license_messages_.SignAndVerifyRequest());
|
|
ASSERT_NO_FATAL_FAILURE(license_messages_.CreateDefaultResponse());
|
|
// Repeat the nonce.
|
|
license_messages_.core_request().nonce = nonce;
|
|
for (unsigned int i = 0; i < license_messages_.num_keys(); i++)
|
|
license_messages_.response_data().keys[i].control.nonce = htonl(nonce);
|
|
ASSERT_NO_FATAL_FAILURE(license_messages_.EncryptAndSignResponse());
|
|
ASSERT_EQ(OEMCrypto_ERROR_INVALID_NONCE, license_messages_.LoadResponse());
|
|
}
|
|
|
|
// This tests that a nonce cannot be used in new session. This is similar to
|
|
// the previous test, but does not use the nonce in the first session. The nonce
|
|
// should be tied to a session, so generating a nonce in the first session and
|
|
// then using it in the second session should fail.
|
|
TEST_P(OEMCryptoLicenseTest, LoadKeyNonceReopenSession) {
|
|
ASSERT_NO_FATAL_FAILURE(session_.GenerateNonce());
|
|
uint32_t nonce = session_.nonce();
|
|
// Do not use the nonce now. Close session and use it after re-opening.
|
|
ASSERT_NO_FATAL_FAILURE(session_.close());
|
|
|
|
// Actually, this isn't the same session. OEMCrypto opens a new session, but
|
|
// we are guarding against the possibility that it re-uses the session data
|
|
// and might not clear out the nonce correctly.
|
|
ASSERT_NO_FATAL_FAILURE(session_.open());
|
|
ASSERT_NO_FATAL_FAILURE(InstallTestDrmKey(&session_));
|
|
license_messages_.skip_nonce_check();
|
|
ASSERT_NO_FATAL_FAILURE(license_messages_.SignAndVerifyRequest());
|
|
ASSERT_NO_FATAL_FAILURE(license_messages_.CreateDefaultResponse());
|
|
license_messages_.core_request().nonce = nonce;
|
|
for (unsigned int i = 0; i < license_messages_.num_keys(); i++)
|
|
license_messages_.response_data().keys[i].control.nonce = htonl(nonce);
|
|
ASSERT_NO_FATAL_FAILURE(license_messages_.EncryptAndSignResponse());
|
|
ASSERT_EQ(OEMCrypto_ERROR_INVALID_NONCE, license_messages_.LoadResponse());
|
|
}
|
|
|
|
// This tests that a nonce cannot be used in wrong session. This is similar to
|
|
// the previous test, except we do not close session 1 before we open session 2.
|
|
TEST_P(OEMCryptoLicenseTest, LoadKeyNonceWrongSession) {
|
|
// First, open a session and generate a nonce. We don't use the nonce in this
|
|
// session.
|
|
Session s2;
|
|
ASSERT_NO_FATAL_FAILURE(s2.open());
|
|
ASSERT_NO_FATAL_FAILURE(InstallTestDrmKey(&s2));
|
|
ASSERT_NO_FATAL_FAILURE(s2.GenerateNonce());
|
|
uint32_t nonce = s2.nonce();
|
|
|
|
// Do not use the nonce. Also, leave the session open. We want to make sure
|
|
// that session_ and s2 do NOT share a nonce. This is different from
|
|
// the LoadKeyNonceReopenSession in that we do not close s1.
|
|
|
|
ASSERT_NO_FATAL_FAILURE(license_messages_.SignAndVerifyRequest());
|
|
ASSERT_NO_FATAL_FAILURE(license_messages_.CreateDefaultResponse());
|
|
license_messages_.core_request().nonce = nonce;
|
|
for (unsigned int i = 0; i < license_messages_.num_keys(); i++)
|
|
license_messages_.response_data().keys[i].control.nonce = htonl(nonce);
|
|
ASSERT_NO_FATAL_FAILURE(license_messages_.EncryptAndSignResponse());
|
|
ASSERT_EQ(OEMCrypto_ERROR_INVALID_NONCE, license_messages_.LoadResponse());
|
|
}
|
|
|
|
// LoadKeys should fail if the key control block as a bad verification string.
|
|
TEST_P(OEMCryptoLicenseTest, LoadKeyWithBadVerification) {
|
|
ASSERT_NO_FATAL_FAILURE(license_messages_.SignAndVerifyRequest());
|
|
ASSERT_NO_FATAL_FAILURE(license_messages_.CreateDefaultResponse());
|
|
license_messages_.response_data().keys[1].control.verification[2] = 'Z';
|
|
ASSERT_NO_FATAL_FAILURE(license_messages_.EncryptAndSignResponse());
|
|
ASSERT_NE(OEMCrypto_SUCCESS, license_messages_.LoadResponse());
|
|
}
|
|
|
|
// This test verifies that LoadKeys still works when the message is not aligned
|
|
// in memory on a word (2 or 4 byte) boundary.
|
|
TEST_P(OEMCryptoLicenseTest, LoadKeyUnalignedMessageAPI16) {
|
|
license_messages_.skip_request_hash();
|
|
|
|
ASSERT_NO_FATAL_FAILURE(license_messages_.SignAndVerifyRequest());
|
|
ASSERT_NO_FATAL_FAILURE(license_messages_.CreateDefaultResponse());
|
|
ASSERT_NO_FATAL_FAILURE(license_messages_.EncryptAndSignResponse());
|
|
|
|
std::vector<uint8_t> buffer(1, '0'); // A string of 1 byte long.
|
|
size_t offset = buffer.size();
|
|
ASSERT_EQ(1u, offset);
|
|
// We assume that vectors are allocated on as a small chunk of data that is
|
|
// aligned on a word boundary. I.e. we assume buffer is word aligned. Next,
|
|
// we append the message to buffer after the single padding byte.
|
|
buffer.insert(buffer.end(),
|
|
license_messages_.encrypted_response_buffer().begin(),
|
|
license_messages_.encrypted_response_buffer().end());
|
|
// Thus, buffer[offset] is NOT word aligned.
|
|
const uint8_t* unaligned_message = &buffer[offset];
|
|
const std::vector<uint8_t> context = session_.GetDefaultContext();
|
|
ASSERT_EQ(OEMCrypto_SUCCESS,
|
|
OEMCrypto_LoadLicense(
|
|
session_.session_id(), context.data(), context.size(),
|
|
session_.enc_session_key().data(),
|
|
session_.enc_session_key().size(), unaligned_message,
|
|
license_messages_.encrypted_response_buffer().size(),
|
|
license_messages_.serialized_core_message().size(),
|
|
license_messages_.response_signature().data(),
|
|
license_messages_.response_signature().size()));
|
|
}
|
|
|
|
// Verifies that a session can't reload a license without being closed and
|
|
// reopened.
|
|
TEST_P(OEMCryptoLicenseTest, LoadLicenseAgainFailureAPI16) {
|
|
ASSERT_NO_FATAL_FAILURE(license_messages_.SignAndVerifyRequest());
|
|
ASSERT_NO_FATAL_FAILURE(license_messages_.CreateDefaultResponse());
|
|
ASSERT_NO_FATAL_FAILURE(license_messages_.EncryptAndSignResponse());
|
|
ASSERT_EQ(OEMCrypto_SUCCESS, license_messages_.LoadResponse());
|
|
ASSERT_NE(OEMCrypto_SUCCESS, license_messages_.LoadResponse());
|
|
}
|
|
|
|
TEST_P(OEMCryptoLicenseTest, LoadKeysBadSignatureAPI16) {
|
|
ASSERT_NO_FATAL_FAILURE(license_messages_.SignAndVerifyRequest());
|
|
ASSERT_NO_FATAL_FAILURE(license_messages_.CreateDefaultResponse());
|
|
ASSERT_NO_FATAL_FAILURE(license_messages_.EncryptAndSignResponse());
|
|
license_messages_.response_signature()[0] ^= 42;
|
|
ASSERT_EQ(OEMCrypto_ERROR_SIGNATURE_FAILURE,
|
|
license_messages_.LoadResponse());
|
|
}
|
|
// LoadKeys should fail if we try to load keys with no keys.
|
|
TEST_P(OEMCryptoLicenseTest, LoadKeyNoKeys) {
|
|
ASSERT_NO_FATAL_FAILURE(license_messages_.SignAndVerifyRequest());
|
|
license_messages_.set_control(0);
|
|
license_messages_.set_num_keys(0);
|
|
ASSERT_NO_FATAL_FAILURE(license_messages_.CreateDefaultResponse());
|
|
ASSERT_NO_FATAL_FAILURE(license_messages_.EncryptAndSignResponse());
|
|
ASSERT_NE(OEMCrypto_SUCCESS, license_messages_.LoadResponse());
|
|
}
|
|
|
|
// Like the previous test, except we ask for a nonce first.
|
|
TEST_P(OEMCryptoLicenseTest, LoadKeyNoKeyWithNonce) {
|
|
ASSERT_NO_FATAL_FAILURE(license_messages_.SignAndVerifyRequest());
|
|
license_messages_.set_num_keys(0);
|
|
ASSERT_NO_FATAL_FAILURE(license_messages_.CreateDefaultResponse());
|
|
ASSERT_NO_FATAL_FAILURE(license_messages_.EncryptAndSignResponse());
|
|
ASSERT_NE(OEMCrypto_SUCCESS, license_messages_.LoadResponse());
|
|
}
|
|
/// @}
|
|
|
|
/// @addtogroup security
|
|
/// @{
|
|
|
|
// Following two tests will test huge values for num bytes clear, num bytes
|
|
// encrypted, input data length and clear address, clear address_length.
|
|
TEST_P(OEMCryptoLicenseTest,
|
|
OEMCryptoMemoryDecryptCENCForHugeNumBytesClearAndBuffers) {
|
|
TestDecryptCENCForHugeBufferLengths(
|
|
[](size_t message_size, OEMCrypto_SampleDescription* sample_description) {
|
|
OEMCrypto_SubSampleDescription* sub_samples =
|
|
const_cast<OEMCrypto_SubSampleDescription*>(
|
|
sample_description->subsamples);
|
|
sub_samples[0].num_bytes_clear =
|
|
sub_samples[0].num_bytes_clear + message_size;
|
|
},
|
|
!kCheckStatus);
|
|
}
|
|
|
|
TEST_P(OEMCryptoLicenseTest,
|
|
DecryptCENCForNumBytesClearPlusEncryptedOverflowsSize) {
|
|
LoadLicense();
|
|
vector<uint8_t> key_handle;
|
|
GetKeyHandleIntoVector(session_.session_id(),
|
|
session_.license().keys[0].key_id,
|
|
session_.license().keys[0].key_id_length,
|
|
OEMCrypto_CipherMode_CENC, key_handle);
|
|
|
|
size_t input_buffer_size = 1;
|
|
vector<uint8_t> in_buffer(input_buffer_size);
|
|
vector<uint8_t> out_buffer(in_buffer.size());
|
|
|
|
OEMCrypto_SampleDescription sample_description;
|
|
OEMCrypto_SubSampleDescription subsample_description;
|
|
GenerateSimpleSampleDescription(in_buffer, out_buffer, &sample_description,
|
|
&subsample_description);
|
|
|
|
OEMCrypto_SubSampleDescription* sub_samples =
|
|
const_cast<OEMCrypto_SubSampleDescription*>(
|
|
sample_description.subsamples);
|
|
// If Decrypt cenc API does not check for overflow on clear + encrypted
|
|
// addition operation. This will result in 1 which will match with input data
|
|
// length, which causes validation to pass.
|
|
sub_samples[0].num_bytes_clear = 2;
|
|
sub_samples[0].num_bytes_encrypted = ~0;
|
|
|
|
// Create the pattern description (always 0,0 for CTR)
|
|
OEMCrypto_CENCEncryptPatternDesc pattern = {0, 0};
|
|
// Try to decrypt the data
|
|
ASSERT_NE(OEMCrypto_SUCCESS,
|
|
OEMCrypto_DecryptCENC(key_handle.data(), key_handle.size(),
|
|
&sample_description, 1, &pattern));
|
|
}
|
|
|
|
TEST_P(OEMCryptoLicenseTest,
|
|
OEMCryptoMemoryDecryptCENCForHugeNumBytesEncryptedAndBuffers) {
|
|
TestDecryptCENCForHugeBufferLengths(
|
|
[](size_t message_size, OEMCrypto_SampleDescription* sample_description) {
|
|
OEMCrypto_SubSampleDescription* sub_samples =
|
|
const_cast<OEMCrypto_SubSampleDescription*>(
|
|
sample_description->subsamples);
|
|
sub_samples[0].num_bytes_encrypted =
|
|
sub_samples[0].num_bytes_encrypted + message_size;
|
|
},
|
|
!kCheckStatus);
|
|
}
|
|
|
|
TEST_P(OEMCryptoLicenseTest,
|
|
OEMCryptoMemoryDecryptCENCForHugeSecureHandleLength) {
|
|
TestDecryptCENCForHugeBufferLengths(
|
|
[](size_t message_size, OEMCrypto_SampleDescription* sample_description) {
|
|
OEMCrypto_SubSampleDescription* sub_samples =
|
|
const_cast<OEMCrypto_SubSampleDescription*>(
|
|
sample_description->subsamples);
|
|
// TestDecryptCENCForHugeBufferLengths alloctes huge secure handle
|
|
// buffer.
|
|
sample_description->buffers.output_descriptor.type =
|
|
OEMCrypto_BufferType_Secure;
|
|
sub_samples[0].num_bytes_clear =
|
|
sub_samples[0].num_bytes_clear + message_size;
|
|
},
|
|
!kCheckStatus);
|
|
}
|
|
|
|
TEST_P(OEMCryptoLicenseTest,
|
|
OEMCryptoMemoryDecryptCENCForOutOfRangeNumBytesClear) {
|
|
TestDecryptCENCForOutOfRangeOffsetsAndLengths(
|
|
[](OEMCrypto_SampleDescription* sample_description) {
|
|
OEMCrypto_SubSampleDescription* sub_samples =
|
|
const_cast<OEMCrypto_SubSampleDescription*>(
|
|
sample_description->subsamples);
|
|
sub_samples[0].num_bytes_clear = sub_samples[0].num_bytes_clear + 1;
|
|
},
|
|
!kDecryptCENCSecureBuffer);
|
|
}
|
|
|
|
TEST_P(OEMCryptoLicenseTest,
|
|
OEMCryptoMemoryDecryptCENCForOutOfRangeNumBytesEncryptedAPI16) {
|
|
TestDecryptCENCForOutOfRangeOffsetsAndLengths(
|
|
[](OEMCrypto_SampleDescription* sample_description) {
|
|
OEMCrypto_SubSampleDescription* sub_samples =
|
|
const_cast<OEMCrypto_SubSampleDescription*>(
|
|
sample_description->subsamples);
|
|
sub_samples[0].num_bytes_encrypted =
|
|
sub_samples[0].num_bytes_encrypted + 1;
|
|
},
|
|
!kDecryptCENCSecureBuffer);
|
|
}
|
|
|
|
TEST_P(OEMCryptoLicenseTest,
|
|
OEMCryptoMemoryDecryptCENCForOutOfRangeSecureBufferOffset) {
|
|
TestDecryptCENCForOutOfRangeOffsetsAndLengths(
|
|
[](OEMCrypto_SampleDescription* sample_description) {
|
|
sample_description->buffers.output_descriptor.type =
|
|
OEMCrypto_BufferType_Secure;
|
|
sample_description->buffers.output_descriptor.buffer.secure.offset =
|
|
sample_description->buffers.output_descriptor.buffer.secure
|
|
.secure_buffer_length +
|
|
1;
|
|
},
|
|
kDecryptCENCSecureBuffer);
|
|
}
|
|
|
|
// After loading keys, we should be able to query the key control block. If we
|
|
// attempt to query a key that has not been loaded, the error should be
|
|
// NO_CONTENT_KEY.
|
|
TEST_P(OEMCryptoLicenseTest, QueryKeyControl) {
|
|
ASSERT_NO_FATAL_FAILURE(license_messages_.SignAndVerifyRequest());
|
|
ASSERT_NO_FATAL_FAILURE(license_messages_.CreateDefaultResponse());
|
|
ASSERT_NO_FATAL_FAILURE(license_messages_.EncryptAndSignResponse());
|
|
ASSERT_EQ(OEMCrypto_SUCCESS, license_messages_.LoadResponse());
|
|
|
|
// Note: successful cases are tested in VerifyTestKeys.
|
|
KeyControlBlock block;
|
|
size_t size = sizeof(block) - 1;
|
|
OEMCryptoResult sts = OEMCrypto_QueryKeyControl(
|
|
session_.session_id(), license_messages_.response_data().keys[0].key_id,
|
|
license_messages_.response_data().keys[0].key_id_length,
|
|
reinterpret_cast<uint8_t*>(&block), &size);
|
|
if (sts == OEMCrypto_ERROR_NOT_IMPLEMENTED) {
|
|
return;
|
|
}
|
|
ASSERT_EQ(OEMCrypto_ERROR_SHORT_BUFFER, sts);
|
|
const char* key_id = "no_key";
|
|
size = sizeof(block);
|
|
ASSERT_EQ(OEMCrypto_ERROR_NO_CONTENT_KEY,
|
|
OEMCrypto_QueryKeyControl(
|
|
session_.session_id(), reinterpret_cast<const uint8_t*>(key_id),
|
|
strlen(key_id), reinterpret_cast<uint8_t*>(&block), &size));
|
|
}
|
|
|
|
// This case tests against the issue where certain 16.4.x SDK versions return a
|
|
// clear key control block (KCB) in the license response. An OEMCrypto v17.1+
|
|
// implementation should be able to handle the clear KCB in the 16.4.x response
|
|
// and load the license correctly.
|
|
TEST_F(OEMCryptoSessionTests, ClearKcbAPI17) {
|
|
if (wvoec::global_features.api_version < 17) {
|
|
GTEST_SKIP() << "Test for versions 17 and up only.";
|
|
}
|
|
Session s;
|
|
ASSERT_NO_FATAL_FAILURE(s.open());
|
|
ASSERT_NO_FATAL_FAILURE(InstallTestDrmKey(&s));
|
|
LicenseRoundTrip license_messages(&s);
|
|
ASSERT_NO_FATAL_FAILURE(license_messages.SignAndVerifyRequest());
|
|
ASSERT_NO_FATAL_FAILURE(license_messages.CreateDefaultResponse());
|
|
// Set odk version in the license response to be 16.4
|
|
oemcrypto_core_message::features::CoreMessageFeatures features = {};
|
|
features.maximum_major_version = 16;
|
|
features.maximum_minor_version = 4;
|
|
constexpr bool kForceClearKcb = true;
|
|
ASSERT_NO_FATAL_FAILURE(
|
|
license_messages.EncryptAndSignResponseWithCoreMessageFeatures(
|
|
features, kForceClearKcb));
|
|
ASSERT_EQ(OEMCrypto_SUCCESS, license_messages.LoadResponse());
|
|
|
|
KeyControlBlock block;
|
|
size_t size = sizeof(block);
|
|
OEMCryptoResult sts = OEMCrypto_QueryKeyControl(
|
|
s.session_id(), license_messages.response_data().keys[0].key_id,
|
|
license_messages.response_data().keys[0].key_id_length,
|
|
reinterpret_cast<uint8_t*>(&block), &size);
|
|
if (sts == OEMCrypto_ERROR_NOT_IMPLEMENTED) {
|
|
return;
|
|
}
|
|
ASSERT_EQ(OEMCrypto_SUCCESS, sts);
|
|
}
|
|
|
|
// If the device says it supports anti-rollback in the hardware, then it should
|
|
// accept a key control block with the anti-rollback hardware bit set.
|
|
// Otherwise, it should reject that key control block.
|
|
TEST_P(OEMCryptoLicenseTest, AntiRollbackHardwareRequired) {
|
|
ASSERT_NO_FATAL_FAILURE(license_messages_.SignAndVerifyRequest());
|
|
license_messages_.set_control(wvoec::kControlRequireAntiRollbackHardware);
|
|
ASSERT_NO_FATAL_FAILURE(license_messages_.CreateDefaultResponse());
|
|
ASSERT_NO_FATAL_FAILURE(license_messages_.EncryptAndSignResponse());
|
|
OEMCryptoResult sts = license_messages_.LoadResponse();
|
|
if (OEMCrypto_IsAntiRollbackHwPresent()) {
|
|
ASSERT_EQ(OEMCrypto_SUCCESS, sts);
|
|
} else {
|
|
ASSERT_EQ(OEMCrypto_ERROR_UNKNOWN_FAILURE, sts);
|
|
}
|
|
}
|
|
|
|
// This test verifies that OEMCrypto can load the number of keys required for
|
|
// the reported resource level.
|
|
TEST_P(OEMCryptoLicenseTest, MinimumKeys) {
|
|
const size_t num_keys = GetResourceValue(kMaxKeysPerSession);
|
|
ASSERT_LE(num_keys, kMaxNumKeys) << "Test constants need updating.";
|
|
license_messages_.set_num_keys(static_cast<uint32_t>(num_keys));
|
|
ASSERT_NO_FATAL_FAILURE(license_messages_.SignAndVerifyRequest());
|
|
ASSERT_NO_FATAL_FAILURE(license_messages_.CreateDefaultResponse());
|
|
ASSERT_NO_FATAL_FAILURE(license_messages_.EncryptAndSignResponse());
|
|
ASSERT_EQ(OEMCrypto_SUCCESS, license_messages_.LoadResponse());
|
|
|
|
constexpr bool kSelectKeyFirst = true;
|
|
for (size_t key_index = 0; key_index < num_keys; key_index++) {
|
|
ASSERT_NO_FATAL_FAILURE(
|
|
session_.TestDecryptCTR(kSelectKeyFirst, OEMCrypto_SUCCESS, key_index));
|
|
}
|
|
}
|
|
|
|
// This test verifies that OEMCrypto can load the total number of keys required
|
|
// for the reported resource level. This maximizes keys per session.
|
|
TEST_P(OEMCryptoLicenseTest, MaxTotalKeysPerSession) {
|
|
const size_t max_num_keys = GetResourceValue(kMaxKeysPerSession);
|
|
TestMaxKeys(this, max_num_keys);
|
|
}
|
|
|
|
// This test verifies that OEMCrypto can load the total number of keys required
|
|
// for the reported resource level. This maximizes number of sessions.
|
|
TEST_P(OEMCryptoLicenseTest, MaxTotalKeysManySessions) {
|
|
const size_t max_total_keys = GetResourceValue(kMaxTotalKeys);
|
|
const size_t max_sessions = GetResourceValue(kMaxConcurrentSession);
|
|
const size_t max_num_keys = max_total_keys / max_sessions + 1;
|
|
TestMaxKeys(this, max_num_keys);
|
|
}
|
|
|
|
// This test verifies that the minimum patch level can be required. The device
|
|
// should accept a key control block with the current patch level, and it should
|
|
// reject any key control blocks with a future patch level.
|
|
TEST_F(OEMCryptoSessionTests, CheckMinimumPatchLevel) {
|
|
uint8_t patch_level = OEMCrypto_Security_Patch_Level();
|
|
printf(" Current Patch Level: %u.\n", patch_level);
|
|
RecordWvProperty("security_patch_level", std::to_string(patch_level));
|
|
{
|
|
Session s;
|
|
ASSERT_NO_FATAL_FAILURE(s.open());
|
|
ASSERT_NO_FATAL_FAILURE(InstallTestDrmKey(&s));
|
|
LicenseRoundTrip license_messages(&s);
|
|
license_messages.set_control(patch_level
|
|
<< wvoec::kControlSecurityPatchLevelShift);
|
|
ASSERT_NO_FATAL_FAILURE(license_messages.SignAndVerifyRequest());
|
|
ASSERT_NO_FATAL_FAILURE(license_messages.CreateDefaultResponse());
|
|
ASSERT_NO_FATAL_FAILURE(license_messages.EncryptAndSignResponse());
|
|
EXPECT_EQ(global_features.api_version, license_messages.api_version());
|
|
ASSERT_EQ(OEMCrypto_SUCCESS, license_messages.LoadResponse());
|
|
}
|
|
// Reject any future patch levels.
|
|
if (patch_level < 0x3F) {
|
|
Session s;
|
|
ASSERT_NO_FATAL_FAILURE(s.open());
|
|
ASSERT_NO_FATAL_FAILURE(InstallTestDrmKey(&s));
|
|
LicenseRoundTrip license_messages(&s);
|
|
license_messages.set_control((patch_level + 1)
|
|
<< wvoec::kControlSecurityPatchLevelShift);
|
|
ASSERT_NO_FATAL_FAILURE(license_messages.SignAndVerifyRequest());
|
|
ASSERT_NO_FATAL_FAILURE(license_messages.CreateDefaultResponse());
|
|
ASSERT_NO_FATAL_FAILURE(license_messages.EncryptAndSignResponse());
|
|
ASSERT_EQ(OEMCrypto_ERROR_UNKNOWN_FAILURE, license_messages.LoadResponse());
|
|
}
|
|
// Accept an old patch level.
|
|
if (patch_level > 0) {
|
|
Session s;
|
|
ASSERT_NO_FATAL_FAILURE(s.open());
|
|
ASSERT_NO_FATAL_FAILURE(InstallTestDrmKey(&s));
|
|
LicenseRoundTrip license_messages(&s);
|
|
license_messages.set_control((patch_level - 1)
|
|
<< wvoec::kControlSecurityPatchLevelShift);
|
|
ASSERT_NO_FATAL_FAILURE(license_messages.SignAndVerifyRequest());
|
|
ASSERT_NO_FATAL_FAILURE(license_messages.CreateDefaultResponse());
|
|
ASSERT_NO_FATAL_FAILURE(license_messages.EncryptAndSignResponse());
|
|
ASSERT_EQ(OEMCrypto_SUCCESS, license_messages.LoadResponse());
|
|
}
|
|
}
|
|
|
|
//
|
|
// Load, Refresh Keys Test
|
|
//
|
|
|
|
// Refresh keys should work if the license uses a nonce.
|
|
TEST_P(OEMCryptoRefreshTest, RefreshWithNonce) {
|
|
LoadLicense();
|
|
RenewalRoundTrip renewal_messages(&license_messages_);
|
|
MakeRenewalRequest(&renewal_messages);
|
|
LoadRenewal(&renewal_messages, OEMCrypto_SUCCESS);
|
|
}
|
|
|
|
// Refresh keys should work if the license does not use a nonce.
|
|
TEST_P(OEMCryptoRefreshTest, RefreshNoNonce) {
|
|
license_messages_.set_control(0);
|
|
LoadLicense();
|
|
RenewalRoundTrip renewal_messages(&license_messages_);
|
|
MakeRenewalRequest(&renewal_messages);
|
|
LoadRenewal(&renewal_messages, OEMCrypto_SUCCESS);
|
|
}
|
|
|
|
// Refresh keys should NOT work if a license has not been loaded.
|
|
TEST_P(OEMCryptoRefreshTestAPI16, RefreshNoLicense) {
|
|
Session s;
|
|
s.open();
|
|
constexpr size_t message_size = kMaxCoreMessage + 42;
|
|
std::vector<uint8_t> data(message_size);
|
|
for (size_t i = 0; i < data.size(); i++) data[i] = i & 0xFF;
|
|
size_t gen_signature_length = 0;
|
|
size_t core_message_length = 0;
|
|
OEMCryptoResult sts = OEMCrypto_PrepAndSignRenewalRequest(
|
|
s.session_id(), data.data(), data.size(), &core_message_length, nullptr,
|
|
&gen_signature_length);
|
|
ASSERT_LT(core_message_length, message_size);
|
|
if (sts == OEMCrypto_ERROR_SHORT_BUFFER) {
|
|
vector<uint8_t> gen_signature(gen_signature_length);
|
|
sts = OEMCrypto_PrepAndSignRenewalRequest(
|
|
s.session_id(), data.data(), data.size(), &core_message_length,
|
|
gen_signature.data(), &gen_signature_length);
|
|
}
|
|
ASSERT_NE(OEMCrypto_SUCCESS, sts);
|
|
}
|
|
|
|
// Refresh keys should fail if the nonce is not in the session.
|
|
TEST_P(OEMCryptoRefreshTestAPI16, RefreshBadNonce) {
|
|
LoadLicense();
|
|
RenewalRoundTrip renewal_messages(&license_messages_);
|
|
MakeRenewalRequest(&renewal_messages);
|
|
renewal_messages.core_request().nonce ^= 42;
|
|
LoadRenewal(&renewal_messages, OEMCrypto_ERROR_INVALID_NONCE);
|
|
}
|
|
|
|
// Refresh keys should fail if the session_id does not match the license.
|
|
TEST_P(OEMCryptoRefreshTestAPI16, RefreshBadSessionID) {
|
|
LoadLicense();
|
|
RenewalRoundTrip renewal_messages(&license_messages_);
|
|
MakeRenewalRequest(&renewal_messages);
|
|
renewal_messages.core_request().session_id += 1;
|
|
LoadRenewal(&renewal_messages, OEMCrypto_ERROR_INVALID_NONCE);
|
|
}
|
|
|
|
// Refresh keys should handle the maximum message size.
|
|
TEST_P(OEMCryptoRefreshTest, RefreshLargeBuffer) {
|
|
LoadLicense();
|
|
RenewalRoundTrip renewal_messages(&license_messages_);
|
|
const size_t max_size = GetResourceValue(kLargeMessageSize);
|
|
renewal_messages.set_message_size(max_size);
|
|
MakeRenewalRequest(&renewal_messages);
|
|
LoadRenewal(&renewal_messages, OEMCrypto_SUCCESS);
|
|
}
|
|
|
|
// This situation would occur if an app only uses one key in the license. When
|
|
// that happens, GetKeyHandle would be called before the first decrypt, and then
|
|
// would not need to be called again, even if the license is refreshed.
|
|
TEST_P(OEMCryptoRefreshTest, RefreshWithNoSelectKey) {
|
|
LoadLicense();
|
|
|
|
// Call select key before the refresh. No calls below to TestDecryptCTR with
|
|
// select key set to true.
|
|
ASSERT_NO_FATAL_FAILURE(session_.TestDecryptCTR(true));
|
|
|
|
// This should still be valid key, even if the refresh failed, because this
|
|
// is before the original license duration.
|
|
wvutil::TestSleep::Sleep(kShortSleep);
|
|
ASSERT_NO_FATAL_FAILURE(session_.TestDecryptCTR(false));
|
|
|
|
// This should be after duration of the original license, but before the
|
|
// expiration of the refresh message. This should fail until we have loaded
|
|
// the renewal.
|
|
wvutil::TestSleep::Sleep(kShortSleep + kLongSleep);
|
|
ASSERT_NO_FATAL_FAILURE(
|
|
session_.TestDecryptCTR(false, OEMCrypto_ERROR_KEY_EXPIRED));
|
|
|
|
RenewalRoundTrip renewal_messages(&license_messages_);
|
|
MakeRenewalRequest(&renewal_messages);
|
|
LoadRenewal(&renewal_messages, OEMCrypto_SUCCESS);
|
|
|
|
// After we've loaded the renewal, decrypt should succeed again.
|
|
ASSERT_NO_FATAL_FAILURE(session_.TestDecryptCTR(false));
|
|
}
|
|
|
|
// Test that playback clock is correctly started and that the license can be
|
|
// renewed.
|
|
TEST_P(OEMCryptoRefreshTest, RenewLicenseLoadSuccess) {
|
|
license_messages_.core_response().renewal_delay_base = OEMCrypto_License_Load;
|
|
timer_limits_.rental_duration_seconds = kDuration; // 2 seconds.
|
|
timer_limits_.initial_renewal_duration_seconds = kLongDuration; // 5 seconds.
|
|
// First version to support Renew on Load.
|
|
constexpr uint32_t kFeatureVersion = 18;
|
|
|
|
// Loading the license should start the playback clock.
|
|
LoadLicense();
|
|
// Sleep until just after rental window is over.
|
|
wvutil::TestSleep::Sleep(kDuration + kShortSleep);
|
|
if (license_api_version_ < kFeatureVersion ||
|
|
global_features.api_version < kFeatureVersion) {
|
|
// If the feature is not supported, then we expect failure because the
|
|
// playback clock was not started and we are outside the rental window.
|
|
ASSERT_NO_FATAL_FAILURE(
|
|
session_.TestDecryptCTR(true, OEMCrypto_ERROR_KEY_EXPIRED));
|
|
return;
|
|
} else {
|
|
// If the feature is supported, we expect decrypt to work because we are
|
|
// still within the initial renewal window, and the playback clock should
|
|
// have started.
|
|
ASSERT_NO_FATAL_FAILURE(session_.TestDecryptCTR(true, OEMCrypto_SUCCESS));
|
|
}
|
|
// This is after the initial renewal duration, so we expect failure before
|
|
// loading the renewal.
|
|
wvutil::TestSleep::Sleep(kShortSleep + kLongSleep);
|
|
ASSERT_NO_FATAL_FAILURE(
|
|
session_.TestDecryptCTR(false, OEMCrypto_ERROR_KEY_EXPIRED));
|
|
|
|
RenewalRoundTrip renewal_messages(&license_messages_);
|
|
MakeRenewalRequest(&renewal_messages);
|
|
LoadRenewal(&renewal_messages, OEMCrypto_SUCCESS);
|
|
|
|
// After we've loaded the renewal, decrypt should succeed again.
|
|
ASSERT_NO_FATAL_FAILURE(session_.TestDecryptCTR(false));
|
|
}
|
|
|
|
TEST_P(OEMCryptoRefreshTest, RenewLicenseLoadOutsideRentalDuration) {
|
|
license_messages_.core_response().renewal_delay_base = OEMCrypto_License_Load;
|
|
timer_limits_.rental_duration_seconds = kDuration; // 2 seconds.
|
|
timer_limits_.initial_renewal_duration_seconds = kLongDuration; // 5 seconds.
|
|
|
|
// Sleep until just after rental window is over.
|
|
wvutil::TestSleep::Sleep(kDuration + kShortSleep);
|
|
// Loading the license should start the playback clock.
|
|
LoadLicense();
|
|
// If the license is loaded after the rental duration window, we expect
|
|
// failure.
|
|
ASSERT_NO_FATAL_FAILURE(
|
|
session_.TestDecryptCTR(false, OEMCrypto_ERROR_UNKNOWN_FAILURE));
|
|
}
|
|
|
|
INSTANTIATE_TEST_SUITE_P(TestAll, OEMCryptoRefreshTest,
|
|
Range<uint32_t>(kCurrentAPI - 1, kCurrentAPI + 1));
|
|
|
|
// These tests only work when the license has a core message.
|
|
INSTANTIATE_TEST_SUITE_P(TestAPI16, OEMCryptoRefreshTestAPI16,
|
|
Range<uint32_t>(kCoreMessagesAPI, kCurrentAPI + 1));
|
|
|
|
/// @}
|
|
} // namespace wvoec
|