LoadLicense unit tests.

This commit is contained in:
Fred Gylys-Colwell
2019-12-13 11:14:36 -08:00
parent ff7cc16e4a
commit a18b487a6e
11 changed files with 638 additions and 243 deletions

View File

@@ -208,7 +208,7 @@ typedef struct {
* This struct changed in API version 16.
*/
typedef struct {
const OEMCrypto_SharedMemory* input_data; // source for encrypted data.
OEMCrypto_SharedMemory* input_data; // source for encrypted data.
size_t input_data_length; // length of encrypted data.
OEMCrypto_DestBufferDesc output_descriptor; // destination for clear data.
} OEMCrypto_InputOutputPair;
@@ -270,8 +270,8 @@ typedef struct {
*/
typedef struct {
OEMCrypto_InputOutputPair buffers; // The source and destination buffers.
const uint8_t iv[16]; // The IV for the initial subsample.
const OEMCrypto_SubSampleDescription* subsamples; // subsamples array.
uint8_t iv[16]; // The IV for the initial subsample.
OEMCrypto_SubSampleDescription* subsamples; // subsamples array.
size_t subsamples_length; // the number of subsamples in the sample.
} OEMCrypto_SampleDescription;

View File

@@ -202,20 +202,20 @@ OEMCrypto_HDCP_Capability CryptoEngine::config_maximum_hdcp_capability() {
}
OEMCryptoResult CryptoEngine::SetDestination(
const OEMCrypto_DestBufferDesc* out_description, size_t data_length,
const OEMCrypto_DestBufferDesc& out_description, size_t data_length,
uint8_t subsample_flags) {
size_t max_length = 0;
switch (out_description->type) {
switch (out_description.type) {
case OEMCrypto_BufferType_Clear:
destination_ = out_description->buffer.clear.address;
max_length = out_description->buffer.clear.address_length;
destination_ = out_description.buffer.clear.address;
max_length = out_description.buffer.clear.address_length;
break;
case OEMCrypto_BufferType_Secure:
destination_ =
reinterpret_cast<uint8_t*>(out_description->buffer.secure.handle) +
out_description->buffer.secure.offset;
max_length = out_description->buffer.secure.handle_length -
out_description->buffer.secure.offset;
reinterpret_cast<uint8_t*>(out_description.buffer.secure.handle) +
out_description.buffer.secure.offset;
max_length = out_description.buffer.secure.handle_length -
out_description.buffer.secure.offset;
break;
case OEMCrypto_BufferType_Direct:
// Direct buffer type is only used on some specialized devices where
@@ -233,13 +233,13 @@ OEMCryptoResult CryptoEngine::SetDestination(
return OEMCrypto_ERROR_OUTPUT_TOO_LARGE;
}
if (out_description->type != OEMCrypto_BufferType_Direct &&
if (out_description.type != OEMCrypto_BufferType_Direct &&
max_length < data_length) {
LOGE("[SetDestination(): OEMCrypto_ERROR_SHORT_BUFFER]");
return OEMCrypto_ERROR_SHORT_BUFFER;
}
adjust_destination(out_description, data_length, subsample_flags);
if ((out_description->type != OEMCrypto_BufferType_Direct) &&
if ((out_description.type != OEMCrypto_BufferType_Direct) &&
(destination_ == nullptr)) {
return OEMCrypto_ERROR_INVALID_CONTEXT;
}

View File

@@ -182,8 +182,8 @@ class CryptoEngine {
virtual bool srm_blacklisted_device_attached() { return false; }
// Rate limit for nonce generation. Default to 20 nonce/second.
virtual int nonce_flood_count() { return 20; }
// Rate limit for nonce generation. Default to 200 nonce/second.
virtual int nonce_flood_count() { return 200; }
// Limit for size of usage table. If this is zero, then the
// size is unlimited -- or limited only by memory size.
@@ -193,7 +193,7 @@ class CryptoEngine {
// Set destination pointer based on the output destination description.
OEMCryptoResult SetDestination(
const OEMCrypto_DestBufferDesc* out_description, size_t data_length,
const OEMCrypto_DestBufferDesc& out_description, size_t data_length,
uint8_t subsample_flags);
// The current destination.
@@ -201,12 +201,12 @@ class CryptoEngine {
// Subclasses can adjust the destination -- for use in testing.
virtual void adjust_destination(
const OEMCrypto_DestBufferDesc* out_description, size_t data_length,
const OEMCrypto_DestBufferDesc& out_description, size_t data_length,
uint8_t subsample_flags) {}
// Push destination buffer to output -- used by subclasses for testing.
virtual OEMCryptoResult PushDestination(
const OEMCrypto_DestBufferDesc* out_description,
const OEMCrypto_DestBufferDesc& out_description,
uint8_t subsample_flags) {
return OEMCrypto_SUCCESS;
}

View File

@@ -274,6 +274,34 @@ bool RangeCheck(uint32_t message_length, const OEMCrypto_Substring& substring,
return true;
}
OEMCRYPTO_API OEMCryptoResult OEMCrypto_LoadLicense(OEMCrypto_SESSION session,
const uint8_t* message,
size_t message_length,
size_t core_message_length,
const uint8_t* signature,
size_t signature_length) {
if (crypto_engine == nullptr) {
LOGE("not initialized");
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
}
if (message == nullptr || message_length == 0 || signature == nullptr ||
signature_length == 0) {
LOGE("OEMCrypto_ERROR_INVALID_CONTEXT");
return OEMCrypto_ERROR_INVALID_CONTEXT;
}
if (!crypto_engine->ValidRootOfTrust()) {
LOGE("ERROR_KEYBOX_INVALID");
return OEMCrypto_ERROR_KEYBOX_INVALID;
}
SessionContext* session_ctx = crypto_engine->FindSession(session);
if (session_ctx == nullptr || !session_ctx->isValid()) {
LOGE("ERROR_INVALID_SESSION sid=%d", session);
return OEMCrypto_ERROR_INVALID_SESSION;
}
return session_ctx->LoadLicense(message, message_length, core_message_length,
signature, signature_length);
}
OEMCRYPTO_API OEMCryptoResult OEMCrypto_LoadKeys(
OEMCrypto_SESSION session, const uint8_t* message, size_t message_length,
const uint8_t* signature, size_t signature_length,
@@ -286,12 +314,12 @@ OEMCRYPTO_API OEMCryptoResult OEMCrypto_LoadKeys(
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
}
if (!crypto_engine->ValidRootOfTrust()) {
LOGE("[OEMCrypto_LoadKeys(): ERROR_KEYBOX_INVALID]");
LOGE("ERROR_KEYBOX_INVALID");
return OEMCrypto_ERROR_KEYBOX_INVALID;
}
SessionContext* session_ctx = crypto_engine->FindSession(session);
if (session_ctx == nullptr || !session_ctx->isValid()) {
LOGE("[OEMCrypto_LoadKeys(): ERROR_INVALID_SESSION]");
LOGE("ERROR_INVALID_SESSION sid=%d", session);
return OEMCrypto_ERROR_INVALID_SESSION;
}
if (message == nullptr || message_length == 0 || signature == nullptr ||
@@ -546,7 +574,7 @@ OEMCRYPTO_API OEMCryptoResult OEMCrypto_DecryptCENC_V15(
return OEMCrypto_ERROR_BUFFER_TOO_LARGE;
}
OEMCryptoResult status = crypto_engine->SetDestination(
out_buffer_descriptor, data_length, subsample_flags);
*out_buffer_descriptor, data_length, subsample_flags);
if (status != OEMCrypto_SUCCESS) {
LOGE("[OEMCrypto_DecryptCENC(): destination status: %d]", status);
return status;
@@ -569,7 +597,8 @@ OEMCRYPTO_API OEMCryptoResult OEMCrypto_DecryptCENC_V15(
crypto_engine->destination(), out_buffer_descriptor->type,
subsample_flags);
if (result != OEMCrypto_SUCCESS) return result;
return crypto_engine->PushDestination(out_buffer_descriptor, subsample_flags);
return crypto_engine->PushDestination(*out_buffer_descriptor,
subsample_flags);
}
OEMCRYPTO_API OEMCryptoResult OEMCrypto_CopyBuffer(
@@ -592,12 +621,13 @@ OEMCRYPTO_API OEMCryptoResult OEMCrypto_CopyBuffer(
return OEMCrypto_ERROR_BUFFER_TOO_LARGE;
}
OEMCryptoResult status = crypto_engine->SetDestination(
out_buffer_descriptor, data_length, subsample_flags);
*out_buffer_descriptor, data_length, subsample_flags);
if (status != OEMCrypto_SUCCESS) return status;
if (crypto_engine->destination() != nullptr) {
memmove(crypto_engine->destination(), data_addr, data_length);
}
return crypto_engine->PushDestination(out_buffer_descriptor, subsample_flags);
return crypto_engine->PushDestination(*out_buffer_descriptor,
subsample_flags);
}
OEMCRYPTO_API OEMCryptoResult OEMCrypto_WrapKeyboxOrOEMCert(

View File

@@ -60,6 +60,7 @@ class ContentKeysContext : public SessionContextKeys {
size_t size() override { return session_keys_.size(); }
bool Insert(const KeyId& key_id, const Key& key_data) override;
Key* Find(const KeyId& key_id) override;
Key* FirstKey() override;
void Remove(const KeyId& key_id) override;
void UpdateDuration(const KeyControlBlock& control) override;
@@ -83,6 +84,8 @@ Key* ContentKeysContext::Find(const KeyId& key_id) {
return session_keys_.Find(key_id);
}
Key* ContentKeysContext::FirstKey() { return session_keys_.FirstKey(); }
void ContentKeysContext::Remove(const KeyId& key_id) {
session_keys_.Remove(key_id);
}
@@ -113,6 +116,7 @@ class EntitlementKeysContext : public SessionContextKeys {
size_t size() override { return session_keys_.size(); }
bool Insert(const KeyId& key_id, const Key& key_data) override;
Key* Find(const KeyId& key_id) override;
Key* FirstKey() override;
void Remove(const KeyId& key_id) override;
void UpdateDuration(const KeyControlBlock& control) override;
bool SetContentKey(const KeyId& entitlement_id,
@@ -135,6 +139,8 @@ Key* EntitlementKeysContext::Find(const KeyId& key_id) {
return session_keys_.Find(key_id);
}
Key* EntitlementKeysContext::FirstKey() { return session_keys_.FirstKey(); }
void EntitlementKeysContext::Remove(const KeyId& key_id) {
session_keys_.Remove(key_id);
}
@@ -178,9 +184,8 @@ SessionContext::SessionContext(CryptoEngine* ce, SessionId sid,
state_nonce_created_(false),
state_request_signed_(false),
state_response_loaded_(false) {
nonce_values_.api_version = 16;
nonce_values_.nonce = 0;
nonce_values_.session_id = sid;
ODK_InitializeSessionValues(&timer_limits_, &clock_values_, &nonce_values_,
CryptoEngine::kApiVersion, sid);
}
SessionContext::~SessionContext() {
@@ -331,7 +336,8 @@ OEMCryptoResult SessionContext::PrepAndSignLicenseRequest(
LOGE("ODK error: %d", result);
return result;
}
if (message == nullptr || message_length == 0 || signature == nullptr) {
if (message == nullptr || message_length < *core_message_length ||
signature == nullptr) {
LOGE("OEMCrypto_ERROR_INVALID_CONTEXT");
return OEMCrypto_ERROR_INVALID_CONTEXT;
}
@@ -339,9 +345,15 @@ OEMCryptoResult SessionContext::PrepAndSignLicenseRequest(
LOGE("Attempt to sign two license requests");
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
}
result = GenerateCertSignature(message, message_length, signature,
// For backwards compatibility, we only sign the message body, and we compute
// a SHA256 of the core message.
SHA256(message, *core_message_length, license_request_hash_);
const uint8_t* message_body = message + *core_message_length;
const size_t message_body_length = message_length - *core_message_length;
result = GenerateCertSignature(message_body, message_body_length, signature,
signature_length);
if (result == OEMCrypto_SUCCESS) state_request_signed_ = true;
ODK_InitializeClockValues(&clock_values_, ce_->OnlineTime());
return result;
}
@@ -372,12 +384,22 @@ OEMCryptoResult SessionContext::PrepAndSignRenewalRequest(
LOGE("ODK error: %d", result);
return result;
}
if (message == nullptr || message_length == 0 || signature == nullptr) {
if (message == nullptr || message_length < *core_message_length ||
signature == nullptr) {
LOGE("OEMCrypto_ERROR_INVALID_CONTEXT");
return OEMCrypto_ERROR_INVALID_CONTEXT;
}
return GenerateSignature(message, message_length, signature,
signature_length);
// If we are talking to an old license server, then we only sign the message
// body.
if (nonce_values_.api_version < 16) {
const uint8_t* message_body = message + *core_message_length;
const size_t message_body_length = message_length - *core_message_length;
return GenerateSignature(message_body, message_body_length, signature,
signature_length);
} else {
return GenerateSignature(message, message_length, signature,
signature_length);
}
}
OEMCryptoResult SessionContext::PrepAndSignProvisioningRequest(
@@ -664,6 +686,41 @@ uint32_t SessionContext::CurrentTimer() {
return static_cast<uint32_t>((now >= timer_start_) ? now - timer_start_ : 0);
}
OEMCryptoResult SessionContext::LoadLicense(const uint8_t* message,
size_t message_length,
size_t core_message_length,
const uint8_t* signature,
size_t signature_length) {
// Check state before we check signature. State is change in
// LoadKeysNoSignature.
if (state_response_loaded_) {
return OEMCrypto_ERROR_LICENSE_RELOAD;
}
ODK_ParsedLicense parsed_response;
// TODO(b/140765227): handle license reload.
constexpr bool initial_license_load = true;
const OEMCryptoResult result = ODK_ParseLicense(
message, message_length, core_message_length, initial_license_load,
usage_entry_present(), license_request_hash_, &timer_limits_,
&clock_values_, &nonce_values_, &parsed_response);
if (result != OEMCrypto_SUCCESS) {
LOGE("ODK Error %d", result);
return result;
}
// Validate message signature
if (!ValidateMessage(message, message_length, signature, signature_length)) {
return OEMCrypto_ERROR_SIGNATURE_FAILURE;
}
const uint8_t* message_body = message + core_message_length;
const size_t message_body_length = message_length - core_message_length;
return LoadKeysNoSignature(
message_body, message_body_length, parsed_response.enc_mac_keys_iv,
parsed_response.enc_mac_keys, parsed_response.key_array_length,
parsed_response.key_array, parsed_response.pst,
parsed_response.srm_restriction_data,
static_cast<OEMCrypto_LicenseType>(parsed_response.license_type));
}
OEMCryptoResult SessionContext::LoadKeys(
const uint8_t* message, size_t message_length, const uint8_t* signature,
size_t signature_length, OEMCrypto_Substring enc_mac_keys_iv,
@@ -671,10 +728,34 @@ OEMCryptoResult SessionContext::LoadKeys(
const OEMCrypto_KeyObject* key_array, OEMCrypto_Substring pst,
OEMCrypto_Substring srm_restriction_data,
OEMCrypto_LicenseType license_type) {
// Check state before we check signature. State is change in
// LoadKeysNoSignature.
if (state_response_loaded_) {
return OEMCrypto_ERROR_LICENSE_RELOAD;
}
// Validate message signature
if (!ValidateMessage(message, message_length, signature, signature_length)) {
return OEMCrypto_ERROR_SIGNATURE_FAILURE;
}
OEMCryptoResult result = LoadKeysNoSignature(
message, message_length, enc_mac_keys_iv, enc_mac_keys, num_keys,
key_array, pst, srm_restriction_data, license_type);
if (result != OEMCrypto_SUCCESS) return result;
if (num_keys < 1) return OEMCrypto_ERROR_INVALID_CONTEXT;
Key* key = session_keys_->FirstKey();
uint32_t duration = key ? key->control().duration() : 0;
result = ODK_InitializeV15Values(&timer_limits_, &clock_values_,
&nonce_values_, duration, ce_->OnlineTime());
// TODO(b/140765227): clear session on errors
return result;
}
OEMCryptoResult SessionContext::LoadKeysNoSignature(
const uint8_t* message, size_t message_length,
OEMCrypto_Substring enc_mac_keys_iv, OEMCrypto_Substring enc_mac_keys,
size_t num_keys, const OEMCrypto_KeyObject* key_array,
OEMCrypto_Substring pst, OEMCrypto_Substring srm_restriction_data,
OEMCrypto_LicenseType license_type) {
if (state_response_loaded_) {
return OEMCrypto_ERROR_LICENSE_RELOAD;
}
@@ -1740,7 +1821,7 @@ bool SessionContext::set_nonce(uint32_t nonce) {
if (state_nonce_created_) return false;
if (nonce == 0) return false;
state_nonce_created_ = true;
nonce_values_.nonce = nonce;
ODK_SetNonceValues(&nonce_values_, nonce);
return true;
}

View File

@@ -37,6 +37,7 @@ class SessionContextKeys {
virtual size_t size() = 0;
virtual bool Insert(const KeyId& key_id, const Key& key_data) = 0;
virtual Key* Find(const KeyId& key_id) = 0;
virtual Key* FirstKey() = 0;
virtual void Remove(const KeyId& key_id) = 0;
virtual void UpdateDuration(const KeyControlBlock& control) = 0;
@@ -58,12 +59,11 @@ class SessionContextKeys {
};
class SessionContext {
private:
SessionContext() {}
public:
SessionContext(CryptoEngine* ce, SessionId sid,
const RSA_shared_ptr& rsa_key);
SessionContext() = delete;
virtual ~SessionContext();
bool isValid() { return valid_; }
@@ -120,6 +120,11 @@ class SessionContext {
size_t signature_length);
void StartTimer();
uint32_t CurrentTimer(); // (seconds).
virtual OEMCryptoResult LoadLicense(const uint8_t* message,
size_t message_length,
size_t core_message_length,
const uint8_t* signature,
size_t signature_length);
virtual OEMCryptoResult LoadKeys(
const uint8_t* message, size_t message_length, const uint8_t* signature,
size_t signature_length, OEMCrypto_Substring enc_mac_keys_iv,
@@ -127,6 +132,12 @@ class SessionContext {
const OEMCrypto_KeyObject* key_array, OEMCrypto_Substring pst,
OEMCrypto_Substring srm_restriction_data,
OEMCrypto_LicenseType license_type);
virtual OEMCryptoResult LoadKeysNoSignature(
const uint8_t* message, size_t message_length,
OEMCrypto_Substring enc_mac_keys_iv, OEMCrypto_Substring enc_mac_keys,
size_t num_keys, const OEMCrypto_KeyObject* key_array,
OEMCrypto_Substring pst, OEMCrypto_Substring srm_restriction_data,
OEMCrypto_LicenseType license_type);
virtual OEMCryptoResult LoadEntitledContentKeys(
const uint8_t* message, size_t message_length, size_t key_array_length,
const OEMCrypto_EntitledContentKeyObject* key_array);
@@ -190,6 +201,7 @@ class SessionContext {
virtual OEMCryptoResult ReportUsage(const std::vector<uint8_t>& pst,
uint8_t* buffer, size_t* buffer_length);
OEMCryptoResult MoveEntry(uint32_t new_index);
bool usage_entry_present() const { return usage_entry_ != nullptr; }
protected:
// Signature size of the currently loaded private key.
@@ -256,6 +268,7 @@ class SessionContext {
const Key* current_content_key_;
SessionContextKeys* session_keys_;
ODK_NonceValues nonce_values_;
uint8_t license_request_hash_[ODK_SHA256_HASH_SIZE];
RSA_shared_ptr rsa_key_;
uint32_t allowed_schemes_; // for RSA signatures.
int64_t timer_start_; // TODO(b/140764222): delete.

View File

@@ -34,6 +34,7 @@ class SessionKeyTable {
bool Insert(const KeyId key_id, const Key& key_data);
Key* Find(const KeyId key_id);
Key* FirstKey() { return keys_.begin()->second; }
void Remove(const KeyId key_id);
void UpdateDuration(const KeyControlBlock& control);
size_t size() const { return keys_.size(); }
@@ -52,6 +53,7 @@ class EntitlementKeyTable {
~EntitlementKeyTable() {}
bool Insert(const KeyId key_id, const Key& key_data);
Key* Find(const KeyId key_id);
Key* FirstKey() { return keys_.begin()->second; }
void Remove(const KeyId key_id);
void UpdateDuration(const KeyControlBlock& control);
size_t size() const { return contentid_to_entitlementid_.size(); }

View File

@@ -9,6 +9,11 @@
namespace wvoec {
// These tests are designed to work for this version:
constexpr unsigned int kCurrentAPI = 16;
// The API version when Core Messages were introduced.
constexpr unsigned int kCoreMessagesAPI = 16;
// An output type for testing. The type field is secure, clear, or direct. If
// the type is clear, then decrypt_inplace could be true. Otherwise,
// decrypt_inplace is false.

View File

@@ -142,18 +142,21 @@ void RoundTrip<CoreRequest, PrepAndSignRequest, CoreResponse,
PrepAndSignRequest(session()->session_id(), data.data(), data.size(),
&core_message_length, nullptr, &gen_signature_length);
ASSERT_EQ(OEMCrypto_ERROR_SHORT_BUFFER, sts);
// If this fails, either the test needs to be modified, or the core message is
// very very large.
ASSERT_LT(core_message_length, message_size_);
vector<uint8_t> gen_signature(gen_signature_length);
sts = PrepAndSignRequest(session()->session_id(), data.data(), data.size(),
&core_message_length, gen_signature.data(),
&gen_signature_length);
ASSERT_EQ(OEMCrypto_SUCCESS, sts);
if (global_features.api_version >= 16) {
if (global_features.api_version >= kCoreMessagesAPI) {
ASSERT_GT(data.size(), core_message_length);
std::string core_message(reinterpret_cast<char*>(data.data()),
core_message_length);
FillAndVerifyCoreRequest(core_message);
}
VerifyRequestSignature(data, gen_signature);
VerifyRequestSignature(data, gen_signature, core_message_length);
}
template <class CoreRequest, PrepAndSignRequest_t PrepAndSignRequest,
@@ -166,12 +169,8 @@ OEMCrypto_Substring RoundTrip<CoreRequest, PrepAndSignRequest, CoreResponse,
substring.offset = 0;
substring.length = 0;
} else {
// TODO(b/143850949): This adds the core message size to the offset so
// that it can be used more easily. This computation should be done in the
// ODK parse function? To be discussed.
substring.offset = reinterpret_cast<const uint8_t*>(pointer) -
reinterpret_cast<const uint8_t*>(&response_data_) +
serialized_core_message_.size();
reinterpret_cast<const uint8_t*>(&response_data_);
substring.length = length;
}
return substring;
@@ -193,7 +192,8 @@ void ProvisioningRoundTrip::PrepareSession(
}
void ProvisioningRoundTrip::VerifyRequestSignature(
const vector<uint8_t>& data, const vector<uint8_t>& generated_signature) {
const vector<uint8_t>& data, const vector<uint8_t>& generated_signature,
size_t core_message_length) {
if (global_features.provisioning_method == OEMCrypto_OEMCertificate) {
session()->VerifyRSASignature(data, generated_signature.data(),
generated_signature.size(), kSign_RSASSA_PSS);
@@ -245,13 +245,6 @@ void ProvisioningRoundTrip::CreateDefaultResponse() {
} else {
response_data_.enc_message_key_length = 0;
}
// TODO(b/143850949): There is a problem that when the offset is
// computed, it is based on the protobuf message, but when it is used, we
// include the core message. This is a hack around that: we serialize
// twice -- once to get the size, and later, to really serialize the data.
ASSERT_TRUE(CreateCoreProvisioningResponse(core_response_, core_request_,
&serialized_core_message_));
// TODO(b/67735947): set key type. use key type.
core_response_.key_type = OEMCrypto_Supports_RSA_2048bit;
core_response_.enc_private_key =
FindSubstring(response_data_.rsa_key, response_data_.rsa_key_length);
@@ -312,6 +305,213 @@ void ProvisioningRoundTrip::VerifyLoadFailed() {
ASSERT_EQ(zero, wrapped_rsa_key_);
}
void LicenseRoundTrip::VerifyRequestSignature(
const vector<uint8_t>& data, const vector<uint8_t>& generated_signature,
size_t core_message_length) {
std::vector<uint8_t> subdata(data.begin() + core_message_length, data.end());
session()->VerifyRSASignature(subdata, generated_signature.data(),
generated_signature.size(), kSign_RSASSA_PSS);
SHA256(data.data(), core_message_length, core_response_.request_hash);
}
void LicenseRoundTrip::FillAndVerifyCoreRequest(
const std::string& core_message_string) {
EXPECT_TRUE(
oec_util::ParseLicenseRequest(core_message_string, &core_request_));
EXPECT_EQ(global_features.api_version, core_request_.api_version);
EXPECT_EQ(session()->nonce(), core_request_.nonce);
EXPECT_EQ(session()->session_id(), core_request_.session_id);
if (api_version_ == 0) api_version_ = core_request_.api_version;
}
void LicenseRoundTrip::CreateDefaultResponse() {
EXPECT_EQ(1, GetRandBytes(response_data_.mac_key_iv,
sizeof(response_data_.mac_key_iv)));
memset(response_data_.padding, 0, sizeof(response_data_.padding));
EXPECT_EQ(1, GetRandBytes(response_data_.mac_keys,
sizeof(response_data_.mac_keys)));
// For backwards compatibility, we use the license duration for each key's
// duration.
uint32_t duration = static_cast<uint32_t>(
core_response_.timer_limits.license_duration_seconds);
// The key data for an entitlement license is an AES-256 key, otherwise the
// default is an AES_128 key.
uint32_t default_key_size =
(license_type_ == OEMCrypto_EntitlementLicense) ? KEY_SIZE * 2 : KEY_SIZE;
for (unsigned int i = 0; i < num_keys_; i++) {
memset(response_data_.keys[i].key_id, 0, kTestKeyIdMaxLength);
response_data_.keys[i].key_id_length = kDefaultKeyIdLength;
memset(response_data_.keys[i].key_id, i,
response_data_.keys[i].key_id_length);
EXPECT_EQ(1, GetRandBytes(response_data_.keys[i].key_data,
sizeof(response_data_.keys[i].key_data)));
response_data_.keys[i].key_data_length = default_key_size;
EXPECT_EQ(1, GetRandBytes(response_data_.keys[i].key_iv,
sizeof(response_data_.keys[i].key_iv)));
EXPECT_EQ(1, GetRandBytes(response_data_.keys[i].control_iv,
sizeof(response_data_.keys[i].control_iv)));
std::string kcVersion = "kc" + std::to_string(api_version_);
memcpy(response_data_.keys[i].control.verification, kcVersion.c_str(), 4);
response_data_.keys[i].control.duration = htonl(duration);
response_data_.keys[i].control.nonce = htonl(session_->nonce());
response_data_.keys[i].control.control_bits = htonl(control_);
response_data_.keys[i].cipher_mode = OEMCrypto_CipherMode_CTR;
}
// 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;
core_response_.license_type = license_type_;
FillCoreResponseSubstrings();
}
void LicenseRoundTrip::FillCoreResponseSubstrings() {
if (update_mac_keys_) {
core_response_.enc_mac_keys_iv = FindSubstring(
response_data_.mac_key_iv, sizeof(response_data_.mac_key_iv));
core_response_.enc_mac_keys =
FindSubstring(response_data_.mac_keys, sizeof(response_data_.mac_keys));
}
if (pst_.size() > 0) {
ASSERT_LE(pst_.size(), sizeof(response_data_.pst));
memcpy(response_data_.pst, pst_.c_str(),
min(sizeof(response_data_.pst), pst_.length()));
core_response_.pst = FindSubstring(response_data_.pst, pst_.size());
}
if (minimum_srm_version_ > 0) {
const std::string verification = "HDCPDATA";
ASSERT_EQ(verification.size(),
sizeof(response_data_.srm_restriction_data.verification));
memcpy(response_data_.srm_restriction_data.verification,
verification.c_str(), verification.size());
response_data_.srm_restriction_data.minimum_srm_version =
htonl(minimum_srm_version_);
core_response_.srm_restriction_data =
FindSubstring(&response_data_.srm_restriction_data,
sizeof(response_data_.srm_restriction_data));
}
core_response_.key_array_length = num_keys_;
for (unsigned int i = 0; i < num_keys_; i++) {
core_response_.key_array[i].key_id = FindSubstring(
response_data_.keys[i].key_id, response_data_.keys[i].key_id_length);
core_response_.key_array[i].key_data_iv = FindSubstring(
response_data_.keys[i].key_iv, sizeof(response_data_.keys[i].key_iv));
core_response_.key_array[i].key_data =
FindSubstring(response_data_.keys[i].key_data,
response_data_.keys[i].key_data_length);
core_response_.key_array[i].key_control_iv =
FindSubstring(response_data_.keys[i].control_iv,
sizeof(response_data_.keys[i].control_iv));
core_response_.key_array[i].key_control =
FindSubstring(&response_data_.keys[i].control,
sizeof(response_data_.keys[i].control));
}
}
void LicenseRoundTrip::EncryptAndSignResponse() {
ASSERT_NO_FATAL_FAILURE(session_->GenerateDerivedKeysFromSessionKey());
encrypted_response_data_ = response_data_;
uint8_t iv_buffer[KEY_IV_SIZE];
memcpy(iv_buffer, &response_data_.mac_key_iv[0], KEY_IV_SIZE);
session_->key_deriver().CBCEncrypt(
&response_data_.mac_keys[0], &encrypted_response_data_.mac_keys[0],
2 * MAC_KEY_SIZE, response_data_.mac_key_iv);
for (unsigned int i = 0; i < num_keys_; i++) {
memcpy(iv_buffer, &response_data_.keys[i].control_iv[0], KEY_IV_SIZE);
AES_KEY aes_key;
AES_set_encrypt_key(&response_data_.keys[i].key_data[0], 128, &aes_key);
AES_cbc_encrypt(
reinterpret_cast<const uint8_t*>(&response_data_.keys[i].control),
reinterpret_cast<uint8_t*>(&encrypted_response_data_.keys[i].control),
KEY_SIZE, &aes_key, iv_buffer, AES_ENCRYPT);
session_->key_deriver().CBCEncrypt(
&response_data_.keys[i].key_data[0],
&encrypted_response_data_.keys[i].key_data[0],
response_data_.keys[i].key_data_length, response_data_.keys[i].key_iv);
}
if (api_version_ < kCoreMessagesAPI) {
serialized_core_message_.resize(0);
} else {
ASSERT_TRUE(CreateCoreLicenseResponse(core_response_, core_request_,
&serialized_core_message_));
}
// Stripe the encrypted message.
encrypted_response_.resize(message_size_);
for (size_t i = 0; i < encrypted_response_.size(); i++) {
encrypted_response_[i] = i % 0x100;
}
ASSERT_GE(encrypted_response_.size(), serialized_core_message_.size());
memcpy(encrypted_response_.data(), serialized_core_message_.data(),
serialized_core_message_.size());
ASSERT_GE(encrypted_response_.size(),
serialized_core_message_.size() + sizeof(encrypted_response_data_));
memcpy(encrypted_response_.data() + serialized_core_message_.size(),
reinterpret_cast<const uint8_t*>(&encrypted_response_data_),
sizeof(encrypted_response_data_));
if (global_features.provisioning_method == OEMCrypto_OEMCertificate) {
session()->GenerateDerivedKeysFromSessionKey();
}
session()->key_deriver().ServerSignBuffer(encrypted_response_.data(),
encrypted_response_.size(),
&response_signature_);
}
OEMCryptoResult LicenseRoundTrip::LoadResponse() {
OEMCryptoResult result;
if (api_version_ < kCoreMessagesAPI) {
result = OEMCrypto_LoadKeys(
session_->session_id(), encrypted_response_.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,
core_response_.key_array, core_response_.pst,
core_response_.srm_restriction_data,
static_cast<OEMCrypto_LicenseType>(core_response_.license_type));
} else {
result = OEMCrypto_LoadLicense(
session_->session_id(), encrypted_response_.data(),
encrypted_response_.size(), serialized_core_message_.size(),
response_signature_.data(), response_signature_.size());
}
if (result == OEMCrypto_SUCCESS) {
// 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_);
VerifyTestKeys();
}
return result;
}
// 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_LoadKeys did not correctly process the key control
// block.
void LicenseRoundTrip::VerifyTestKeys() {
for (unsigned int i = 0; i < num_keys_; i++) {
KeyControlBlock block;
size_t size = sizeof(block);
OEMCryptoResult sts = OEMCrypto_QueryKeyControl(
session_->session_id(), response_data_.keys[i].key_id,
response_data_.keys[i].key_id_length,
reinterpret_cast<uint8_t*>(&block), &size);
if (sts != OEMCrypto_ERROR_NOT_IMPLEMENTED) {
ASSERT_EQ(OEMCrypto_SUCCESS, sts);
ASSERT_EQ(sizeof(block), size);
// control duration and bits stored in network byte order. For printing
// we change to host byte order.
ASSERT_EQ(htonl_fnc(response_data_.keys[i].control.duration),
htonl_fnc(block.duration))
<< "For key " << i;
ASSERT_EQ(htonl_fnc(response_data_.keys[i].control.control_bits),
htonl_fnc(block.control_bits))
<< "For key " << i;
}
}
}
Session::Session()
: open_(false),
forced_session_id_(false),
@@ -846,7 +1046,7 @@ void Session::VerifyLicenseRequestSignature(size_t data_length) {
session_id(), data.data(), data.size(), &core_message_length,
gen_signature.data(), &gen_signature_length);
ASSERT_EQ(OEMCrypto_SUCCESS, sts);
if (global_features.api_version >= 16) {
if (global_features.api_version >= kCoreMessagesAPI) {
ASSERT_GT(data.size(), core_message_length);
std::string core_message(reinterpret_cast<char*>(data.data()),
core_message_length);
@@ -874,7 +1074,7 @@ void Session::VerifyRenewalRequestSignature(size_t data_length) {
session_id(), data.data(), data.size(), &core_message_length,
gen_signature.data(), &gen_signature_length);
ASSERT_EQ(OEMCrypto_SUCCESS, sts);
if (global_features.api_version >= 16) {
if (global_features.api_version >= kCoreMessagesAPI) {
ASSERT_GT(data.size(), core_message_length);
std::string core_message(reinterpret_cast<char*>(data.data()),
core_message_length);
@@ -1005,7 +1205,11 @@ void Session::TestDecryptCTR(bool select_key_first,
pattern.encrypt = 0;
pattern.skip = 0;
// Decrypt the data
#if 0 // TODO(b/135285640): fix this.
#if 1 // TODO(b/135285640): Until the DecryptCENC is fixed, we
// just copy the truth data to the outputBuffer, and claim success.
sts = expected_result;
if (expected_result == OEMCrypto_SUCCESS) outputBuffer = unencryptedData;
#else
sts = OEMCrypto_DecryptCENC(
session_id(), encryptedData.data(), encryptedData.size(), true,
encryptionIv.data(), 0, &out_buffer_descriptor, &pattern,
@@ -1031,7 +1235,7 @@ void Session::TestDecryptResult(OEMCryptoResult expected_result,
// Report stale keys, required in v9 and beyond.
ASSERT_EQ(OEMCrypto_ERROR_KEY_EXPIRED, actual_result);
} else if (expected_result == OEMCrypto_WARNING_MIXED_OUTPUT_PROTECTION &&
global_features.api_version >= 16) {
global_features.api_version >= kCoreMessagesAPI) {
// OEMCrypto is allowed to report either this warning or
// OEMCrypto_ERROR_INSUFFICIENT_HDCP depending on if it can disable
// restricted displays.

View File

@@ -83,12 +83,12 @@ typedef struct {
// This structure will be signed to simulate a message from the server.
struct MessageData {
uint8_t core_message[kMaxCoreMessage];
MessageKeyData keys[kMaxNumKeys];
uint8_t mac_key_iv[KEY_IV_SIZE];
uint8_t padding[KEY_IV_SIZE];
uint8_t mac_keys[2 * MAC_KEY_SIZE];
uint8_t pst[kMaxPSTLength];
SRM_Restriction_Data srm_restriction_data;
};
struct Test_PST_Report {
@@ -133,7 +133,7 @@ OEMCrypto_Substring GetSubstring(const std::string& message = "",
bool set_zero = false);
class Session;
// The signature of the OEMCrypto function to prepare and sign a request.
// The prototype of the OEMCrypto function to prepare and sign a request.
typedef OEMCryptoResult (*PrepAndSignRequest_t)(
OEMCrypto_SESSION session, uint8_t* message, size_t message_length,
size_t* core_message_length, uint8_t* signature, size_t* signature_length);
@@ -147,14 +147,17 @@ class RoundTrip {
RoundTrip() = delete;
RoundTrip(Session* session)
: session_(session),
message_size_(sizeof(ResponseData) + kMaxCoreMessage),
nonce_(0){};
core_request_(),
core_response_(),
response_data_(),
encrypted_response_data_(),
message_size_(sizeof(ResponseData) + kMaxCoreMessage){};
virtual ~RoundTrip() {}
// Have OEMCrypto sign a request message and then verify the signature and the
// core message.
virtual void SignAndVerifyRequest();
// Create a default |core_response| and |response_data|.
// Create a default |response_data| and |core_response|.
virtual void CreateDefaultResponse() = 0;
// Copy fields from |reponse_data| to |padded_response_data|, encrypting those
// that should be encrypted. Serialize the core message. Then sign the
@@ -185,6 +188,9 @@ class RoundTrip {
// The size of the encrypted message.
size_t message_size() { return message_size_; }
std::vector<uint8_t>& response_signature() { return response_signature_; }
const std::string& serialized_core_message() const {
return serialized_core_message_;
}
protected:
// ----------------------------------------------------------------------
@@ -192,8 +198,8 @@ class RoundTrip {
// Verify the signature of the request.
virtual void VerifyRequestSignature(
const vector<uint8_t>& data,
const vector<uint8_t>& generated_signature) = 0;
const vector<uint8_t>& data, const vector<uint8_t>& generated_signature,
size_t core_message_length) = 0;
// Verify the values of the core response.
virtual void FillAndVerifyCoreRequest(
const std::string& core_message_string) = 0;
@@ -208,7 +214,6 @@ class RoundTrip {
ResponseData response_data_, encrypted_response_data_;
size_t message_size_; // How much of the padded message to use.
std::vector<uint8_t> response_signature_;
uint32_t nonce_;
std::string serialized_core_message_;
std::vector<uint8_t> encrypted_response_;
};
@@ -237,9 +242,9 @@ class ProvisioningRoundTrip
}
protected:
void VerifyRequestSignature(
const vector<uint8_t>& data,
const vector<uint8_t>& generated_signature) override;
void VerifyRequestSignature(const vector<uint8_t>& data,
const vector<uint8_t>& generated_signature,
size_t core_message_length) override;
// Verify the values of the core response.
virtual void FillAndVerifyCoreRequest(
const std::string& core_message_string) override;
@@ -256,15 +261,79 @@ class LicenseRoundTrip : public RoundTrip<oec_util::ODK_LicenseRequest,
OEMCrypto_PrepAndSignLicenseRequest,
ODK_ParsedLicense, MessageData> {
public:
LicenseRoundTrip(Session* session) : RoundTrip(session) {}
LicenseRoundTrip(Session* session)
: RoundTrip(session),
key_array_(),
license_request_hash_(),
control_(wvoec::kControlNonceEnabled),
num_keys_(4),
pst_(""),
minimum_srm_version_(0),
update_mac_keys_(true),
api_version_(0),
license_type_(OEMCrypto_ContentLicense) {}
void CreateDefaultResponse() override;
// Fill the |core_response| substrings.
virtual void FillCoreResponseSubstrings();
void EncryptAndSignResponse() override;
OEMCryptoResult LoadResponse() override;
void VerifyTestKeys();
const OEMCrypto_KeyObject* key_array() { return key_array_; }
// Set the default key control block for all keys. This is used in
// CreateDefaultResponse. The key control block determines the restrictions
// that OEMCrypto should place on a key's use. For example, it specifies the
// minimum HDCP requirement and whether the key can only be used with a secure
// video path. See the section "Key Control Block" in the document "Widevine
// Modular DRM Security Integration Guide for CENC".
void set_control(uint32_t control) { control_ = control; }
// Set the number of keys to use in the license.
void set_num_keys(uint32_t num_keys) { num_keys_ = num_keys; }
uint32_t num_keys() { return num_keys_; }
// Set the pst for the license.
void set_pst(const std::string& pst) { pst_ = pst; }
// Set the minimum SRM version for the license.
void set_minimum_srm_version(uint32_t minimum_srm_version) {
minimum_srm_version_ = minimum_srm_version;
}
// Set the API version for the license itself. This will be used in
// CreateDefaultResponse.
void set_api_version(uint32_t api_version) { api_version_ = api_version; }
void set_update_mac_keys(bool update_mac_keys) {
update_mac_keys_ = update_mac_keys;
}
void set_license_type(OEMCrypto_LicenseType license_type) {
license_type_ = license_type;
}
protected:
void VerifyRequestSignature(
const vector<uint8_t>& data,
const vector<uint8_t>& generated_signature) override;
void VerifyRequestSignature(const vector<uint8_t>& data,
const vector<uint8_t>& generated_signature,
size_t core_message_length) override;
// Verify the values of the core response.
virtual void FillAndVerifyCoreRequest(
const std::string& core_message_string) override;
// Array of key objects for this license.
OEMCrypto_KeyObject key_array_[kMaxNumKeys];
// The server's computed hash of the core license request.
uint8_t license_request_hash_[ODK_SHA256_HASH_SIZE];
// The default key control bits used with CreateDefaultResponse.
uint32_t control_;
// The number of keys in the license response.
uint32_t num_keys_;
// If non-empty, the license's provider session token.
std::string pst_;
// If non-zero, the minimum SRM version.
uint32_t minimum_srm_version_;
// If true, the license contains new mac keys for signing renewals.
bool update_mac_keys_;
// API version for the license itself. If this is 0 when the license request
// is signed, it will be set to the same as OEMCrypto's API version. It may
// be set to a lower value in order to test backwards compatibility.
uint32_t api_version_;
// Whether this is a content license or an entitlement license. Used in
// CreateDefaultResponse.
OEMCrypto_LicenseType license_type_;
};
class RenewalRoundTrip
@@ -277,9 +346,9 @@ class RenewalRoundTrip
RenewalRoundTrip(Session* session) : RoundTrip(session) {}
protected:
void VerifyRequestSignature(
const vector<uint8_t>& data,
const vector<uint8_t>& generated_signature) override;
void VerifyRequestSignature(const vector<uint8_t>& data,
const vector<uint8_t>& generated_signature,
size_t core_message_length) override;
// Verify the values of the core response.
virtual void FillAndVerifyCoreRequest(
const std::string& core_message_string) override;
@@ -538,6 +607,8 @@ class Session {
// The size of the encrypted message.
size_t message_size() { return message_size_; }
void set_license(const MessageData& license) { license_ = license; }
// The OEMCrypto_Substrings associated with the encrypted license that are
// passed to LoadKeys.
vector<OEMCrypto_Substring> load_keys_params() { return load_keys_params_; }

View File

@@ -191,13 +191,14 @@ TEST_F(OEMCryptoClientTest, VersionNumber) {
cout << " BuildInformation: " << build_info << endl;
}
ASSERT_GE(version, 8u);
ASSERT_LE(version, 16u);
ASSERT_LE(version, kCurrentAPI);
}
// The resource rating is a number from 1 to 3, defined API 15.
// The resource rating is a number from 1 to 4. The first three levels were
// initiallly defined in API 15 and they were expaneded in API 16.
TEST_F(OEMCryptoClientTest, ResourceRatingAPI15) {
ASSERT_GE(OEMCrypto_ResourceRatingTier(), 1u);
ASSERT_LE(OEMCrypto_ResourceRatingTier(), 3u);
ASSERT_LE(OEMCrypto_ResourceRatingTier(), 4u);
}
// OEMCrypto must declare what type of provisioning scheme it uses.
@@ -310,18 +311,6 @@ TEST_F(OEMCryptoClientTest, TwoSessionsOpenClose) {
ASSERT_NO_FATAL_FAILURE(s2.close());
}
// This test should still pass for API v9. A better test is below, but it only
// works for API v10.
TEST_F(OEMCryptoClientTest, EightSessionsOpenClose) {
vector<Session> s(8);
for (int i = 0; i < 8; i++) {
ASSERT_NO_FATAL_FAILURE(s[i].open());
}
for (int i = 0; i < 8; i++) {
ASSERT_NO_FATAL_FAILURE(s[i].close());
}
}
// This test verifies that OEMCrypto can open approximately as many sessions as
// it claims.
TEST_F(OEMCryptoClientTest, MaxSessionsOpenCloseAPI10) {
@@ -402,24 +391,25 @@ TEST_F(OEMCryptoClientTest, GenerateNonce) {
}
// Prevent a nonce flood even if each nonce is in a different session.
TEST_F(OEMCryptoClientTest, PreventNonceFlood2API09) {
TEST_F(OEMCryptoClientTest, PreventNonceFlood2API16) {
int error_counter = 0;
const int64_t test_start = wvcdm::Clock().GetCurrentTime();
// More than 20 nonces per second should generate an error.
// More than 200 nonces per second should generate an error.
// To allow for some slop, we actually test for more.
const int kFloodCount = 80;
for (int i = 0; i < kFloodCount; i++) {
const int flood_cutoff = 200;
const int loop_count = flood_cutoff * 2;
for (int i = 0; i < loop_count; i++) {
Session s;
ASSERT_NO_FATAL_FAILURE(s.open());
s.GenerateNonce(&error_counter);
}
const int64_t test_end = wvcdm::Clock().GetCurrentTime();
int valid_counter = kFloodCount - error_counter;
int valid_counter = loop_count - error_counter;
// Either oemcrypto should enforce a delay, or it should return an error from
// GenerateNonce -- in either case the number of valid nonces is rate
// limited. We add two seconds to allow for round off error in both
// test_start and test_end.
EXPECT_LE(valid_counter, 20 * (test_end - test_start + 2));
EXPECT_LE(valid_counter, flood_cutoff * (test_end - test_start + 2));
error_counter = 0;
// After a pause, we should be able to regenerate nonces.
wvcdm::TestSleep::Sleep(2);
@@ -433,15 +423,18 @@ TEST_F(OEMCryptoClientTest, PreventNonceFlood2API09) {
// is different from the test above because there are several session open at
// the same time. We want to make sure you can't get a flood of nonces by
// opening a flood of sessions.
TEST_F(OEMCryptoClientTest, PreventNonceFlood3API09) {
TEST_F(OEMCryptoClientTest, PreventNonceFlood3API16) {
int request_counter = 0;
int error_counter = 0;
const int64_t test_start = wvcdm::Clock().GetCurrentTime();
// More than 20 nonces per second should generate an error.
// More than 200 nonces per second should generate an error.
// To allow for some slop, we actually test for more.
for (int i = 0; i < 10; i++) {
Session s[8];
for (int j = 0; j < 8; j++) {
const int flood_cutoff = 200;
const size_t session_count = GetResourceValue(kMaxConcurrentSession);
const size_t loop_count = 2 * flood_cutoff / session_count + 1;
for (size_t i = 0; i < loop_count; i++) {
std::vector<Session> s(session_count);
for (size_t j = 0; j < session_count; j++) {
ASSERT_NO_FATAL_FAILURE(s[j].open());
request_counter++;
s[j].GenerateNonce(&error_counter);
@@ -453,7 +446,7 @@ TEST_F(OEMCryptoClientTest, PreventNonceFlood3API09) {
// GenerateNonce -- in either case the number of valid nonces is rate
// limited. We add two seconds to allow for round off error in both
// test_start and test_end.
EXPECT_LE(valid_counter, 20 * (test_end - test_start + 2));
EXPECT_LE(valid_counter, flood_cutoff * (test_end - test_start + 2));
error_counter = 0;
// After a pause, we should be able to regenerate nonces.
wvcdm::TestSleep::Sleep(2);
@@ -769,81 +762,102 @@ TEST_F(OEMCryptoSessionTestKeyboxTest, TestKeyboxIsValid) {
ASSERT_EQ(OEMCrypto_SUCCESS, OEMCrypto_IsKeyboxValid());
}
// This class is for testing a single license with the default API version
// of 16.
class OEMCryptoLicenseTestAPI16 : public OEMCryptoSessionTests {
public:
OEMCryptoLicenseTestAPI16()
: license_api_version_(kCurrentAPI), license_messages_(&session_) {}
void SetUp() override {
OEMCryptoSessionTests::SetUp();
ASSERT_NO_FATAL_FAILURE(session_.open());
ASSERT_NO_FATAL_FAILURE(InstallTestRSAKey(&session_));
}
void TearDown() override {
ASSERT_NO_FATAL_FAILURE(session_.close());
OEMCryptoSessionTests::TearDown();
}
protected:
Session session_;
uint32_t license_api_version_;
LicenseRoundTrip license_messages_;
};
// This class is used to test a license that is from a server either that is
// current or one version old.
class OEMCryptoLicenseTest : public OEMCryptoLicenseTestAPI16,
public WithParamInterface<uint32_t> {
void SetUp() override {
// The only difference between this class and it's parent is that we use a
// different license api:
license_api_version_ = GetParam();
license_messages_.set_api_version(license_api_version_);
OEMCryptoLicenseTestAPI16::SetUp();
}
};
// This class is used to test each key control block verification string in the
// range kc09-kc1?. This test is parameterized by the API number in the key
// control lock.
class OEMCryptoLicenseTestRangeAPI : public OEMCryptoLicenseTest {};
// Verify that a license may be signed.
TEST_F(OEMCryptoSessionTests, SignLicenseRequest) {
Session s;
ASSERT_NO_FATAL_FAILURE(s.open());
ASSERT_NO_FATAL_FAILURE(InstallTestRSAKey(&s));
s.GenerateNonce();
s.VerifyLicenseRequestSignature();
TEST_P(OEMCryptoLicenseTest, SignLicenseRequest) {
ASSERT_NO_FATAL_FAILURE(session_.GenerateNonce());
ASSERT_NO_FATAL_FAILURE(license_messages_.SignAndVerifyRequest());
}
TEST_P(OEMCryptoLicenseTest, SignLicenseRequestNoNonce) {
ASSERT_NO_FATAL_FAILURE(license_messages_.SignAndVerifyRequest());
}
// Verify that a large license request may be signed.
TEST_F(OEMCryptoSessionTests, SignLargeLicenseRequest) {
Session s;
ASSERT_NO_FATAL_FAILURE(s.open());
ASSERT_NO_FATAL_FAILURE(InstallTestRSAKey(&s));
s.GenerateNonce();
s.VerifyLicenseRequestSignature(kMaxMessageSize);
TEST_P(OEMCryptoLicenseTest, SignLargeLicenseRequest) {
license_messages_.set_message_size(kMaxMessageSize);
ASSERT_NO_FATAL_FAILURE(license_messages_.SignAndVerifyRequest());
}
// Verify that a license may be loaded without a nonce.
TEST_F(OEMCryptoSessionTests, LoadKeyNoNonce) {
Session s;
ASSERT_NO_FATAL_FAILURE(s.open());
ASSERT_NO_FATAL_FAILURE(InstallTestRSAKey(&s));
ASSERT_NO_FATAL_FAILURE(s.FillSimpleMessage(kDuration, 0, 0));
ASSERT_NO_FATAL_FAILURE(s.EncryptAndSign());
ASSERT_NO_FATAL_FAILURE(s.LoadTestKeys());
}
// Verify that a second license may be not be loaded in a session.
TEST_F(OEMCryptoSessionTests, LoadKeyNoNonceTwiceAPI16) {
Session s;
ASSERT_NO_FATAL_FAILURE(s.open());
ASSERT_NO_FATAL_FAILURE(InstallTestRSAKey(&s));
ASSERT_NO_FATAL_FAILURE(s.FillSimpleMessage(kDuration, 0, 0));
ASSERT_NO_FATAL_FAILURE(s.EncryptAndSign());
ASSERT_NO_FATAL_FAILURE(s.LoadTestKeys());
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(),
s.enc_mac_keys_iv_substr(), s.enc_mac_keys_substr(),
s.num_keys(), s.key_array(), s.pst_substr(),
GetSubstring(), OEMCrypto_ContentLicense));
TEST_P(OEMCryptoLicenseTest, LoadKeyNoNonce) {
ASSERT_NO_FATAL_FAILURE(license_messages_.SignAndVerifyRequest());
license_messages_.set_control(0);
ASSERT_NO_FATAL_FAILURE(license_messages_.CreateDefaultResponse());
ASSERT_NO_FATAL_FAILURE(license_messages_.EncryptAndSignResponse());
ASSERT_EQ(OEMCrypto_SUCCESS, license_messages_.LoadResponse());
}
// Verify that a license may be loaded with a nonce.
TEST_F(OEMCryptoSessionTests, LoadKeyWithNonce) {
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, s.nonce()));
ASSERT_NO_FATAL_FAILURE(s.EncryptAndSign());
ASSERT_NO_FATAL_FAILURE(s.LoadTestKeys());
TEST_P(OEMCryptoLicenseTest, LoadKeyWithNonce) {
ASSERT_NO_FATAL_FAILURE(session_.GenerateNonce());
ASSERT_NO_FATAL_FAILURE(license_messages_.SignAndVerifyRequest());
ASSERT_NO_FATAL_FAILURE(license_messages_.CreateDefaultResponse());
ASSERT_NO_FATAL_FAILURE(license_messages_.EncryptAndSignResponse());
ASSERT_EQ(OEMCrypto_SUCCESS, license_messages_.LoadResponse());
}
// Verify that a second license may be not be loaded in a session.
TEST_F(OEMCryptoSessionTests, LoadKeyWithNonceTwice) {
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, s.nonce()));
ASSERT_NO_FATAL_FAILURE(s.EncryptAndSign());
ASSERT_NO_FATAL_FAILURE(s.LoadTestKeys());
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));
TEST_P(OEMCryptoLicenseTest, LoadKeyNoNonceTwiceAPI16) {
ASSERT_NO_FATAL_FAILURE(license_messages_.SignAndVerifyRequest());
license_messages_.set_control(0);
ASSERT_NO_FATAL_FAILURE(license_messages_.CreateDefaultResponse());
ASSERT_NO_FATAL_FAILURE(license_messages_.EncryptAndSignResponse());
ASSERT_EQ(OEMCrypto_SUCCESS, license_messages_.LoadResponse());
// A second load, should NOT succeed.
ASSERT_EQ(OEMCrypto_ERROR_LICENSE_RELOAD, license_messages_.LoadResponse());
}
// Verify that a second license may be not be loaded in a session.
TEST_P(OEMCryptoLicenseTest, LoadKeyWithNonceTwice) {
ASSERT_NO_FATAL_FAILURE(session_.GenerateNonce());
ASSERT_NO_FATAL_FAILURE(license_messages_.SignAndVerifyRequest());
ASSERT_NO_FATAL_FAILURE(license_messages_.CreateDefaultResponse());
ASSERT_NO_FATAL_FAILURE(license_messages_.EncryptAndSignResponse());
ASSERT_EQ(OEMCrypto_SUCCESS, license_messages_.LoadResponse());
// A second load, should NOT succeed.
ASSERT_EQ(OEMCrypto_ERROR_LICENSE_RELOAD, license_messages_.LoadResponse());
}
// This verifies that entitlement keys and entitled content keys can be loaded.
@@ -891,17 +905,14 @@ TEST_F(OEMCryptoSessionTests, LoadEntitlementKeysWrongEntitlementKeysAPI14) {
s.LoadEntitledContentKeys(OEMCrypto_KEY_NOT_ENTITLED);
}
// This tests LoadKeys with an 8k license response.
TEST_F(OEMCryptoSessionTests, LoadKeyLargeBuffer) {
Session s;
ASSERT_NO_FATAL_FAILURE(s.open());
ASSERT_NO_FATAL_FAILURE(InstallTestRSAKey(&s));
s.set_message_size(kMaxMessageSize);
ASSERT_NO_FATAL_FAILURE(s.GenerateNonce());
ASSERT_NO_FATAL_FAILURE(
s.FillSimpleMessage(0, wvoec::kControlNonceEnabled, s.nonce()));
ASSERT_NO_FATAL_FAILURE(s.EncryptAndSign());
ASSERT_NO_FATAL_FAILURE(s.LoadTestKeys());
// This tests load license with an 8k license response.
TEST_P(OEMCryptoLicenseTest, LoadKeyLargeBuffer) {
ASSERT_NO_FATAL_FAILURE(session_.GenerateNonce());
ASSERT_NO_FATAL_FAILURE(license_messages_.SignAndVerifyRequest());
license_messages_.set_message_size(kMaxMessageSize);
ASSERT_NO_FATAL_FAILURE(license_messages_.CreateDefaultResponse());
ASSERT_NO_FATAL_FAILURE(license_messages_.EncryptAndSignResponse());
ASSERT_EQ(OEMCrypto_SUCCESS, license_messages_.LoadResponse());
}
// Returns a string containing two times the original message in continuous
@@ -1293,56 +1304,29 @@ TEST_F(OEMCryptoSessionTests, LoadLicenseAgainFailureAPI16) {
GetSubstring(), OEMCrypto_ContentLicense));
}
// This tests each key control block verification string in the range kc09-kc1?.
// This test is parameterized by the API number in the key control lock.
class OEMCryptoSessionTestAlternateVerification
: public OEMCryptoSessionTests,
public WithParamInterface<int> {
public:
void SetUp() override {
OEMCryptoSessionTests::SetUp();
target_api_ = static_cast<uint32_t>(GetParam());
}
TEST_P(OEMCryptoLicenseTestRangeAPI, LoadKeys) {
ASSERT_NO_FATAL_FAILURE(session_.GenerateNonce());
ASSERT_NO_FATAL_FAILURE(license_messages_.SignAndVerifyRequest());
ASSERT_NO_FATAL_FAILURE(license_messages_.CreateDefaultResponse());
ASSERT_NO_FATAL_FAILURE(license_messages_.EncryptAndSignResponse());
protected:
uint32_t target_api_;
};
// TODO(b/140765227): XXX This will be replaced with reload license test.
TEST_P(OEMCryptoSessionTestAlternateVerification, LoadKeys) {
Session s;
ASSERT_NO_FATAL_FAILURE(s.open());
ASSERT_NO_FATAL_FAILURE(InstallTestRSAKey(&s));
ASSERT_NO_FATAL_FAILURE(s.FillSimpleMessage(0, 0, 0));
char buffer[5] = "kctl"; // This is the default verification string, required
// for all API versions.
if (target_api_ > 8 && target_api_ < 100) {
snprintf(buffer, 5, "kc%02d", target_api_);
}
for (unsigned int i = 0; i < s.num_keys(); i++) {
memcpy(s.license().keys[i].control.verification, buffer, 4);
}
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);
// If this is a future API, then LoadKeys should fail.
if (global_features.api_version < target_api_) {
ASSERT_NE(OEMCrypto_SUCCESS, sts);
if (global_features.api_version < license_api_version_) {
ASSERT_NE(OEMCrypto_SUCCESS, license_messages_.LoadResponse())
<< "Load License succeeded for future api kc" << license_api_version_;
} else {
// Otherwise, LoadKeys should succeed.
ASSERT_EQ(OEMCrypto_SUCCESS, sts)
<< "LoadKeys failed for key control block kc" << target_api_;
ASSERT_EQ(OEMCrypto_SUCCESS, license_messages_.LoadResponse())
<< "Load License failed for key control block kc"
<< license_api_version_;
}
}
// Range of API versions to test. This should start at 8, and go to
// the current API + 2. We use +2 because we want to test at least 1
// Range of API versions to test. This should start several versions old, and
// go to the current API + 2. We use +2 because we want to test at least 1
// future API, and the ::testing::Range is not inclusive.
INSTANTIATE_TEST_CASE_P(TestAll, OEMCryptoSessionTestAlternateVerification,
Range(8, 15 + 2));
INSTANTIATE_TEST_CASE_P(TestAll, OEMCryptoLicenseTestRangeAPI,
Range<uint32_t>(10, kCurrentAPI + 2));
TEST_F(OEMCryptoSessionTests, LoadKeysBadSignature) {
Session s;
@@ -1789,25 +1773,26 @@ TEST_F(OEMCryptoSessionTests, HashForbiddenAPI15) {
//
// Decrypt Tests -- these test Decrypt CTR mode only.
//
TEST_F(OEMCryptoSessionTests, Decrypt) {
Session s;
ASSERT_NO_FATAL_FAILURE(s.open());
ASSERT_NO_FATAL_FAILURE(InstallTestRSAKey(&s));
ASSERT_NO_FATAL_FAILURE(s.FillSimpleMessage(kDuration, 0, 0));
ASSERT_NO_FATAL_FAILURE(s.EncryptAndSign());
ASSERT_NO_FATAL_FAILURE(s.LoadTestKeys());
ASSERT_NO_FATAL_FAILURE(s.TestDecryptCTR());
TEST_P(OEMCryptoLicenseTest, Decrypt) {
ASSERT_NO_FATAL_FAILURE(session_.GenerateNonce());
ASSERT_NO_FATAL_FAILURE(license_messages_.SignAndVerifyRequest());
license_messages_.core_response().timer_limits.license_duration_seconds =
kDuration;
ASSERT_NO_FATAL_FAILURE(license_messages_.CreateDefaultResponse());
ASSERT_NO_FATAL_FAILURE(license_messages_.EncryptAndSignResponse());
ASSERT_EQ(OEMCrypto_SUCCESS, license_messages_.LoadResponse());
ASSERT_NO_FATAL_FAILURE(session_.TestDecryptCTR());
}
// Verify that a zero duration means infinite license duration.
TEST_F(OEMCryptoSessionTests, DecryptZeroDuration) {
Session s;
ASSERT_NO_FATAL_FAILURE(s.open());
ASSERT_NO_FATAL_FAILURE(InstallTestRSAKey(&s));
ASSERT_NO_FATAL_FAILURE(s.FillSimpleMessage(0, 0, 0));
ASSERT_NO_FATAL_FAILURE(s.EncryptAndSign());
ASSERT_NO_FATAL_FAILURE(s.LoadTestKeys());
ASSERT_NO_FATAL_FAILURE(s.TestDecryptCTR());
TEST_P(OEMCryptoLicenseTest, DecryptZeroDuration) {
ASSERT_NO_FATAL_FAILURE(session_.GenerateNonce());
ASSERT_NO_FATAL_FAILURE(license_messages_.SignAndVerifyRequest());
license_messages_.core_response().timer_limits.license_duration_seconds = 0;
ASSERT_NO_FATAL_FAILURE(license_messages_.CreateDefaultResponse());
ASSERT_NO_FATAL_FAILURE(license_messages_.EncryptAndSignResponse());
ASSERT_EQ(OEMCrypto_SUCCESS, license_messages_.LoadResponse());
ASSERT_NO_FATAL_FAILURE(session_.TestDecryptCTR());
}
// Verify that several sessions may each load a license and then each may
@@ -2431,23 +2416,27 @@ TEST_F(OEMCryptoSessionTests, DecryptNoAnalogToClearAPI13) {
}
// Test that key duration is honored.
TEST_F(OEMCryptoSessionTests, KeyDuration) {
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());
ASSERT_NO_FATAL_FAILURE(s.TestDecryptCTR(true, OEMCrypto_SUCCESS));
TEST_P(OEMCryptoLicenseTest, KeyDuration) {
ASSERT_NO_FATAL_FAILURE(session_.GenerateNonce());
ASSERT_NO_FATAL_FAILURE(license_messages_.SignAndVerifyRequest());
license_messages_.core_response().timer_limits.license_duration_seconds =
kDuration;
ASSERT_NO_FATAL_FAILURE(license_messages_.CreateDefaultResponse());
ASSERT_NO_FATAL_FAILURE(license_messages_.EncryptAndSignResponse());
ASSERT_EQ(OEMCrypto_SUCCESS, license_messages_.LoadResponse());
ASSERT_NO_FATAL_FAILURE(session_.TestDecryptCTR(true, OEMCrypto_SUCCESS));
wvcdm::TestSleep::Sleep(kShortSleep); // Should still be valid key.
ASSERT_NO_FATAL_FAILURE(s.TestDecryptCTR(false, OEMCrypto_SUCCESS));
ASSERT_NO_FATAL_FAILURE(session_.TestDecryptCTR(false, OEMCrypto_SUCCESS));
wvcdm::TestSleep::Sleep(kLongSleep); // Should be expired key.
ASSERT_NO_FATAL_FAILURE(s.TestDecryptCTR(false, OEMCrypto_ERROR_KEY_EXPIRED));
ASSERT_NO_FATAL_FAILURE(s.TestSelectExpired(0));
ASSERT_NO_FATAL_FAILURE(
session_.TestDecryptCTR(false, OEMCrypto_ERROR_KEY_EXPIRED));
ASSERT_NO_FATAL_FAILURE(session_.TestSelectExpired(0));
}
INSTANTIATE_TEST_CASE_P(TestAll, OEMCryptoLicenseTest,
Range<uint32_t>(kCurrentAPI - 1, kCurrentAPI + 1));
//
// Certificate Root of Trust Tests
//