Merge "Update ODK Library and add license release unit test" into rvc-dev

This commit is contained in:
Fred Gylys-Colwell
2020-03-05 21:16:55 +00:00
committed by Android (Google) Code Review
17 changed files with 1528 additions and 866 deletions

View File

@@ -81,6 +81,7 @@ cc_test {
srcs: [ srcs: [
"test/odk_test.cpp", "test/odk_test.cpp",
"test/odk_test_helper.cpp",
"test/odk_timer_test.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 This library is used by both OEMCrypto on a device, and by Widvine license and
provisioning servers. provisioning servers.

View File

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

View File

@@ -15,48 +15,63 @@
#include "odk_util.h" #include "odk_util.h"
#include "serialization_base.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 */ /* @ private odk functions */
static OEMCryptoResult ODK_PrepareRequest(uint8_t* buffer, size_t buffer_length, static OEMCryptoResult ODK_PrepareRequest(
size_t* core_message_length, uint8_t* message, size_t message_length, size_t* core_message_length,
uint32_t message_type, uint32_t message_type, const ODK_NonceValues* nonce_values,
const ODK_NonceValues* nonce_values, void* prepared_request_buffer, size_t prepared_request_buffer_length) {
ODK_CoreMessage* core_message) {
if (nonce_values == NULL || core_message_length == NULL || 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; return ODK_ERROR_CORE_MESSAGE;
} }
Message* msg = NULL; Message* msg = NULL;
AllocateMessage(&msg, message_block); 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){ *core_message = (ODK_CoreMessage){
message_type, message_type,
0, 0,
*nonce_values, *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) { switch (message_type) {
case ODK_License_Request_Type: { case ODK_License_Request_Type: {
core_message->message_length = ODK_LICENSE_REQUEST_SIZE; core_message->message_length = ODK_LICENSE_REQUEST_SIZE;
if (sizeof(ODK_PreparedLicenseRequest) > prepared_request_buffer_length) {
return ODK_ERROR_CORE_MESSAGE;
}
Pack_ODK_PreparedLicenseRequest( Pack_ODK_PreparedLicenseRequest(
msg, (ODK_PreparedLicenseRequest*)core_message); msg, (ODK_PreparedLicenseRequest*)prepared_request_buffer);
break; break;
} }
case ODK_Renewal_Request_Type: { case ODK_Renewal_Request_Type: {
core_message->message_length = ODK_RENEWAL_REQUEST_SIZE; core_message->message_length = ODK_RENEWAL_REQUEST_SIZE;
if (sizeof(ODK_PreparedRenewalRequest) > prepared_request_buffer_length) {
return ODK_ERROR_CORE_MESSAGE;
}
Pack_ODK_PreparedRenewalRequest( Pack_ODK_PreparedRenewalRequest(
msg, (ODK_PreparedRenewalRequest*)core_message); msg, (ODK_PreparedRenewalRequest*)prepared_request_buffer);
break; break;
} }
case ODK_Provisioning_Request_Type: { case ODK_Provisioning_Request_Type: {
core_message->message_length = ODK_PROVISIONING_REQUEST_SIZE; core_message->message_length = ODK_PROVISIONING_REQUEST_SIZE;
if (sizeof(ODK_PreparedProvisioningRequest) >
prepared_request_buffer_length) {
return ODK_ERROR_CORE_MESSAGE;
}
Pack_ODK_PreparedProvisioningRequest( Pack_ODK_PreparedProvisioningRequest(
msg, (ODK_PreparedProvisioningRequest*)core_message); msg, (ODK_PreparedProvisioningRequest*)prepared_request_buffer);
break; break;
} }
default: { default: {
@@ -65,43 +80,59 @@ static OEMCryptoResult ODK_PrepareRequest(uint8_t* buffer, size_t buffer_length,
} }
*core_message_length = core_message->message_length; *core_message_length = core_message->message_length;
if (GetStatus(msg) != MESSAGE_STATUS_OK || if (GetStatus(msg) != MESSAGE_STATUS_OK) {
GetSize(msg) != *core_message_length) { /* 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; 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; return OEMCrypto_SUCCESS;
} }
static OEMCryptoResult ODK_ParseResponse(const uint8_t* buf, static OEMCryptoResult ODK_ParseResponse(
size_t message_length, const uint8_t* message, size_t message_length, size_t core_message_length,
size_t core_message_length, uint32_t message_type, const ODK_NonceValues* nonce_values,
uint32_t message_type, void* response_buffer, uint32_t response_buffer_length) {
const ODK_NonceValues* nonce_values, if (message == NULL || response_buffer == NULL ||
ODK_CoreMessage* const core_message) { core_message_length > message_length) {
if (core_message_length > message_length) {
return ODK_ERROR_CORE_MESSAGE; return ODK_ERROR_CORE_MESSAGE;
} }
Message* msg = NULL; Message* msg = NULL;
AllocateMessage(&msg, message_block); AllocateMessage(&msg, message_block);
/* We initialize the message buffer with a size of the entire message /* We initialize the message buffer with a size of the entire message
* length. */ * 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 /* 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. */ * shorter length. The core message is the part we are parsing. */
SetSize(msg, core_message_length); SetSize(msg, core_message_length);
/* Parse message and unpack it into response buffer. */
switch (message_type) { switch (message_type) {
case ODK_License_Response_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; break;
} }
case ODK_Renewal_Response_Type: { 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; break;
} }
case ODK_Provisioning_Response_Type: { case ODK_Provisioning_Response_Type: {
Unpack_ODK_ProvisioningResponse(msg, if (sizeof(ODK_ProvisioningResponse) > response_buffer_length) {
(ODK_ProvisioningResponse*)core_message); return ODK_ERROR_CORE_MESSAGE;
}
Unpack_ODK_ProvisioningResponse(
msg, (ODK_ProvisioningResponse*)response_buffer);
break; break;
} }
default: { 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 || if (GetStatus(msg) != MESSAGE_STATUS_OK ||
message_type != core_message->message_type || message_type != core_message->message_type ||
GetOffset(msg) != core_message->message_length) { GetOffset(msg) != core_message->message_length) {
@@ -117,12 +149,7 @@ static OEMCryptoResult ODK_ParseResponse(const uint8_t* buf,
if (nonce_values) { if (nonce_values) {
/* always verify nonce_values for Renewal and Provisioning responses */ /* always verify nonce_values for Renewal and Provisioning responses */
if (nonce_values->api_major_version != if (!ODK_NonceValuesEqual(nonce_values, &(core_message->nonce_values))) {
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) {
return OEMCrypto_ERROR_INVALID_NONCE; return OEMCrypto_ERROR_INVALID_NONCE;
} }
} }
@@ -137,12 +164,15 @@ static OEMCryptoResult ODK_ParseResponse(const uint8_t* buf,
OEMCryptoResult ODK_PrepareCoreLicenseRequest( OEMCryptoResult ODK_PrepareCoreLicenseRequest(
uint8_t* message, size_t message_length, size_t* core_message_length, uint8_t* message, size_t message_length, size_t* core_message_length,
const ODK_NonceValues* nonce_values) { const ODK_NonceValues* nonce_values) {
if (core_message_length == NULL || nonce_values == NULL) {
return ODK_ERROR_CORE_MESSAGE;
}
ODK_PreparedLicenseRequest license_request = { ODK_PreparedLicenseRequest license_request = {
{0}, {0},
}; };
return ODK_PrepareRequest(message, message_length, core_message_length, return ODK_PrepareRequest(
ODK_License_Request_Type, nonce_values, message, message_length, core_message_length, ODK_License_Request_Type,
&license_request.core_message); nonce_values, &license_request, sizeof(ODK_PreparedLicenseRequest));
} }
OEMCryptoResult ODK_PrepareCoreRenewalRequest(uint8_t* message, OEMCryptoResult ODK_PrepareCoreRenewalRequest(uint8_t* message,
@@ -151,8 +181,22 @@ OEMCryptoResult ODK_PrepareCoreRenewalRequest(uint8_t* message,
ODK_NonceValues* nonce_values, ODK_NonceValues* nonce_values,
ODK_ClockValues* clock_values, ODK_ClockValues* clock_values,
uint64_t system_time_seconds) { 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; 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}; ODK_PreparedRenewalRequest renewal_request = {{0}, 0};
/* First, we compute the time this request was made relative to the playback /* First, we compute the time this request was made relative to the playback
* clock. */ * clock. */
@@ -168,19 +212,24 @@ OEMCryptoResult ODK_PrepareCoreRenewalRequest(uint8_t* message,
return ODK_ERROR_CORE_MESSAGE; return ODK_ERROR_CORE_MESSAGE;
} }
} }
/* Save time for this request so that we can verify the response. This makes /* 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 * all earlier requests invalid. If preparing this request fails, then all
* requests will be bad. */ * requests will be bad. */
clock_values->time_of_renewal_request = renewal_request.playback_time; 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, return ODK_PrepareRequest(
&renewal_request.core_message); message, message_length, core_message_size, ODK_Renewal_Request_Type,
nonce_values, &renewal_request, sizeof(ODK_PreparedRenewalRequest));
} }
OEMCryptoResult ODK_PrepareCoreProvisioningRequest( OEMCryptoResult ODK_PrepareCoreProvisioningRequest(
uint8_t* message, size_t message_length, size_t* core_message_length, uint8_t* message, size_t message_length, size_t* core_message_length,
const ODK_NonceValues* nonce_values, const uint8_t* device_id, const ODK_NonceValues* nonce_values, const uint8_t* device_id,
size_t device_id_length) { size_t device_id_length) {
if (core_message_length == NULL || nonce_values == NULL) {
return ODK_ERROR_CORE_MESSAGE;
}
ODK_PreparedProvisioningRequest provisioning_request = { ODK_PreparedProvisioningRequest provisioning_request = {
{0}, {0},
0, 0,
@@ -195,7 +244,8 @@ OEMCryptoResult ODK_PrepareCoreProvisioningRequest(
} }
return ODK_PrepareRequest(message, message_length, core_message_length, return ODK_PrepareRequest(message, message_length, core_message_length,
ODK_Provisioning_Request_Type, nonce_values, ODK_Provisioning_Request_Type, nonce_values,
&provisioning_request.core_message); &provisioning_request,
sizeof(ODK_PreparedProvisioningRequest));
} }
/* @@ parse request functions */ /* @@ parse request functions */
@@ -214,7 +264,7 @@ OEMCryptoResult ODK_ParseLicense(
ODK_LicenseResponse license_response = {{{0}}, parsed_license, {0}}; ODK_LicenseResponse license_response = {{{0}}, parsed_license, {0}};
const OEMCryptoResult err = ODK_ParseResponse( const OEMCryptoResult err = ODK_ParseResponse(
message, message_length, core_message_length, ODK_License_Response_Type, 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) { if (err != OEMCrypto_SUCCESS) {
return err; return err;
@@ -279,11 +329,11 @@ OEMCryptoResult ODK_ParseRenewal(const uint8_t* message, size_t message_length,
{{0}, 0}, {{0}, 0},
0, 0,
}; };
OEMCryptoResult err = ODK_ParseResponse( const OEMCryptoResult err = ODK_ParseResponse(
message, message_length, core_message_length, ODK_Renewal_Response_Type, 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; return err;
} }
@@ -318,24 +368,24 @@ OEMCryptoResult ODK_ParseProvisioning(
return ODK_ERROR_CORE_MESSAGE; return ODK_ERROR_CORE_MESSAGE;
} }
OEMCryptoResult err = const OEMCryptoResult err = ODK_ParseResponse(
ODK_ParseResponse(message, message_length, core_message_length, message, message_length, core_message_length,
ODK_Provisioning_Response_Type, nonce_values, ODK_Provisioning_Response_Type, nonce_values, &provisioning_response,
&provisioning_response.request.core_message); sizeof(ODK_ProvisioningResponse));
if (err) { if (err != OEMCrypto_SUCCESS) {
return err; return err;
} }
if (memcmp(device_id, provisioning_response.request.device_id, if (crypto_memcmp(device_id, provisioning_response.request.device_id,
device_id_length)) { device_id_length) != 0) {
return ODK_ERROR_CORE_MESSAGE; 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 */ /* check bytes beyond device_id_length are 0 */
if (memcmp(zero, provisioning_response.request.device_id + device_id_length, 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; return ODK_ERROR_CORE_MESSAGE;
} }

View File

@@ -60,6 +60,10 @@ typedef struct {
ODK_ParsedProvisioning* parsed_provisioning; ODK_ParsedProvisioning* parsed_provisioning;
} ODK_ProvisioningResponse; } 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. */ /* These are the possible timer status values. */
#define ODK_CLOCK_TIMER_STATUS_UNDEFINED 0 /* Should not happen. */ #define ODK_CLOCK_TIMER_STATUS_UNDEFINED 0 /* Should not happen. */
/* When the structure has been initialized, but no license is loaded. */ /* When the structure has been initialized, but no license is loaded. */

View File

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

View File

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

View File

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

View File

@@ -9,7 +9,7 @@
#include <cstring> #include <cstring>
#include <functional> #include <functional>
#include <memory> #include <memory>
#include <string> #include <vector>
#include "OEMCryptoCENCCommon.h" #include "OEMCryptoCENCCommon.h"
#include "core_message_deserialize.h" #include "core_message_deserialize.h"
@@ -20,48 +20,74 @@
#include "odk_structs.h" #include "odk_structs.h"
#include "odk_structs_priv.h" #include "odk_structs_priv.h"
// TODO(b/147297226): remove this: using namespace std; typedef std::function<size_t(const uint8_t*, uint8_t*, size_t, size_t)>
// TODO(b/147297226): remove this: using namespace oec_util; 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 using oemcrypto_core_message::deserialize::CoreLicenseRequestFromMessage;
static OEMCryptoResult odk_fun_LicenseRequest( using oemcrypto_core_message::deserialize::CoreProvisioningRequestFromMessage;
uint8_t* out, size_t* size, uint32_t api_version, uint32_t nonce, using oemcrypto_core_message::deserialize::CoreRenewalRequestFromMessage;
uint32_t session_id, const ODK_LicenseRequest& /*core_license_request*/) {
return ODK_PrepareCoreLicenseRequest(out, SIZE_MAX, size, api_version, nonce, using oemcrypto_core_message::serialize::CreateCoreLicenseResponse;
session_id); 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( static OEMCryptoResult odk_serialize_RenewalRequest(
uint8_t* out, size_t* size, uint32_t api_version, uint32_t nonce, const void* in, uint8_t* out, size_t* size,
uint32_t session_id, const ODK_RenewalRequest& core_renewal) { const ODK_RenewalRequest& core_renewal, ODK_NonceValues* nonce_values) {
// todo: fuzz ODK_ClockValues ODK_ClockValues clock{};
ODK_ClockValues clock = {}; memcpy(&clock, in, sizeof(ODK_ClockValues));
uint64_t system_time_seconds = core_renewal.playback_time; uint64_t system_time_seconds = core_renewal.playback_time_seconds;
return ODK_PrepareCoreRenewalRequest(out, SIZE_MAX, size, api_version, nonce, return ODK_PrepareCoreRenewalRequest(out, SIZE_MAX, size, nonce_values,
session_id, &clock, system_time_seconds); &clock, system_time_seconds);
} }
static OEMCryptoResult odk_fun_ProvisioningRequest( static OEMCryptoResult odk_serialize_ProvisioningRequest(
uint8_t* out, size_t* size, uint32_t api_version, uint32_t nonce, const void* in, uint8_t* out, size_t* size,
uint32_t session_id, const ODK_ProvisioningRequest& core_provisioning) { const ODK_ProvisioningRequest& core_provisioning,
const ODK_NonceValues* nonce_values) {
const std::string& device_id = core_provisioning.device_id; const std::string& device_id = core_provisioning.device_id;
return ODK_PrepareCoreProvisioningRequest( 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()); 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> template <typename T, typename F, typename G>
static roundtrip_fun kdo_odk(const F& kdo_fun, const G& odk_fun) { 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 { auto roundtrip = [&](const uint8_t* in, uint8_t* out, size_t size,
std::string input(reinterpret_cast<const char*>(in), 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 = {}; T t = {};
if (!kdo_fun(input, &t)) { if (!kdo_fun(input, &t)) {
return 0; return 0;
} }
OEMCryptoResult err = ODK_NonceValues nonce_values = {t.api_minor_version, t.api_major_version,
odk_fun(out, &size, t.api_version, t.nonce, t.session_id, t); t.nonce, t.session_id};
OEMCryptoResult err = odk_fun(in, out, &size, t, &nonce_values);
return OEMCrypto_SUCCESS == err ? size : 0; return OEMCrypto_SUCCESS == err ? size : 0;
}; };
return roundtrip; return roundtrip;
@@ -70,7 +96,8 @@ static roundtrip_fun kdo_odk(const F& kdo_fun, const G& odk_fun) {
// @ odk deserialize; kdo serialize // @ odk deserialize; kdo serialize
namespace { namespace {
struct ODK_Common_Args { struct ODK_Common_Args {
uint32_t api_version; uint16_t api_minor_version;
uint16_t api_major_version;
uint32_t nonce; uint32_t nonce;
uint32_t session_id; uint32_t session_id;
}; };
@@ -78,6 +105,9 @@ struct ODK_ParseLicense_Args {
ODK_Common_Args common; ODK_Common_Args common;
uint8_t initial_license_load; uint8_t initial_license_load;
uint8_t usage_entry_present; uint8_t usage_entry_present;
uint8_t request_hash[32];
ODK_TimerLimits timer_limits;
ODK_ClockValues clock_values;
}; };
struct ODK_ParseRenewal_Args { struct ODK_ParseRenewal_Args {
ODK_Common_Args common; ODK_Common_Args common;
@@ -92,93 +122,128 @@ struct ODK_ParseProvisioning_Args {
}; };
} // namespace } // namespace
static OEMCryptoResult odk_fun_LicenseResponse( uint8_t convert_byte_to_valid_boolean(const bool* in) {
const uint8_t* message, size_t message_length, uint32_t api_version, uint8_t boolean_value;
uint32_t nonce, uint32_t session_id, const ODK_ParseLicense_Args* a, memcpy(&boolean_value, in, 1);
ODK_ParsedLicense& parsed_lic) { return boolean_value % 2;
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);
} }
static bool kdo_fun_LicenseResponse(const ODK_ParseLicense_Args* args, static OEMCryptoResult odk_deserialize_LicenseResponse(
const ODK_ParsedLicense& parsed_lic, const uint8_t* message, size_t core_message_length,
std::string* oemcrypto_core_message) { 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; 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}; common.session_id};
return CreateCoreLicenseResponse(parsed_lic, core_request, std::string core_request_sha_256(
oemcrypto_core_message); 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( static OEMCryptoResult odk_deserialize_RenewalResponse(
const uint8_t* buf, size_t len, uint32_t api_version, uint32_t nonce, const uint8_t* buf, size_t len, ODK_ParseRenewal_Args* a,
uint32_t session_id, ODK_ParseRenewal_Args* a, ODK_NonceValues* nonce_values, ODK_PreparedRenewalRequest* renewal_msg) {
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; uint64_t timer_value = 0;
OEMCryptoResult err = 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); &a->timer_limits, &a->clock_values, &timer_value);
if (OEMCrypto_SUCCESS == err) { if (OEMCrypto_SUCCESS == err) {
Message* msg = nullptr; Message* msg = nullptr;
AllocateMessage(&msg, message_block); AllocateMessage(&msg, message_block);
InitMessage(msg, const_cast<uint8_t*>(buf), len); InitMessage(msg, const_cast<uint8_t*>(buf), len);
SetSize(msg, len); SetSize(msg, len);
Unpack_ODK_PreparedRenewalRequest(msg, &renewal_msg); Unpack_ODK_PreparedRenewalRequest(msg, renewal_msg);
assert(ValidMessage(msg)); assert(ValidMessage(msg));
} }
return err; return err;
} }
static bool kdo_fun_RenewalResponse( static bool kdo_serialize_RenewalResponse(
const ODK_ParseRenewal_Args* args, const ODK_ParseRenewal_Args* args,
const ODK_PreparedRenewalRequest& renewal_msg, const ODK_PreparedRenewalRequest& renewal_msg,
std::string* oemcrypto_core_message) { std::string* oemcrypto_core_message) {
const auto& common = args->common; 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}; 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( static OEMCryptoResult odk_deserialize_ProvisioningResponse(
const uint8_t* buf, size_t len, uint32_t api_version, uint32_t nonce, const uint8_t* buf, size_t len, ODK_ParseProvisioning_Args* a,
uint32_t session_id, ODK_ParseProvisioning_Args* a, ODK_NonceValues* nonce_values, ODK_ParsedProvisioning* parsed_prov) {
ODK_ParsedProvisioning& parsed_prov) { return ODK_ParseProvisioning(buf, SIZE_MAX, len, nonce_values, a->device_id,
return ODK_ParseProvisioning(buf, len, api_version, nonce, session_id, a->device_id_length, parsed_prov);
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_ParseProvisioning_Args* args,
const ODK_ParsedProvisioning& parsed_prov, const ODK_ParsedProvisioning& parsed_prov,
std::string* oemcrypto_core_message) { std::string* oemcrypto_core_message) {
const auto& common = args->common; const auto& common = args->common;
assert(args->device_id_length <= sizeof(args->device_id)); assert(args->device_id_length <= sizeof(args->device_id));
ODK_ProvisioningRequest core_request{ 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), std::string(reinterpret_cast<const char*>(args->device_id),
args->device_id_length)}; args->device_id_length)};
return CreateCoreProvisioningResponse(parsed_prov, core_request, return CreateCoreProvisioningResponse(parsed_prov, core_request,
oemcrypto_core_message); 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> template <typename A, typename T, typename F, typename G>
static roundtrip_fun odk_kdo(const F& odk_fun, const G& kdo_fun) { 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 { auto roundtrip = [&](const uint8_t* in, uint8_t* out, size_t size,
if (sizeof(A) > size) { size_t args_size) -> size_t {
// Input byte array format: [function arguments][data to parse]
if (args_size > size) {
return 0; return 0;
} }
T t = {}; T t = {};
const uint8_t* buf = in + sizeof(A); const uint8_t* buf = in + args_size;
size_t len = size - sizeof(A); size_t len = size - args_size;
std::shared_ptr<A> _args(new A()); std::shared_ptr<A> _args(new A());
A* args = _args.get(); A* args = _args.get();
memcpy(args, in, sizeof(A)); memcpy(args, in, args_size);
const auto& common = args->common; const auto& common = args->common;
OEMCryptoResult err = odk_fun(buf, len, common.api_version, common.nonce, ODK_NonceValues nonce_values = {common.api_minor_version,
common.session_id, args, t); common.api_major_version, common.nonce,
common.session_id};
OEMCryptoResult err = odk_fun(buf, len, args, &nonce_values, &t);
if (err != OEMCrypto_SUCCESS) { if (err != OEMCrypto_SUCCESS) {
return 0; return 0;
} }
@@ -197,33 +262,43 @@ static roundtrip_fun odk_kdo(const F& odk_fun, const G& kdo_fun) {
// @ fuzz raw -> parsed -> raw // @ fuzz raw -> parsed -> raw
static void verify_roundtrip(const uint8_t* in, size_t size, 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); std::vector<uint8_t> _out(size);
auto out = _out.data(); auto out = _out.data();
size_t n = roundtrip(in, out, size); size_t n = roundtrip(in, out, size, args_size);
assert(!n || (n <= size && 0 == memcmp(in, out, n))); 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) { 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( verify_roundtrip(
data, size, data, size,
kdo_odk<ODK_LicenseRequest>(ParseLicenseRequest, odk_fun_LicenseRequest)); kdo_odk<ODK_ProvisioningRequest>(CoreProvisioningRequestFromMessage,
odk_serialize_ProvisioningRequest),
0);
verify_roundtrip( verify_roundtrip(
data, size, data, size,
kdo_odk<ODK_RenewalRequest>(ParseRenewalRequest, odk_fun_RenewalRequest)); odk_kdo<ODK_ParseLicense_Args, ODK_ParsedLicense>(
verify_roundtrip(data, size, odk_deserialize_LicenseResponse, kdo_serialize_LicenseResponse),
kdo_odk<ODK_ProvisioningRequest>( sizeof(ODK_ParseLicense_Args));
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));
verify_roundtrip( verify_roundtrip(
data, size, data, size,
odk_kdo<ODK_ParseProvisioning_Args, ODK_ParsedProvisioning>( odk_kdo<ODK_ParseRenewal_Args, ODK_PreparedRenewalRequest>(
odk_fun_ProvisioningResponse, kdo_fun_ProvisioningResponse)); 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; 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( void RenewalRoundTrip::FillAndVerifyCoreRequest(
const std::string& core_message_string) { const std::string& core_message_string) {
if (license_messages_->api_version() < kCoreMessagesAPI) { if (license_messages_->api_version() < kCoreMessagesAPI || is_release_) {
// For v15, we expect that no core request was created. // For v15 or for a release, we expect that no core request was created.
EXPECT_FALSE( EXPECT_FALSE(
oemcrypto_core_message::deserialize::CoreRenewalRequestFromMessage( oemcrypto_core_message::deserialize::CoreRenewalRequestFromMessage(
core_message_string, &core_request_)); core_message_string, &core_request_));
@@ -837,13 +837,9 @@ void RenewalRoundTrip::FillAndVerifyCoreRequest(
core_message_string, &core_request_)); core_message_string, &core_request_));
EXPECT_EQ(license_messages_->core_request().api_major_version, EXPECT_EQ(license_messages_->core_request().api_major_version,
core_request_.api_major_version); core_request_.api_major_version);
if (!is_release_) { EXPECT_EQ(license_messages_->core_request().nonce, core_request_.nonce);
// For a license release, we do not expect the nonce to be correct. That EXPECT_EQ(license_messages_->core_request().session_id,
// is because a release might be sent without loading the license first. 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)); 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) { void Session::LoadUsageEntry(uint32_t index, const vector<uint8_t>& buffer) {
usage_entry_number_ = index; usage_entry_number_ = index;
encrypted_usage_entry_ = buffer; encrypted_usage_entry_ = buffer;

View File

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

View File

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