Update ODK Library and add license release unit test

Merge from Widevine repo of two CLs.

Merge from Widevine repo of http://go/wvgerrit/94743

A license release should not have a core message. This CL adjusts the
existing unit tests to verify this. There is also a new unit test called
SecureStop that explicitly tests sending a secure stop in a new
session without first loading the license.

Merge from Widevine repo of http://go/wvgerrit/94865

This CL has the following changes copied from google3:
http://cr/298871728 Remove odk_static_assert for Message size temporarily
http://cr/298755935 Fix a compiling error during macro expansion
http://cr/298481745 Add missing header for android
http://cr/298448142 Fix odk_test gyp file
http://cr/298419641 Remove header from Android.bp
http://cr/298402053 Separate sizeOf(args) bytes in fuzz tests
http://cr/297730316 No core messages for license release
http://cr/297714346 Add copybara_test and piper_sot_to_gerrit
http://cr/297636713 Adding some comments around boolean conversion code
http://cr/297420679 Autofuzzer when ran with address sanitizer ...
http://cr/296513584 Minor fix with fuzzing odk clock values
http://cr/296322024 Fixing errors in code with how request ...
http://cr/296313159 Fuzzing ODK clock values by setting aside ...
http://cr/295763207 Add more odk tests and move helper functions to test helper
http://cr/294524098 Adding a Build Rule for ODK_KDO_Fuzzer and updating
http://cr/294492213 Address a few review comments of ODK
http://cr/293674368 odk_fuzz: add TODOs & comments
http://cr/293492806 Fix spelling

Bug: 150243585
Bug: 150020278
Bug: 150095506
Bug: 147297226
Bug: 148290294
Bug: 148907684
Bug: 150608451
Test: unit tests
Change-Id: I25fd406f29f4eba40f5cb27e9a1317dce4ffc2f5
This commit is contained in:
Fred Gylys-Colwell
2020-02-28 12:03:28 -08:00
committed by Jeff Tinker
parent 0947bd185b
commit c5b7a01ab5
17 changed files with 1528 additions and 866 deletions

View File

@@ -81,6 +81,7 @@ cc_test {
srcs: [
"test/odk_test.cpp",
"test/odk_test_helper.cpp",
"test/odk_timer_test.cpp",
],

View File

@@ -1,4 +1,5 @@
The ODK Library is used to generate and parse core OEMCrypto messages.
The ODK Library is used to generate and parse core OEMCrypto messages for
OEMCrypto v16 and above.
This library is used by both OEMCrypto on a device, and by Widvine license and
provisioning servers.

View File

@@ -138,10 +138,8 @@ bool CreateCoreLicenseResponseFromProto(const std::string& serialized_license,
parsed_lic.nonce_required = nonce_required;
const auto& policy = lic.policy();
ODK_TimerLimits& timer_limits = parsed_lic.timer_limits;
// TODO(b/148241181): add field to protobuf.
// timer_limits.soft_enforce_rental_duration =
// policy.soft_enforce_rental_duration();
timer_limits.soft_enforce_rental_duration = true;
timer_limits.soft_enforce_rental_duration =
policy.soft_enforce_rental_duration();
timer_limits.soft_enforce_playback_duration =
policy.soft_enforce_playback_duration();
timer_limits.earliest_playback_start_seconds = 0;

View File

@@ -15,48 +15,63 @@
#include "odk_util.h"
#include "serialization_base.h"
#define ODK_LICENSE_REQUEST_SIZE 20
#define ODK_RENEWAL_REQUEST_SIZE 28
#define ODK_PROVISIONING_REQUEST_SIZE 88
/* @ private odk functions */
static OEMCryptoResult ODK_PrepareRequest(uint8_t* buffer, size_t buffer_length,
size_t* core_message_length,
uint32_t message_type,
const ODK_NonceValues* nonce_values,
ODK_CoreMessage* core_message) {
static OEMCryptoResult ODK_PrepareRequest(
uint8_t* message, size_t message_length, size_t* core_message_length,
uint32_t message_type, const ODK_NonceValues* nonce_values,
void* prepared_request_buffer, size_t prepared_request_buffer_length) {
if (nonce_values == NULL || core_message_length == NULL ||
core_message == NULL || *core_message_length > buffer_length) {
prepared_request_buffer == NULL ||
*core_message_length > message_length) {
return ODK_ERROR_CORE_MESSAGE;
}
Message* msg = NULL;
AllocateMessage(&msg, message_block);
InitMessage(msg, buffer, *core_message_length);
InitMessage(msg, message, *core_message_length);
/* The core message should be at the beginning of the buffer, and with a
* shorter length. */
if (sizeof(ODK_CoreMessage) > prepared_request_buffer_length) {
return ODK_ERROR_CORE_MESSAGE;
}
ODK_CoreMessage* core_message = (ODK_CoreMessage*)prepared_request_buffer;
*core_message = (ODK_CoreMessage){
message_type,
0,
*nonce_values,
};
/* Set core message length, and pack prepared request into message if the
* message buffer has been correctly initialized by the caller. */
switch (message_type) {
case ODK_License_Request_Type: {
core_message->message_length = ODK_LICENSE_REQUEST_SIZE;
if (sizeof(ODK_PreparedLicenseRequest) > prepared_request_buffer_length) {
return ODK_ERROR_CORE_MESSAGE;
}
Pack_ODK_PreparedLicenseRequest(
msg, (ODK_PreparedLicenseRequest*)core_message);
msg, (ODK_PreparedLicenseRequest*)prepared_request_buffer);
break;
}
case ODK_Renewal_Request_Type: {
core_message->message_length = ODK_RENEWAL_REQUEST_SIZE;
if (sizeof(ODK_PreparedRenewalRequest) > prepared_request_buffer_length) {
return ODK_ERROR_CORE_MESSAGE;
}
Pack_ODK_PreparedRenewalRequest(
msg, (ODK_PreparedRenewalRequest*)core_message);
msg, (ODK_PreparedRenewalRequest*)prepared_request_buffer);
break;
}
case ODK_Provisioning_Request_Type: {
core_message->message_length = ODK_PROVISIONING_REQUEST_SIZE;
if (sizeof(ODK_PreparedProvisioningRequest) >
prepared_request_buffer_length) {
return ODK_ERROR_CORE_MESSAGE;
}
Pack_ODK_PreparedProvisioningRequest(
msg, (ODK_PreparedProvisioningRequest*)core_message);
msg, (ODK_PreparedProvisioningRequest*)prepared_request_buffer);
break;
}
default: {
@@ -65,43 +80,59 @@ static OEMCryptoResult ODK_PrepareRequest(uint8_t* buffer, size_t buffer_length,
}
*core_message_length = core_message->message_length;
if (GetStatus(msg) != MESSAGE_STATUS_OK ||
GetSize(msg) != *core_message_length) {
if (GetStatus(msg) != MESSAGE_STATUS_OK) {
/* This is to indicate the caller that the core_message_length has been
* appropriately set, but the message buffer is either empty or too small,
* which needs to be initialized and filled in the subsequent call. */
return OEMCrypto_ERROR_SHORT_BUFFER;
}
if (GetSize(msg) != *core_message_length) {
/* This should not happen. Something is wrong. */
return ODK_ERROR_CORE_MESSAGE;
}
return OEMCrypto_SUCCESS;
}
static OEMCryptoResult ODK_ParseResponse(const uint8_t* buf,
size_t message_length,
size_t core_message_length,
uint32_t message_type,
const ODK_NonceValues* nonce_values,
ODK_CoreMessage* const core_message) {
if (core_message_length > message_length) {
static OEMCryptoResult ODK_ParseResponse(
const uint8_t* message, size_t message_length, size_t core_message_length,
uint32_t message_type, const ODK_NonceValues* nonce_values,
void* response_buffer, uint32_t response_buffer_length) {
if (message == NULL || response_buffer == NULL ||
core_message_length > message_length) {
return ODK_ERROR_CORE_MESSAGE;
}
Message* msg = NULL;
AllocateMessage(&msg, message_block);
/* We initialize the message buffer with a size of the entire message
* length. */
InitMessage(msg, (uint8_t*)buf, message_length);
InitMessage(msg, (uint8_t*)message, message_length);
/* The core message should be at the beginning of the buffer, and with a
* shorter length. The core message is the part we are parsing. */
SetSize(msg, core_message_length);
/* Parse message and unpack it into response buffer. */
switch (message_type) {
case ODK_License_Response_Type: {
Unpack_ODK_LicenseResponse(msg, (ODK_LicenseResponse*)core_message);
if (sizeof(ODK_LicenseResponse) > response_buffer_length) {
return ODK_ERROR_CORE_MESSAGE;
}
Unpack_ODK_LicenseResponse(msg, (ODK_LicenseResponse*)response_buffer);
break;
}
case ODK_Renewal_Response_Type: {
Unpack_ODK_RenewalResponse(msg, (ODK_RenewalResponse*)core_message);
if (sizeof(ODK_RenewalResponse) > response_buffer_length) {
return ODK_ERROR_CORE_MESSAGE;
}
Unpack_ODK_RenewalResponse(msg, (ODK_RenewalResponse*)response_buffer);
break;
}
case ODK_Provisioning_Response_Type: {
Unpack_ODK_ProvisioningResponse(msg,
(ODK_ProvisioningResponse*)core_message);
if (sizeof(ODK_ProvisioningResponse) > response_buffer_length) {
return ODK_ERROR_CORE_MESSAGE;
}
Unpack_ODK_ProvisioningResponse(
msg, (ODK_ProvisioningResponse*)response_buffer);
break;
}
default: {
@@ -109,6 +140,7 @@ static OEMCryptoResult ODK_ParseResponse(const uint8_t* buf,
}
}
ODK_CoreMessage* core_message = (ODK_CoreMessage*)response_buffer;
if (GetStatus(msg) != MESSAGE_STATUS_OK ||
message_type != core_message->message_type ||
GetOffset(msg) != core_message->message_length) {
@@ -117,12 +149,7 @@ static OEMCryptoResult ODK_ParseResponse(const uint8_t* buf,
if (nonce_values) {
/* always verify nonce_values for Renewal and Provisioning responses */
if (nonce_values->api_major_version !=
core_message->nonce_values.api_major_version ||
nonce_values->api_minor_version !=
core_message->nonce_values.api_minor_version ||
nonce_values->nonce != core_message->nonce_values.nonce ||
nonce_values->session_id != core_message->nonce_values.session_id) {
if (!ODK_NonceValuesEqual(nonce_values, &(core_message->nonce_values))) {
return OEMCrypto_ERROR_INVALID_NONCE;
}
}
@@ -137,12 +164,15 @@ static OEMCryptoResult ODK_ParseResponse(const uint8_t* buf,
OEMCryptoResult ODK_PrepareCoreLicenseRequest(
uint8_t* message, size_t message_length, size_t* core_message_length,
const ODK_NonceValues* nonce_values) {
if (core_message_length == NULL || nonce_values == NULL) {
return ODK_ERROR_CORE_MESSAGE;
}
ODK_PreparedLicenseRequest license_request = {
{0},
};
return ODK_PrepareRequest(message, message_length, core_message_length,
ODK_License_Request_Type, nonce_values,
&license_request.core_message);
return ODK_PrepareRequest(
message, message_length, core_message_length, ODK_License_Request_Type,
nonce_values, &license_request, sizeof(ODK_PreparedLicenseRequest));
}
OEMCryptoResult ODK_PrepareCoreRenewalRequest(uint8_t* message,
@@ -151,8 +181,22 @@ OEMCryptoResult ODK_PrepareCoreRenewalRequest(uint8_t* message,
ODK_NonceValues* nonce_values,
ODK_ClockValues* clock_values,
uint64_t system_time_seconds) {
if (nonce_values == NULL || clock_values == NULL)
if (core_message_size == NULL || nonce_values == NULL ||
clock_values == NULL) {
return ODK_ERROR_CORE_MESSAGE;
}
/* If the license has not been loaded, then this is release instead of a
* renewal. All releases use v15. */
if (clock_values->timer_status == ODK_CLOCK_TIMER_STATUS_LICENSE_NOT_LOADED ||
clock_values->timer_status == ODK_CLOCK_TIMER_STATUS_LICENSE_INACTIVE) {
nonce_values->api_major_version = 15;
}
if (nonce_values->api_major_version < 16) {
*core_message_size = 0;
return OEMCrypto_SUCCESS;
}
ODK_PreparedRenewalRequest renewal_request = {{0}, 0};
/* First, we compute the time this request was made relative to the playback
* clock. */
@@ -168,19 +212,24 @@ OEMCryptoResult ODK_PrepareCoreRenewalRequest(uint8_t* message,
return ODK_ERROR_CORE_MESSAGE;
}
}
/* Save time for this request so that we can verify the response. This makes
* all earlier requests invalid. If preparing this request fails, then all
* requests will be bad. */
clock_values->time_of_renewal_request = renewal_request.playback_time;
return ODK_PrepareRequest(message, message_length, core_message_size,
ODK_Renewal_Request_Type, nonce_values,
&renewal_request.core_message);
return ODK_PrepareRequest(
message, message_length, core_message_size, ODK_Renewal_Request_Type,
nonce_values, &renewal_request, sizeof(ODK_PreparedRenewalRequest));
}
OEMCryptoResult ODK_PrepareCoreProvisioningRequest(
uint8_t* message, size_t message_length, size_t* core_message_length,
const ODK_NonceValues* nonce_values, const uint8_t* device_id,
size_t device_id_length) {
if (core_message_length == NULL || nonce_values == NULL) {
return ODK_ERROR_CORE_MESSAGE;
}
ODK_PreparedProvisioningRequest provisioning_request = {
{0},
0,
@@ -195,7 +244,8 @@ OEMCryptoResult ODK_PrepareCoreProvisioningRequest(
}
return ODK_PrepareRequest(message, message_length, core_message_length,
ODK_Provisioning_Request_Type, nonce_values,
&provisioning_request.core_message);
&provisioning_request,
sizeof(ODK_PreparedProvisioningRequest));
}
/* @@ parse request functions */
@@ -214,7 +264,7 @@ OEMCryptoResult ODK_ParseLicense(
ODK_LicenseResponse license_response = {{{0}}, parsed_license, {0}};
const OEMCryptoResult err = ODK_ParseResponse(
message, message_length, core_message_length, ODK_License_Response_Type,
NULL, &license_response.request.core_message);
NULL, &license_response, sizeof(ODK_LicenseResponse));
if (err != OEMCrypto_SUCCESS) {
return err;
@@ -279,11 +329,11 @@ OEMCryptoResult ODK_ParseRenewal(const uint8_t* message, size_t message_length,
{{0}, 0},
0,
};
OEMCryptoResult err = ODK_ParseResponse(
const OEMCryptoResult err = ODK_ParseResponse(
message, message_length, core_message_length, ODK_Renewal_Response_Type,
nonce_values, &renewal_response.request.core_message);
nonce_values, &renewal_response, sizeof(ODK_RenewalResponse));
if (err) {
if (err != OEMCrypto_SUCCESS) {
return err;
}
@@ -318,24 +368,24 @@ OEMCryptoResult ODK_ParseProvisioning(
return ODK_ERROR_CORE_MESSAGE;
}
OEMCryptoResult err =
ODK_ParseResponse(message, message_length, core_message_length,
ODK_Provisioning_Response_Type, nonce_values,
&provisioning_response.request.core_message);
const OEMCryptoResult err = ODK_ParseResponse(
message, message_length, core_message_length,
ODK_Provisioning_Response_Type, nonce_values, &provisioning_response,
sizeof(ODK_ProvisioningResponse));
if (err) {
if (err != OEMCrypto_SUCCESS) {
return err;
}
if (memcmp(device_id, provisioning_response.request.device_id,
device_id_length)) {
if (crypto_memcmp(device_id, provisioning_response.request.device_id,
device_id_length) != 0) {
return ODK_ERROR_CORE_MESSAGE;
}
uint8_t zero[ODK_DEVICE_ID_LEN_MAX] = {0};
const uint8_t zero[ODK_DEVICE_ID_LEN_MAX] = {0};
/* check bytes beyond device_id_length are 0 */
if (memcmp(zero, provisioning_response.request.device_id + device_id_length,
ODK_DEVICE_ID_LEN_MAX - device_id_length)) {
ODK_DEVICE_ID_LEN_MAX - device_id_length) != 0) {
return ODK_ERROR_CORE_MESSAGE;
}

View File

@@ -60,6 +60,10 @@ typedef struct {
ODK_ParsedProvisioning* parsed_provisioning;
} ODK_ProvisioningResponse;
#define ODK_LICENSE_REQUEST_SIZE 20
#define ODK_RENEWAL_REQUEST_SIZE 28
#define ODK_PROVISIONING_REQUEST_SIZE 88
/* These are the possible timer status values. */
#define ODK_CLOCK_TIMER_STATUS_UNDEFINED 0 /* Should not happen. */
/* When the structure has been initialized, but no license is loaded. */

View File

@@ -4,7 +4,6 @@
#include <stdint.h>
#include <string.h>
#include "odk.h"
#include "odk_overflow.h"
#include "odk_structs_priv.h"
@@ -12,8 +11,7 @@
/* Private function. Checks to see if the license is active. Returns
* ODK_TIMER_EXPIRED if the license is valid but inactive. Returns
* OEMCrypto_SUCCESS if the license is active. Returns
* OEMCrypto_ERROR_UNKNOWN_FAILURE on other errors. This also updates the
* timer_status if appropriate. */
* OEMCrypto_ERROR_UNKNOWN_FAILURE on other errors. */
static OEMCryptoResult ODK_LicenseActive(const ODK_TimerLimits* timer_limits,
ODK_ClockValues* clock_values) {
/* Check some basic errors. */
@@ -26,7 +24,6 @@ static OEMCryptoResult ODK_LicenseActive(const ODK_TimerLimits* timer_limits,
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
}
if (clock_values->status > kActive) {
clock_values->timer_status = ODK_CLOCK_TIMER_STATUS_LICENSE_INACTIVE;
return ODK_TIMER_EXPIRED;
}
return OEMCrypto_SUCCESS;
@@ -157,7 +154,9 @@ static OEMCryptoResult ODK_CheckPlaybackWindow(
* have already computed the timer limit. */
static void ODK_UpdateTimerStatusForRenewal(ODK_ClockValues* clock_values,
uint32_t new_status) {
if (clock_values == NULL) return; /* should not happen. */
if (clock_values == NULL) {
return; /* should not happen. */
}
if (clock_values->timer_status == ODK_CLOCK_TIMER_STATUS_LICENSE_LOADED) {
/* Signal that the timer is already set. */
clock_values->timer_status = ODK_CLOCK_TIMER_STATUS_RENEWAL_LOADED;
@@ -174,17 +173,21 @@ OEMCryptoResult ODK_ComputeRenewalDuration(const ODK_TimerLimits* timer_limits,
uint64_t system_time_seconds,
uint64_t new_renewal_duration,
uint64_t* timer_value) {
if (timer_limits == NULL || clock_values == NULL)
if (timer_limits == NULL || clock_values == NULL) {
return OEMCrypto_ERROR_INVALID_CONTEXT; /* should not happen. */
}
/* If this is before the license was signed, something is odd. Return an
* error. */
if (system_time_seconds < clock_values->time_of_license_signed)
if (system_time_seconds < clock_values->time_of_license_signed) {
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
}
const OEMCryptoResult license_status =
ODK_LicenseActive(timer_limits, clock_values);
/* If the license is not active, then we cannot renew the license. */
if (license_status != OEMCrypto_SUCCESS) return license_status;
if (license_status != OEMCrypto_SUCCESS) {
return license_status;
}
/* We start with the new renewal duration as the new timer limit. */
uint64_t new_timer_value = new_renewal_duration;
@@ -193,9 +196,12 @@ OEMCryptoResult ODK_ComputeRenewalDuration(const ODK_TimerLimits* timer_limits,
* new_timer_value. */
const OEMCryptoResult rental_status = ODK_CheckRentalWindow(
timer_limits, clock_values, system_time_seconds, &new_timer_value);
/* If the rental status forbids playback, then we're done. */
if ((rental_status != ODK_DISABLE_TIMER) && (rental_status != ODK_SET_TIMER))
if ((rental_status != ODK_DISABLE_TIMER) &&
(rental_status != ODK_SET_TIMER)) {
return rental_status;
}
/* If playback has already started and it has hard enforcement, then check
* total playback window. */
@@ -207,8 +213,9 @@ OEMCryptoResult ODK_ComputeRenewalDuration(const ODK_TimerLimits* timer_limits,
/* If the timer limits forbid playback in the playback window, then we're
* done. */
if ((playback_status != ODK_DISABLE_TIMER) &&
(playback_status != ODK_SET_TIMER))
(playback_status != ODK_SET_TIMER)) {
return playback_status;
}
}
/* If new_timer_value is infinite (represented by 0), then there are no
@@ -219,6 +226,7 @@ OEMCryptoResult ODK_ComputeRenewalDuration(const ODK_TimerLimits* timer_limits,
ODK_CLOCK_TIMER_STATUS_UNLIMITED);
return ODK_DISABLE_TIMER;
}
/* If the caller gave us a pointer to store the new timer value. Fill it. */
if (timer_value != NULL) {
*timer_value = new_timer_value;
@@ -241,8 +249,9 @@ OEMCryptoResult ODK_InitializeSessionValues(ODK_TimerLimits* timer_limits,
ODK_NonceValues* nonce_values,
uint32_t api_major_version,
uint32_t session_id) {
if (clock_values == NULL || clock_values == NULL || nonce_values == NULL)
if (timer_limits == NULL || clock_values == NULL || nonce_values == NULL) {
return OEMCrypto_ERROR_INVALID_CONTEXT;
}
/* Check that the API version passed in from OEMCrypto matches the version of
* this ODK library. */
if (api_major_version != ODK_MAJOR_VERSION) {
@@ -269,7 +278,9 @@ OEMCryptoResult ODK_InitializeSessionValues(ODK_TimerLimits* timer_limits,
* OEMCrypto_GenerateNonce. */
OEMCryptoResult ODK_SetNonceValues(ODK_NonceValues* nonce_values,
uint32_t nonce) {
if (nonce_values == NULL) return OEMCrypto_ERROR_INVALID_CONTEXT;
if (nonce_values == NULL) {
return OEMCrypto_ERROR_INVALID_CONTEXT;
}
/* Setting the nonce should only happen once per session. */
if (nonce_values->nonce != 0) {
return OEMCrypto_ERROR_INVALID_CONTEXT;
@@ -281,7 +292,9 @@ OEMCryptoResult ODK_SetNonceValues(ODK_NonceValues* nonce_values,
/* This is called when OEMCrypto signs a license. */
OEMCryptoResult ODK_InitializeClockValues(ODK_ClockValues* clock_values,
uint64_t system_time_seconds) {
if (clock_values == NULL) return OEMCrypto_ERROR_INVALID_CONTEXT;
if (clock_values == NULL) {
return OEMCrypto_ERROR_INVALID_CONTEXT;
}
clock_values->time_of_license_signed = system_time_seconds;
clock_values->time_of_first_decrypt = 0;
clock_values->time_of_last_decrypt = 0;
@@ -298,7 +311,9 @@ OEMCryptoResult ODK_ReloadClockValues(ODK_ClockValues* clock_values,
uint64_t time_of_last_decrypt,
enum OEMCrypto_Usage_Entry_Status status,
uint64_t system_time_seconds) {
if (clock_values == NULL) return OEMCrypto_ERROR_INVALID_CONTEXT;
if (clock_values == NULL) {
return OEMCrypto_ERROR_INVALID_CONTEXT;
}
clock_values->time_of_license_signed = time_of_license_signed;
clock_values->time_of_first_decrypt = time_of_first_decrypt;
clock_values->time_of_last_decrypt = time_of_last_decrypt;
@@ -313,8 +328,9 @@ OEMCryptoResult ODK_AttemptFirstPlayback(uint64_t system_time_seconds,
const ODK_TimerLimits* timer_limits,
ODK_ClockValues* clock_values,
uint64_t* timer_value) {
if (clock_values == NULL || timer_limits == NULL)
if (clock_values == NULL || timer_limits == NULL) {
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
}
/* All times are relative to when the license was signed. */
uint64_t rental_time = 0;
if (odk_sub_overflow_u64(system_time_seconds,
@@ -328,7 +344,9 @@ OEMCryptoResult ODK_AttemptFirstPlayback(uint64_t system_time_seconds,
}
/* If the license is inactive or not loaded, then playback is not allowed. */
OEMCryptoResult status = ODK_LicenseActive(timer_limits, clock_values);
if (status != OEMCrypto_SUCCESS) return status;
if (status != OEMCrypto_SUCCESS) {
return status;
}
/* We start with the initial renewal duration as the timer limit. */
uint64_t new_timer_value = timer_limits->initial_renewal_duration_seconds;
@@ -348,7 +366,9 @@ OEMCryptoResult ODK_AttemptFirstPlayback(uint64_t system_time_seconds,
* new_timer_value. */
status = ODK_CheckRentalWindow(timer_limits, clock_values,
system_time_seconds, &new_timer_value);
if ((status != ODK_DISABLE_TIMER) && (status != ODK_SET_TIMER)) return status;
if ((status != ODK_DISABLE_TIMER) && (status != ODK_SET_TIMER)) {
return status;
}
/* If playback has not already started, then this is the first playback. */
if (clock_values->time_of_first_decrypt == 0) {
@@ -360,7 +380,9 @@ OEMCryptoResult ODK_AttemptFirstPlayback(uint64_t system_time_seconds,
* restrictions. This might decrease new_timer_value. */
status = ODK_CheckPlaybackWindow(timer_limits, clock_values,
system_time_seconds, &new_timer_value);
if ((status != ODK_DISABLE_TIMER) && (status != ODK_SET_TIMER)) return status;
if ((status != ODK_DISABLE_TIMER) && (status != ODK_SET_TIMER)) {
return status;
}
/* We know we are allowed to decrypt. The rest computes the timer duration. */
clock_values->time_of_last_decrypt = system_time_seconds;
@@ -390,7 +412,9 @@ OEMCryptoResult ODK_UpdateLastPlaybackTime(uint64_t system_time_seconds,
const ODK_TimerLimits* timer_limits,
ODK_ClockValues* clock_values) {
OEMCryptoResult status = ODK_LicenseActive(timer_limits, clock_values);
if (status != OEMCrypto_SUCCESS) return status;
if (status != OEMCrypto_SUCCESS) {
return status;
}
switch (clock_values->timer_status) {
case ODK_CLOCK_TIMER_STATUS_UNLIMITED:
break;
@@ -413,7 +437,9 @@ OEMCryptoResult ODK_UpdateLastPlaybackTime(uint64_t system_time_seconds,
/* This is called from OEMCrypto_DeactivateUsageEntry. */
OEMCryptoResult ODK_DeactivateUsageEntry(ODK_ClockValues* clock_values) {
if (clock_values == NULL) return OEMCrypto_ERROR_UNKNOWN_FAILURE;
if (clock_values == NULL) {
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
}
if (clock_values->status == kUnused) {
clock_values->status = kInactiveUnused;
} else if (clock_values->status == kActive) {
@@ -430,8 +456,9 @@ OEMCryptoResult ODK_InitializeV15Values(ODK_TimerLimits* timer_limits,
ODK_NonceValues* nonce_values,
uint32_t key_duration,
uint64_t system_time_seconds) {
if (clock_values == NULL || clock_values == NULL || nonce_values == NULL)
if (timer_limits == NULL || clock_values == NULL || nonce_values == NULL) {
return OEMCrypto_ERROR_INVALID_CONTEXT;
}
timer_limits->soft_enforce_playback_duration = false;
timer_limits->soft_enforce_rental_duration = false;
timer_limits->earliest_playback_start_seconds = 0;
@@ -458,12 +485,14 @@ OEMCryptoResult ODK_RefreshV15Values(const ODK_TimerLimits* timer_limits,
uint64_t system_time_seconds,
uint32_t new_key_duration,
uint64_t* timer_value) {
if (timer_limits == NULL || clock_values == NULL || nonce_values == NULL)
if (timer_limits == NULL || clock_values == NULL || nonce_values == NULL) {
return OEMCrypto_ERROR_INVALID_CONTEXT;
if (nonce_values->api_major_version != 15)
}
if (nonce_values->api_major_version != 15) {
return OEMCrypto_ERROR_INVALID_NONCE;
}
if (clock_values->status > kActive) {
clock_values->timer_status = ODK_TIMER_EXPIRED;
clock_values->timer_status = ODK_CLOCK_TIMER_STATUS_LICENSE_INACTIVE;
return ODK_TIMER_EXPIRED;
}
return ODK_ComputeRenewalDuration(timer_limits, clock_values,

View File

@@ -23,3 +23,12 @@ int crypto_memcmp(const void* in_a, const void* in_b, size_t len) {
}
return x;
}
bool ODK_NonceValuesEqual(const ODK_NonceValues* a, const ODK_NonceValues* b) {
if (a == NULL || b == NULL) {
return (a == b);
}
return (a->api_major_version == b->api_major_version &&
a->api_minor_version == b->api_minor_version &&
a->nonce == b->nonce && a->session_id == b->session_id);
}

View File

@@ -8,6 +8,8 @@
#include <stddef.h>
#include <stdint.h>
#include "odk_structs.h"
#ifdef __cplusplus
extern "C" {
#endif
@@ -18,6 +20,8 @@ extern "C" {
* return value when a != b is undefined, other than being non-zero. */
int crypto_memcmp(const void* a, const void* b, size_t len);
bool ODK_NonceValuesEqual(const ODK_NonceValues* a, const ODK_NonceValues* b);
#ifdef __cplusplus
} /* extern "C" */
#endif

View File

@@ -20,8 +20,12 @@ struct _Message {
MessageStatus status;
};
odk_static_assert(SIZE_OF_MESSAGE_STRUCT >= sizeof(Message),
"SIZE_OF_MESSAGE_STRUCT too small");
/* TODO(b/150776214): this can be removed once AllocateMessage gets cleaned up
*/
/*
* odk_static_assert(SIZE_OF_MESSAGE_STRUCT >= sizeof(struct _Message),
* "SIZE_OF_MESSAGE_STRUCT too small");
*/
bool ValidMessage(Message* message) {
if (message == NULL) {
@@ -235,7 +239,8 @@ size_t GetSize(Message* message) {
void SetSize(Message* message, size_t size) {
if (message == NULL) return;
if (size > message->capacity) message->status = MESSAGE_STATUS_OVERFLOW_ERROR;
if (size > message->capacity)
message->status = MESSAGE_STATUS_OVERFLOW_ERROR;
else
message->size = size;
}

View File

@@ -28,10 +28,20 @@ extern "C" {
*/
#define AllocateMessage(msg, blk) \
uint8_t blk[SIZE_OF_MESSAGE_STRUCT]; \
*(msg) = (Message*)(blk);
*(msg) = (Message*)(blk)
typedef struct _Message Message;
typedef enum {
MESSAGE_STATUS_OK,
MESSAGE_STATUS_UNKNOWN_ERROR,
MESSAGE_STATUS_OVERFLOW_ERROR,
MESSAGE_STATUS_UNDERFLOW_ERROR,
MESSAGE_STATUS_PARSE_ERROR,
MESSAGE_STATUS_NULL_POINTER_ERROR,
MESSAGE_STATUS_API_VALUE_ERROR
} MessageStatus;
bool ValidMessage(Message* message);
void Pack_enum(Message* message, int value);
@@ -51,16 +61,6 @@ void UnpackArray(Message* message, uint8_t* address,
size_t size); /* copy out */
void Unpack_OEMCrypto_Substring(Message* msg, OEMCrypto_Substring* obj);
typedef enum {
MESSAGE_STATUS_OK,
MESSAGE_STATUS_UNKNOWN_ERROR,
MESSAGE_STATUS_OVERFLOW_ERROR,
MESSAGE_STATUS_UNDERFLOW_ERROR,
MESSAGE_STATUS_PARSE_ERROR,
MESSAGE_STATUS_NULL_POINTER_ERROR,
MESSAGE_STATUS_API_VALUE_ERROR
} MessageStatus;
/*
* Create a message from a buffer. The message structure consumes the first
* sizeof(Message) bytes of the buffer. The caller is responsible for ensuring

View File

@@ -9,7 +9,7 @@
#include <cstring>
#include <functional>
#include <memory>
#include <string>
#include <vector>
#include "OEMCryptoCENCCommon.h"
#include "core_message_deserialize.h"
@@ -20,48 +20,74 @@
#include "odk_structs.h"
#include "odk_structs_priv.h"
// TODO(b/147297226): remove this: using namespace std;
// TODO(b/147297226): remove this: using namespace oec_util;
typedef std::function<size_t(const uint8_t*, uint8_t*, size_t, size_t)>
roundtrip_fun;
typedef std::function<size_t(const uint8_t*, uint8_t*, size_t)> roundtrip_fun;
using oemcrypto_core_message::ODK_LicenseRequest;
using oemcrypto_core_message::ODK_ProvisioningRequest;
using oemcrypto_core_message::ODK_RenewalRequest;
// @ kdo deserialize; odk derialize
static OEMCryptoResult odk_fun_LicenseRequest(
uint8_t* out, size_t* size, uint32_t api_version, uint32_t nonce,
uint32_t session_id, const ODK_LicenseRequest& /*core_license_request*/) {
return ODK_PrepareCoreLicenseRequest(out, SIZE_MAX, size, api_version, nonce,
session_id);
using oemcrypto_core_message::deserialize::CoreLicenseRequestFromMessage;
using oemcrypto_core_message::deserialize::CoreProvisioningRequestFromMessage;
using oemcrypto_core_message::deserialize::CoreRenewalRequestFromMessage;
using oemcrypto_core_message::serialize::CreateCoreLicenseResponse;
using oemcrypto_core_message::serialize::CreateCoreProvisioningResponse;
using oemcrypto_core_message::serialize::CreateCoreRenewalResponse;
// @ kdo deserialize; odk serialize
static OEMCryptoResult odk_serialize_LicenseRequest(
const void* in, uint8_t* out, size_t* size,
const ODK_LicenseRequest& core_license_request,
const ODK_NonceValues* nonce_values) {
return ODK_PrepareCoreLicenseRequest(out, SIZE_MAX, size, nonce_values);
}
static OEMCryptoResult odk_fun_RenewalRequest(
uint8_t* out, size_t* size, uint32_t api_version, uint32_t nonce,
uint32_t session_id, const ODK_RenewalRequest& core_renewal) {
// todo: fuzz ODK_ClockValues
ODK_ClockValues clock = {};
uint64_t system_time_seconds = core_renewal.playback_time;
return ODK_PrepareCoreRenewalRequest(out, SIZE_MAX, size, api_version, nonce,
session_id, &clock, system_time_seconds);
static OEMCryptoResult odk_serialize_RenewalRequest(
const void* in, uint8_t* out, size_t* size,
const ODK_RenewalRequest& core_renewal, ODK_NonceValues* nonce_values) {
ODK_ClockValues clock{};
memcpy(&clock, in, sizeof(ODK_ClockValues));
uint64_t system_time_seconds = core_renewal.playback_time_seconds;
return ODK_PrepareCoreRenewalRequest(out, SIZE_MAX, size, nonce_values,
&clock, system_time_seconds);
}
static OEMCryptoResult odk_fun_ProvisioningRequest(
uint8_t* out, size_t* size, uint32_t api_version, uint32_t nonce,
uint32_t session_id, const ODK_ProvisioningRequest& core_provisioning) {
static OEMCryptoResult odk_serialize_ProvisioningRequest(
const void* in, uint8_t* out, size_t* size,
const ODK_ProvisioningRequest& core_provisioning,
const ODK_NonceValues* nonce_values) {
const std::string& device_id = core_provisioning.device_id;
return ODK_PrepareCoreProvisioningRequest(
out, SIZE_MAX, size, api_version, nonce, session_id,
out, SIZE_MAX, size, nonce_values,
reinterpret_cast<const uint8_t*>(device_id.data()), device_id.size());
}
/**
* Template arguments:
* T: kdo deserialize output/odk serialize input structure
* F: kdo deserialize function
* G: odk serialize function
*
* raw bytes -> F deserialize -> struct T -> G serialize -> raw bytes
*/
template <typename T, typename F, typename G>
static roundtrip_fun kdo_odk(const F& kdo_fun, const G& odk_fun) {
auto roundtrip = [&](const uint8_t* in, uint8_t* out, size_t size) -> size_t {
std::string input(reinterpret_cast<const char*>(in), size);
auto roundtrip = [&](const uint8_t* in, uint8_t* out, size_t size,
size_t clock_value_size) -> size_t {
if (size <= clock_value_size) {
return 0;
}
// Input byte array format: [Clock Values][data to parse]
std::string input(reinterpret_cast<const char*>(in) + clock_value_size,
size - clock_value_size);
T t = {};
if (!kdo_fun(input, &t)) {
return 0;
}
OEMCryptoResult err =
odk_fun(out, &size, t.api_version, t.nonce, t.session_id, t);
ODK_NonceValues nonce_values = {t.api_minor_version, t.api_major_version,
t.nonce, t.session_id};
OEMCryptoResult err = odk_fun(in, out, &size, t, &nonce_values);
return OEMCrypto_SUCCESS == err ? size : 0;
};
return roundtrip;
@@ -70,7 +96,8 @@ static roundtrip_fun kdo_odk(const F& kdo_fun, const G& odk_fun) {
// @ odk deserialize; kdo serialize
namespace {
struct ODK_Common_Args {
uint32_t api_version;
uint16_t api_minor_version;
uint16_t api_major_version;
uint32_t nonce;
uint32_t session_id;
};
@@ -78,6 +105,9 @@ struct ODK_ParseLicense_Args {
ODK_Common_Args common;
uint8_t initial_license_load;
uint8_t usage_entry_present;
uint8_t request_hash[32];
ODK_TimerLimits timer_limits;
ODK_ClockValues clock_values;
};
struct ODK_ParseRenewal_Args {
ODK_Common_Args common;
@@ -92,93 +122,128 @@ struct ODK_ParseProvisioning_Args {
};
} // namespace
static OEMCryptoResult odk_fun_LicenseResponse(
const uint8_t* message, size_t message_length, uint32_t api_version,
uint32_t nonce, uint32_t session_id, const ODK_ParseLicense_Args* a,
ODK_ParsedLicense& parsed_lic) {
return ODK_ParseLicense(
message, message_length, api_version, nonce, session_id,
static_cast<bool>(a->initial_license_load),
static_cast<bool>(a->usage_entry_present), &parsed_lic);
uint8_t convert_byte_to_valid_boolean(const bool* in) {
uint8_t boolean_value;
memcpy(&boolean_value, in, 1);
return boolean_value % 2;
}
static bool kdo_fun_LicenseResponse(const ODK_ParseLicense_Args* args,
const ODK_ParsedLicense& parsed_lic,
std::string* oemcrypto_core_message) {
static OEMCryptoResult odk_deserialize_LicenseResponse(
const uint8_t* message, size_t core_message_length,
ODK_ParseLicense_Args* a, ODK_NonceValues* nonce_values,
ODK_ParsedLicense* parsed_lic) {
return ODK_ParseLicense(message, SIZE_MAX, core_message_length,
static_cast<bool>(a->initial_license_load),
static_cast<bool>(a->usage_entry_present),
a->request_hash, &a->timer_limits, &a->clock_values,
nonce_values, parsed_lic);
}
static bool kdo_serialize_LicenseResponse(const ODK_ParseLicense_Args* args,
const ODK_ParsedLicense& parsed_lic,
std::string* oemcrypto_core_message) {
const auto& common = args->common;
ODK_LicenseRequest core_request{common.api_version, common.nonce,
ODK_LicenseRequest core_request{common.api_minor_version,
common.api_major_version, common.nonce,
common.session_id};
return CreateCoreLicenseResponse(parsed_lic, core_request,
oemcrypto_core_message);
std::string core_request_sha_256(
reinterpret_cast<const char*>(args->request_hash), 32);
return CreateCoreLicenseResponse(
parsed_lic, core_request, core_request_sha_256, oemcrypto_core_message);
}
static OEMCryptoResult odk_fun_RenewalResponse(
const uint8_t* buf, size_t len, uint32_t api_version, uint32_t nonce,
uint32_t session_id, ODK_ParseRenewal_Args* a,
ODK_PreparedRenewalRequest& renewal_msg) {
static OEMCryptoResult odk_deserialize_RenewalResponse(
const uint8_t* buf, size_t len, ODK_ParseRenewal_Args* a,
ODK_NonceValues* nonce_values, ODK_PreparedRenewalRequest* renewal_msg) {
/* Address Sanitizer doesn't like values other than 0 OR 1 for boolean
* variables. Input from fuzzer can be parsed and any random bytes can be
* assigned to boolean variables. Using the workaround to mitigate sanitizer
* errors in fuzzer code and converting random bytes to 0 OR 1.
* This has no negative security impact*/
a->timer_limits.soft_enforce_playback_duration =
convert_byte_to_valid_boolean(
&a->timer_limits.soft_enforce_playback_duration);
a->timer_limits.soft_enforce_rental_duration = convert_byte_to_valid_boolean(
&a->timer_limits.soft_enforce_rental_duration);
uint64_t timer_value = 0;
OEMCryptoResult err =
ODK_ParseRenewal(buf, len, api_version, nonce, session_id, a->system_time,
ODK_ParseRenewal(buf, SIZE_MAX, len, nonce_values, a->system_time,
&a->timer_limits, &a->clock_values, &timer_value);
if (OEMCrypto_SUCCESS == err) {
Message* msg = nullptr;
AllocateMessage(&msg, message_block);
InitMessage(msg, const_cast<uint8_t*>(buf), len);
SetSize(msg, len);
Unpack_ODK_PreparedRenewalRequest(msg, &renewal_msg);
Unpack_ODK_PreparedRenewalRequest(msg, renewal_msg);
assert(ValidMessage(msg));
}
return err;
}
static bool kdo_fun_RenewalResponse(
static bool kdo_serialize_RenewalResponse(
const ODK_ParseRenewal_Args* args,
const ODK_PreparedRenewalRequest& renewal_msg,
std::string* oemcrypto_core_message) {
const auto& common = args->common;
ODK_RenewalRequest core_request{common.api_version, common.nonce,
ODK_RenewalRequest core_request{common.api_minor_version,
common.api_major_version, common.nonce,
common.session_id, renewal_msg.playback_time};
return CreateCoreRenewalResponse(core_request, oemcrypto_core_message);
return CreateCoreRenewalResponse(
core_request, args->timer_limits.initial_renewal_duration_seconds,
oemcrypto_core_message);
}
static OEMCryptoResult odk_fun_ProvisioningResponse(
const uint8_t* buf, size_t len, uint32_t api_version, uint32_t nonce,
uint32_t session_id, ODK_ParseProvisioning_Args* a,
ODK_ParsedProvisioning& parsed_prov) {
return ODK_ParseProvisioning(buf, len, api_version, nonce, session_id,
a->device_id, a->device_id_length, &parsed_prov);
static OEMCryptoResult odk_deserialize_ProvisioningResponse(
const uint8_t* buf, size_t len, ODK_ParseProvisioning_Args* a,
ODK_NonceValues* nonce_values, ODK_ParsedProvisioning* parsed_prov) {
return ODK_ParseProvisioning(buf, SIZE_MAX, len, nonce_values, a->device_id,
a->device_id_length, parsed_prov);
}
static bool kdo_fun_ProvisioningResponse(
static bool kdo_serialize_ProvisioningResponse(
const ODK_ParseProvisioning_Args* args,
const ODK_ParsedProvisioning& parsed_prov,
std::string* oemcrypto_core_message) {
const auto& common = args->common;
assert(args->device_id_length <= sizeof(args->device_id));
ODK_ProvisioningRequest core_request{
common.api_version, common.nonce, common.session_id,
common.api_minor_version, common.api_major_version, common.nonce,
common.session_id,
std::string(reinterpret_cast<const char*>(args->device_id),
args->device_id_length)};
return CreateCoreProvisioningResponse(parsed_prov, core_request,
oemcrypto_core_message);
}
/**
* Template arguments:
* A: struct holding function arguments
* T: odk deserialize output/kdo serialize input structure
* F: odk deserialize function
* G: kdo serialize function
*
* raw bytes -> F deserialize -> struct T -> G serialize -> raw bytes
*/
template <typename A, typename T, typename F, typename G>
static roundtrip_fun odk_kdo(const F& odk_fun, const G& kdo_fun) {
auto roundtrip = [&](const uint8_t* in, uint8_t* out, size_t size) -> size_t {
if (sizeof(A) > size) {
auto roundtrip = [&](const uint8_t* in, uint8_t* out, size_t size,
size_t args_size) -> size_t {
// Input byte array format: [function arguments][data to parse]
if (args_size > size) {
return 0;
}
T t = {};
const uint8_t* buf = in + sizeof(A);
size_t len = size - sizeof(A);
const uint8_t* buf = in + args_size;
size_t len = size - args_size;
std::shared_ptr<A> _args(new A());
A* args = _args.get();
memcpy(args, in, sizeof(A));
memcpy(args, in, args_size);
const auto& common = args->common;
OEMCryptoResult err = odk_fun(buf, len, common.api_version, common.nonce,
common.session_id, args, t);
ODK_NonceValues nonce_values = {common.api_minor_version,
common.api_major_version, common.nonce,
common.session_id};
OEMCryptoResult err = odk_fun(buf, len, args, &nonce_values, &t);
if (err != OEMCrypto_SUCCESS) {
return 0;
}
@@ -197,33 +262,43 @@ static roundtrip_fun odk_kdo(const F& odk_fun, const G& kdo_fun) {
// @ fuzz raw -> parsed -> raw
static void verify_roundtrip(const uint8_t* in, size_t size,
roundtrip_fun roundtrip) {
roundtrip_fun roundtrip, size_t args_size) {
std::vector<uint8_t> _out(size);
auto out = _out.data();
size_t n = roundtrip(in, out, size);
assert(!n || (n <= size && 0 == memcmp(in, out, n)));
size_t n = roundtrip(in, out, size, args_size);
assert(n <= size && 0 == memcmp(in + args_size, out, n));
}
// Entry point for fuzzer, data is random bytes program gets from autofuzzer
extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
verify_roundtrip(data, size,
kdo_odk<ODK_LicenseRequest>(CoreLicenseRequestFromMessage,
odk_serialize_LicenseRequest),
0);
verify_roundtrip(data, size,
kdo_odk<ODK_RenewalRequest>(CoreRenewalRequestFromMessage,
odk_serialize_RenewalRequest),
sizeof(ODK_ClockValues));
verify_roundtrip(
data, size,
kdo_odk<ODK_LicenseRequest>(ParseLicenseRequest, odk_fun_LicenseRequest));
kdo_odk<ODK_ProvisioningRequest>(CoreProvisioningRequestFromMessage,
odk_serialize_ProvisioningRequest),
0);
verify_roundtrip(
data, size,
kdo_odk<ODK_RenewalRequest>(ParseRenewalRequest, odk_fun_RenewalRequest));
verify_roundtrip(data, size,
kdo_odk<ODK_ProvisioningRequest>(
ParseProvisioningRequest, odk_fun_ProvisioningRequest));
verify_roundtrip(data, size,
odk_kdo<ODK_ParseLicense_Args, ODK_ParsedLicense>(
odk_fun_LicenseResponse, kdo_fun_LicenseResponse));
verify_roundtrip(data, size,
odk_kdo<ODK_ParseRenewal_Args, ODK_PreparedRenewalRequest>(
odk_fun_RenewalResponse, kdo_fun_RenewalResponse));
odk_kdo<ODK_ParseLicense_Args, ODK_ParsedLicense>(
odk_deserialize_LicenseResponse, kdo_serialize_LicenseResponse),
sizeof(ODK_ParseLicense_Args));
verify_roundtrip(
data, size,
odk_kdo<ODK_ParseProvisioning_Args, ODK_ParsedProvisioning>(
odk_fun_ProvisioningResponse, kdo_fun_ProvisioningResponse));
odk_kdo<ODK_ParseRenewal_Args, ODK_PreparedRenewalRequest>(
odk_deserialize_RenewalResponse, kdo_serialize_RenewalResponse),
sizeof(ODK_ParseRenewal_Args));
verify_roundtrip(data, size,
odk_kdo<ODK_ParseProvisioning_Args, ODK_ParsedProvisioning>(
odk_deserialize_ProvisioningResponse,
kdo_serialize_ProvisioningResponse),
sizeof(ODK_ParseProvisioning_Args));
return 0;
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,488 @@
// Copyright 2019 Google LLC. All rights reserved. This file and proprietary
// source code may only be used and distributed under the Widevine Master
// License Agreement.
#include "odk_test_helper.h"
#include <endian.h>
#include <cstdint>
#include <cstdlib>
#include <cstring>
#include <fstream>
#include <iostream>
#include <vector>
#include "OEMCryptoCENCCommon.h"
#include "gtest/gtest.h"
#include "odk_structs.h"
#include "odk_structs_priv.h"
namespace wvodk_test {
void ODK_SetDefaultCoreFields(ODK_CoreMessage* core_message,
uint32_t message_type) {
ASSERT_TRUE(core_message != nullptr);
core_message->message_type = message_type;
core_message->message_length = 0;
core_message->nonce_values.api_minor_version = ODK_MINOR_VERSION;
core_message->nonce_values.api_major_version = ODK_MAJOR_VERSION;
core_message->nonce_values.nonce = 0xdeadbeef;
core_message->nonce_values.session_id = 0xcafebabe;
}
void ODK_SetDefaultLicenseResponseParams(ODK_LicenseResponseParams* params) {
ODK_SetDefaultCoreFields(&(params->core_message), ODK_License_Response_Type);
params->initial_license_load = true;
params->usage_entry_present = true;
params->parsed_license = {
.enc_mac_keys_iv = {.offset = 0, .length = 1},
.enc_mac_keys = {.offset = 2, .length = 3},
.pst = {.offset = 4, .length = 5},
.srm_restriction_data = {.offset = 6, .length = 7},
.license_type = OEMCrypto_EntitlementLicense,
.nonce_required = true,
.timer_limits =
{
.soft_enforce_rental_duration = true,
.soft_enforce_playback_duration = false,
.earliest_playback_start_seconds = 10,
.rental_duration_seconds = 11,
.total_playback_duration_seconds = 12,
.initial_renewal_duration_seconds = 13,
},
.key_array_length = 3,
.key_array =
{
{
.key_id = {.offset = 15, .length = 16},
.key_data_iv = {.offset = 17, .length = 18},
.key_data = {.offset = 19, .length = 20},
.key_control_iv = {.offset = 21, .length = 22},
.key_control = {.offset = 23, .length = 24},
},
{
.key_id = {.offset = 25, .length = 26},
.key_data_iv = {.offset = 27, .length = 28},
.key_data = {.offset = 29, .length = 30},
.key_control_iv = {.offset = 31, .length = 32},
.key_control = {.offset = 33, .length = 34},
},
{
.key_id = {.offset = 35, .length = 36},
.key_data_iv = {.offset = 37, .length = 38},
.key_data = {.offset = 39, .length = 40},
.key_control_iv = {.offset = 41, .length = 42},
.key_control = {.offset = 43, .length = 44},
},
},
};
memset(params->request_hash, 0xaa, sizeof(params->request_hash));
params->extra_fields = {
{ODK_SUBSTRING, &(params->parsed_license.enc_mac_keys_iv),
".enc_mac_keys_iv"},
{ODK_SUBSTRING, &(params->parsed_license.enc_mac_keys), ".enc_mac_keys"},
{ODK_SUBSTRING, &(params->parsed_license.pst), ".pst"},
{ODK_SUBSTRING, &(params->parsed_license.srm_restriction_data),
".srm_restriction_data"},
{ODK_UINT32, &(params->parsed_license.license_type), ".license_type"},
{ODK_UINT32, &(params->parsed_license.nonce_required), ".nonce_required"},
{ODK_UINT32,
&(params->parsed_license.timer_limits.soft_enforce_rental_duration),
".soft_enforce_rental_duration"},
{ODK_UINT32,
&(params->parsed_license.timer_limits.soft_enforce_playback_duration),
".soft_enforce_playback_duration"},
{ODK_UINT64,
&(params->parsed_license.timer_limits.earliest_playback_start_seconds),
".earliest_playback_start_seconds"},
{ODK_UINT64,
&(params->parsed_license.timer_limits.rental_duration_seconds),
".rental_duration_seconds"},
{ODK_UINT64,
&(params->parsed_license.timer_limits.total_playback_duration_seconds),
".total_playback_duration_seconds"},
{ODK_UINT64,
&(params->parsed_license.timer_limits.initial_renewal_duration_seconds),
".initial_renewal_duration_seconds"},
{ODK_UINT32, &(params->parsed_license.key_array_length),
".key_array_length"},
{ODK_SUBSTRING, &(params->parsed_license.key_array[0].key_id), ".key_id"},
{ODK_SUBSTRING, &(params->parsed_license.key_array[0].key_data_iv),
".key_data_iv"},
{ODK_SUBSTRING, &(params->parsed_license.key_array[0].key_data),
".key_data"},
{ODK_SUBSTRING, &(params->parsed_license.key_array[0].key_control_iv),
".key_control_iv"},
{ODK_SUBSTRING, &(params->parsed_license.key_array[0].key_control),
".key_control"},
{ODK_SUBSTRING, &(params->parsed_license.key_array[1].key_id), ".key_id"},
{ODK_SUBSTRING, &(params->parsed_license.key_array[1].key_data_iv),
".key_data_iv"},
{ODK_SUBSTRING, &(params->parsed_license.key_array[1].key_data),
".key_data"},
{ODK_SUBSTRING, &(params->parsed_license.key_array[1].key_control_iv),
".key_control_iv"},
{ODK_SUBSTRING, &(params->parsed_license.key_array[1].key_control),
".key_control"},
{ODK_SUBSTRING, &(params->parsed_license.key_array[2].key_id), ".key_id"},
{ODK_SUBSTRING, &(params->parsed_license.key_array[2].key_data_iv),
".key_data_iv"},
{ODK_SUBSTRING, &(params->parsed_license.key_array[2].key_data),
".key_data"},
{ODK_SUBSTRING, &(params->parsed_license.key_array[2].key_control_iv),
".key_control_iv"},
{ODK_SUBSTRING, &(params->parsed_license.key_array[2].key_control),
".key_control"},
{ODK_HASH, params->request_hash, ".request_hash"},
};
}
void ODK_SetDefaultRenewalResponseParams(ODK_RenewalResponseParams* params) {
ODK_SetDefaultCoreFields(&(params->core_message), ODK_Renewal_Response_Type);
params->system_time = 0xfaceb00c;
params->playback_clock = 10;
params->playback_timer = 20;
params->renewal_duration = 300;
params->extra_fields = {
{ODK_UINT64, &(params->playback_clock), "playback_clock"},
{ODK_UINT64, &(params->renewal_duration), "renewal_duration"},
};
params->timer_limits = {
.soft_enforce_rental_duration = false,
.soft_enforce_playback_duration = false,
.earliest_playback_start_seconds = 0,
.rental_duration_seconds = 1000,
.total_playback_duration_seconds = 2000,
.initial_renewal_duration_seconds = 300,
};
params->clock_values = {
.time_of_license_signed =
params->system_time - params->playback_clock - 42,
.time_of_first_decrypt = params->system_time - params->playback_clock,
.time_of_last_decrypt = params->system_time - params->playback_clock,
.time_of_renewal_request = params->playback_clock,
.time_when_timer_expires = params->system_time + params->playback_timer,
.timer_status = ODK_CLOCK_TIMER_STATUS_ACTIVE,
.status = kActive,
};
}
void ODK_SetDefaultProvisioningResponseParams(
ODK_ProvisioningResponseParams* params) {
ODK_SetDefaultCoreFields(&(params->core_message),
ODK_Provisioning_Response_Type);
params->device_id_length = ODK_DEVICE_ID_LEN_MAX / 2;
memset(params->device_id, 0xff, params->device_id_length);
memset(params->device_id + params->device_id_length, 0,
ODK_DEVICE_ID_LEN_MAX - params->device_id_length);
params->parsed_provisioning = {
.enc_private_key = {.offset = 0, .length = 1},
.enc_private_key_iv = {.offset = 2, .length = 3},
.encrypted_message_key = {.offset = 4, .length = 5},
};
params->extra_fields = {
{ODK_UINT32, &(params->device_id_length), "device_id_length"},
{ODK_DEVICEID, params->device_id, "device_id"},
{ODK_UINT32, &(params->parsed_provisioning).key_type, "key_type"},
{ODK_SUBSTRING, &(params->parsed_provisioning).enc_private_key,
"enc_private_key"},
{ODK_SUBSTRING, &(params->parsed_provisioning).enc_private_key_iv,
"enc_private_key_iv"},
{ODK_SUBSTRING, &(params->parsed_provisioning).encrypted_message_key,
"encrypted_message_key"},
};
}
size_t ODK_FieldLength(ODK_FieldType type) {
switch (type) {
case ODK_UINT16:
return sizeof(uint16_t);
case ODK_UINT32:
return sizeof(uint32_t);
case ODK_UINT64:
return sizeof(uint64_t);
case ODK_SUBSTRING:
return sizeof(uint32_t) + sizeof(uint32_t);
case ODK_DEVICEID:
return ODK_DEVICE_ID_LEN_MAX;
case ODK_HASH:
return ODK_SHA256_HASH_SIZE;
default:
return SIZE_MAX;
}
}
size_t ODK_AllocSize(ODK_FieldType type) {
if (type == ODK_SUBSTRING) {
return sizeof(OEMCrypto_Substring);
}
return ODK_FieldLength(type);
}
OEMCryptoResult ODK_WriteSingleField(uint8_t* buf, const ODK_Field* field) {
if (buf == nullptr || field == nullptr || field->value == nullptr) {
return ODK_ERROR_CORE_MESSAGE;
}
switch (field->type) {
case ODK_UINT16: {
uint16_t u16 = htobe16(*static_cast<uint16_t*>(field->value));
memcpy(buf, &u16, sizeof(u16));
break;
}
case ODK_UINT32: {
uint32_t u32 = htobe32(*static_cast<uint32_t*>(field->value));
memcpy(buf, &u32, sizeof(u32));
break;
}
case ODK_UINT64: {
uint64_t u64 = htobe64(*static_cast<uint64_t*>(field->value));
memcpy(buf, &u64, sizeof(u64));
break;
}
case ODK_SUBSTRING: {
OEMCrypto_Substring* s = static_cast<OEMCrypto_Substring*>(field->value);
uint32_t off = htobe32(s->offset);
uint32_t len = htobe32(s->length);
memcpy(buf, &off, sizeof(off));
memcpy(buf + sizeof(off), &len, sizeof(len));
break;
}
case ODK_DEVICEID:
case ODK_HASH: {
const size_t field_len = ODK_FieldLength(field->type);
const uint8_t* const id = static_cast<uint8_t*>(field->value);
memcpy(buf, id, field_len);
break;
}
default:
return ODK_ERROR_CORE_MESSAGE;
}
return OEMCrypto_SUCCESS;
}
OEMCryptoResult ODK_ReadSingleField(const uint8_t* buf,
const ODK_Field* field) {
if (field == nullptr || field->value == nullptr) {
return ODK_ERROR_CORE_MESSAGE;
}
switch (field->type) {
case ODK_UINT16: {
memcpy(field->value, buf, sizeof(uint16_t));
uint16_t* u16p = static_cast<uint16_t*>(field->value);
*u16p = be16toh(*u16p);
break;
}
case ODK_UINT32: {
memcpy(field->value, buf, sizeof(uint32_t));
uint32_t* u32p = static_cast<uint32_t*>(field->value);
*u32p = be32toh(*u32p);
break;
}
case ODK_UINT64: {
memcpy(field->value, buf, sizeof(uint64_t));
uint64_t* u64p = static_cast<uint64_t*>(field->value);
*u64p = be64toh(*u64p);
break;
}
case ODK_SUBSTRING: {
OEMCrypto_Substring* s = static_cast<OEMCrypto_Substring*>(field->value);
uint32_t off = 0;
uint32_t len = 0;
memcpy(&off, buf, sizeof(off));
memcpy(&len, buf + sizeof(off), sizeof(len));
s->offset = be32toh(off);
s->length = be32toh(len);
break;
}
case ODK_DEVICEID:
case ODK_HASH: {
const size_t field_len = ODK_FieldLength(field->type);
uint8_t* const id = static_cast<uint8_t*>(field->value);
memcpy(id, buf, field_len);
break;
}
default:
return ODK_ERROR_CORE_MESSAGE;
}
return OEMCrypto_SUCCESS;
}
OEMCryptoResult ODK_DumpSingleField(const uint8_t* buf,
const ODK_Field* field) {
if (field == nullptr || field->value == nullptr) {
return ODK_ERROR_CORE_MESSAGE;
}
switch (field->type) {
case ODK_UINT16: {
uint16_t val;
memcpy(&val, buf, sizeof(uint16_t));
val = be16toh(val);
std::cerr << field->name << ": " << val << " = 0x" << std::hex << val
<< "\n";
break;
}
case ODK_UINT32: {
uint32_t val;
memcpy(&val, buf, sizeof(uint32_t));
val = be32toh(val);
std::cerr << field->name << ": " << val << " = 0x" << std::hex << val
<< "\n";
break;
}
case ODK_UINT64: {
uint64_t val;
memcpy(&val, buf, sizeof(uint64_t));
val = be64toh(val);
std::cerr << field->name << ": " << val << " = 0x" << std::hex << val
<< "\n";
break;
}
case ODK_SUBSTRING: {
uint32_t off = 0;
uint32_t len = 0;
memcpy(&off, buf, sizeof(off));
memcpy(&len, buf + sizeof(off), sizeof(len));
std::cerr << field->name << ": (off=" << off << ", len=" << len << ")\n";
break;
}
case ODK_DEVICEID:
case ODK_HASH: {
const size_t field_len = ODK_FieldLength(field->type);
std::cerr << field->name << ": ";
for (size_t i = 0; i < field_len; i++) {
std::cerr << std::hex << std::setfill('0') << std::setw(2)
<< static_cast<unsigned int>(buf[i]);
}
std::cerr << "\n";
break;
}
default:
return ODK_ERROR_CORE_MESSAGE;
}
std::cerr << std::dec; // Return to normal.
return OEMCrypto_SUCCESS;
}
/*
* Parameters:
* [in] size_in: buffer size
* [out] size_out: bytes processed
*/
OEMCryptoResult ODK_IterFields(ODK_FieldMode mode, uint8_t* buf,
const size_t size_in, size_t* size_out,
const std::vector<ODK_Field>& fields) {
if (buf == nullptr || size_out == nullptr) {
return ODK_ERROR_CORE_MESSAGE;
}
size_t off = 0, off2 = 0;
for (size_t i = 0; i < fields.size(); i++) {
if (__builtin_add_overflow(off, ODK_FieldLength(fields[i].type), &off2) ||
off2 > size_in) {
return ODK_ERROR_CORE_MESSAGE;
}
uintptr_t base = reinterpret_cast<uintptr_t>(buf);
if (__builtin_add_overflow(base, off, &base)) {
return ODK_ERROR_CORE_MESSAGE;
}
uint8_t* const buf_off = buf + off;
switch (mode) {
case ODK_WRITE:
ODK_WriteSingleField(buf_off, &fields[i]);
break;
case ODK_READ:
ODK_ReadSingleField(buf_off, &fields[i]);
break;
case ODK_DUMP:
ODK_DumpSingleField(buf_off, &fields[i]);
break;
default:
return ODK_ERROR_CORE_MESSAGE;
}
off = off2;
}
*size_out = off;
if (*size_out > size_in) {
return ODK_ERROR_CORE_MESSAGE;
}
return OEMCrypto_SUCCESS;
}
void ODK_ExpectEqualBuf(const void* s1, const void* s2, size_t n,
const std::vector<ODK_Field>& fields) {
if (memcmp(s1, s2, n) != 0) {
const void* buffers[] = {s1, s2};
for (int i = 0; i < 2; i++) {
char _tmp[] = "/tmp/fileXXXXXX";
const int temp_fd = mkstemp(_tmp);
if (temp_fd >= 0) {
close(temp_fd);
} else {
std::cerr << "Failed to open temp file." << std::endl;
break;
}
std::string tmp(_tmp);
std::fstream out(tmp, std::ios::out | std::ios::binary);
out.write(static_cast<const char*>(buffers[i]), n);
out.close();
std::cerr << "buffer " << i << " dumped to " << tmp << std::endl;
size_t bytes_written;
uint8_t* buf =
const_cast<uint8_t*>(reinterpret_cast<const uint8_t*>(buffers[i]));
ODK_IterFields(ODK_DUMP, buf, n, &bytes_written, fields);
}
FAIL();
}
}
void ODK_ResetOdkFields(std::vector<ODK_Field>* fields) {
if (fields == nullptr) {
return;
}
for (auto& field : *fields) {
if (field.value != nullptr) {
size_t size = ODK_AllocSize(field.type);
memset(field.value, 0, size);
}
}
}
void ODK_BuildMessageBuffer(ODK_CoreMessage* core_message,
const std::vector<ODK_Field>& extra_fields,
uint8_t*& buf, uint32_t* buf_size) {
ASSERT_TRUE(core_message != nullptr);
ASSERT_TRUE(buf_size != nullptr);
std::vector<ODK_Field> total_fields = {
{ODK_UINT32, &(core_message->message_type), "message_type"},
{ODK_UINT32, &(core_message->message_length), "message_size"},
{ODK_UINT16, &(core_message->nonce_values.api_minor_version),
"api_minor_version"},
{ODK_UINT16, &(core_message->nonce_values.api_major_version),
"api_major_version"},
{ODK_UINT32, &(core_message->nonce_values.nonce), "nonce"},
{ODK_UINT32, &(core_message->nonce_values.session_id), "session_id"},
};
uint32_t header_size = 0;
for (auto& field : total_fields) {
header_size += ODK_FieldLength(field.type);
}
total_fields.insert(total_fields.end(), extra_fields.begin(),
extra_fields.end());
for (auto& field : total_fields) {
*buf_size += ODK_FieldLength(field.type);
}
// update message_size
*(reinterpret_cast<uint32_t*>(total_fields[1].value)) = *buf_size;
buf = new uint8_t[*buf_size]();
size_t bytes_written = 0;
// serialize ODK fields to message buffer
EXPECT_EQ(OEMCrypto_SUCCESS, ODK_IterFields(ODK_WRITE, buf, SIZE_MAX,
&bytes_written, total_fields));
EXPECT_EQ(bytes_written, *buf_size);
}
} // namespace wvodk_test

View File

@@ -0,0 +1,99 @@
/* Copyright 2019 Google LLC. All rights reserved. This file and proprietary */
/* source code may only be used and distributed under the Widevine Master */
/* License Agreement. */
#ifndef WIDEVINE_ODK_TEST_ODK_TEST_HELPER_H_
#define WIDEVINE_ODK_TEST_ODK_TEST_HELPER_H_
#include <cstdint>
#include <string>
#include <vector>
#include "odk_structs.h"
#include "odk_structs_priv.h"
namespace wvodk_test {
enum ODK_FieldType {
ODK_UINT16,
ODK_UINT32,
ODK_UINT64,
ODK_SUBSTRING,
ODK_DEVICEID,
ODK_HASH,
ODK_NUMTYPES,
};
enum ODK_FieldMode {
ODK_READ,
ODK_WRITE,
ODK_DUMP,
};
struct ODK_Field {
ODK_FieldType type;
void* value;
std::string name;
};
struct ODK_LicenseResponseParams {
ODK_CoreMessage core_message;
bool initial_license_load;
bool usage_entry_present;
uint8_t request_hash[ODK_SHA256_HASH_SIZE];
ODK_TimerLimits timer_limits;
ODK_ClockValues clock_values;
ODK_ParsedLicense parsed_license;
std::vector<ODK_Field> extra_fields;
};
struct ODK_RenewalResponseParams {
ODK_CoreMessage core_message;
uint64_t system_time;
uint64_t playback_clock;
uint64_t renewal_duration;
ODK_TimerLimits timer_limits;
ODK_ClockValues clock_values;
uint64_t playback_timer;
std::vector<ODK_Field> extra_fields;
};
struct ODK_ProvisioningResponseParams {
ODK_CoreMessage core_message;
uint8_t device_id[ODK_DEVICE_ID_LEN_MAX];
uint32_t device_id_length;
ODK_ParsedProvisioning parsed_provisioning;
std::vector<ODK_Field> extra_fields;
};
/* Default values in core_message for testing */
void ODK_SetDefaultCoreFields(ODK_CoreMessage* core_message,
uint32_t message_type);
void ODK_SetDefaultLicenseResponseParams(ODK_LicenseResponseParams* params);
void ODK_SetDefaultRenewalResponseParams(ODK_RenewalResponseParams* params);
void ODK_SetDefaultProvisioningResponseParams(
ODK_ProvisioningResponseParams* params);
size_t ODK_FieldLength(ODK_FieldType type);
size_t ODK_AllocSize(ODK_FieldType type);
/* Copy ODK_Field to buf */
OEMCryptoResult ODK_WriteSingleField(uint8_t* buf, const ODK_Field* field);
/* Load buf to ODK_Field */
OEMCryptoResult ODK_ReadSingleField(const uint8_t* buf, const ODK_Field* field);
OEMCryptoResult ODK_DumpSingleField(const uint8_t* buf, const ODK_Field* field);
OEMCryptoResult ODK_IterFields(ODK_FieldMode mode, uint8_t* buf,
const size_t size_in, size_t* size_out,
const std::vector<ODK_Field>& fields);
void ODK_ExpectEqualBuf(const void* s1, const void* s2, size_t n,
const std::vector<ODK_Field>& fields);
void ODK_ResetOdkFields(std::vector<ODK_Field>* fields);
/* Serialize core_message and extra_fields into buf */
void ODK_BuildMessageBuffer(ODK_CoreMessage* core_message,
const std::vector<ODK_Field>& extra_fields,
uint8_t*& buf, uint32_t* buf_size);
} /* namespace wvodk_test */
#endif /* WIDEVINE_ODK_TEST_ODK_TEST_HELPER_H_ */

View File

@@ -826,8 +826,8 @@ void RenewalRoundTrip::VerifyRequestSignature(
void RenewalRoundTrip::FillAndVerifyCoreRequest(
const std::string& core_message_string) {
if (license_messages_->api_version() < kCoreMessagesAPI) {
// For v15, we expect that no core request was created.
if (license_messages_->api_version() < kCoreMessagesAPI || is_release_) {
// For v15 or for a release, we expect that no core request was created.
EXPECT_FALSE(
oemcrypto_core_message::deserialize::CoreRenewalRequestFromMessage(
core_message_string, &core_request_));
@@ -837,13 +837,9 @@ void RenewalRoundTrip::FillAndVerifyCoreRequest(
core_message_string, &core_request_));
EXPECT_EQ(license_messages_->core_request().api_major_version,
core_request_.api_major_version);
if (!is_release_) {
// For a license release, we do not expect the nonce to be correct. That
// is because a release might be sent without loading the license first.
EXPECT_EQ(license_messages_->core_request().nonce, core_request_.nonce);
EXPECT_EQ(license_messages_->core_request().session_id,
core_request_.session_id);
}
EXPECT_EQ(license_messages_->core_request().nonce, core_request_.nonce);
EXPECT_EQ(license_messages_->core_request().session_id,
core_request_.session_id);
}
}
@@ -1397,13 +1393,6 @@ void Session::UpdateUsageEntry(std::vector<uint8_t>* header_buffer) {
encrypted_usage_entry_.data(), &entry_buffer_length));
}
void Session::DeactivateUsageEntry(const std::string& pst) {
ASSERT_EQ(OEMCrypto_SUCCESS,
OEMCrypto_DeactivateUsageEntry(
session_id(), reinterpret_cast<const uint8_t*>(pst.c_str()),
pst.length()));
}
void Session::LoadUsageEntry(uint32_t index, const vector<uint8_t>& buffer) {
usage_entry_number_ = index;
encrypted_usage_entry_ = buffer;

View File

@@ -527,8 +527,6 @@ class Session {
void ReloadUsageEntry() { LoadUsageEntry(*this); }
// Update the usage entry and save the header to the specified buffer.
void UpdateUsageEntry(std::vector<uint8_t>* header_buffer);
// Deactivate this session's usage entry.
void DeactivateUsageEntry(const std::string& pst);
// The usage entry number for this session's usage entry.
uint32_t usage_entry_number() const { return usage_entry_number_; }
void set_usage_entry_number(uint32_t v) { usage_entry_number_ = v; }

View File

@@ -15,9 +15,9 @@
#include <stdint.h>
#ifdef _WIN32
# include <windows.h>
# include <windows.h>
#else
# include <sys/time.h>
# include <sys/time.h>
#endif
#include <gtest/gtest.h>
@@ -46,9 +46,9 @@
using ::testing::Bool;
using ::testing::Combine;
using ::testing::Range;
using ::testing::tuple;
using ::testing::Values;
using ::testing::WithParamInterface;
using ::testing::tuple;
using namespace std;
namespace std { // GTest wants PrintTo to be in the std namespace.
@@ -92,11 +92,11 @@ constexpr size_t MiB = 1024 * 1024;
// depending on the resource rating reported by OEMCrypto. This function looks
// up the required value for the specified resource for the target OEMCrypto
// library.
template<typename T, size_t N>
template <typename T, size_t N>
T GetResourceValue(T (&resource_values)[N]) {
if (global_features.resource_rating < 1) return resource_values[0];
if (global_features.resource_rating > N) return resource_values[N-1];
return resource_values[global_features.resource_rating-1];
if (global_features.resource_rating > N) return resource_values[N - 1];
return resource_values[global_features.resource_rating - 1];
}
// After API 16, we require 300 entries in the usage table. Before API 16, we
@@ -124,8 +124,8 @@ const size_t kLargeMessageSize[] = { 8*KiB, 8*KiB, 16*KiB, 32*KiB};
/** @return The Unix time of the given time point. */
template <typename Duration>
uint64_t UnixTime(const std::chrono::time_point<std::chrono::system_clock,
Duration>& point) {
uint64_t UnixTime(
const std::chrono::time_point<std::chrono::system_clock, Duration>& point) {
return point.time_since_epoch() / std::chrono::seconds(1);
}
@@ -143,7 +143,8 @@ void AddNativeTime(int64_t delta_seconds, NativeTime* time) {
ASSERT_TRUE(SystemTimeToFileTime(time, &file_time));
uint64_t long_time = static_cast<uint64_t>(file_time.dwLowDateTime) |
(static_cast<uint64_t>(file_time.dwHighDateTime) << 32);
long_time += delta_seconds * 1e7; // long_time is in 100-nanosecond intervals.
long_time +=
delta_seconds * 1e7; // long_time is in 100-nanosecond intervals.
file_time.dwLowDateTime = long_time & ((1ull << 32) - 1);
file_time.dwHighDateTime = long_time >> 32;
ASSERT_TRUE(FileTimeToSystemTime(&file_time, time));
@@ -344,8 +345,7 @@ TEST_F(OEMCryptoClientTest, MaxSessionsOpenCloseAPI10) {
ASSERT_GE(max_sessions, required_number);
// We allow GetMaxNumberOfSessions to return an estimate. This tests with a
// pad of 5%. Even if it's just an estimate, we still require 8 sessions.
size_t max_sessions_with_pad =
max(max_sessions * 19 / 20, required_number);
size_t max_sessions_with_pad = max(max_sessions * 19 / 20, required_number);
vector<OEMCrypto_SESSION> sessions;
// Limit the number of sessions for testing.
const size_t kMaxNumberOfSessionsForTesting = 0x100u;
@@ -631,7 +631,7 @@ TEST_F(OEMCryptoKeyboxTest, GenerateDerivedKeysFromKeyboxLargeBuffer) {
ASSERT_EQ(OEMCrypto_SUCCESS,
OEMCrypto_GenerateDerivedKeys(
s.session_id(), mac_context.data(), mac_context.size(),
enc_context.data(),enc_context.size()));
enc_context.data(), enc_context.size()));
}
// This class is for tests that have an OEM Certificate instead of a keybox.
@@ -660,7 +660,6 @@ TEST_F(OEMCryptoProv30Test, GetDeviceId) {
ASSERT_EQ(OEMCrypto_SUCCESS, sts);
}
// The OEM certificate must be valid.
TEST_F(OEMCryptoProv30Test, CertValidAPI15) {
ASSERT_EQ(OEMCrypto_SUCCESS, OEMCrypto_IsKeyboxOrOEMCertValid());
@@ -2843,12 +2842,12 @@ TEST_F(OEMCryptoLoadsCertificate, LoadWrappedRSAKey) {
TEST_F(OEMCryptoLoadsCertificate, TestLargeRSAKey3072) {
encoded_rsa_key_.assign(kTestRSAPKCS8PrivateKeyInfo3_3072,
kTestRSAPKCS8PrivateKeyInfo3_3072 +
sizeof(kTestRSAPKCS8PrivateKeyInfo3_3072));
sizeof(kTestRSAPKCS8PrivateKeyInfo3_3072));
CreateWrappedRSAKey();
Session s;
ASSERT_NO_FATAL_FAILURE(s.open());
ASSERT_NO_FATAL_FAILURE(s.PreparePublicKey(encoded_rsa_key_.data(),
encoded_rsa_key_.size()));
ASSERT_NO_FATAL_FAILURE(
s.PreparePublicKey(encoded_rsa_key_.data(), encoded_rsa_key_.size()));
ASSERT_NO_FATAL_FAILURE(s.InstallRSASessionTestKey(wrapped_rsa_key_));
LicenseRoundTrip license_messages(&s);
@@ -2869,8 +2868,8 @@ TEST_F(OEMCryptoLoadsCertificate, TestCarmichaelRSAKey) {
CreateWrappedRSAKey();
Session s;
ASSERT_NO_FATAL_FAILURE(s.open());
ASSERT_NO_FATAL_FAILURE(s.PreparePublicKey(encoded_rsa_key_.data(),
encoded_rsa_key_.size()));
ASSERT_NO_FATAL_FAILURE(
s.PreparePublicKey(encoded_rsa_key_.data(), encoded_rsa_key_.size()));
ASSERT_NO_FATAL_FAILURE(s.InstallRSASessionTestKey(wrapped_rsa_key_));
LicenseRoundTrip license_messages(&s);
@@ -2888,8 +2887,8 @@ TEST_F(OEMCryptoLoadsCertificate, TestMultipleRSAKeys) {
Session s1; // Session s1 loads the default rsa key, but doesn't use it
// until after s2 uses its key.
ASSERT_NO_FATAL_FAILURE(s1.open());
ASSERT_NO_FATAL_FAILURE(s1.PreparePublicKey(encoded_rsa_key_.data(),
encoded_rsa_key_.size()));
ASSERT_NO_FATAL_FAILURE(
s1.PreparePublicKey(encoded_rsa_key_.data(), encoded_rsa_key_.size()));
ASSERT_EQ(OEMCrypto_SUCCESS,
OEMCrypto_LoadDeviceRSAKey(s1.session_id(), wrapped_rsa_key_.data(),
wrapped_rsa_key_.size()));
@@ -2900,8 +2899,8 @@ TEST_F(OEMCryptoLoadsCertificate, TestMultipleRSAKeys) {
sizeof(kTestRSAPKCS8PrivateKeyInfo4_2048));
CreateWrappedRSAKey();
ASSERT_NO_FATAL_FAILURE(s2.open());
ASSERT_NO_FATAL_FAILURE(s2.PreparePublicKey(encoded_rsa_key_.data(),
encoded_rsa_key_.size()));
ASSERT_NO_FATAL_FAILURE(
s2.PreparePublicKey(encoded_rsa_key_.data(), encoded_rsa_key_.size()));
ASSERT_NO_FATAL_FAILURE(s2.InstallRSASessionTestKey(wrapped_rsa_key_));
LicenseRoundTrip license_messages2(&s2);
ASSERT_NO_FATAL_FAILURE(s2.GenerateNonce());
@@ -3006,8 +3005,8 @@ TEST_F(OEMCryptoLoadsCertificate, RSAPerformance) {
wrapped_rsa_key_.size()));
vector<uint8_t> session_key;
vector<uint8_t> enc_session_key;
ASSERT_NO_FATAL_FAILURE(s.PreparePublicKey(encoded_rsa_key_.data(),
encoded_rsa_key_.size()));
ASSERT_NO_FATAL_FAILURE(
s.PreparePublicKey(encoded_rsa_key_.data(), encoded_rsa_key_.size()));
ASSERT_TRUE(s.GenerateRSASessionKey(&session_key, &enc_session_key));
vector<uint8_t> mac_context;
vector<uint8_t> enc_context;
@@ -3035,10 +3034,9 @@ TEST_F(OEMCryptoLoadsCertificate, RSAPerformance) {
while (clock.now() - start_time < kTestDuration) {
ASSERT_EQ(OEMCrypto_SUCCESS,
OEMCrypto_DeriveKeysFromSessionKey(
s.session_id(),
enc_session_key.data(), enc_session_key.size(),
mac_context.data(), mac_context.size(),
enc_context.data(), enc_context.size()));
s.session_id(), enc_session_key.data(),
enc_session_key.size(), mac_context.data(),
mac_context.size(), enc_context.data(), enc_context.size()));
count++;
}
delta_time = clock.now() - start_time;
@@ -3068,9 +3066,8 @@ TEST_F(OEMCryptoUsesCertificate, GenerateDerivedKeysLargeBuffer) {
}
ASSERT_EQ(OEMCrypto_SUCCESS,
OEMCrypto_DeriveKeysFromSessionKey(
session_.session_id(),
enc_session_key.data(), enc_session_key.size(),
mac_context.data(), mac_context.size(),
session_.session_id(), enc_session_key.data(),
enc_session_key.size(), mac_context.data(), mac_context.size(),
enc_context.data(), enc_context.size()));
}
@@ -3090,19 +3087,16 @@ class OEMCryptoLoadsCertificateAlternates : public OEMCryptoLoadsCertificate {
GetRandBytes(licenseRequest.data(), licenseRequest.size());
size_t signature_length = 256;
vector<uint8_t> signature(signature_length);
sts = OEMCrypto_GenerateRSASignature(s.session_id(), licenseRequest.data(),
licenseRequest.size(),
signature.data(), &signature_length,
scheme);
sts = OEMCrypto_GenerateRSASignature(
s.session_id(), licenseRequest.data(), licenseRequest.size(),
signature.data(), &signature_length, scheme);
// Allow OEMCrypto to request a full buffer.
if (sts == OEMCrypto_ERROR_SHORT_BUFFER) {
ASSERT_NE(static_cast<size_t>(0), signature_length);
signature.assign(signature_length, 0);
sts = OEMCrypto_GenerateRSASignature(s.session_id(),
licenseRequest.data(),
licenseRequest.size(),
signature.data(), &signature_length,
scheme);
sts = OEMCrypto_GenerateRSASignature(
s.session_id(), licenseRequest.data(), licenseRequest.size(),
signature.data(), &signature_length, scheme);
}
EXPECT_NE(OEMCrypto_SUCCESS, sts)
@@ -3137,8 +3131,8 @@ class OEMCryptoLoadsCertificateAlternates : public OEMCryptoLoadsCertificate {
ASSERT_EQ(OEMCrypto_SUCCESS, sts)
<< "Failed to sign with padding scheme=" << (int)scheme
<< ", size=" << (int)size;
ASSERT_NO_FATAL_FAILURE(s.PreparePublicKey(encoded_rsa_key_.data(),
encoded_rsa_key_.size()));
ASSERT_NO_FATAL_FAILURE(
s.PreparePublicKey(encoded_rsa_key_.data(), encoded_rsa_key_.size()));
ASSERT_NO_FATAL_FAILURE(s.VerifyRSASignature(licenseRequest, signature,
signature_length, scheme));
delete[] signature;
@@ -3154,8 +3148,8 @@ class OEMCryptoLoadsCertificateAlternates : public OEMCryptoLoadsCertificate {
s.GenerateNonce();
vector<uint8_t> session_key;
vector<uint8_t> enc_session_key;
ASSERT_NO_FATAL_FAILURE(s.PreparePublicKey(encoded_rsa_key_.data(),
encoded_rsa_key_.size()));
ASSERT_NO_FATAL_FAILURE(
s.PreparePublicKey(encoded_rsa_key_.data(), encoded_rsa_key_.size()));
ASSERT_TRUE(s.GenerateRSASessionKey(&session_key, &enc_session_key));
vector<uint8_t> mac_context;
vector<uint8_t> enc_context;
@@ -3359,7 +3353,7 @@ class OEMCryptoCastReceiverTest : public OEMCryptoLoadsCertificateAlternates {
"01" // 1
"05" // null object. (field=parameter?)
"00" // size of null object
);
);
vector<uint8_t> pkey = wvcdm::a2b_hex("020100"); // integer, version = 0.
pkey = concat(pkey, field_n);
@@ -3416,8 +3410,8 @@ class OEMCryptoCastReceiverTest : public OEMCryptoLoadsCertificateAlternates {
ASSERT_EQ(OEMCrypto_SUCCESS, sts)
<< "Failed to sign with padding scheme=" << (int)scheme
<< ", size=" << (int)message.size();
ASSERT_NO_FATAL_FAILURE(s.PreparePublicKey(encoded_rsa_key_.data(),
encoded_rsa_key_.size()));
ASSERT_NO_FATAL_FAILURE(
s.PreparePublicKey(encoded_rsa_key_.data(), encoded_rsa_key_.size()));
// Verify that the signature matches the official test vector.
ASSERT_EQ(correct_signature.size(), signature_length);
@@ -3426,8 +3420,8 @@ class OEMCryptoCastReceiverTest : public OEMCryptoLoadsCertificateAlternates {
// Also verify that our verification algorithm agrees. This is not needed
// to test OEMCrypto, but it does verify that this test is valid.
ASSERT_NO_FATAL_FAILURE(s.VerifyRSASignature(
digest, signature.data(), signature_length, scheme));
ASSERT_NO_FATAL_FAILURE(s.VerifyRSASignature(digest, signature.data(),
signature_length, scheme));
ASSERT_NO_FATAL_FAILURE(s.VerifyRSASignature(
digest, correct_signature.data(), correct_signature.size(), scheme));
}
@@ -3435,7 +3429,8 @@ class OEMCryptoCastReceiverTest : public OEMCryptoLoadsCertificateAlternates {
// CAST Receivers should report that they support cast certificates.
TEST_F(OEMCryptoCastReceiverTest, SupportsCertificatesAPI13) {
ASSERT_NE(0u, OEMCrypto_Supports_RSA_CAST & OEMCrypto_SupportedCertificates());
ASSERT_NE(0u,
OEMCrypto_Supports_RSA_CAST & OEMCrypto_SupportedCertificates());
}
// # PKCS#1 v1.5 Signature Example 15.1
@@ -4215,10 +4210,9 @@ class OEMCryptoGenericCryptoTest : public OEMCryptoRefreshTest {
OEMCrypto_CipherMode_CTR);
ASSERT_EQ(OEMCrypto_SUCCESS, sts);
vector<uint8_t> encrypted(buffer_length);
sts =
OEMCrypto_Generic_Encrypt(session_.session_id(), clear_buffer_.data(),
buffer_length, iv_, algorithm,
encrypted.data());
sts = OEMCrypto_Generic_Encrypt(session_.session_id(), clear_buffer_.data(),
buffer_length, iv_, algorithm,
encrypted.data());
EXPECT_NE(OEMCrypto_SUCCESS, sts);
expected_encrypted.resize(buffer_length);
EXPECT_NE(encrypted, expected_encrypted);
@@ -4237,10 +4231,9 @@ class OEMCryptoGenericCryptoTest : public OEMCryptoRefreshTest {
OEMCrypto_CipherMode_CTR);
ASSERT_EQ(OEMCrypto_SUCCESS, sts);
vector<uint8_t> resultant(encrypted.size());
sts =
OEMCrypto_Generic_Decrypt(session_.session_id(), encrypted.data(),
buffer_length, iv_, algorithm,
resultant.data());
sts = OEMCrypto_Generic_Decrypt(session_.session_id(), encrypted.data(),
buffer_length, iv_, algorithm,
resultant.data());
EXPECT_NE(OEMCrypto_SUCCESS, sts);
EXPECT_NE(clear_buffer_, resultant);
}
@@ -4310,11 +4303,11 @@ TEST_P(OEMCryptoGenericCryptoTest, GenericKeyEncrypt) {
session_.license().keys[key_index].key_id_length,
OEMCrypto_CipherMode_CTR));
vector<uint8_t> encrypted(clear_buffer_.size());
ASSERT_EQ(OEMCrypto_SUCCESS,
OEMCrypto_Generic_Encrypt(
session_.session_id(), clear_buffer_.data(),
clear_buffer_.size(), iv_, OEMCrypto_AES_CBC_128_NO_PADDING,
encrypted.data()));
ASSERT_EQ(
OEMCrypto_SUCCESS,
OEMCrypto_Generic_Encrypt(
session_.session_id(), clear_buffer_.data(), clear_buffer_.size(),
iv_, OEMCrypto_AES_CBC_128_NO_PADDING, encrypted.data()));
ASSERT_EQ(expected_encrypted, encrypted);
}
@@ -4346,8 +4339,8 @@ TEST_P(OEMCryptoGenericCryptoTest, GenericKeyEncryptSameBufferAPI12) {
vector<uint8_t> buffer = clear_buffer_;
ASSERT_EQ(OEMCrypto_SUCCESS,
OEMCrypto_Generic_Encrypt(
session_.session_id(), buffer.data(), buffer.size(),
iv_, OEMCrypto_AES_CBC_128_NO_PADDING, buffer.data()));
session_.session_id(), buffer.data(), buffer.size(), iv_,
OEMCrypto_AES_CBC_128_NO_PADDING, buffer.data()));
ASSERT_EQ(expected_encrypted, buffer);
}
@@ -4474,11 +4467,10 @@ TEST_P(OEMCryptoGenericCryptoTest, GenericKeyVerify) {
session_.license().keys[key_index].key_id,
session_.license().keys[key_index].key_id_length,
OEMCrypto_CipherMode_CTR));
ASSERT_EQ(OEMCrypto_SUCCESS,
OEMCrypto_Generic_Verify(
session_.session_id(), clear_buffer_.data(),
clear_buffer_.size(), OEMCrypto_HMAC_SHA256, signature.data(),
signature.size()));
ASSERT_EQ(OEMCrypto_SUCCESS, OEMCrypto_Generic_Verify(
session_.session_id(), clear_buffer_.data(),
clear_buffer_.size(), OEMCrypto_HMAC_SHA256,
signature.data(), signature.size()));
}
// Test that the Generic_Verify function fails when not allowed.
@@ -4507,11 +4499,11 @@ TEST_P(OEMCryptoGenericCryptoTest, GenericKeyEncryptLargeBuffer) {
session_.license().keys[key_index].key_id_length,
OEMCrypto_CipherMode_CTR));
vector<uint8_t> encrypted(clear_buffer_.size());
ASSERT_EQ(OEMCrypto_SUCCESS,
OEMCrypto_Generic_Encrypt(
session_.session_id(), clear_buffer_.data(),
clear_buffer_.size(), iv_, OEMCrypto_AES_CBC_128_NO_PADDING,
encrypted.data()));
ASSERT_EQ(
OEMCrypto_SUCCESS,
OEMCrypto_Generic_Encrypt(
session_.session_id(), clear_buffer_.data(), clear_buffer_.size(),
iv_, OEMCrypto_AES_CBC_128_NO_PADDING, encrypted.data()));
ASSERT_EQ(expected_encrypted, encrypted);
}
@@ -4579,11 +4571,10 @@ TEST_P(OEMCryptoGenericCryptoTest, GenericKeyVerifyLargeBuffer) {
session_.license().keys[key_index].key_id,
session_.license().keys[key_index].key_id_length,
OEMCrypto_CipherMode_CTR));
ASSERT_EQ(OEMCrypto_SUCCESS,
OEMCrypto_Generic_Verify(
session_.session_id(), clear_buffer_.data(),
clear_buffer_.size(), OEMCrypto_HMAC_SHA256, signature.data(),
signature.size()));
ASSERT_EQ(OEMCrypto_SUCCESS, OEMCrypto_Generic_Verify(
session_.session_id(), clear_buffer_.data(),
clear_buffer_.size(), OEMCrypto_HMAC_SHA256,
signature.data(), signature.size()));
}
// Test Generic_Encrypt when the key duration has expired.
@@ -4604,11 +4595,11 @@ TEST_P(OEMCryptoGenericCryptoTest, KeyDurationEncrypt) {
session_.license().keys[key_index].key_id,
session_.license().keys[key_index].key_id_length,
OEMCrypto_CipherMode_CTR));
ASSERT_EQ(OEMCrypto_SUCCESS,
OEMCrypto_Generic_Encrypt(
session_.session_id(), clear_buffer_.data(),
clear_buffer_.size(), iv_, OEMCrypto_AES_CBC_128_NO_PADDING,
encrypted.data()));
ASSERT_EQ(
OEMCrypto_SUCCESS,
OEMCrypto_Generic_Encrypt(
session_.session_id(), clear_buffer_.data(), clear_buffer_.size(),
iv_, OEMCrypto_AES_CBC_128_NO_PADDING, encrypted.data()));
ASSERT_EQ(expected_encrypted, encrypted);
wvcdm::TestSleep::Sleep(kLongSleep + kShortSleep); // Should be expired key.
@@ -4709,11 +4700,10 @@ TEST_P(OEMCryptoGenericCryptoTest, KeyDurationVerify) {
session_.license().keys[key_index].key_id,
session_.license().keys[key_index].key_id_length,
OEMCrypto_CipherMode_CTR));
ASSERT_EQ(OEMCrypto_SUCCESS,
OEMCrypto_Generic_Verify(
session_.session_id(), clear_buffer_.data(),
clear_buffer_.size(), OEMCrypto_HMAC_SHA256, signature.data(),
signature.size()));
ASSERT_EQ(OEMCrypto_SUCCESS, OEMCrypto_Generic_Verify(
session_.session_id(), clear_buffer_.data(),
clear_buffer_.size(), OEMCrypto_HMAC_SHA256,
signature.data(), signature.size()));
wvcdm::TestSleep::Sleep(kLongSleep + kShortSleep); // Should be expired key.
OEMCryptoResult status = OEMCrypto_Generic_Verify(
@@ -4827,7 +4817,8 @@ class LicenseWithUsageEntry {
generic_crypto_(false),
time_license_received_(0),
time_first_decrypt_(0),
time_last_decrypt_(0) {
time_last_decrypt_(0),
active_(true) {
license_messages_.set_pst(pst);
}
@@ -4888,6 +4879,15 @@ class LicenseWithUsageEntry {
if (time_first_decrypt_ == 0) time_first_decrypt_ = time_last_decrypt_;
}
void DeactivateUsageEntry() {
active_ = false;
ASSERT_EQ(
OEMCrypto_SUCCESS,
OEMCrypto_DeactivateUsageEntry(
session_.session_id(),
reinterpret_cast<const uint8_t*>(pst().c_str()), pst().length()));
}
void GenerateVerifyReport(OEMCrypto_Usage_Entry_Status status) {
ASSERT_NO_FATAL_FAILURE(session_.GenerateReport(pst()));
Test_PST_Report expected(pst(), status);
@@ -4897,7 +4897,7 @@ class LicenseWithUsageEntry {
// The PST report was signed above. Below we verify that the entire message
// that is sent to the server will be signed by the right mac keys.
RenewalRoundTrip renewal_messages(&license_messages_);
renewal_messages.set_is_release(true);
renewal_messages.set_is_release(!active_);
ASSERT_NO_FATAL_FAILURE(renewal_messages.SignAndVerifyRequest());
}
@@ -4921,6 +4921,7 @@ class LicenseWithUsageEntry {
int64_t time_license_received_;
int64_t time_first_decrypt_;
int64_t time_last_decrypt_;
bool active_;
};
class OEMCryptoUsageTableTest : public OEMCryptoGenericCryptoTest {
@@ -4973,7 +4974,7 @@ TEST_P(OEMCryptoUsageTableTest, OnlineLicense) {
ASSERT_NO_FATAL_FAILURE(s.UpdateUsageEntry(&encrypted_usage_header_));
ASSERT_NO_FATAL_FAILURE(entry.GenerateVerifyReport(kActive));
// Flag the entry as inactive.
ASSERT_NO_FATAL_FAILURE(s.DeactivateUsageEntry(entry.pst()));
ASSERT_NO_FATAL_FAILURE(entry.DeactivateUsageEntry());
ASSERT_NO_FATAL_FAILURE(s.UpdateUsageEntry(&encrypted_usage_header_));
// It should report as inactive.
ASSERT_NO_FATAL_FAILURE(entry.GenerateVerifyReport(kInactiveUsed));
@@ -4982,7 +4983,7 @@ TEST_P(OEMCryptoUsageTableTest, OnlineLicense) {
entry.TestDecryptCTR(false, OEMCrypto_ERROR_UNKNOWN_FAILURE));
// We could call DeactivateUsageEntry multiple times. The state should not
// change.
ASSERT_NO_FATAL_FAILURE(s.DeactivateUsageEntry(entry.pst()));
ASSERT_NO_FATAL_FAILURE(entry.DeactivateUsageEntry());
ASSERT_NO_FATAL_FAILURE(s.UpdateUsageEntry(&encrypted_usage_header_));
// It should report as inactive.
ASSERT_NO_FATAL_FAILURE(entry.GenerateVerifyReport(kInactiveUsed));
@@ -4999,7 +5000,7 @@ TEST_P(OEMCryptoUsageTableTest, OnlineLicenseUnused) {
// No decrypt. We do not use this license.
ASSERT_NO_FATAL_FAILURE(entry.GenerateVerifyReport(kUnused));
// Flag the entry as inactive.
ASSERT_NO_FATAL_FAILURE(s.DeactivateUsageEntry(entry.pst()));
ASSERT_NO_FATAL_FAILURE(entry.DeactivateUsageEntry());
ASSERT_NO_FATAL_FAILURE(s.UpdateUsageEntry(&encrypted_usage_header_));
// It should report as inactive.
ASSERT_NO_FATAL_FAILURE(entry.GenerateVerifyReport(kInactiveUnused));
@@ -5008,7 +5009,7 @@ TEST_P(OEMCryptoUsageTableTest, OnlineLicenseUnused) {
entry.TestDecryptCTR(false, OEMCrypto_ERROR_UNKNOWN_FAILURE));
// We could call DeactivateUsageEntry multiple times. The state should not
// change.
ASSERT_NO_FATAL_FAILURE(s.DeactivateUsageEntry(entry.pst()));
ASSERT_NO_FATAL_FAILURE(entry.DeactivateUsageEntry());
ASSERT_NO_FATAL_FAILURE(s.UpdateUsageEntry(&encrypted_usage_header_));
// It should report as inactive.
ASSERT_NO_FATAL_FAILURE(entry.GenerateVerifyReport(kInactiveUnused));
@@ -5031,7 +5032,7 @@ TEST_P(OEMCryptoUsageTableTest, ForbidReportWithNoUpdate) {
// Now it's OK.
ASSERT_NO_FATAL_FAILURE(entry.GenerateVerifyReport(kActive));
// Flag the entry as inactive.
ASSERT_NO_FATAL_FAILURE(s.DeactivateUsageEntry(entry.pst()));
ASSERT_NO_FATAL_FAILURE(entry.DeactivateUsageEntry());
// Cannot generate a report without first updating the file.
ASSERT_NO_FATAL_FAILURE(
s.GenerateReport(entry.pst(), OEMCrypto_ERROR_ENTRY_NEEDS_UPDATE));
@@ -5201,7 +5202,7 @@ TEST_P(OEMCryptoUsageTableTest, GenericCryptoEncrypt) {
EXPECT_EQ(expected_encrypted, encrypted);
ASSERT_NO_FATAL_FAILURE(s.UpdateUsageEntry(&encrypted_usage_header_));
ASSERT_NO_FATAL_FAILURE(entry.GenerateVerifyReport(kActive));
ASSERT_NO_FATAL_FAILURE(s.DeactivateUsageEntry(entry.pst()));
ASSERT_NO_FATAL_FAILURE(entry.DeactivateUsageEntry());
ASSERT_NO_FATAL_FAILURE(s.UpdateUsageEntry(&encrypted_usage_header_));
ASSERT_NO_FATAL_FAILURE(entry.GenerateVerifyReport(kInactiveUsed));
encrypted.assign(clear_buffer_.size(), 0);
@@ -5236,7 +5237,7 @@ TEST_P(OEMCryptoUsageTableTest, GenericCryptoDecrypt) {
EXPECT_EQ(clear_buffer_, resultant);
ASSERT_NO_FATAL_FAILURE(s.UpdateUsageEntry(&encrypted_usage_header_));
ASSERT_NO_FATAL_FAILURE(entry.GenerateVerifyReport(kActive));
ASSERT_NO_FATAL_FAILURE(s.DeactivateUsageEntry(entry.pst()));
ASSERT_NO_FATAL_FAILURE(entry.DeactivateUsageEntry());
ASSERT_NO_FATAL_FAILURE(s.UpdateUsageEntry(&encrypted_usage_header_));
ASSERT_NO_FATAL_FAILURE(entry.GenerateVerifyReport(kInactiveUsed));
resultant.assign(encrypted.size(), 0);
@@ -5279,7 +5280,7 @@ TEST_P(OEMCryptoUsageTableTest, GenericCryptoSign) {
ASSERT_NO_FATAL_FAILURE(s.UpdateUsageEntry(&encrypted_usage_header_));
ASSERT_NO_FATAL_FAILURE(entry.GenerateVerifyReport(kActive));
ASSERT_NO_FATAL_FAILURE(s.DeactivateUsageEntry(entry.pst()));
ASSERT_NO_FATAL_FAILURE(entry.DeactivateUsageEntry());
ASSERT_NO_FATAL_FAILURE(s.UpdateUsageEntry(&encrypted_usage_header_));
ASSERT_NO_FATAL_FAILURE(entry.GenerateVerifyReport(kInactiveUsed));
signature.assign(SHA256_DIGEST_LENGTH, 0);
@@ -5314,7 +5315,7 @@ TEST_P(OEMCryptoUsageTableTest, GenericCryptoVerify) {
ASSERT_EQ(OEMCrypto_SUCCESS, sts);
ASSERT_NO_FATAL_FAILURE(s.UpdateUsageEntry(&encrypted_usage_header_));
ASSERT_NO_FATAL_FAILURE(entry.GenerateVerifyReport(kActive));
ASSERT_NO_FATAL_FAILURE(s.DeactivateUsageEntry(entry.pst()));
ASSERT_NO_FATAL_FAILURE(entry.DeactivateUsageEntry());
ASSERT_NO_FATAL_FAILURE(s.UpdateUsageEntry(&encrypted_usage_header_));
ASSERT_NO_FATAL_FAILURE(entry.GenerateVerifyReport(kInactiveUsed));
sts = OEMCrypto_Generic_Verify(s.session_id(), clear_buffer_.data(),
@@ -5505,8 +5506,7 @@ TEST_P(OEMCryptoUsageTableTest, DeactivateOfflineLicense) {
ASSERT_NO_FATAL_FAILURE(entry.OpenAndReload(this));
ASSERT_NO_FATAL_FAILURE(
entry.TestDecryptCTR()); // Should be able to decrypt.
ASSERT_NO_FATAL_FAILURE(
s.DeactivateUsageEntry(entry.pst())); // Then deactivate.
ASSERT_NO_FATAL_FAILURE(entry.DeactivateUsageEntry()); // Then deactivate.
// After deactivate, should not be able to decrypt.
ASSERT_NO_FATAL_FAILURE(
entry.TestDecryptCTR(false, OEMCrypto_ERROR_UNKNOWN_FAILURE));
@@ -5531,7 +5531,7 @@ TEST_P(OEMCryptoUsageTableTest, DeactivateOfflineLicense) {
ASSERT_NO_FATAL_FAILURE(entry.GenerateVerifyReport(kInactiveUsed));
// We could call DeactivateUsageEntry multiple times. The state should not
// change.
ASSERT_NO_FATAL_FAILURE(s.DeactivateUsageEntry(entry.pst()));
ASSERT_NO_FATAL_FAILURE(entry.DeactivateUsageEntry());
ASSERT_NO_FATAL_FAILURE(s.UpdateUsageEntry(&encrypted_usage_header_));
ASSERT_NO_FATAL_FAILURE(entry.GenerateVerifyReport(kInactiveUsed));
}
@@ -5545,8 +5545,7 @@ TEST_P(OEMCryptoUsageTableTest, DeactivateOfflineLicenseUnused) {
Session& s = entry.session();
// No Decrypt. This license is unused.
ASSERT_NO_FATAL_FAILURE(entry.OpenAndReload(this));
ASSERT_NO_FATAL_FAILURE(
s.DeactivateUsageEntry(entry.pst())); // Then deactivate.
ASSERT_NO_FATAL_FAILURE(entry.DeactivateUsageEntry()); // Then deactivate.
// After deactivate, should not be able to decrypt.
ASSERT_NO_FATAL_FAILURE(
entry.TestDecryptCTR(false, OEMCrypto_ERROR_UNKNOWN_FAILURE));
@@ -5571,11 +5570,30 @@ TEST_P(OEMCryptoUsageTableTest, DeactivateOfflineLicenseUnused) {
ASSERT_NO_FATAL_FAILURE(entry.GenerateVerifyReport(kInactiveUnused));
// We could call DeactivateUsageEntry multiple times. The state should not
// change.
ASSERT_NO_FATAL_FAILURE(s.DeactivateUsageEntry(entry.pst()));
ASSERT_NO_FATAL_FAILURE(entry.DeactivateUsageEntry());
ASSERT_NO_FATAL_FAILURE(s.UpdateUsageEntry(&encrypted_usage_header_));
ASSERT_NO_FATAL_FAILURE(entry.GenerateVerifyReport(kInactiveUnused));
}
TEST_P(OEMCryptoUsageTableTest, SecureStop) {
LicenseWithUsageEntry entry;
entry.license_messages().set_api_version(license_api_version_);
entry.MakeAndLoadOnline(this);
Session& s = entry.session();
ASSERT_NO_FATAL_FAILURE(s.UpdateUsageEntry(&encrypted_usage_header_));
ASSERT_NO_FATAL_FAILURE(entry.TestDecryptCTR());
ASSERT_NO_FATAL_FAILURE(s.UpdateUsageEntry(&encrypted_usage_header_));
ASSERT_NO_FATAL_FAILURE(s.close());
// When we generate a secure stop without loading the license first, it
// should assume the server does not support core messages.
ASSERT_NO_FATAL_FAILURE(s.open());
ASSERT_NO_FATAL_FAILURE(entry.ReloadUsageEntry());
ASSERT_NO_FATAL_FAILURE(entry.DeactivateUsageEntry());
ASSERT_NO_FATAL_FAILURE(s.UpdateUsageEntry(&encrypted_usage_header_));
// It should report as inactive.
ASSERT_NO_FATAL_FAILURE(entry.GenerateVerifyReport(kInactiveUsed));
}
// Test update usage table fails when passed a null pointer.
TEST_P(OEMCryptoUsageTableTest, UpdateFailsWithNullPtr) {
LicenseWithUsageEntry entry;
@@ -5638,8 +5656,7 @@ class OEMCryptoUsageTableDefragTest : public OEMCryptoUsageTableTest {
ASSERT_LT(0u, header_buffer_length);
encrypted_usage_header_.resize(header_buffer_length);
sts = OEMCrypto_ShrinkUsageTableHeader(
new_size, encrypted_usage_header_.data(),
&header_buffer_length);
new_size, encrypted_usage_header_.data(), &header_buffer_length);
ASSERT_EQ(expected_result, sts);
}
};
@@ -5895,26 +5912,23 @@ TEST_P(OEMCryptoUsageTableTest, ReloadUsageTableWithSkew) {
nullptr, old_usage_header_2_.size()));
ASSERT_NO_FATAL_FAILURE(s.open());
// Cannot load an entry if header didn't load.
ASSERT_EQ(
OEMCrypto_ERROR_UNKNOWN_FAILURE,
OEMCrypto_LoadUsageEntry(s.session_id(), s.usage_entry_number(),
s.encrypted_usage_entry().data(),
s.encrypted_usage_entry().size()));
ASSERT_EQ(OEMCrypto_ERROR_UNKNOWN_FAILURE,
OEMCrypto_LoadUsageEntry(s.session_id(), s.usage_entry_number(),
s.encrypted_usage_entry().data(),
s.encrypted_usage_entry().size()));
ASSERT_NO_FATAL_FAILURE(s.close());
// Modified header generates error.
vector<uint8_t> bad_header = encrypted_usage_header_;
bad_header[3] ^= 42;
ASSERT_NE(OEMCrypto_SUCCESS,
OEMCrypto_LoadUsageTableHeader(bad_header.data(),
bad_header.size()));
ASSERT_NE(OEMCrypto_SUCCESS, OEMCrypto_LoadUsageTableHeader(
bad_header.data(), bad_header.size()));
ASSERT_NO_FATAL_FAILURE(s.open());
// Cannot load an entry if header didn't load.
ASSERT_EQ(
OEMCrypto_ERROR_UNKNOWN_FAILURE,
OEMCrypto_LoadUsageEntry(s.session_id(), s.usage_entry_number(),
s.encrypted_usage_entry().data(),
s.encrypted_usage_entry().size()));
ASSERT_EQ(OEMCrypto_ERROR_UNKNOWN_FAILURE,
OEMCrypto_LoadUsageEntry(s.session_id(), s.usage_entry_number(),
s.encrypted_usage_entry().data(),
s.encrypted_usage_entry().size()));
ASSERT_NO_FATAL_FAILURE(s.close());
// Old by 2 generation numbers is error.
@@ -5923,11 +5937,10 @@ TEST_P(OEMCryptoUsageTableTest, ReloadUsageTableWithSkew) {
old_usage_header_2_.size()));
ASSERT_NO_FATAL_FAILURE(s.open());
// Cannot load an entry if header didn't load.
ASSERT_NE(
OEMCrypto_SUCCESS,
OEMCrypto_LoadUsageEntry(s.session_id(), s.usage_entry_number(),
s.encrypted_usage_entry().data(),
s.encrypted_usage_entry().size()));
ASSERT_NE(OEMCrypto_SUCCESS,
OEMCrypto_LoadUsageEntry(s.session_id(), s.usage_entry_number(),
s.encrypted_usage_entry().data(),
s.encrypted_usage_entry().size()));
ASSERT_NO_FATAL_FAILURE(s.close());
// Old by 1 generation numbers is just warning.
@@ -5936,11 +5949,10 @@ TEST_P(OEMCryptoUsageTableTest, ReloadUsageTableWithSkew) {
old_usage_header_1_.size()));
// Everything else should still work. Skew by 1 is just a warning.
ASSERT_NO_FATAL_FAILURE(s.open());
ASSERT_EQ(
OEMCrypto_WARNING_GENERATION_SKEW,
OEMCrypto_LoadUsageEntry(s.session_id(), s.usage_entry_number(),
s.encrypted_usage_entry().data(),
s.encrypted_usage_entry().size()));
ASSERT_EQ(OEMCrypto_WARNING_GENERATION_SKEW,
OEMCrypto_LoadUsageEntry(s.session_id(), s.usage_entry_number(),
s.encrypted_usage_entry().data(),
s.encrypted_usage_entry().size()));
ASSERT_NO_FATAL_FAILURE(InstallTestRSAKey(&s));
ASSERT_EQ(OEMCrypto_SUCCESS, entry.license_messages().LoadResponse());
}
@@ -5952,8 +5964,8 @@ TEST_P(OEMCryptoUsageTableTest, GenerateReportWrongPST) {
entry.MakeAndLoadOnline(this);
Session& s = entry.session();
ASSERT_NO_FATAL_FAILURE(s.UpdateUsageEntry(&encrypted_usage_header_));
ASSERT_NO_FATAL_FAILURE(s.GenerateReport("wrong_pst",
OEMCrypto_ERROR_WRONG_PST));
ASSERT_NO_FATAL_FAILURE(
s.GenerateReport("wrong_pst", OEMCrypto_ERROR_WRONG_PST));
}
// Test usage table timing.
@@ -5992,7 +6004,7 @@ TEST_P(OEMCryptoUsageTableTest, TimingTest) {
ASSERT_NO_FATAL_FAILURE(entry2.TestDecryptCTR());
wvcdm::TestSleep::Sleep(kLongSleep);
ASSERT_NO_FATAL_FAILURE(s1.DeactivateUsageEntry(entry1.pst()));
ASSERT_NO_FATAL_FAILURE(entry1.DeactivateUsageEntry());
ASSERT_NO_FATAL_FAILURE(s1.UpdateUsageEntry(&encrypted_usage_header_));
ASSERT_NO_FATAL_FAILURE(s2.UpdateUsageEntry(&encrypted_usage_header_));
ASSERT_NO_FATAL_FAILURE(s1.close());
@@ -6105,7 +6117,7 @@ TEST_P(OEMCryptoUsageTableTest, VerifyUsageTimes) {
// |<------------------------------------| = seconds_since_license_received
ASSERT_NO_FATAL_FAILURE(s.UpdateUsageEntry(&encrypted_usage_header_));
ASSERT_NO_FATAL_FAILURE(entry.GenerateVerifyReport(kActive));
ASSERT_NO_FATAL_FAILURE(s.DeactivateUsageEntry(entry.pst()));
ASSERT_NO_FATAL_FAILURE(entry.DeactivateUsageEntry());
ASSERT_NO_FATAL_FAILURE(s.UpdateUsageEntry(&encrypted_usage_header_));
ASSERT_NO_FATAL_FAILURE(entry.GenerateVerifyReport(kInactiveUsed));
ASSERT_NO_FATAL_FAILURE(
@@ -6195,8 +6207,8 @@ TEST_P(OEMCryptoUsageTableTestWallClock, TimeRollbackPrevention) {
// Rollback the wall clock time.
cout << "Rolling the system time back..." << endl;
ASSERT_NO_FATAL_FAILURE(SetWallTimeDelta(
-static_cast<int64_t>(kLongDuration) * 10));
ASSERT_NO_FATAL_FAILURE(
SetWallTimeDelta(-static_cast<int64_t>(kLongDuration) * 10));
// Try to playback again.
ASSERT_NO_FATAL_FAILURE(entry.OpenAndReload(this));
@@ -6210,8 +6222,8 @@ TEST_P(OEMCryptoUsageTableTestWallClock, TimeRollbackPrevention) {
// not report negative times.
const auto test_duration = third_decrypt_monotonic - first_decrypt_monotonic;
cout << "Rolling the system time forward to the absolute time..." << endl;
ASSERT_NO_FATAL_FAILURE(SetWallTimeDelta(
test_duration / std::chrono::seconds(1)));
ASSERT_NO_FATAL_FAILURE(
SetWallTimeDelta(test_duration / std::chrono::seconds(1)));
// Need to update time created since the verification checks the time of PST
// report creation.
expected.time_created = UnixTime(wall_clock.now());
@@ -6232,8 +6244,7 @@ TEST_P(OEMCryptoUsageTableTest, PSTLargeBuffer) {
ASSERT_NO_FATAL_FAILURE(entry.OpenAndReload(this));
ASSERT_NO_FATAL_FAILURE(
entry.TestDecryptCTR()); // Should be able to decrypt.
ASSERT_NO_FATAL_FAILURE(
s.DeactivateUsageEntry(entry.pst())); // Then deactivate.
ASSERT_NO_FATAL_FAILURE(entry.DeactivateUsageEntry()); // Then deactivate.
// After deactivate, should not be able to decrypt.
ASSERT_NO_FATAL_FAILURE(
entry.TestDecryptCTR(false, OEMCrypto_ERROR_UNKNOWN_FAILURE));