From c5b7a01ab5dd80fa99c31ae926ed9b0696c299d2 Mon Sep 17 00:00:00 2001 From: Fred Gylys-Colwell Date: Fri, 28 Feb 2020 12:03:28 -0800 Subject: [PATCH] Update ODK Library and add license release unit test Merge from Widevine repo of two CLs. Merge from Widevine repo of http://go/wvgerrit/94743 A license release should not have a core message. This CL adjusts the existing unit tests to verify this. There is also a new unit test called SecureStop that explicitly tests sending a secure stop in a new session without first loading the license. Merge from Widevine repo of http://go/wvgerrit/94865 This CL has the following changes copied from google3: http://cr/298871728 Remove odk_static_assert for Message size temporarily http://cr/298755935 Fix a compiling error during macro expansion http://cr/298481745 Add missing header for android http://cr/298448142 Fix odk_test gyp file http://cr/298419641 Remove header from Android.bp http://cr/298402053 Separate sizeOf(args) bytes in fuzz tests http://cr/297730316 No core messages for license release http://cr/297714346 Add copybara_test and piper_sot_to_gerrit http://cr/297636713 Adding some comments around boolean conversion code http://cr/297420679 Autofuzzer when ran with address sanitizer ... http://cr/296513584 Minor fix with fuzzing odk clock values http://cr/296322024 Fixing errors in code with how request ... http://cr/296313159 Fuzzing ODK clock values by setting aside ... http://cr/295763207 Add more odk tests and move helper functions to test helper http://cr/294524098 Adding a Build Rule for ODK_KDO_Fuzzer and updating http://cr/294492213 Address a few review comments of ODK http://cr/293674368 odk_fuzz: add TODOs & comments http://cr/293492806 Fix spelling Bug: 150243585 Bug: 150020278 Bug: 150095506 Bug: 147297226 Bug: 148290294 Bug: 148907684 Bug: 150608451 Test: unit tests Change-Id: I25fd406f29f4eba40f5cb27e9a1317dce4ffc2f5 --- libwvdrmengine/oemcrypto/odk/Android.bp | 1 + libwvdrmengine/oemcrypto/odk/README | 3 +- .../odk/src/core_message_serialize_proto.cpp | 6 +- libwvdrmengine/oemcrypto/odk/src/odk.c | 160 ++- .../oemcrypto/odk/src/odk_structs_priv.h | 4 + libwvdrmengine/oemcrypto/odk/src/odk_timer.c | 77 +- libwvdrmengine/oemcrypto/odk/src/odk_util.c | 9 + libwvdrmengine/oemcrypto/odk/src/odk_util.h | 4 + .../oemcrypto/odk/src/serialization_base.c | 11 +- .../oemcrypto/odk/src/serialization_base.h | 22 +- .../oemcrypto/odk/test/odk_fuzz.cpp | 237 +++-- .../oemcrypto/odk/test/odk_test.cpp | 955 ++++++++---------- .../oemcrypto/odk/test/odk_test_helper.cpp | 488 +++++++++ .../oemcrypto/odk/test/odk_test_helper.h | 99 ++ .../oemcrypto/test/oec_session_util.cpp | 21 +- .../oemcrypto/test/oec_session_util.h | 2 - .../oemcrypto/test/oemcrypto_test.cpp | 295 +++--- 17 files changed, 1528 insertions(+), 866 deletions(-) create mode 100644 libwvdrmengine/oemcrypto/odk/test/odk_test_helper.cpp create mode 100644 libwvdrmengine/oemcrypto/odk/test/odk_test_helper.h diff --git a/libwvdrmengine/oemcrypto/odk/Android.bp b/libwvdrmengine/oemcrypto/odk/Android.bp index 7aa8eb0a..41aa3e3a 100644 --- a/libwvdrmengine/oemcrypto/odk/Android.bp +++ b/libwvdrmengine/oemcrypto/odk/Android.bp @@ -81,6 +81,7 @@ cc_test { srcs: [ "test/odk_test.cpp", + "test/odk_test_helper.cpp", "test/odk_timer_test.cpp", ], diff --git a/libwvdrmengine/oemcrypto/odk/README b/libwvdrmengine/oemcrypto/odk/README index 6bd1f544..6b4cdff6 100644 --- a/libwvdrmengine/oemcrypto/odk/README +++ b/libwvdrmengine/oemcrypto/odk/README @@ -1,4 +1,5 @@ -The ODK Library is used to generate and parse core OEMCrypto messages. +The ODK Library is used to generate and parse core OEMCrypto messages for +OEMCrypto v16 and above. This library is used by both OEMCrypto on a device, and by Widvine license and provisioning servers. diff --git a/libwvdrmengine/oemcrypto/odk/src/core_message_serialize_proto.cpp b/libwvdrmengine/oemcrypto/odk/src/core_message_serialize_proto.cpp index 25f1887f..90b5fdd9 100644 --- a/libwvdrmengine/oemcrypto/odk/src/core_message_serialize_proto.cpp +++ b/libwvdrmengine/oemcrypto/odk/src/core_message_serialize_proto.cpp @@ -138,10 +138,8 @@ bool CreateCoreLicenseResponseFromProto(const std::string& serialized_license, parsed_lic.nonce_required = nonce_required; const auto& policy = lic.policy(); ODK_TimerLimits& timer_limits = parsed_lic.timer_limits; - // TODO(b/148241181): add field to protobuf. - // timer_limits.soft_enforce_rental_duration = - // policy.soft_enforce_rental_duration(); - timer_limits.soft_enforce_rental_duration = true; + timer_limits.soft_enforce_rental_duration = + policy.soft_enforce_rental_duration(); timer_limits.soft_enforce_playback_duration = policy.soft_enforce_playback_duration(); timer_limits.earliest_playback_start_seconds = 0; diff --git a/libwvdrmengine/oemcrypto/odk/src/odk.c b/libwvdrmengine/oemcrypto/odk/src/odk.c index 1ce0d3bc..d23efd7e 100644 --- a/libwvdrmengine/oemcrypto/odk/src/odk.c +++ b/libwvdrmengine/oemcrypto/odk/src/odk.c @@ -15,48 +15,63 @@ #include "odk_util.h" #include "serialization_base.h" -#define ODK_LICENSE_REQUEST_SIZE 20 -#define ODK_RENEWAL_REQUEST_SIZE 28 -#define ODK_PROVISIONING_REQUEST_SIZE 88 - /* @ private odk functions */ -static OEMCryptoResult ODK_PrepareRequest(uint8_t* buffer, size_t buffer_length, - size_t* core_message_length, - uint32_t message_type, - const ODK_NonceValues* nonce_values, - ODK_CoreMessage* core_message) { +static OEMCryptoResult ODK_PrepareRequest( + uint8_t* message, size_t message_length, size_t* core_message_length, + uint32_t message_type, const ODK_NonceValues* nonce_values, + void* prepared_request_buffer, size_t prepared_request_buffer_length) { if (nonce_values == NULL || core_message_length == NULL || - core_message == NULL || *core_message_length > buffer_length) { + prepared_request_buffer == NULL || + *core_message_length > message_length) { return ODK_ERROR_CORE_MESSAGE; } Message* msg = NULL; AllocateMessage(&msg, message_block); - InitMessage(msg, buffer, *core_message_length); + InitMessage(msg, message, *core_message_length); + + /* The core message should be at the beginning of the buffer, and with a + * shorter length. */ + if (sizeof(ODK_CoreMessage) > prepared_request_buffer_length) { + return ODK_ERROR_CORE_MESSAGE; + } + ODK_CoreMessage* core_message = (ODK_CoreMessage*)prepared_request_buffer; *core_message = (ODK_CoreMessage){ message_type, 0, *nonce_values, }; + /* Set core message length, and pack prepared request into message if the + * message buffer has been correctly initialized by the caller. */ switch (message_type) { case ODK_License_Request_Type: { core_message->message_length = ODK_LICENSE_REQUEST_SIZE; + if (sizeof(ODK_PreparedLicenseRequest) > prepared_request_buffer_length) { + return ODK_ERROR_CORE_MESSAGE; + } Pack_ODK_PreparedLicenseRequest( - msg, (ODK_PreparedLicenseRequest*)core_message); + msg, (ODK_PreparedLicenseRequest*)prepared_request_buffer); break; } case ODK_Renewal_Request_Type: { core_message->message_length = ODK_RENEWAL_REQUEST_SIZE; + if (sizeof(ODK_PreparedRenewalRequest) > prepared_request_buffer_length) { + return ODK_ERROR_CORE_MESSAGE; + } Pack_ODK_PreparedRenewalRequest( - msg, (ODK_PreparedRenewalRequest*)core_message); + msg, (ODK_PreparedRenewalRequest*)prepared_request_buffer); break; } case ODK_Provisioning_Request_Type: { core_message->message_length = ODK_PROVISIONING_REQUEST_SIZE; + if (sizeof(ODK_PreparedProvisioningRequest) > + prepared_request_buffer_length) { + return ODK_ERROR_CORE_MESSAGE; + } Pack_ODK_PreparedProvisioningRequest( - msg, (ODK_PreparedProvisioningRequest*)core_message); + msg, (ODK_PreparedProvisioningRequest*)prepared_request_buffer); break; } default: { @@ -65,43 +80,59 @@ static OEMCryptoResult ODK_PrepareRequest(uint8_t* buffer, size_t buffer_length, } *core_message_length = core_message->message_length; - if (GetStatus(msg) != MESSAGE_STATUS_OK || - GetSize(msg) != *core_message_length) { + if (GetStatus(msg) != MESSAGE_STATUS_OK) { + /* This is to indicate the caller that the core_message_length has been + * appropriately set, but the message buffer is either empty or too small, + * which needs to be initialized and filled in the subsequent call. */ return OEMCrypto_ERROR_SHORT_BUFFER; } + if (GetSize(msg) != *core_message_length) { + /* This should not happen. Something is wrong. */ + return ODK_ERROR_CORE_MESSAGE; + } return OEMCrypto_SUCCESS; } -static OEMCryptoResult ODK_ParseResponse(const uint8_t* buf, - size_t message_length, - size_t core_message_length, - uint32_t message_type, - const ODK_NonceValues* nonce_values, - ODK_CoreMessage* const core_message) { - if (core_message_length > message_length) { +static OEMCryptoResult ODK_ParseResponse( + const uint8_t* message, size_t message_length, size_t core_message_length, + uint32_t message_type, const ODK_NonceValues* nonce_values, + void* response_buffer, uint32_t response_buffer_length) { + if (message == NULL || response_buffer == NULL || + core_message_length > message_length) { return ODK_ERROR_CORE_MESSAGE; } + Message* msg = NULL; AllocateMessage(&msg, message_block); /* We initialize the message buffer with a size of the entire message * length. */ - InitMessage(msg, (uint8_t*)buf, message_length); + InitMessage(msg, (uint8_t*)message, message_length); /* The core message should be at the beginning of the buffer, and with a * shorter length. The core message is the part we are parsing. */ SetSize(msg, core_message_length); + /* Parse message and unpack it into response buffer. */ switch (message_type) { case ODK_License_Response_Type: { - Unpack_ODK_LicenseResponse(msg, (ODK_LicenseResponse*)core_message); + if (sizeof(ODK_LicenseResponse) > response_buffer_length) { + return ODK_ERROR_CORE_MESSAGE; + } + Unpack_ODK_LicenseResponse(msg, (ODK_LicenseResponse*)response_buffer); break; } case ODK_Renewal_Response_Type: { - Unpack_ODK_RenewalResponse(msg, (ODK_RenewalResponse*)core_message); + if (sizeof(ODK_RenewalResponse) > response_buffer_length) { + return ODK_ERROR_CORE_MESSAGE; + } + Unpack_ODK_RenewalResponse(msg, (ODK_RenewalResponse*)response_buffer); break; } case ODK_Provisioning_Response_Type: { - Unpack_ODK_ProvisioningResponse(msg, - (ODK_ProvisioningResponse*)core_message); + if (sizeof(ODK_ProvisioningResponse) > response_buffer_length) { + return ODK_ERROR_CORE_MESSAGE; + } + Unpack_ODK_ProvisioningResponse( + msg, (ODK_ProvisioningResponse*)response_buffer); break; } default: { @@ -109,6 +140,7 @@ static OEMCryptoResult ODK_ParseResponse(const uint8_t* buf, } } + ODK_CoreMessage* core_message = (ODK_CoreMessage*)response_buffer; if (GetStatus(msg) != MESSAGE_STATUS_OK || message_type != core_message->message_type || GetOffset(msg) != core_message->message_length) { @@ -117,12 +149,7 @@ static OEMCryptoResult ODK_ParseResponse(const uint8_t* buf, if (nonce_values) { /* always verify nonce_values for Renewal and Provisioning responses */ - if (nonce_values->api_major_version != - core_message->nonce_values.api_major_version || - nonce_values->api_minor_version != - core_message->nonce_values.api_minor_version || - nonce_values->nonce != core_message->nonce_values.nonce || - nonce_values->session_id != core_message->nonce_values.session_id) { + if (!ODK_NonceValuesEqual(nonce_values, &(core_message->nonce_values))) { return OEMCrypto_ERROR_INVALID_NONCE; } } @@ -137,12 +164,15 @@ static OEMCryptoResult ODK_ParseResponse(const uint8_t* buf, OEMCryptoResult ODK_PrepareCoreLicenseRequest( uint8_t* message, size_t message_length, size_t* core_message_length, const ODK_NonceValues* nonce_values) { + if (core_message_length == NULL || nonce_values == NULL) { + return ODK_ERROR_CORE_MESSAGE; + } ODK_PreparedLicenseRequest license_request = { {0}, }; - return ODK_PrepareRequest(message, message_length, core_message_length, - ODK_License_Request_Type, nonce_values, - &license_request.core_message); + return ODK_PrepareRequest( + message, message_length, core_message_length, ODK_License_Request_Type, + nonce_values, &license_request, sizeof(ODK_PreparedLicenseRequest)); } OEMCryptoResult ODK_PrepareCoreRenewalRequest(uint8_t* message, @@ -151,8 +181,22 @@ OEMCryptoResult ODK_PrepareCoreRenewalRequest(uint8_t* message, ODK_NonceValues* nonce_values, ODK_ClockValues* clock_values, uint64_t system_time_seconds) { - if (nonce_values == NULL || clock_values == NULL) + if (core_message_size == NULL || nonce_values == NULL || + clock_values == NULL) { return ODK_ERROR_CORE_MESSAGE; + } + + /* If the license has not been loaded, then this is release instead of a + * renewal. All releases use v15. */ + if (clock_values->timer_status == ODK_CLOCK_TIMER_STATUS_LICENSE_NOT_LOADED || + clock_values->timer_status == ODK_CLOCK_TIMER_STATUS_LICENSE_INACTIVE) { + nonce_values->api_major_version = 15; + } + if (nonce_values->api_major_version < 16) { + *core_message_size = 0; + return OEMCrypto_SUCCESS; + } + ODK_PreparedRenewalRequest renewal_request = {{0}, 0}; /* First, we compute the time this request was made relative to the playback * clock. */ @@ -168,19 +212,24 @@ OEMCryptoResult ODK_PrepareCoreRenewalRequest(uint8_t* message, return ODK_ERROR_CORE_MESSAGE; } } + /* Save time for this request so that we can verify the response. This makes * all earlier requests invalid. If preparing this request fails, then all * requests will be bad. */ clock_values->time_of_renewal_request = renewal_request.playback_time; - return ODK_PrepareRequest(message, message_length, core_message_size, - ODK_Renewal_Request_Type, nonce_values, - &renewal_request.core_message); + + return ODK_PrepareRequest( + message, message_length, core_message_size, ODK_Renewal_Request_Type, + nonce_values, &renewal_request, sizeof(ODK_PreparedRenewalRequest)); } OEMCryptoResult ODK_PrepareCoreProvisioningRequest( uint8_t* message, size_t message_length, size_t* core_message_length, const ODK_NonceValues* nonce_values, const uint8_t* device_id, size_t device_id_length) { + if (core_message_length == NULL || nonce_values == NULL) { + return ODK_ERROR_CORE_MESSAGE; + } ODK_PreparedProvisioningRequest provisioning_request = { {0}, 0, @@ -195,7 +244,8 @@ OEMCryptoResult ODK_PrepareCoreProvisioningRequest( } return ODK_PrepareRequest(message, message_length, core_message_length, ODK_Provisioning_Request_Type, nonce_values, - &provisioning_request.core_message); + &provisioning_request, + sizeof(ODK_PreparedProvisioningRequest)); } /* @@ parse request functions */ @@ -214,7 +264,7 @@ OEMCryptoResult ODK_ParseLicense( ODK_LicenseResponse license_response = {{{0}}, parsed_license, {0}}; const OEMCryptoResult err = ODK_ParseResponse( message, message_length, core_message_length, ODK_License_Response_Type, - NULL, &license_response.request.core_message); + NULL, &license_response, sizeof(ODK_LicenseResponse)); if (err != OEMCrypto_SUCCESS) { return err; @@ -279,11 +329,11 @@ OEMCryptoResult ODK_ParseRenewal(const uint8_t* message, size_t message_length, {{0}, 0}, 0, }; - OEMCryptoResult err = ODK_ParseResponse( + const OEMCryptoResult err = ODK_ParseResponse( message, message_length, core_message_length, ODK_Renewal_Response_Type, - nonce_values, &renewal_response.request.core_message); + nonce_values, &renewal_response, sizeof(ODK_RenewalResponse)); - if (err) { + if (err != OEMCrypto_SUCCESS) { return err; } @@ -318,24 +368,24 @@ OEMCryptoResult ODK_ParseProvisioning( return ODK_ERROR_CORE_MESSAGE; } - OEMCryptoResult err = - ODK_ParseResponse(message, message_length, core_message_length, - ODK_Provisioning_Response_Type, nonce_values, - &provisioning_response.request.core_message); + const OEMCryptoResult err = ODK_ParseResponse( + message, message_length, core_message_length, + ODK_Provisioning_Response_Type, nonce_values, &provisioning_response, + sizeof(ODK_ProvisioningResponse)); - if (err) { + if (err != OEMCrypto_SUCCESS) { return err; } - if (memcmp(device_id, provisioning_response.request.device_id, - device_id_length)) { + if (crypto_memcmp(device_id, provisioning_response.request.device_id, + device_id_length) != 0) { return ODK_ERROR_CORE_MESSAGE; } - uint8_t zero[ODK_DEVICE_ID_LEN_MAX] = {0}; + const uint8_t zero[ODK_DEVICE_ID_LEN_MAX] = {0}; /* check bytes beyond device_id_length are 0 */ if (memcmp(zero, provisioning_response.request.device_id + device_id_length, - ODK_DEVICE_ID_LEN_MAX - device_id_length)) { + ODK_DEVICE_ID_LEN_MAX - device_id_length) != 0) { return ODK_ERROR_CORE_MESSAGE; } diff --git a/libwvdrmengine/oemcrypto/odk/src/odk_structs_priv.h b/libwvdrmengine/oemcrypto/odk/src/odk_structs_priv.h index 00e31bf5..a9e39703 100644 --- a/libwvdrmengine/oemcrypto/odk/src/odk_structs_priv.h +++ b/libwvdrmengine/oemcrypto/odk/src/odk_structs_priv.h @@ -60,6 +60,10 @@ typedef struct { ODK_ParsedProvisioning* parsed_provisioning; } ODK_ProvisioningResponse; +#define ODK_LICENSE_REQUEST_SIZE 20 +#define ODK_RENEWAL_REQUEST_SIZE 28 +#define ODK_PROVISIONING_REQUEST_SIZE 88 + /* These are the possible timer status values. */ #define ODK_CLOCK_TIMER_STATUS_UNDEFINED 0 /* Should not happen. */ /* When the structure has been initialized, but no license is loaded. */ diff --git a/libwvdrmengine/oemcrypto/odk/src/odk_timer.c b/libwvdrmengine/oemcrypto/odk/src/odk_timer.c index 8bef3d9a..10a0f7dc 100644 --- a/libwvdrmengine/oemcrypto/odk/src/odk_timer.c +++ b/libwvdrmengine/oemcrypto/odk/src/odk_timer.c @@ -4,7 +4,6 @@ #include #include - #include "odk.h" #include "odk_overflow.h" #include "odk_structs_priv.h" @@ -12,8 +11,7 @@ /* Private function. Checks to see if the license is active. Returns * ODK_TIMER_EXPIRED if the license is valid but inactive. Returns * OEMCrypto_SUCCESS if the license is active. Returns - * OEMCrypto_ERROR_UNKNOWN_FAILURE on other errors. This also updates the - * timer_status if appropriate. */ + * OEMCrypto_ERROR_UNKNOWN_FAILURE on other errors. */ static OEMCryptoResult ODK_LicenseActive(const ODK_TimerLimits* timer_limits, ODK_ClockValues* clock_values) { /* Check some basic errors. */ @@ -26,7 +24,6 @@ static OEMCryptoResult ODK_LicenseActive(const ODK_TimerLimits* timer_limits, return OEMCrypto_ERROR_UNKNOWN_FAILURE; } if (clock_values->status > kActive) { - clock_values->timer_status = ODK_CLOCK_TIMER_STATUS_LICENSE_INACTIVE; return ODK_TIMER_EXPIRED; } return OEMCrypto_SUCCESS; @@ -157,7 +154,9 @@ static OEMCryptoResult ODK_CheckPlaybackWindow( * have already computed the timer limit. */ static void ODK_UpdateTimerStatusForRenewal(ODK_ClockValues* clock_values, uint32_t new_status) { - if (clock_values == NULL) return; /* should not happen. */ + if (clock_values == NULL) { + return; /* should not happen. */ + } if (clock_values->timer_status == ODK_CLOCK_TIMER_STATUS_LICENSE_LOADED) { /* Signal that the timer is already set. */ clock_values->timer_status = ODK_CLOCK_TIMER_STATUS_RENEWAL_LOADED; @@ -174,17 +173,21 @@ OEMCryptoResult ODK_ComputeRenewalDuration(const ODK_TimerLimits* timer_limits, uint64_t system_time_seconds, uint64_t new_renewal_duration, uint64_t* timer_value) { - if (timer_limits == NULL || clock_values == NULL) + if (timer_limits == NULL || clock_values == NULL) { return OEMCrypto_ERROR_INVALID_CONTEXT; /* should not happen. */ + } /* If this is before the license was signed, something is odd. Return an * error. */ - if (system_time_seconds < clock_values->time_of_license_signed) + if (system_time_seconds < clock_values->time_of_license_signed) { return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } const OEMCryptoResult license_status = ODK_LicenseActive(timer_limits, clock_values); /* If the license is not active, then we cannot renew the license. */ - if (license_status != OEMCrypto_SUCCESS) return license_status; + if (license_status != OEMCrypto_SUCCESS) { + return license_status; + } /* We start with the new renewal duration as the new timer limit. */ uint64_t new_timer_value = new_renewal_duration; @@ -193,9 +196,12 @@ OEMCryptoResult ODK_ComputeRenewalDuration(const ODK_TimerLimits* timer_limits, * new_timer_value. */ const OEMCryptoResult rental_status = ODK_CheckRentalWindow( timer_limits, clock_values, system_time_seconds, &new_timer_value); + /* If the rental status forbids playback, then we're done. */ - if ((rental_status != ODK_DISABLE_TIMER) && (rental_status != ODK_SET_TIMER)) + if ((rental_status != ODK_DISABLE_TIMER) && + (rental_status != ODK_SET_TIMER)) { return rental_status; + } /* If playback has already started and it has hard enforcement, then check * total playback window. */ @@ -207,8 +213,9 @@ OEMCryptoResult ODK_ComputeRenewalDuration(const ODK_TimerLimits* timer_limits, /* If the timer limits forbid playback in the playback window, then we're * done. */ if ((playback_status != ODK_DISABLE_TIMER) && - (playback_status != ODK_SET_TIMER)) + (playback_status != ODK_SET_TIMER)) { return playback_status; + } } /* If new_timer_value is infinite (represented by 0), then there are no @@ -219,6 +226,7 @@ OEMCryptoResult ODK_ComputeRenewalDuration(const ODK_TimerLimits* timer_limits, ODK_CLOCK_TIMER_STATUS_UNLIMITED); return ODK_DISABLE_TIMER; } + /* If the caller gave us a pointer to store the new timer value. Fill it. */ if (timer_value != NULL) { *timer_value = new_timer_value; @@ -241,8 +249,9 @@ OEMCryptoResult ODK_InitializeSessionValues(ODK_TimerLimits* timer_limits, ODK_NonceValues* nonce_values, uint32_t api_major_version, uint32_t session_id) { - if (clock_values == NULL || clock_values == NULL || nonce_values == NULL) + if (timer_limits == NULL || clock_values == NULL || nonce_values == NULL) { return OEMCrypto_ERROR_INVALID_CONTEXT; + } /* Check that the API version passed in from OEMCrypto matches the version of * this ODK library. */ if (api_major_version != ODK_MAJOR_VERSION) { @@ -269,7 +278,9 @@ OEMCryptoResult ODK_InitializeSessionValues(ODK_TimerLimits* timer_limits, * OEMCrypto_GenerateNonce. */ OEMCryptoResult ODK_SetNonceValues(ODK_NonceValues* nonce_values, uint32_t nonce) { - if (nonce_values == NULL) return OEMCrypto_ERROR_INVALID_CONTEXT; + if (nonce_values == NULL) { + return OEMCrypto_ERROR_INVALID_CONTEXT; + } /* Setting the nonce should only happen once per session. */ if (nonce_values->nonce != 0) { return OEMCrypto_ERROR_INVALID_CONTEXT; @@ -281,7 +292,9 @@ OEMCryptoResult ODK_SetNonceValues(ODK_NonceValues* nonce_values, /* This is called when OEMCrypto signs a license. */ OEMCryptoResult ODK_InitializeClockValues(ODK_ClockValues* clock_values, uint64_t system_time_seconds) { - if (clock_values == NULL) return OEMCrypto_ERROR_INVALID_CONTEXT; + if (clock_values == NULL) { + return OEMCrypto_ERROR_INVALID_CONTEXT; + } clock_values->time_of_license_signed = system_time_seconds; clock_values->time_of_first_decrypt = 0; clock_values->time_of_last_decrypt = 0; @@ -298,7 +311,9 @@ OEMCryptoResult ODK_ReloadClockValues(ODK_ClockValues* clock_values, uint64_t time_of_last_decrypt, enum OEMCrypto_Usage_Entry_Status status, uint64_t system_time_seconds) { - if (clock_values == NULL) return OEMCrypto_ERROR_INVALID_CONTEXT; + if (clock_values == NULL) { + return OEMCrypto_ERROR_INVALID_CONTEXT; + } clock_values->time_of_license_signed = time_of_license_signed; clock_values->time_of_first_decrypt = time_of_first_decrypt; clock_values->time_of_last_decrypt = time_of_last_decrypt; @@ -313,8 +328,9 @@ OEMCryptoResult ODK_AttemptFirstPlayback(uint64_t system_time_seconds, const ODK_TimerLimits* timer_limits, ODK_ClockValues* clock_values, uint64_t* timer_value) { - if (clock_values == NULL || timer_limits == NULL) + if (clock_values == NULL || timer_limits == NULL) { return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } /* All times are relative to when the license was signed. */ uint64_t rental_time = 0; if (odk_sub_overflow_u64(system_time_seconds, @@ -328,7 +344,9 @@ OEMCryptoResult ODK_AttemptFirstPlayback(uint64_t system_time_seconds, } /* If the license is inactive or not loaded, then playback is not allowed. */ OEMCryptoResult status = ODK_LicenseActive(timer_limits, clock_values); - if (status != OEMCrypto_SUCCESS) return status; + if (status != OEMCrypto_SUCCESS) { + return status; + } /* We start with the initial renewal duration as the timer limit. */ uint64_t new_timer_value = timer_limits->initial_renewal_duration_seconds; @@ -348,7 +366,9 @@ OEMCryptoResult ODK_AttemptFirstPlayback(uint64_t system_time_seconds, * new_timer_value. */ status = ODK_CheckRentalWindow(timer_limits, clock_values, system_time_seconds, &new_timer_value); - if ((status != ODK_DISABLE_TIMER) && (status != ODK_SET_TIMER)) return status; + if ((status != ODK_DISABLE_TIMER) && (status != ODK_SET_TIMER)) { + return status; + } /* If playback has not already started, then this is the first playback. */ if (clock_values->time_of_first_decrypt == 0) { @@ -360,7 +380,9 @@ OEMCryptoResult ODK_AttemptFirstPlayback(uint64_t system_time_seconds, * restrictions. This might decrease new_timer_value. */ status = ODK_CheckPlaybackWindow(timer_limits, clock_values, system_time_seconds, &new_timer_value); - if ((status != ODK_DISABLE_TIMER) && (status != ODK_SET_TIMER)) return status; + if ((status != ODK_DISABLE_TIMER) && (status != ODK_SET_TIMER)) { + return status; + } /* We know we are allowed to decrypt. The rest computes the timer duration. */ clock_values->time_of_last_decrypt = system_time_seconds; @@ -390,7 +412,9 @@ OEMCryptoResult ODK_UpdateLastPlaybackTime(uint64_t system_time_seconds, const ODK_TimerLimits* timer_limits, ODK_ClockValues* clock_values) { OEMCryptoResult status = ODK_LicenseActive(timer_limits, clock_values); - if (status != OEMCrypto_SUCCESS) return status; + if (status != OEMCrypto_SUCCESS) { + return status; + } switch (clock_values->timer_status) { case ODK_CLOCK_TIMER_STATUS_UNLIMITED: break; @@ -413,7 +437,9 @@ OEMCryptoResult ODK_UpdateLastPlaybackTime(uint64_t system_time_seconds, /* This is called from OEMCrypto_DeactivateUsageEntry. */ OEMCryptoResult ODK_DeactivateUsageEntry(ODK_ClockValues* clock_values) { - if (clock_values == NULL) return OEMCrypto_ERROR_UNKNOWN_FAILURE; + if (clock_values == NULL) { + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } if (clock_values->status == kUnused) { clock_values->status = kInactiveUnused; } else if (clock_values->status == kActive) { @@ -430,8 +456,9 @@ OEMCryptoResult ODK_InitializeV15Values(ODK_TimerLimits* timer_limits, ODK_NonceValues* nonce_values, uint32_t key_duration, uint64_t system_time_seconds) { - if (clock_values == NULL || clock_values == NULL || nonce_values == NULL) + if (timer_limits == NULL || clock_values == NULL || nonce_values == NULL) { return OEMCrypto_ERROR_INVALID_CONTEXT; + } timer_limits->soft_enforce_playback_duration = false; timer_limits->soft_enforce_rental_duration = false; timer_limits->earliest_playback_start_seconds = 0; @@ -458,12 +485,14 @@ OEMCryptoResult ODK_RefreshV15Values(const ODK_TimerLimits* timer_limits, uint64_t system_time_seconds, uint32_t new_key_duration, uint64_t* timer_value) { - if (timer_limits == NULL || clock_values == NULL || nonce_values == NULL) + if (timer_limits == NULL || clock_values == NULL || nonce_values == NULL) { return OEMCrypto_ERROR_INVALID_CONTEXT; - if (nonce_values->api_major_version != 15) + } + if (nonce_values->api_major_version != 15) { return OEMCrypto_ERROR_INVALID_NONCE; + } if (clock_values->status > kActive) { - clock_values->timer_status = ODK_TIMER_EXPIRED; + clock_values->timer_status = ODK_CLOCK_TIMER_STATUS_LICENSE_INACTIVE; return ODK_TIMER_EXPIRED; } return ODK_ComputeRenewalDuration(timer_limits, clock_values, diff --git a/libwvdrmengine/oemcrypto/odk/src/odk_util.c b/libwvdrmengine/oemcrypto/odk/src/odk_util.c index 7a85b1a4..d1024262 100644 --- a/libwvdrmengine/oemcrypto/odk/src/odk_util.c +++ b/libwvdrmengine/oemcrypto/odk/src/odk_util.c @@ -23,3 +23,12 @@ int crypto_memcmp(const void* in_a, const void* in_b, size_t len) { } return x; } + +bool ODK_NonceValuesEqual(const ODK_NonceValues* a, const ODK_NonceValues* b) { + if (a == NULL || b == NULL) { + return (a == b); + } + return (a->api_major_version == b->api_major_version && + a->api_minor_version == b->api_minor_version && + a->nonce == b->nonce && a->session_id == b->session_id); +} diff --git a/libwvdrmengine/oemcrypto/odk/src/odk_util.h b/libwvdrmengine/oemcrypto/odk/src/odk_util.h index 42d492b1..7de668ee 100644 --- a/libwvdrmengine/oemcrypto/odk/src/odk_util.h +++ b/libwvdrmengine/oemcrypto/odk/src/odk_util.h @@ -8,6 +8,8 @@ #include #include +#include "odk_structs.h" + #ifdef __cplusplus extern "C" { #endif @@ -18,6 +20,8 @@ extern "C" { * return value when a != b is undefined, other than being non-zero. */ int crypto_memcmp(const void* a, const void* b, size_t len); +bool ODK_NonceValuesEqual(const ODK_NonceValues* a, const ODK_NonceValues* b); + #ifdef __cplusplus } /* extern "C" */ #endif diff --git a/libwvdrmengine/oemcrypto/odk/src/serialization_base.c b/libwvdrmengine/oemcrypto/odk/src/serialization_base.c index 505d6486..4681b54e 100644 --- a/libwvdrmengine/oemcrypto/odk/src/serialization_base.c +++ b/libwvdrmengine/oemcrypto/odk/src/serialization_base.c @@ -20,8 +20,12 @@ struct _Message { MessageStatus status; }; -odk_static_assert(SIZE_OF_MESSAGE_STRUCT >= sizeof(Message), - "SIZE_OF_MESSAGE_STRUCT too small"); +/* TODO(b/150776214): this can be removed once AllocateMessage gets cleaned up + */ +/* + * odk_static_assert(SIZE_OF_MESSAGE_STRUCT >= sizeof(struct _Message), + * "SIZE_OF_MESSAGE_STRUCT too small"); + */ bool ValidMessage(Message* message) { if (message == NULL) { @@ -235,7 +239,8 @@ size_t GetSize(Message* message) { void SetSize(Message* message, size_t size) { if (message == NULL) return; - if (size > message->capacity) message->status = MESSAGE_STATUS_OVERFLOW_ERROR; + if (size > message->capacity) + message->status = MESSAGE_STATUS_OVERFLOW_ERROR; else message->size = size; } diff --git a/libwvdrmengine/oemcrypto/odk/src/serialization_base.h b/libwvdrmengine/oemcrypto/odk/src/serialization_base.h index 109bd496..c99f4d13 100644 --- a/libwvdrmengine/oemcrypto/odk/src/serialization_base.h +++ b/libwvdrmengine/oemcrypto/odk/src/serialization_base.h @@ -28,10 +28,20 @@ extern "C" { */ #define AllocateMessage(msg, blk) \ uint8_t blk[SIZE_OF_MESSAGE_STRUCT]; \ - *(msg) = (Message*)(blk); + *(msg) = (Message*)(blk) typedef struct _Message Message; +typedef enum { + MESSAGE_STATUS_OK, + MESSAGE_STATUS_UNKNOWN_ERROR, + MESSAGE_STATUS_OVERFLOW_ERROR, + MESSAGE_STATUS_UNDERFLOW_ERROR, + MESSAGE_STATUS_PARSE_ERROR, + MESSAGE_STATUS_NULL_POINTER_ERROR, + MESSAGE_STATUS_API_VALUE_ERROR +} MessageStatus; + bool ValidMessage(Message* message); void Pack_enum(Message* message, int value); @@ -51,16 +61,6 @@ void UnpackArray(Message* message, uint8_t* address, size_t size); /* copy out */ void Unpack_OEMCrypto_Substring(Message* msg, OEMCrypto_Substring* obj); -typedef enum { - MESSAGE_STATUS_OK, - MESSAGE_STATUS_UNKNOWN_ERROR, - MESSAGE_STATUS_OVERFLOW_ERROR, - MESSAGE_STATUS_UNDERFLOW_ERROR, - MESSAGE_STATUS_PARSE_ERROR, - MESSAGE_STATUS_NULL_POINTER_ERROR, - MESSAGE_STATUS_API_VALUE_ERROR -} MessageStatus; - /* * Create a message from a buffer. The message structure consumes the first * sizeof(Message) bytes of the buffer. The caller is responsible for ensuring diff --git a/libwvdrmengine/oemcrypto/odk/test/odk_fuzz.cpp b/libwvdrmengine/oemcrypto/odk/test/odk_fuzz.cpp index 75dd7093..d129e694 100644 --- a/libwvdrmengine/oemcrypto/odk/test/odk_fuzz.cpp +++ b/libwvdrmengine/oemcrypto/odk/test/odk_fuzz.cpp @@ -9,7 +9,7 @@ #include #include #include -#include +#include #include "OEMCryptoCENCCommon.h" #include "core_message_deserialize.h" @@ -20,48 +20,74 @@ #include "odk_structs.h" #include "odk_structs_priv.h" -// TODO(b/147297226): remove this: using namespace std; -// TODO(b/147297226): remove this: using namespace oec_util; +typedef std::function + roundtrip_fun; -typedef std::function roundtrip_fun; +using oemcrypto_core_message::ODK_LicenseRequest; +using oemcrypto_core_message::ODK_ProvisioningRequest; +using oemcrypto_core_message::ODK_RenewalRequest; -// @ kdo deserialize; odk derialize -static OEMCryptoResult odk_fun_LicenseRequest( - uint8_t* out, size_t* size, uint32_t api_version, uint32_t nonce, - uint32_t session_id, const ODK_LicenseRequest& /*core_license_request*/) { - return ODK_PrepareCoreLicenseRequest(out, SIZE_MAX, size, api_version, nonce, - session_id); +using oemcrypto_core_message::deserialize::CoreLicenseRequestFromMessage; +using oemcrypto_core_message::deserialize::CoreProvisioningRequestFromMessage; +using oemcrypto_core_message::deserialize::CoreRenewalRequestFromMessage; + +using oemcrypto_core_message::serialize::CreateCoreLicenseResponse; +using oemcrypto_core_message::serialize::CreateCoreProvisioningResponse; +using oemcrypto_core_message::serialize::CreateCoreRenewalResponse; + +// @ kdo deserialize; odk serialize +static OEMCryptoResult odk_serialize_LicenseRequest( + const void* in, uint8_t* out, size_t* size, + const ODK_LicenseRequest& core_license_request, + const ODK_NonceValues* nonce_values) { + return ODK_PrepareCoreLicenseRequest(out, SIZE_MAX, size, nonce_values); } -static OEMCryptoResult odk_fun_RenewalRequest( - uint8_t* out, size_t* size, uint32_t api_version, uint32_t nonce, - uint32_t session_id, const ODK_RenewalRequest& core_renewal) { - // todo: fuzz ODK_ClockValues - ODK_ClockValues clock = {}; - uint64_t system_time_seconds = core_renewal.playback_time; - return ODK_PrepareCoreRenewalRequest(out, SIZE_MAX, size, api_version, nonce, - session_id, &clock, system_time_seconds); +static OEMCryptoResult odk_serialize_RenewalRequest( + const void* in, uint8_t* out, size_t* size, + const ODK_RenewalRequest& core_renewal, ODK_NonceValues* nonce_values) { + ODK_ClockValues clock{}; + memcpy(&clock, in, sizeof(ODK_ClockValues)); + uint64_t system_time_seconds = core_renewal.playback_time_seconds; + return ODK_PrepareCoreRenewalRequest(out, SIZE_MAX, size, nonce_values, + &clock, system_time_seconds); } -static OEMCryptoResult odk_fun_ProvisioningRequest( - uint8_t* out, size_t* size, uint32_t api_version, uint32_t nonce, - uint32_t session_id, const ODK_ProvisioningRequest& core_provisioning) { +static OEMCryptoResult odk_serialize_ProvisioningRequest( + const void* in, uint8_t* out, size_t* size, + const ODK_ProvisioningRequest& core_provisioning, + const ODK_NonceValues* nonce_values) { const std::string& device_id = core_provisioning.device_id; return ODK_PrepareCoreProvisioningRequest( - out, SIZE_MAX, size, api_version, nonce, session_id, + out, SIZE_MAX, size, nonce_values, reinterpret_cast(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 static roundtrip_fun kdo_odk(const F& kdo_fun, const G& odk_fun) { - auto roundtrip = [&](const uint8_t* in, uint8_t* out, size_t size) -> size_t { - std::string input(reinterpret_cast(in), size); + auto roundtrip = [&](const uint8_t* in, uint8_t* out, size_t size, + size_t clock_value_size) -> size_t { + if (size <= clock_value_size) { + return 0; + } + // Input byte array format: [Clock Values][data to parse] + std::string input(reinterpret_cast(in) + clock_value_size, + size - clock_value_size); T t = {}; if (!kdo_fun(input, &t)) { return 0; } - OEMCryptoResult err = - odk_fun(out, &size, t.api_version, t.nonce, t.session_id, t); + ODK_NonceValues nonce_values = {t.api_minor_version, t.api_major_version, + t.nonce, t.session_id}; + OEMCryptoResult err = odk_fun(in, out, &size, t, &nonce_values); return OEMCrypto_SUCCESS == err ? size : 0; }; return roundtrip; @@ -70,7 +96,8 @@ static roundtrip_fun kdo_odk(const F& kdo_fun, const G& odk_fun) { // @ odk deserialize; kdo serialize namespace { struct ODK_Common_Args { - uint32_t api_version; + uint16_t api_minor_version; + uint16_t api_major_version; uint32_t nonce; uint32_t session_id; }; @@ -78,6 +105,9 @@ struct ODK_ParseLicense_Args { ODK_Common_Args common; uint8_t initial_license_load; uint8_t usage_entry_present; + uint8_t request_hash[32]; + ODK_TimerLimits timer_limits; + ODK_ClockValues clock_values; }; struct ODK_ParseRenewal_Args { ODK_Common_Args common; @@ -92,93 +122,128 @@ struct ODK_ParseProvisioning_Args { }; } // namespace -static OEMCryptoResult odk_fun_LicenseResponse( - const uint8_t* message, size_t message_length, uint32_t api_version, - uint32_t nonce, uint32_t session_id, const ODK_ParseLicense_Args* a, - ODK_ParsedLicense& parsed_lic) { - return ODK_ParseLicense( - message, message_length, api_version, nonce, session_id, - static_cast(a->initial_license_load), - static_cast(a->usage_entry_present), &parsed_lic); +uint8_t convert_byte_to_valid_boolean(const bool* in) { + uint8_t boolean_value; + memcpy(&boolean_value, in, 1); + return boolean_value % 2; } -static bool kdo_fun_LicenseResponse(const ODK_ParseLicense_Args* args, - const ODK_ParsedLicense& parsed_lic, - std::string* oemcrypto_core_message) { +static OEMCryptoResult odk_deserialize_LicenseResponse( + const uint8_t* message, size_t core_message_length, + ODK_ParseLicense_Args* a, ODK_NonceValues* nonce_values, + ODK_ParsedLicense* parsed_lic) { + return ODK_ParseLicense(message, SIZE_MAX, core_message_length, + static_cast(a->initial_license_load), + static_cast(a->usage_entry_present), + a->request_hash, &a->timer_limits, &a->clock_values, + nonce_values, parsed_lic); +} + +static bool kdo_serialize_LicenseResponse(const ODK_ParseLicense_Args* args, + const ODK_ParsedLicense& parsed_lic, + std::string* oemcrypto_core_message) { const auto& common = args->common; - ODK_LicenseRequest core_request{common.api_version, common.nonce, + ODK_LicenseRequest core_request{common.api_minor_version, + common.api_major_version, common.nonce, common.session_id}; - return CreateCoreLicenseResponse(parsed_lic, core_request, - oemcrypto_core_message); + std::string core_request_sha_256( + reinterpret_cast(args->request_hash), 32); + return CreateCoreLicenseResponse( + parsed_lic, core_request, core_request_sha_256, oemcrypto_core_message); } -static OEMCryptoResult odk_fun_RenewalResponse( - const uint8_t* buf, size_t len, uint32_t api_version, uint32_t nonce, - uint32_t session_id, ODK_ParseRenewal_Args* a, - ODK_PreparedRenewalRequest& renewal_msg) { +static OEMCryptoResult odk_deserialize_RenewalResponse( + const uint8_t* buf, size_t len, ODK_ParseRenewal_Args* a, + ODK_NonceValues* nonce_values, ODK_PreparedRenewalRequest* renewal_msg) { + /* Address Sanitizer doesn't like values other than 0 OR 1 for boolean + * variables. Input from fuzzer can be parsed and any random bytes can be + * assigned to boolean variables. Using the workaround to mitigate sanitizer + * errors in fuzzer code and converting random bytes to 0 OR 1. + * This has no negative security impact*/ + a->timer_limits.soft_enforce_playback_duration = + convert_byte_to_valid_boolean( + &a->timer_limits.soft_enforce_playback_duration); + a->timer_limits.soft_enforce_rental_duration = convert_byte_to_valid_boolean( + &a->timer_limits.soft_enforce_rental_duration); uint64_t timer_value = 0; OEMCryptoResult err = - ODK_ParseRenewal(buf, len, api_version, nonce, session_id, a->system_time, + ODK_ParseRenewal(buf, SIZE_MAX, len, nonce_values, a->system_time, &a->timer_limits, &a->clock_values, &timer_value); if (OEMCrypto_SUCCESS == err) { Message* msg = nullptr; AllocateMessage(&msg, message_block); InitMessage(msg, const_cast(buf), len); SetSize(msg, len); - Unpack_ODK_PreparedRenewalRequest(msg, &renewal_msg); + Unpack_ODK_PreparedRenewalRequest(msg, renewal_msg); assert(ValidMessage(msg)); } return err; } -static bool kdo_fun_RenewalResponse( +static bool kdo_serialize_RenewalResponse( const ODK_ParseRenewal_Args* args, const ODK_PreparedRenewalRequest& renewal_msg, std::string* oemcrypto_core_message) { const auto& common = args->common; - ODK_RenewalRequest core_request{common.api_version, common.nonce, + ODK_RenewalRequest core_request{common.api_minor_version, + common.api_major_version, common.nonce, common.session_id, renewal_msg.playback_time}; - return CreateCoreRenewalResponse(core_request, oemcrypto_core_message); + return CreateCoreRenewalResponse( + core_request, args->timer_limits.initial_renewal_duration_seconds, + oemcrypto_core_message); } -static OEMCryptoResult odk_fun_ProvisioningResponse( - const uint8_t* buf, size_t len, uint32_t api_version, uint32_t nonce, - uint32_t session_id, ODK_ParseProvisioning_Args* a, - ODK_ParsedProvisioning& parsed_prov) { - return ODK_ParseProvisioning(buf, len, api_version, nonce, session_id, - a->device_id, a->device_id_length, &parsed_prov); +static OEMCryptoResult odk_deserialize_ProvisioningResponse( + const uint8_t* buf, size_t len, ODK_ParseProvisioning_Args* a, + ODK_NonceValues* nonce_values, ODK_ParsedProvisioning* parsed_prov) { + return ODK_ParseProvisioning(buf, SIZE_MAX, len, nonce_values, a->device_id, + a->device_id_length, parsed_prov); } -static bool kdo_fun_ProvisioningResponse( +static bool kdo_serialize_ProvisioningResponse( const ODK_ParseProvisioning_Args* args, const ODK_ParsedProvisioning& parsed_prov, std::string* oemcrypto_core_message) { const auto& common = args->common; assert(args->device_id_length <= sizeof(args->device_id)); ODK_ProvisioningRequest core_request{ - common.api_version, common.nonce, common.session_id, + common.api_minor_version, common.api_major_version, common.nonce, + common.session_id, std::string(reinterpret_cast(args->device_id), args->device_id_length)}; return CreateCoreProvisioningResponse(parsed_prov, core_request, oemcrypto_core_message); } +/** + * Template arguments: + * A: struct holding function arguments + * T: odk deserialize output/kdo serialize input structure + * F: odk deserialize function + * G: kdo serialize function + * + * raw bytes -> F deserialize -> struct T -> G serialize -> raw bytes + */ template static roundtrip_fun odk_kdo(const F& odk_fun, const G& kdo_fun) { - auto roundtrip = [&](const uint8_t* in, uint8_t* out, size_t size) -> size_t { - if (sizeof(A) > size) { + auto roundtrip = [&](const uint8_t* in, uint8_t* out, size_t size, + size_t args_size) -> size_t { + // Input byte array format: [function arguments][data to parse] + if (args_size > size) { return 0; } T t = {}; - const uint8_t* buf = in + sizeof(A); - size_t len = size - sizeof(A); + const uint8_t* buf = in + args_size; + size_t len = size - args_size; std::shared_ptr _args(new A()); A* args = _args.get(); - memcpy(args, in, sizeof(A)); + memcpy(args, in, args_size); const auto& common = args->common; - OEMCryptoResult err = odk_fun(buf, len, common.api_version, common.nonce, - common.session_id, args, t); + ODK_NonceValues nonce_values = {common.api_minor_version, + common.api_major_version, common.nonce, + common.session_id}; + OEMCryptoResult err = odk_fun(buf, len, args, &nonce_values, &t); if (err != OEMCrypto_SUCCESS) { return 0; } @@ -197,33 +262,43 @@ static roundtrip_fun odk_kdo(const F& odk_fun, const G& kdo_fun) { // @ fuzz raw -> parsed -> raw static void verify_roundtrip(const uint8_t* in, size_t size, - roundtrip_fun roundtrip) { + roundtrip_fun roundtrip, size_t args_size) { std::vector _out(size); auto out = _out.data(); - size_t n = roundtrip(in, out, size); - assert(!n || (n <= size && 0 == memcmp(in, out, n))); + size_t n = roundtrip(in, out, size, args_size); + assert(n <= size && 0 == memcmp(in + args_size, out, n)); } +// Entry point for fuzzer, data is random bytes program gets from autofuzzer extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { + verify_roundtrip(data, size, + kdo_odk(CoreLicenseRequestFromMessage, + odk_serialize_LicenseRequest), + 0); + verify_roundtrip(data, size, + kdo_odk(CoreRenewalRequestFromMessage, + odk_serialize_RenewalRequest), + sizeof(ODK_ClockValues)); verify_roundtrip( data, size, - kdo_odk(ParseLicenseRequest, odk_fun_LicenseRequest)); + kdo_odk(CoreProvisioningRequestFromMessage, + odk_serialize_ProvisioningRequest), + 0); verify_roundtrip( data, size, - kdo_odk(ParseRenewalRequest, odk_fun_RenewalRequest)); - verify_roundtrip(data, size, - kdo_odk( - ParseProvisioningRequest, odk_fun_ProvisioningRequest)); - verify_roundtrip(data, size, - odk_kdo( - odk_fun_LicenseResponse, kdo_fun_LicenseResponse)); - verify_roundtrip(data, size, - odk_kdo( - odk_fun_RenewalResponse, kdo_fun_RenewalResponse)); + odk_kdo( + odk_deserialize_LicenseResponse, kdo_serialize_LicenseResponse), + sizeof(ODK_ParseLicense_Args)); verify_roundtrip( data, size, - odk_kdo( - odk_fun_ProvisioningResponse, kdo_fun_ProvisioningResponse)); + odk_kdo( + odk_deserialize_RenewalResponse, kdo_serialize_RenewalResponse), + sizeof(ODK_ParseRenewal_Args)); + verify_roundtrip(data, size, + odk_kdo( + odk_deserialize_ProvisioningResponse, + kdo_serialize_ProvisioningResponse), + sizeof(ODK_ParseProvisioning_Args)); return 0; } diff --git a/libwvdrmengine/oemcrypto/odk/test/odk_test.cpp b/libwvdrmengine/oemcrypto/odk/test/odk_test.cpp index 016b8bfa..22ee24d0 100644 --- a/libwvdrmengine/oemcrypto/odk/test/odk_test.cpp +++ b/libwvdrmengine/oemcrypto/odk/test/odk_test.cpp @@ -6,23 +6,18 @@ #include // TODO(b/147944591): use this one? Or odk_endian.h? -#include -#include #include #include -#include -#include -#include -#include -#include #include "OEMCryptoCENCCommon.h" #include "core_message_deserialize.h" #include "core_message_serialize.h" #include "core_message_types.h" #include "gtest/gtest.h" -#include "odk_structs.h" #include "odk_structs_priv.h" +#include "odk_test_helper.h" + +namespace wvodk_test { namespace { @@ -38,271 +33,6 @@ using oemcrypto_core_message::serialize::CreateCoreLicenseResponse; using oemcrypto_core_message::serialize::CreateCoreProvisioningResponse; using oemcrypto_core_message::serialize::CreateCoreRenewalResponse; -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; -}; - -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* const buf, - const ODK_Field* const field) { - if (!buf || !field || !field->value) { - return ODK_ERROR_CORE_MESSAGE; - } - switch (field->type) { - case ODK_UINT16: { - uint16_t u16 = htobe16(*static_cast(field->value)); - memcpy(buf, &u16, sizeof(u16)); - break; - } - case ODK_UINT32: { - uint32_t u32 = htobe32(*static_cast(field->value)); - memcpy(buf, &u32, sizeof(u32)); - break; - } - case ODK_UINT64: { - uint64_t u64 = htobe64(*static_cast(field->value)); - memcpy(buf, &u64, sizeof(u64)); - break; - } - case ODK_SUBSTRING: { - OEMCrypto_Substring* s = static_cast(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(field->value); - memcpy(buf, id, field_len); - - break; - } - default: - return ODK_ERROR_CORE_MESSAGE; - } - return OEMCrypto_SUCCESS; -} - -OEMCryptoResult ODK_ReadSingleField(const uint8_t* const buf, - const ODK_Field* const field) { - if (!field || !field->value) { - return ODK_ERROR_CORE_MESSAGE; - } - switch (field->type) { - case ODK_UINT16: { - memcpy(field->value, buf, sizeof(uint16_t)); - uint16_t* u16p = static_cast(field->value); - *u16p = be16toh(*u16p); - break; - } - case ODK_UINT32: { - memcpy(field->value, buf, sizeof(uint32_t)); - uint32_t* u32p = static_cast(field->value); - *u32p = be32toh(*u32p); - break; - } - case ODK_UINT64: { - memcpy(field->value, buf, sizeof(uint64_t)); - uint64_t* u64p = static_cast(field->value); - *u64p = be64toh(*u64p); - break; - } - case ODK_SUBSTRING: { - OEMCrypto_Substring* s = static_cast(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(field->value); - memcpy(id, buf, field_len); - break; - } - default: - return ODK_ERROR_CORE_MESSAGE; - } - return OEMCrypto_SUCCESS; -} - -OEMCryptoResult ODK_DumpSingleField(const uint8_t* const buf, - const ODK_Field* const field) { - if (!field || !field->value) { - 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) << buf[i] - << "\n"; - } - 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* const buf, - const size_t size_in, size_t* size_out, - const std::vector& fields) { - if (!buf || !size_out) { - 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(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 expect_eq_buf(const void* s1, const void* s2, size_t n, - const std::vector& fields) { - if (memcmp(s1, s2, n)) { - 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(buffers[i]), n); - out.close(); - std::cerr << "buffer " << i << " dumped to " << tmp << std::endl; - size_t bytes_written; - uint8_t* buf = - const_cast(reinterpret_cast(buffers[i])); - ODK_IterFields(ODK_DUMP, buf, n, &bytes_written, fields); - } - FAIL(); - } -} - template void ValidateRequest(uint32_t message_type, const std::vector& extra_fields, @@ -329,21 +59,32 @@ void ValidateRequest(uint32_t message_type, message_size += ODK_FieldLength(field.type); } + // empty buf, expect core message length to be set correctly + size_t core_message_length = 0; + uint8_t* buf_empty = nullptr; + EXPECT_EQ(OEMCrypto_ERROR_SHORT_BUFFER, + odk_prepare_func(buf_empty, &core_message_length, &nonce_values)); + EXPECT_EQ(core_message_length, message_size); + EXPECT_EQ(nullptr, buf_empty); + + // non-empty buf, expect core message length to be set correctly, and buf is + // filled with ODK_Field values appropriately uint8_t* buf = new uint8_t[message_size](); - uint8_t* buf2 = new uint8_t[message_size](); - size_t bytes_written = message_size; - EXPECT_EQ(OEMCrypto_SUCCESS, - odk_prepare_func(buf, &bytes_written, &nonce_values)); - EXPECT_EQ(bytes_written, message_size); + odk_prepare_func(buf, &core_message_length, &nonce_values)); + EXPECT_EQ(core_message_length, message_size); - EXPECT_EQ(OEMCrypto_SUCCESS, ODK_IterFields(ODK_WRITE, buf2, SIZE_MAX, - &bytes_written, total_fields)); - EXPECT_EQ(bytes_written, message_size); + uint8_t* buf_expected = new uint8_t[message_size](); + size_t buf_len_expected = 0; + EXPECT_EQ(OEMCrypto_SUCCESS, ODK_IterFields(ODK_WRITE, buf_expected, SIZE_MAX, + &buf_len_expected, total_fields)); + EXPECT_EQ(buf_len_expected, message_size); - EXPECT_NO_FATAL_FAILURE(expect_eq_buf(buf, buf2, message_size, total_fields)); + EXPECT_NO_FATAL_FAILURE( + ODK_ExpectEqualBuf(buf_expected, buf, message_size, total_fields)); - // odk kdo roundtrip + // odk kdo round-trip: deserialize from buf, then serialize it to buf2 + // expect them to be identical T t = {}; std::string oemcrypto_core_message(reinterpret_cast(buf), message_size); @@ -352,12 +93,15 @@ void ValidateRequest(uint32_t message_type, nonce_values.api_major_version = t.api_major_version; nonce_values.nonce = t.nonce; nonce_values.session_id = t.session_id; + uint8_t* buf2 = new uint8_t[message_size](); EXPECT_EQ(OEMCrypto_SUCCESS, - odk_prepare_func(buf2, &bytes_written, &nonce_values)); - EXPECT_EQ(bytes_written, message_size); - EXPECT_NO_FATAL_FAILURE(expect_eq_buf(buf, buf2, message_size, total_fields)); + odk_prepare_func(buf2, &core_message_length, &nonce_values)); + EXPECT_EQ(core_message_length, message_size); + EXPECT_NO_FATAL_FAILURE( + ODK_ExpectEqualBuf(buf, buf2, message_size, total_fields)); delete[] buf; + delete[] buf_expected; delete[] buf2; } @@ -368,68 +112,39 @@ void ValidateRequest(uint32_t message_type, * G: kdo serializer */ template -void ValidateResponse(uint32_t message_type, +void ValidateResponse(ODK_CoreMessage* core_message, const std::vector& extra_fields, const F& odk_parse_func, const G& kdo_prepare_func) { - uint32_t message_size = 0; - uint16_t api_minor_version = ODK_MINOR_VERSION; - uint16_t api_major_version = ODK_MAJOR_VERSION; - uint32_t nonce = 0xdeadbeef; - uint32_t session_id = 0xcafebabe; - std::vector total_fields = { - {ODK_UINT32, &message_type, "message_type"}, - {ODK_UINT32, &message_size, "message_size"}, - {ODK_UINT16, &api_minor_version, "api_minor_version"}, - {ODK_UINT16, &api_major_version, "api_major_version"}, - {ODK_UINT32, &nonce, "nonce"}, - {ODK_UINT32, &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) { - message_size += ODK_FieldLength(field.type); - } - - uint8_t* buf = new uint8_t[message_size](); - uint8_t* zero = new uint8_t[message_size](); - size_t bytes_read = 0, bytes_written = 0; - T t = {}; - t.api_minor_version = api_minor_version; - t.api_major_version = api_major_version; - t.nonce = nonce; - t.session_id = session_id; + t.api_minor_version = core_message->nonce_values.api_minor_version; + t.api_major_version = core_message->nonce_values.api_major_version; + t.nonce = core_message->nonce_values.nonce; + t.session_id = core_message->nonce_values.session_id; - // serialize input to buf - EXPECT_EQ(OEMCrypto_SUCCESS, ODK_IterFields(ODK_WRITE, buf, SIZE_MAX, - &bytes_written, total_fields)); - EXPECT_EQ(bytes_written, message_size); + uint8_t* buf = nullptr; + uint32_t buf_size = 0; + ODK_BuildMessageBuffer(core_message, extra_fields, buf, &buf_size); + uint8_t* zero = new uint8_t[buf_size](); + size_t bytes_read = 0; // zero-out input - EXPECT_EQ(OEMCrypto_SUCCESS, ODK_IterFields(ODK_READ, zero, bytes_written, + EXPECT_EQ(OEMCrypto_SUCCESS, ODK_IterFields(ODK_READ, zero, buf_size, &bytes_read, extra_fields)); - EXPECT_TRUE(bytes_written > bytes_read && - bytes_written - bytes_read == header_size); // parse buf with odk - ODK_NonceValues nonce_values{ODK_MINOR_VERSION, api_major_version, nonce, - session_id}; - EXPECT_EQ(OEMCrypto_SUCCESS, - odk_parse_func(buf, bytes_written, &nonce_values)); + EXPECT_EQ(OEMCrypto_SUCCESS, odk_parse_func(buf, buf_size)); + + size_t size_out = 0; + ODK_IterFields(ODK_FieldMode::ODK_DUMP, buf, buf_size, &size_out, + extra_fields); // serialize odk output to oemcrypto_core_message std::string oemcrypto_core_message; EXPECT_TRUE(kdo_prepare_func(t, &oemcrypto_core_message)); - EXPECT_EQ(bytes_written, message_size); - EXPECT_NO_FATAL_FAILURE(expect_eq_buf(buf, oemcrypto_core_message.data(), - message_size, total_fields)); + // verify round-trip works + EXPECT_NO_FATAL_FAILURE(ODK_ExpectEqualBuf(buf, oemcrypto_core_message.data(), + buf_size, extra_fields)); delete[] buf; delete[] zero; } @@ -448,10 +163,12 @@ TEST(OdkTest, SerializeFields) { uint8_t buf2[1024] = {0}; size_t bytes_read = 0, bytes_written = 0; ODK_IterFields(ODK_WRITE, buf, SIZE_MAX, &bytes_read, fields); + std::vector fields2(fields.size()); + ODK_ResetOdkFields(&fields); ODK_IterFields(ODK_READ, buf, bytes_read, &bytes_written, fields); ODK_IterFields(ODK_WRITE, buf2, SIZE_MAX, &bytes_read, fields); - EXPECT_NO_FATAL_FAILURE(expect_eq_buf(buf, buf2, bytes_read, fields)); + EXPECT_NO_FATAL_FAILURE(ODK_ExpectEqualBuf(buf, buf2, bytes_read, fields)); } TEST(OdkTest, SerializeFieldsStress) { @@ -479,7 +196,7 @@ TEST(OdkTest, SerializeFieldsStress) { ODK_IterFields(ODK_WRITE, buf2, total_size, &bytes_written, fields); EXPECT_EQ(bytes_written, total_size); - EXPECT_NO_FATAL_FAILURE(expect_eq_buf(buf, buf2, total_size, fields)); + EXPECT_NO_FATAL_FAILURE(ODK_ExpectEqualBuf(buf, buf2, total_size, fields)); // cleanup for (int i = 0; i < n; i++) { @@ -489,7 +206,205 @@ TEST(OdkTest, SerializeFieldsStress) { delete[] buf2; } -TEST(OdkTest, LicenseRequest) { +TEST(OdkTest, NullRequestTest) { + size_t core_message_length = 0; + ODK_NonceValues nonce_values{0}; + ODK_ClockValues clock_values{0}; + + // Assert that nullptr does not cause a core dump. + EXPECT_EQ(ODK_ERROR_CORE_MESSAGE, ODK_PrepareCoreLicenseRequest( + nullptr, 0uL, nullptr, &nonce_values)); + EXPECT_EQ(ODK_ERROR_CORE_MESSAGE, + ODK_PrepareCoreLicenseRequest(nullptr, 0uL, &core_message_length, + nullptr)); + + EXPECT_EQ(ODK_ERROR_CORE_MESSAGE, + ODK_PrepareCoreRenewalRequest(nullptr, 0uL, nullptr, &nonce_values, + &clock_values, 0uL)); + EXPECT_EQ(ODK_ERROR_CORE_MESSAGE, + ODK_PrepareCoreRenewalRequest(nullptr, 0uL, &core_message_length, + nullptr, &clock_values, 0uL)); + EXPECT_EQ(ODK_ERROR_CORE_MESSAGE, + ODK_PrepareCoreRenewalRequest(nullptr, 0uL, &core_message_length, + &nonce_values, nullptr, 0uL)); + + EXPECT_EQ(ODK_ERROR_CORE_MESSAGE, + ODK_PrepareCoreProvisioningRequest( + nullptr, 0uL, &core_message_length, nullptr, nullptr, 0uL)); + EXPECT_EQ(ODK_ERROR_CORE_MESSAGE, + ODK_PrepareCoreProvisioningRequest(nullptr, 0uL, nullptr, + &nonce_values, nullptr, 0uL)); + + // Null device id in provisioning request is ok + uint8_t message[ODK_PROVISIONING_REQUEST_SIZE] = {0}; + core_message_length = ODK_PROVISIONING_REQUEST_SIZE; + EXPECT_EQ(OEMCrypto_SUCCESS, + ODK_PrepareCoreProvisioningRequest( + message, ODK_PROVISIONING_REQUEST_SIZE, &core_message_length, + &nonce_values, nullptr, 0uL)); +} + +TEST(OdkTest, NullResponseTest) { + const size_t message_size = 64; + uint8_t message[message_size] = {0}; + size_t core_message_length = message_size; + uint8_t request_hash[ODK_SHA256_HASH_SIZE] = {0}; + ODK_TimerLimits timer_limits{0}; + ODK_ParsedLicense parsed_license; + ODK_NonceValues nonce_values{0}; + ODK_ClockValues clock_values{0}; + + // Assert that nullptr does not cause a core dump. + EXPECT_EQ(ODK_ERROR_CORE_MESSAGE, + ODK_ParseLicense(message, message_size, core_message_length, true, + true, request_hash, &timer_limits, &clock_values, + &nonce_values, nullptr)); + EXPECT_EQ(ODK_ERROR_CORE_MESSAGE, + ODK_ParseLicense(message, message_size, core_message_length, true, + true, request_hash, &timer_limits, &clock_values, + nullptr, &parsed_license)); + EXPECT_EQ(ODK_ERROR_CORE_MESSAGE, + ODK_ParseLicense(message, message_size, core_message_length, true, + true, request_hash, &timer_limits, nullptr, + &nonce_values, &parsed_license)); + EXPECT_EQ(ODK_ERROR_CORE_MESSAGE, + ODK_ParseLicense(message, message_size, core_message_length, true, + true, request_hash, nullptr, &clock_values, + &nonce_values, &parsed_license)); + EXPECT_EQ(ODK_ERROR_CORE_MESSAGE, + ODK_ParseLicense(message, message_size, core_message_length, true, + true, nullptr, &timer_limits, &clock_values, + &nonce_values, &parsed_license)); + EXPECT_EQ(ODK_ERROR_CORE_MESSAGE, + ODK_ParseLicense(nullptr, message_size, core_message_length, true, + true, request_hash, &timer_limits, &clock_values, + &nonce_values, &parsed_license)); + + uint64_t system_time = 0; + uint64_t timer_value = 0; + EXPECT_EQ(ODK_ERROR_CORE_MESSAGE, + ODK_ParseRenewal(message, message_size, core_message_length, + &nonce_values, system_time, &timer_limits, nullptr, + &timer_value)); + EXPECT_EQ(ODK_ERROR_CORE_MESSAGE, + ODK_ParseRenewal(message, message_size, core_message_length, + &nonce_values, system_time, nullptr, &clock_values, + &timer_value)); + EXPECT_EQ(ODK_ERROR_CORE_MESSAGE, + ODK_ParseRenewal(message, message_size, core_message_length, + nullptr, system_time, &timer_limits, &clock_values, + &timer_value)); + EXPECT_EQ(ODK_ERROR_CORE_MESSAGE, + ODK_ParseRenewal(nullptr, message_size, core_message_length, + &nonce_values, system_time, &timer_limits, + &clock_values, &timer_value)); + + uint8_t device_id[ODK_DEVICE_ID_LEN_MAX] = {0}; + ODK_ParsedProvisioning parsed_response; + EXPECT_EQ(ODK_ERROR_CORE_MESSAGE, + ODK_ParseProvisioning(message, message_size, core_message_length, + &nonce_values, device_id, + ODK_DEVICE_ID_LEN_MAX, nullptr)); + EXPECT_EQ(ODK_ERROR_CORE_MESSAGE, + ODK_ParseProvisioning(message, message_size, core_message_length, + &nonce_values, nullptr, 0, &parsed_response)); + EXPECT_EQ(ODK_ERROR_CORE_MESSAGE, + ODK_ParseProvisioning(message, message_size, core_message_length, + nullptr, device_id, ODK_DEVICE_ID_LEN_MAX, + &parsed_response)); + EXPECT_EQ(ODK_ERROR_CORE_MESSAGE, + ODK_ParseProvisioning(nullptr, message_size, core_message_length, + &nonce_values, device_id, + ODK_DEVICE_ID_LEN_MAX, &parsed_response)); +} + +TEST(OdkTest, PrepareCoreLicenseRequest) { + uint8_t license_message[ODK_LICENSE_REQUEST_SIZE] = {0}; + size_t core_message_length = sizeof(license_message); + ODK_NonceValues nonce_values{0}; + EXPECT_EQ(OEMCrypto_SUCCESS, ODK_PrepareCoreLicenseRequest( + license_message, sizeof(license_message), + &core_message_length, &nonce_values)); +} + +TEST(OdkTest, PrepareCoreLicenseRequestSize) { + uint8_t license_message[ODK_LICENSE_REQUEST_SIZE] = {0}; + size_t core_message_length = sizeof(license_message); + ODK_NonceValues nonce_values{0}; + // message length smaller than core message length + size_t core_message_length_invalid = core_message_length + 1; + EXPECT_EQ(ODK_ERROR_CORE_MESSAGE, + ODK_PrepareCoreLicenseRequest( + license_message, sizeof(license_message), + &core_message_length_invalid, &nonce_values)); + // message length larger than core message length + uint8_t license_message_large[ODK_LICENSE_REQUEST_SIZE * 2] = {0}; + EXPECT_EQ(OEMCrypto_SUCCESS, + ODK_PrepareCoreLicenseRequest(license_message_large, + sizeof(license_message_large), + &core_message_length, &nonce_values)); +} + +TEST(OdkTest, PrepareCoreRenewalRequest) { + uint8_t renewal_message[ODK_RENEWAL_REQUEST_SIZE] = {0}; + size_t core_message_length = sizeof(renewal_message); + ODK_NonceValues nonce_values{0}; + ODK_ClockValues clock_values{0}; + uint64_t system_time_seconds = 10; + EXPECT_EQ(OEMCrypto_SUCCESS, + ODK_PrepareCoreRenewalRequest( + renewal_message, sizeof(renewal_message), &core_message_length, + &nonce_values, &clock_values, system_time_seconds)); +} + +TEST(OdkTest, PrepareCoreRenewalRequestTimer) { + uint8_t renewal_message[ODK_RENEWAL_REQUEST_SIZE] = {0}; + size_t core_message_length = sizeof(renewal_message); + ODK_NonceValues nonce_values{2, 16, 0, 0}; + uint64_t system_time_seconds = 10; + ODK_ClockValues clock_values_updated{0}; + // system time smaller than first decrypt time + clock_values_updated.time_of_first_decrypt = system_time_seconds + 1; + EXPECT_EQ(ODK_ERROR_CORE_MESSAGE, + ODK_PrepareCoreRenewalRequest( + renewal_message, sizeof(renewal_message), &core_message_length, + &nonce_values, &clock_values_updated, system_time_seconds)); + clock_values_updated.time_of_first_decrypt = system_time_seconds - 1; + EXPECT_EQ(OEMCrypto_SUCCESS, + ODK_PrepareCoreRenewalRequest( + renewal_message, sizeof(renewal_message), &core_message_length, + &nonce_values, &clock_values_updated, system_time_seconds)); + // clock_values.time_of_renewal_request should get updated + EXPECT_EQ(system_time_seconds - clock_values_updated.time_of_first_decrypt, + clock_values_updated.time_of_renewal_request); +} + +TEST(OdkTest, PrepareCoreProvisioningRequest) { + uint8_t provisioning_message[ODK_PROVISIONING_REQUEST_SIZE] = {0}; + size_t core_message_length = sizeof(provisioning_message); + ODK_NonceValues nonce_values{0}; + uint8_t device_id[ODK_DEVICE_ID_LEN_MAX] = {0}; + EXPECT_EQ( + OEMCrypto_SUCCESS, + ODK_PrepareCoreProvisioningRequest( + provisioning_message, sizeof(provisioning_message), + &core_message_length, &nonce_values, device_id, sizeof(device_id))); +} + +TEST(OdkTest, PrepareCoreProvisioningRequestDeviceId) { + uint8_t provisioning_message[ODK_PROVISIONING_REQUEST_SIZE] = {0}; + size_t core_message_length = sizeof(provisioning_message); + ODK_NonceValues nonce_values{0}; + uint8_t device_id_invalid[ODK_DEVICE_ID_LEN_MAX + 1] = {0}; + EXPECT_EQ(ODK_ERROR_CORE_MESSAGE, + ODK_PrepareCoreProvisioningRequest( + provisioning_message, sizeof(provisioning_message), + &core_message_length, &nonce_values, device_id_invalid, + sizeof(device_id_invalid))); +} + +// Serialize and de-serialize license request +TEST(OdkTest, LicenseRequestRoundtrip) { std::vector empty; auto odk_prepare_func = [&](uint8_t* const buf, size_t* size, ODK_NonceValues* nonce_values) { @@ -500,7 +415,7 @@ TEST(OdkTest, LicenseRequest) { odk_prepare_func, kdo_parse_func); } -TEST(OdkTest, RenewalRequest) { +TEST(OdkTest, RenewalRequestRoundtrip) { constexpr uint64_t system_time_seconds = 0xBADDCAFE000FF1CE; uint64_t playback_time = 0xCAFE00000000; const uint64_t playback_start = system_time_seconds - playback_time; @@ -518,16 +433,13 @@ TEST(OdkTest, RenewalRequest) { ODK_RenewalRequest* core_renewal_request) { bool ok = CoreRenewalRequestFromMessage(oemcrypto_core_message, core_renewal_request); - if (ok) { - playback_time = core_renewal_request->playback_time_seconds; - } return ok; }; ValidateRequest(ODK_Renewal_Request_Type, extra_fields, odk_prepare_func, kdo_parse_func); } -TEST(OdkTest, ProvisionRequest) { +TEST(OdkTest, ProvisionRequestRoundtrip) { uint32_t device_id_length = ODK_DEVICE_ID_LEN_MAX / 2; uint8_t device_id[ODK_DEVICE_ID_LEN_MAX] = {0}; memset(device_id, 0xff, device_id_length); @@ -545,12 +457,6 @@ TEST(OdkTest, ProvisionRequest) { ODK_ProvisioningRequest* core_provisioning_request) { bool ok = CoreProvisioningRequestFromMessage(oemcrypto_core_message, core_provisioning_request); - if (ok) { - const std::string& device_id_str = - core_provisioning_request->device_id; - device_id_length = device_id_str.size(); - memcpy(device_id, device_id_str.data(), device_id_length); - } return ok; }; ValidateRequest(ODK_Provisioning_Request_Type, @@ -558,217 +464,187 @@ TEST(OdkTest, ProvisionRequest) { kdo_parse_func); } -TEST(OdkTest, LicenseResponse) { - ODK_ParsedLicense 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}, - }, - }, - }; +TEST(OdkTest, ParseLicenseErrorNonce) { + ODK_LicenseResponseParams params; + ODK_SetDefaultLicenseResponseParams(¶ms); + uint8_t* buf = nullptr; + uint32_t buf_size = 0; + ODK_BuildMessageBuffer(&(params.core_message), params.extra_fields, buf, + &buf_size); + // temporarily mess up with nonce + uint32_t nonce = params.core_message.nonce_values.nonce; + params.core_message.nonce_values.nonce = 0; + OEMCryptoResult err = ODK_ParseLicense( + buf, buf_size + 128, buf_size, params.initial_license_load, + params.usage_entry_present, params.request_hash, &(params.timer_limits), + &(params.clock_values), &(params.core_message.nonce_values), + &(params.parsed_license)); + EXPECT_EQ(OEMCrypto_ERROR_INVALID_NONCE, err); + // restore value + params.core_message.nonce_values.nonce = nonce; + delete[] buf; +} - const uint8_t request_hash[ODK_SHA256_HASH_SIZE] = { - 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, - 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31}; +TEST(OdkTest, ParseLicenseErrorUsageEntry) { + ODK_LicenseResponseParams params; + ODK_SetDefaultLicenseResponseParams(¶ms); + uint8_t* buf = nullptr; + uint32_t buf_size = 0; + ODK_BuildMessageBuffer(&(params.core_message), params.extra_fields, buf, + &buf_size); + bool usage_entry_present = params.usage_entry_present; + params.usage_entry_present = false; + OEMCryptoResult err = ODK_ParseLicense( + buf, buf_size + 128, buf_size, params.initial_license_load, + params.usage_entry_present, params.request_hash, &(params.timer_limits), + &(params.clock_values), &(params.core_message.nonce_values), + &(params.parsed_license)); + EXPECT_EQ(ODK_ERROR_CORE_MESSAGE, err); + params.usage_entry_present = usage_entry_present; + delete[] buf; +} + +TEST(OdkTest, ParseLicenseErrorRequestHash) { + ODK_LicenseResponseParams params; + ODK_SetDefaultLicenseResponseParams(¶ms); + uint8_t* buf = nullptr; + uint32_t buf_size = 0; + ODK_BuildMessageBuffer(&(params.core_message), params.extra_fields, buf, + &buf_size); + // temporarily mess up with request hash + uint32_t first_byte = params.request_hash[0]; + params.request_hash[0] = 0xff; + OEMCryptoResult err = ODK_ParseLicense( + buf, buf_size + 128, buf_size, params.initial_license_load, + params.usage_entry_present, params.request_hash, &(params.timer_limits), + &(params.clock_values), &(params.core_message.nonce_values), + &(params.parsed_license)); + EXPECT_EQ(ODK_ERROR_CORE_MESSAGE, err); + // restore value + params.request_hash[0] = first_byte; + delete[] buf; +} + +TEST(OdkTest, ParseRenewalErrorTimer) { + ODK_RenewalResponseParams params; + ODK_SetDefaultRenewalResponseParams(¶ms); + uint8_t* buf = nullptr; + uint32_t buf_size = 0; + ODK_BuildMessageBuffer(&(params.core_message), params.extra_fields, buf, + &buf_size); + // temporarily mess up with time_of_renewal_request + uint64_t time_of_renewal_request = + params.clock_values.time_of_renewal_request; + params.clock_values.time_of_renewal_request = 0; + OEMCryptoResult err = ODK_ParseRenewal( + buf, buf_size, buf_size, &(params.core_message.nonce_values), + params.system_time, &(params.timer_limits), &(params.clock_values), + &(params.playback_timer)); + EXPECT_EQ(ODK_STALE_RENEWAL, err); + // restore value + params.clock_values.time_of_renewal_request = time_of_renewal_request; + delete[] buf; +} + +TEST(OdkTest, ParsePrivisioningErrorDeviceId) { + ODK_ProvisioningResponseParams params; + ODK_SetDefaultProvisioningResponseParams(¶ms); + uint8_t* buf = nullptr; + uint32_t buf_size = 0; + ODK_BuildMessageBuffer(&(params.core_message), params.extra_fields, buf, + &buf_size); + // temporarily mess up with device_id + uint8_t first_byte = params.device_id[0]; + params.device_id[0] = 0; + OEMCryptoResult err = ODK_ParseProvisioning( + buf, buf_size + 16, buf_size, &(params.core_message.nonce_values), + params.device_id, params.device_id_length, &(params.parsed_provisioning)); + EXPECT_EQ(ODK_ERROR_CORE_MESSAGE, err); + // restore value + params.device_id[0] = first_byte; + delete[] buf; +} + +// Serialize and de-serialize license response +TEST(OdkTest, LicenseResponseRoundtrip) { + ODK_LicenseResponseParams params; + ODK_SetDefaultLicenseResponseParams(¶ms); + // save a copy of params.request_hash as it will be zero out during the test uint8_t request_hash_read[ODK_SHA256_HASH_SIZE]; - memcpy(request_hash_read, request_hash, sizeof(request_hash)); - std::vector extra_fields = { - {ODK_SUBSTRING, &parsed_license.enc_mac_keys_iv, ".enc_mac_keys_iv"}, - {ODK_SUBSTRING, &parsed_license.enc_mac_keys, ".enc_mac_keys"}, - {ODK_SUBSTRING, &parsed_license.pst, ".pst"}, - {ODK_SUBSTRING, &parsed_license.srm_restriction_data, - ".srm_restriction_data"}, - {ODK_UINT32, &parsed_license.license_type, ".license_type"}, - {ODK_UINT32, &parsed_license.nonce_required, ".nonce_required"}, - {ODK_UINT32, &parsed_license.timer_limits.soft_enforce_rental_duration, - ".soft_enforce_rental_duration"}, - {ODK_UINT32, &parsed_license.timer_limits.soft_enforce_playback_duration, - ".soft_enforce_playback_duration"}, - {ODK_UINT64, &parsed_license.timer_limits.earliest_playback_start_seconds, - ".earliest_playback_start_seconds"}, - {ODK_UINT64, &parsed_license.timer_limits.rental_duration_seconds, - ".rental_duration_seconds"}, - {ODK_UINT64, &parsed_license.timer_limits.total_playback_duration_seconds, - ".total_playback_duration_seconds"}, - {ODK_UINT64, - &parsed_license.timer_limits.initial_renewal_duration_seconds, - ".initial_renewal_duration_seconds"}, - {ODK_UINT32, &parsed_license.key_array_length, ".key_array_length"}, - {ODK_SUBSTRING, &parsed_license.key_array[0].key_id, ".key_id"}, - {ODK_SUBSTRING, &parsed_license.key_array[0].key_data_iv, ".key_data_iv"}, - {ODK_SUBSTRING, &parsed_license.key_array[0].key_data, ".key_data"}, - {ODK_SUBSTRING, &parsed_license.key_array[0].key_control_iv, - ".key_control_iv"}, - {ODK_SUBSTRING, &parsed_license.key_array[0].key_control, ".key_control"}, - {ODK_SUBSTRING, &parsed_license.key_array[1].key_id, ".key_id"}, - {ODK_SUBSTRING, &parsed_license.key_array[1].key_data_iv, ".key_data_iv"}, - {ODK_SUBSTRING, &parsed_license.key_array[1].key_data, ".key_data"}, - {ODK_SUBSTRING, &parsed_license.key_array[1].key_control_iv, - ".key_control_iv"}, - {ODK_SUBSTRING, &parsed_license.key_array[1].key_control, ".key_control"}, - {ODK_SUBSTRING, &parsed_license.key_array[2].key_id, ".key_id"}, - {ODK_SUBSTRING, &parsed_license.key_array[2].key_data_iv, ".key_data_iv"}, - {ODK_SUBSTRING, &parsed_license.key_array[2].key_data, ".key_data"}, - {ODK_SUBSTRING, &parsed_license.key_array[2].key_control_iv, - ".key_control_iv"}, - {ODK_SUBSTRING, &parsed_license.key_array[2].key_control, ".key_control"}, - {ODK_HASH, request_hash_read, ".request_hash"}, + memcpy(request_hash_read, params.request_hash, sizeof(request_hash_read)); + auto odk_parse_func = [&](const uint8_t* buf, size_t size) { + return ODK_ParseLicense(buf, size + 128, size, params.initial_license_load, + params.usage_entry_present, request_hash_read, + &(params.timer_limits), &(params.clock_values), + &(params.core_message.nonce_values), + &(params.parsed_license)); }; const std::string request_hash_string( - reinterpret_cast(request_hash), sizeof(request_hash)); - auto odk_parse_func = [&](const uint8_t* buf, size_t size, - ODK_NonceValues* nonce_values) { - ODK_TimerLimits timer_limits; - ODK_ClockValues clock_values; - constexpr bool initial_license_load = true; - constexpr bool usage_entry_present = true; - return ODK_ParseLicense(buf, size + 128, size, initial_license_load, - usage_entry_present, request_hash, &timer_limits, - &clock_values, nonce_values, &parsed_license); - }; + reinterpret_cast(request_hash_read), + sizeof(request_hash_read)); auto kdo_prepare_func = [&](const ODK_LicenseRequest& core_request, std::string* oemcrypto_core_message) { - return CreateCoreLicenseResponse(parsed_license, core_request, + return CreateCoreLicenseResponse(params.parsed_license, core_request, request_hash_string, oemcrypto_core_message); }; - ValidateResponse(ODK_License_Response_Type, extra_fields, - odk_parse_func, kdo_prepare_func); + ValidateResponse(&(params.core_message), + params.extra_fields, odk_parse_func, + kdo_prepare_func); } -TEST(OdkTest, RenewalResponse) { - uint64_t system_time = 0xfaceb00c; - uint64_t playback_clock = 11; - uint64_t playback_timer = 12; - uint64_t message_playback_clock = 10; - constexpr uint64_t renewal_duration = 130; - uint64_t var_renewal_duration = renewal_duration; - std::vector extra_fields = { - {ODK_UINT64, &message_playback_clock, "message_playback_clock"}, - {ODK_UINT64, &var_renewal_duration, "renewal_duration"}, - }; - - ODK_TimerLimits 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 = 30, - }; - - ODK_ClockValues clock_values = { - .time_of_license_signed = system_time - playback_clock - 42, - .time_of_first_decrypt = system_time - playback_clock, - .time_of_last_decrypt = 0, - .time_of_renewal_request = message_playback_clock, - .time_when_timer_expires = system_time + playback_timer, - .timer_status = ODK_CLOCK_TIMER_STATUS_LICENSE_LOADED, - .status = kUnused, - }; - - auto odk_parse_func = [&](const uint8_t* buf, size_t size, - ODK_NonceValues* nonce_values) { +TEST(OdkTest, RenewalResponseRoundtrip) { + ODK_RenewalResponseParams params; + ODK_SetDefaultRenewalResponseParams(¶ms); + uint64_t playback_clock = params.playback_clock; + uint64_t renewal_duration = params.renewal_duration; + auto odk_parse_func = [&](const uint8_t* buf, size_t size) { OEMCryptoResult err = - ODK_ParseRenewal(buf, size, size, nonce_values, system_time, - &timer_limits, &clock_values, &playback_timer); + ODK_ParseRenewal(buf, size, size, &(params.core_message.nonce_values), + params.system_time, &(params.timer_limits), + &(params.clock_values), &(params.playback_timer)); EXPECT_EQ(ODK_SET_TIMER, err); - EXPECT_EQ(renewal_duration, playback_timer); - EXPECT_EQ(clock_values.time_when_timer_expires, - system_time + playback_timer); + EXPECT_EQ(renewal_duration, params.playback_timer); + EXPECT_EQ(params.clock_values.time_when_timer_expires, + params.system_time + params.playback_timer); - // manually restore message_playback_clock since ODK_ParseRenewal doesn't - // generate output - message_playback_clock = 10; return OEMCrypto_SUCCESS; }; auto kdo_prepare_func = [&](ODK_RenewalRequest& core_request, std::string* oemcrypto_core_message) { - core_request.playback_time_seconds = message_playback_clock; + core_request.playback_time_seconds = playback_clock; return CreateCoreRenewalResponse(core_request, renewal_duration, oemcrypto_core_message); }; - ValidateResponse(ODK_Renewal_Response_Type, extra_fields, - odk_parse_func, kdo_prepare_func); + ValidateResponse(&(params.core_message), + params.extra_fields, odk_parse_func, + kdo_prepare_func); } -TEST(OdkTest, ProvisionResponse) { - uint32_t device_id_length = ODK_DEVICE_ID_LEN_MAX / 2; +TEST(OdkTest, ProvisionResponseRoundtrip) { + ODK_ProvisioningResponseParams params; + ODK_SetDefaultProvisioningResponseParams(¶ms); + // save a copy of params.device_id as it will be zero out during the test + uint32_t device_id_length = params.device_id_length; uint8_t device_id[ODK_DEVICE_ID_LEN_MAX] = {0}; - memset(device_id, 0xff, device_id_length); + memcpy(device_id, params.device_id, device_id_length); - ODK_ParsedProvisioning parsed_response = { - .enc_private_key = {.offset = 0, .length = 1}, - .enc_private_key_iv = {.offset = 2, .length = 3}, - .encrypted_message_key = {.offset = 4, .length = 5}, - }; - - std::vector extra_fields = { - {ODK_UINT32, &device_id_length, "device_id_length"}, - {ODK_DEVICEID, device_id, "device_id"}, - {ODK_UINT32, &parsed_response.key_type, "key_type"}, - {ODK_SUBSTRING, &parsed_response.enc_private_key, "enc_private_key"}, - {ODK_SUBSTRING, &parsed_response.enc_private_key_iv, - "enc_private_key_iv"}, - {ODK_SUBSTRING, &parsed_response.encrypted_message_key, - "encrypted_message_key"}, - }; - - auto odk_parse_func = [&](const uint8_t* buf, size_t size, - ODK_NonceValues* nonce_values) { - // restore device id because it is not part of parsed_response - device_id_length = ODK_DEVICE_ID_LEN_MAX / 2; - memset(device_id, 0xff, device_id_length); - OEMCryptoResult err = - ODK_ParseProvisioning(buf, size + 16, size, nonce_values, device_id, - device_id_length, &parsed_response); + auto odk_parse_func = [&](const uint8_t* buf, size_t size) { + OEMCryptoResult err = ODK_ParseProvisioning( + buf, size + 16, size, &(params.core_message.nonce_values), device_id, + device_id_length, &(params.parsed_provisioning)); return err; }; auto kdo_prepare_func = [&](ODK_ProvisioningRequest& core_request, std::string* oemcrypto_core_message) { core_request.device_id.assign(reinterpret_cast(device_id), device_id_length); - return CreateCoreProvisioningResponse(parsed_response, core_request, - oemcrypto_core_message); + return CreateCoreProvisioningResponse(params.parsed_provisioning, + core_request, oemcrypto_core_message); }; - ValidateResponse(ODK_Provisioning_Response_Type, - extra_fields, odk_parse_func, + ValidateResponse(&(params.core_message), + params.extra_fields, odk_parse_func, kdo_prepare_func); } @@ -795,11 +671,12 @@ TEST(OdkSizeTest, RenewalRequest) { size_t message_length = 0; size_t core_message_length = 0; uint16_t api_minor_version = ODK_MINOR_VERSION; - uint16_t api_major_version = 0; + uint16_t api_major_version = ODK_MAJOR_VERSION; uint32_t nonce = 0; uint32_t session_id = 0; ODK_ClockValues clock_values = {}; clock_values.time_of_first_decrypt = 10; + clock_values.timer_status = ODK_CLOCK_TIMER_STATUS_LICENSE_LOADED; uint64_t system_time_seconds = 15; ODK_NonceValues nonce_values{api_minor_version, api_major_version, nonce, session_id}; @@ -812,6 +689,28 @@ TEST(OdkSizeTest, RenewalRequest) { EXPECT_GE(core_message_length, minimum_message_size); } +TEST(OdkSizeTest, ReleaseRequest) { + uint8_t* message = nullptr; + size_t message_length = 0; + size_t core_message_length = 0; + uint16_t api_minor_version = ODK_MINOR_VERSION; + uint16_t api_major_version = 0; + uint32_t nonce = 0; + uint32_t session_id = 0; + ODK_ClockValues clock_values = {}; + clock_values.time_of_first_decrypt = 10; + clock_values.timer_status = ODK_CLOCK_TIMER_STATUS_LICENSE_INACTIVE; + uint64_t system_time_seconds = 15; + ODK_NonceValues nonce_values{api_minor_version, api_major_version, nonce, + session_id}; + EXPECT_EQ(OEMCrypto_SUCCESS, + ODK_PrepareCoreRenewalRequest(message, message_length, + &core_message_length, &nonce_values, + &clock_values, system_time_seconds)); + // Release requests do not have a core message. + EXPECT_GE(core_message_length, 0); +} + TEST(OdkSizeTest, ProvisioningRequest) { uint8_t* message = nullptr; size_t message_length = 0; @@ -833,3 +732,5 @@ TEST(OdkSizeTest, ProvisioningRequest) { } } // namespace + +} // namespace wvodk_test diff --git a/libwvdrmengine/oemcrypto/odk/test/odk_test_helper.cpp b/libwvdrmengine/oemcrypto/odk/test/odk_test_helper.cpp new file mode 100644 index 00000000..02962307 --- /dev/null +++ b/libwvdrmengine/oemcrypto/odk/test/odk_test_helper.cpp @@ -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 + +#include +#include +#include +#include +#include +#include + +#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(field->value)); + memcpy(buf, &u16, sizeof(u16)); + break; + } + case ODK_UINT32: { + uint32_t u32 = htobe32(*static_cast(field->value)); + memcpy(buf, &u32, sizeof(u32)); + break; + } + case ODK_UINT64: { + uint64_t u64 = htobe64(*static_cast(field->value)); + memcpy(buf, &u64, sizeof(u64)); + break; + } + case ODK_SUBSTRING: { + OEMCrypto_Substring* s = static_cast(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(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(field->value); + *u16p = be16toh(*u16p); + break; + } + case ODK_UINT32: { + memcpy(field->value, buf, sizeof(uint32_t)); + uint32_t* u32p = static_cast(field->value); + *u32p = be32toh(*u32p); + break; + } + case ODK_UINT64: { + memcpy(field->value, buf, sizeof(uint64_t)); + uint64_t* u64p = static_cast(field->value); + *u64p = be64toh(*u64p); + break; + } + case ODK_SUBSTRING: { + OEMCrypto_Substring* s = static_cast(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(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(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& 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(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& 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(buffers[i]), n); + out.close(); + std::cerr << "buffer " << i << " dumped to " << tmp << std::endl; + size_t bytes_written; + uint8_t* buf = + const_cast(reinterpret_cast(buffers[i])); + ODK_IterFields(ODK_DUMP, buf, n, &bytes_written, fields); + } + FAIL(); + } +} + +void ODK_ResetOdkFields(std::vector* 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& extra_fields, + uint8_t*& buf, uint32_t* buf_size) { + ASSERT_TRUE(core_message != nullptr); + ASSERT_TRUE(buf_size != nullptr); + std::vector 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(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 diff --git a/libwvdrmengine/oemcrypto/odk/test/odk_test_helper.h b/libwvdrmengine/oemcrypto/odk/test/odk_test_helper.h new file mode 100644 index 00000000..95d56638 --- /dev/null +++ b/libwvdrmengine/oemcrypto/odk/test/odk_test_helper.h @@ -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 +#include +#include + +#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 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 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 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& fields); +void ODK_ExpectEqualBuf(const void* s1, const void* s2, size_t n, + const std::vector& fields); +void ODK_ResetOdkFields(std::vector* fields); + +/* Serialize core_message and extra_fields into buf */ +void ODK_BuildMessageBuffer(ODK_CoreMessage* core_message, + const std::vector& extra_fields, + uint8_t*& buf, uint32_t* buf_size); + +} /* namespace wvodk_test */ + +#endif /* WIDEVINE_ODK_TEST_ODK_TEST_HELPER_H_ */ diff --git a/libwvdrmengine/oemcrypto/test/oec_session_util.cpp b/libwvdrmengine/oemcrypto/test/oec_session_util.cpp index 5720c208..1dd11dc0 100644 --- a/libwvdrmengine/oemcrypto/test/oec_session_util.cpp +++ b/libwvdrmengine/oemcrypto/test/oec_session_util.cpp @@ -826,8 +826,8 @@ void RenewalRoundTrip::VerifyRequestSignature( void RenewalRoundTrip::FillAndVerifyCoreRequest( const std::string& core_message_string) { - if (license_messages_->api_version() < kCoreMessagesAPI) { - // For v15, we expect that no core request was created. + if (license_messages_->api_version() < kCoreMessagesAPI || is_release_) { + // For v15 or for a release, we expect that no core request was created. EXPECT_FALSE( oemcrypto_core_message::deserialize::CoreRenewalRequestFromMessage( core_message_string, &core_request_)); @@ -837,13 +837,9 @@ void RenewalRoundTrip::FillAndVerifyCoreRequest( core_message_string, &core_request_)); EXPECT_EQ(license_messages_->core_request().api_major_version, core_request_.api_major_version); - if (!is_release_) { - // For a license release, we do not expect the nonce to be correct. That - // is because a release might be sent without loading the license first. - EXPECT_EQ(license_messages_->core_request().nonce, core_request_.nonce); - EXPECT_EQ(license_messages_->core_request().session_id, - core_request_.session_id); - } + EXPECT_EQ(license_messages_->core_request().nonce, core_request_.nonce); + EXPECT_EQ(license_messages_->core_request().session_id, + core_request_.session_id); } } @@ -1397,13 +1393,6 @@ void Session::UpdateUsageEntry(std::vector* header_buffer) { encrypted_usage_entry_.data(), &entry_buffer_length)); } -void Session::DeactivateUsageEntry(const std::string& pst) { - ASSERT_EQ(OEMCrypto_SUCCESS, - OEMCrypto_DeactivateUsageEntry( - session_id(), reinterpret_cast(pst.c_str()), - pst.length())); -} - void Session::LoadUsageEntry(uint32_t index, const vector& buffer) { usage_entry_number_ = index; encrypted_usage_entry_ = buffer; diff --git a/libwvdrmengine/oemcrypto/test/oec_session_util.h b/libwvdrmengine/oemcrypto/test/oec_session_util.h index e3fa6967..e092fb49 100644 --- a/libwvdrmengine/oemcrypto/test/oec_session_util.h +++ b/libwvdrmengine/oemcrypto/test/oec_session_util.h @@ -527,8 +527,6 @@ class Session { void ReloadUsageEntry() { LoadUsageEntry(*this); } // Update the usage entry and save the header to the specified buffer. void UpdateUsageEntry(std::vector* header_buffer); - // Deactivate this session's usage entry. - void DeactivateUsageEntry(const std::string& pst); // The usage entry number for this session's usage entry. uint32_t usage_entry_number() const { return usage_entry_number_; } void set_usage_entry_number(uint32_t v) { usage_entry_number_ = v; } diff --git a/libwvdrmengine/oemcrypto/test/oemcrypto_test.cpp b/libwvdrmengine/oemcrypto/test/oemcrypto_test.cpp index 6e0cdfb3..e8649e2b 100644 --- a/libwvdrmengine/oemcrypto/test/oemcrypto_test.cpp +++ b/libwvdrmengine/oemcrypto/test/oemcrypto_test.cpp @@ -15,9 +15,9 @@ #include #ifdef _WIN32 -# include +# include #else -# include +# include #endif #include @@ -46,9 +46,9 @@ using ::testing::Bool; using ::testing::Combine; using ::testing::Range; +using ::testing::tuple; using ::testing::Values; using ::testing::WithParamInterface; -using ::testing::tuple; using namespace std; namespace std { // GTest wants PrintTo to be in the std namespace. @@ -92,11 +92,11 @@ constexpr size_t MiB = 1024 * 1024; // depending on the resource rating reported by OEMCrypto. This function looks // up the required value for the specified resource for the target OEMCrypto // library. -template +template T GetResourceValue(T (&resource_values)[N]) { if (global_features.resource_rating < 1) return resource_values[0]; - if (global_features.resource_rating > N) return resource_values[N-1]; - return resource_values[global_features.resource_rating-1]; + if (global_features.resource_rating > N) return resource_values[N - 1]; + return resource_values[global_features.resource_rating - 1]; } // After API 16, we require 300 entries in the usage table. Before API 16, we @@ -124,8 +124,8 @@ const size_t kLargeMessageSize[] = { 8*KiB, 8*KiB, 16*KiB, 32*KiB}; /** @return The Unix time of the given time point. */ template -uint64_t UnixTime(const std::chrono::time_point& point) { +uint64_t UnixTime( + const std::chrono::time_point& point) { return point.time_since_epoch() / std::chrono::seconds(1); } @@ -143,7 +143,8 @@ void AddNativeTime(int64_t delta_seconds, NativeTime* time) { ASSERT_TRUE(SystemTimeToFileTime(time, &file_time)); uint64_t long_time = static_cast(file_time.dwLowDateTime) | (static_cast(file_time.dwHighDateTime) << 32); - long_time += delta_seconds * 1e7; // long_time is in 100-nanosecond intervals. + long_time += + delta_seconds * 1e7; // long_time is in 100-nanosecond intervals. file_time.dwLowDateTime = long_time & ((1ull << 32) - 1); file_time.dwHighDateTime = long_time >> 32; ASSERT_TRUE(FileTimeToSystemTime(&file_time, time)); @@ -344,8 +345,7 @@ TEST_F(OEMCryptoClientTest, MaxSessionsOpenCloseAPI10) { ASSERT_GE(max_sessions, required_number); // We allow GetMaxNumberOfSessions to return an estimate. This tests with a // pad of 5%. Even if it's just an estimate, we still require 8 sessions. - size_t max_sessions_with_pad = - max(max_sessions * 19 / 20, required_number); + size_t max_sessions_with_pad = max(max_sessions * 19 / 20, required_number); vector sessions; // Limit the number of sessions for testing. const size_t kMaxNumberOfSessionsForTesting = 0x100u; @@ -631,7 +631,7 @@ TEST_F(OEMCryptoKeyboxTest, GenerateDerivedKeysFromKeyboxLargeBuffer) { ASSERT_EQ(OEMCrypto_SUCCESS, OEMCrypto_GenerateDerivedKeys( s.session_id(), mac_context.data(), mac_context.size(), - enc_context.data(),enc_context.size())); + enc_context.data(), enc_context.size())); } // This class is for tests that have an OEM Certificate instead of a keybox. @@ -660,7 +660,6 @@ TEST_F(OEMCryptoProv30Test, GetDeviceId) { ASSERT_EQ(OEMCrypto_SUCCESS, sts); } - // The OEM certificate must be valid. TEST_F(OEMCryptoProv30Test, CertValidAPI15) { ASSERT_EQ(OEMCrypto_SUCCESS, OEMCrypto_IsKeyboxOrOEMCertValid()); @@ -2843,12 +2842,12 @@ TEST_F(OEMCryptoLoadsCertificate, LoadWrappedRSAKey) { TEST_F(OEMCryptoLoadsCertificate, TestLargeRSAKey3072) { encoded_rsa_key_.assign(kTestRSAPKCS8PrivateKeyInfo3_3072, kTestRSAPKCS8PrivateKeyInfo3_3072 + - sizeof(kTestRSAPKCS8PrivateKeyInfo3_3072)); + sizeof(kTestRSAPKCS8PrivateKeyInfo3_3072)); CreateWrappedRSAKey(); Session s; ASSERT_NO_FATAL_FAILURE(s.open()); - ASSERT_NO_FATAL_FAILURE(s.PreparePublicKey(encoded_rsa_key_.data(), - encoded_rsa_key_.size())); + ASSERT_NO_FATAL_FAILURE( + s.PreparePublicKey(encoded_rsa_key_.data(), encoded_rsa_key_.size())); ASSERT_NO_FATAL_FAILURE(s.InstallRSASessionTestKey(wrapped_rsa_key_)); LicenseRoundTrip license_messages(&s); @@ -2869,8 +2868,8 @@ TEST_F(OEMCryptoLoadsCertificate, TestCarmichaelRSAKey) { CreateWrappedRSAKey(); Session s; ASSERT_NO_FATAL_FAILURE(s.open()); - ASSERT_NO_FATAL_FAILURE(s.PreparePublicKey(encoded_rsa_key_.data(), - encoded_rsa_key_.size())); + ASSERT_NO_FATAL_FAILURE( + s.PreparePublicKey(encoded_rsa_key_.data(), encoded_rsa_key_.size())); ASSERT_NO_FATAL_FAILURE(s.InstallRSASessionTestKey(wrapped_rsa_key_)); LicenseRoundTrip license_messages(&s); @@ -2888,8 +2887,8 @@ TEST_F(OEMCryptoLoadsCertificate, TestMultipleRSAKeys) { Session s1; // Session s1 loads the default rsa key, but doesn't use it // until after s2 uses its key. ASSERT_NO_FATAL_FAILURE(s1.open()); - ASSERT_NO_FATAL_FAILURE(s1.PreparePublicKey(encoded_rsa_key_.data(), - encoded_rsa_key_.size())); + ASSERT_NO_FATAL_FAILURE( + s1.PreparePublicKey(encoded_rsa_key_.data(), encoded_rsa_key_.size())); ASSERT_EQ(OEMCrypto_SUCCESS, OEMCrypto_LoadDeviceRSAKey(s1.session_id(), wrapped_rsa_key_.data(), wrapped_rsa_key_.size())); @@ -2900,8 +2899,8 @@ TEST_F(OEMCryptoLoadsCertificate, TestMultipleRSAKeys) { sizeof(kTestRSAPKCS8PrivateKeyInfo4_2048)); CreateWrappedRSAKey(); ASSERT_NO_FATAL_FAILURE(s2.open()); - ASSERT_NO_FATAL_FAILURE(s2.PreparePublicKey(encoded_rsa_key_.data(), - encoded_rsa_key_.size())); + ASSERT_NO_FATAL_FAILURE( + s2.PreparePublicKey(encoded_rsa_key_.data(), encoded_rsa_key_.size())); ASSERT_NO_FATAL_FAILURE(s2.InstallRSASessionTestKey(wrapped_rsa_key_)); LicenseRoundTrip license_messages2(&s2); ASSERT_NO_FATAL_FAILURE(s2.GenerateNonce()); @@ -3006,8 +3005,8 @@ TEST_F(OEMCryptoLoadsCertificate, RSAPerformance) { wrapped_rsa_key_.size())); vector session_key; vector enc_session_key; - ASSERT_NO_FATAL_FAILURE(s.PreparePublicKey(encoded_rsa_key_.data(), - encoded_rsa_key_.size())); + ASSERT_NO_FATAL_FAILURE( + s.PreparePublicKey(encoded_rsa_key_.data(), encoded_rsa_key_.size())); ASSERT_TRUE(s.GenerateRSASessionKey(&session_key, &enc_session_key)); vector mac_context; vector enc_context; @@ -3035,10 +3034,9 @@ TEST_F(OEMCryptoLoadsCertificate, RSAPerformance) { while (clock.now() - start_time < kTestDuration) { ASSERT_EQ(OEMCrypto_SUCCESS, OEMCrypto_DeriveKeysFromSessionKey( - s.session_id(), - enc_session_key.data(), enc_session_key.size(), - mac_context.data(), mac_context.size(), - enc_context.data(), enc_context.size())); + s.session_id(), enc_session_key.data(), + enc_session_key.size(), mac_context.data(), + mac_context.size(), enc_context.data(), enc_context.size())); count++; } delta_time = clock.now() - start_time; @@ -3068,9 +3066,8 @@ TEST_F(OEMCryptoUsesCertificate, GenerateDerivedKeysLargeBuffer) { } ASSERT_EQ(OEMCrypto_SUCCESS, OEMCrypto_DeriveKeysFromSessionKey( - session_.session_id(), - enc_session_key.data(), enc_session_key.size(), - mac_context.data(), mac_context.size(), + session_.session_id(), enc_session_key.data(), + enc_session_key.size(), mac_context.data(), mac_context.size(), enc_context.data(), enc_context.size())); } @@ -3090,19 +3087,16 @@ class OEMCryptoLoadsCertificateAlternates : public OEMCryptoLoadsCertificate { GetRandBytes(licenseRequest.data(), licenseRequest.size()); size_t signature_length = 256; vector signature(signature_length); - sts = OEMCrypto_GenerateRSASignature(s.session_id(), licenseRequest.data(), - licenseRequest.size(), - signature.data(), &signature_length, - scheme); + sts = OEMCrypto_GenerateRSASignature( + s.session_id(), licenseRequest.data(), licenseRequest.size(), + signature.data(), &signature_length, scheme); // Allow OEMCrypto to request a full buffer. if (sts == OEMCrypto_ERROR_SHORT_BUFFER) { ASSERT_NE(static_cast(0), signature_length); signature.assign(signature_length, 0); - sts = OEMCrypto_GenerateRSASignature(s.session_id(), - licenseRequest.data(), - licenseRequest.size(), - signature.data(), &signature_length, - scheme); + sts = OEMCrypto_GenerateRSASignature( + s.session_id(), licenseRequest.data(), licenseRequest.size(), + signature.data(), &signature_length, scheme); } EXPECT_NE(OEMCrypto_SUCCESS, sts) @@ -3137,8 +3131,8 @@ class OEMCryptoLoadsCertificateAlternates : public OEMCryptoLoadsCertificate { ASSERT_EQ(OEMCrypto_SUCCESS, sts) << "Failed to sign with padding scheme=" << (int)scheme << ", size=" << (int)size; - ASSERT_NO_FATAL_FAILURE(s.PreparePublicKey(encoded_rsa_key_.data(), - encoded_rsa_key_.size())); + ASSERT_NO_FATAL_FAILURE( + s.PreparePublicKey(encoded_rsa_key_.data(), encoded_rsa_key_.size())); ASSERT_NO_FATAL_FAILURE(s.VerifyRSASignature(licenseRequest, signature, signature_length, scheme)); delete[] signature; @@ -3154,8 +3148,8 @@ class OEMCryptoLoadsCertificateAlternates : public OEMCryptoLoadsCertificate { s.GenerateNonce(); vector session_key; vector enc_session_key; - ASSERT_NO_FATAL_FAILURE(s.PreparePublicKey(encoded_rsa_key_.data(), - encoded_rsa_key_.size())); + ASSERT_NO_FATAL_FAILURE( + s.PreparePublicKey(encoded_rsa_key_.data(), encoded_rsa_key_.size())); ASSERT_TRUE(s.GenerateRSASessionKey(&session_key, &enc_session_key)); vector mac_context; vector enc_context; @@ -3359,7 +3353,7 @@ class OEMCryptoCastReceiverTest : public OEMCryptoLoadsCertificateAlternates { "01" // 1 "05" // null object. (field=parameter?) "00" // size of null object - ); + ); vector pkey = wvcdm::a2b_hex("020100"); // integer, version = 0. pkey = concat(pkey, field_n); @@ -3416,8 +3410,8 @@ class OEMCryptoCastReceiverTest : public OEMCryptoLoadsCertificateAlternates { ASSERT_EQ(OEMCrypto_SUCCESS, sts) << "Failed to sign with padding scheme=" << (int)scheme << ", size=" << (int)message.size(); - ASSERT_NO_FATAL_FAILURE(s.PreparePublicKey(encoded_rsa_key_.data(), - encoded_rsa_key_.size())); + ASSERT_NO_FATAL_FAILURE( + s.PreparePublicKey(encoded_rsa_key_.data(), encoded_rsa_key_.size())); // Verify that the signature matches the official test vector. ASSERT_EQ(correct_signature.size(), signature_length); @@ -3426,8 +3420,8 @@ class OEMCryptoCastReceiverTest : public OEMCryptoLoadsCertificateAlternates { // Also verify that our verification algorithm agrees. This is not needed // to test OEMCrypto, but it does verify that this test is valid. - ASSERT_NO_FATAL_FAILURE(s.VerifyRSASignature( - digest, signature.data(), signature_length, scheme)); + ASSERT_NO_FATAL_FAILURE(s.VerifyRSASignature(digest, signature.data(), + signature_length, scheme)); ASSERT_NO_FATAL_FAILURE(s.VerifyRSASignature( digest, correct_signature.data(), correct_signature.size(), scheme)); } @@ -3435,7 +3429,8 @@ class OEMCryptoCastReceiverTest : public OEMCryptoLoadsCertificateAlternates { // CAST Receivers should report that they support cast certificates. TEST_F(OEMCryptoCastReceiverTest, SupportsCertificatesAPI13) { - ASSERT_NE(0u, OEMCrypto_Supports_RSA_CAST & OEMCrypto_SupportedCertificates()); + ASSERT_NE(0u, + OEMCrypto_Supports_RSA_CAST & OEMCrypto_SupportedCertificates()); } // # PKCS#1 v1.5 Signature Example 15.1 @@ -4215,10 +4210,9 @@ class OEMCryptoGenericCryptoTest : public OEMCryptoRefreshTest { OEMCrypto_CipherMode_CTR); ASSERT_EQ(OEMCrypto_SUCCESS, sts); vector encrypted(buffer_length); - sts = - OEMCrypto_Generic_Encrypt(session_.session_id(), clear_buffer_.data(), - buffer_length, iv_, algorithm, - encrypted.data()); + sts = OEMCrypto_Generic_Encrypt(session_.session_id(), clear_buffer_.data(), + buffer_length, iv_, algorithm, + encrypted.data()); EXPECT_NE(OEMCrypto_SUCCESS, sts); expected_encrypted.resize(buffer_length); EXPECT_NE(encrypted, expected_encrypted); @@ -4237,10 +4231,9 @@ class OEMCryptoGenericCryptoTest : public OEMCryptoRefreshTest { OEMCrypto_CipherMode_CTR); ASSERT_EQ(OEMCrypto_SUCCESS, sts); vector resultant(encrypted.size()); - sts = - OEMCrypto_Generic_Decrypt(session_.session_id(), encrypted.data(), - buffer_length, iv_, algorithm, - resultant.data()); + sts = OEMCrypto_Generic_Decrypt(session_.session_id(), encrypted.data(), + buffer_length, iv_, algorithm, + resultant.data()); EXPECT_NE(OEMCrypto_SUCCESS, sts); EXPECT_NE(clear_buffer_, resultant); } @@ -4310,11 +4303,11 @@ TEST_P(OEMCryptoGenericCryptoTest, GenericKeyEncrypt) { session_.license().keys[key_index].key_id_length, OEMCrypto_CipherMode_CTR)); vector encrypted(clear_buffer_.size()); - ASSERT_EQ(OEMCrypto_SUCCESS, - OEMCrypto_Generic_Encrypt( - session_.session_id(), clear_buffer_.data(), - clear_buffer_.size(), iv_, OEMCrypto_AES_CBC_128_NO_PADDING, - encrypted.data())); + ASSERT_EQ( + OEMCrypto_SUCCESS, + OEMCrypto_Generic_Encrypt( + session_.session_id(), clear_buffer_.data(), clear_buffer_.size(), + iv_, OEMCrypto_AES_CBC_128_NO_PADDING, encrypted.data())); ASSERT_EQ(expected_encrypted, encrypted); } @@ -4346,8 +4339,8 @@ TEST_P(OEMCryptoGenericCryptoTest, GenericKeyEncryptSameBufferAPI12) { vector buffer = clear_buffer_; ASSERT_EQ(OEMCrypto_SUCCESS, OEMCrypto_Generic_Encrypt( - session_.session_id(), buffer.data(), buffer.size(), - iv_, OEMCrypto_AES_CBC_128_NO_PADDING, buffer.data())); + session_.session_id(), buffer.data(), buffer.size(), iv_, + OEMCrypto_AES_CBC_128_NO_PADDING, buffer.data())); ASSERT_EQ(expected_encrypted, buffer); } @@ -4474,11 +4467,10 @@ TEST_P(OEMCryptoGenericCryptoTest, GenericKeyVerify) { session_.license().keys[key_index].key_id, session_.license().keys[key_index].key_id_length, OEMCrypto_CipherMode_CTR)); - ASSERT_EQ(OEMCrypto_SUCCESS, - OEMCrypto_Generic_Verify( - session_.session_id(), clear_buffer_.data(), - clear_buffer_.size(), OEMCrypto_HMAC_SHA256, signature.data(), - signature.size())); + ASSERT_EQ(OEMCrypto_SUCCESS, OEMCrypto_Generic_Verify( + session_.session_id(), clear_buffer_.data(), + clear_buffer_.size(), OEMCrypto_HMAC_SHA256, + signature.data(), signature.size())); } // Test that the Generic_Verify function fails when not allowed. @@ -4507,11 +4499,11 @@ TEST_P(OEMCryptoGenericCryptoTest, GenericKeyEncryptLargeBuffer) { session_.license().keys[key_index].key_id_length, OEMCrypto_CipherMode_CTR)); vector encrypted(clear_buffer_.size()); - ASSERT_EQ(OEMCrypto_SUCCESS, - OEMCrypto_Generic_Encrypt( - session_.session_id(), clear_buffer_.data(), - clear_buffer_.size(), iv_, OEMCrypto_AES_CBC_128_NO_PADDING, - encrypted.data())); + ASSERT_EQ( + OEMCrypto_SUCCESS, + OEMCrypto_Generic_Encrypt( + session_.session_id(), clear_buffer_.data(), clear_buffer_.size(), + iv_, OEMCrypto_AES_CBC_128_NO_PADDING, encrypted.data())); ASSERT_EQ(expected_encrypted, encrypted); } @@ -4579,11 +4571,10 @@ TEST_P(OEMCryptoGenericCryptoTest, GenericKeyVerifyLargeBuffer) { session_.license().keys[key_index].key_id, session_.license().keys[key_index].key_id_length, OEMCrypto_CipherMode_CTR)); - ASSERT_EQ(OEMCrypto_SUCCESS, - OEMCrypto_Generic_Verify( - session_.session_id(), clear_buffer_.data(), - clear_buffer_.size(), OEMCrypto_HMAC_SHA256, signature.data(), - signature.size())); + ASSERT_EQ(OEMCrypto_SUCCESS, OEMCrypto_Generic_Verify( + session_.session_id(), clear_buffer_.data(), + clear_buffer_.size(), OEMCrypto_HMAC_SHA256, + signature.data(), signature.size())); } // Test Generic_Encrypt when the key duration has expired. @@ -4604,11 +4595,11 @@ TEST_P(OEMCryptoGenericCryptoTest, KeyDurationEncrypt) { session_.license().keys[key_index].key_id, session_.license().keys[key_index].key_id_length, OEMCrypto_CipherMode_CTR)); - ASSERT_EQ(OEMCrypto_SUCCESS, - OEMCrypto_Generic_Encrypt( - session_.session_id(), clear_buffer_.data(), - clear_buffer_.size(), iv_, OEMCrypto_AES_CBC_128_NO_PADDING, - encrypted.data())); + ASSERT_EQ( + OEMCrypto_SUCCESS, + OEMCrypto_Generic_Encrypt( + session_.session_id(), clear_buffer_.data(), clear_buffer_.size(), + iv_, OEMCrypto_AES_CBC_128_NO_PADDING, encrypted.data())); ASSERT_EQ(expected_encrypted, encrypted); wvcdm::TestSleep::Sleep(kLongSleep + kShortSleep); // Should be expired key. @@ -4709,11 +4700,10 @@ TEST_P(OEMCryptoGenericCryptoTest, KeyDurationVerify) { session_.license().keys[key_index].key_id, session_.license().keys[key_index].key_id_length, OEMCrypto_CipherMode_CTR)); - ASSERT_EQ(OEMCrypto_SUCCESS, - OEMCrypto_Generic_Verify( - session_.session_id(), clear_buffer_.data(), - clear_buffer_.size(), OEMCrypto_HMAC_SHA256, signature.data(), - signature.size())); + ASSERT_EQ(OEMCrypto_SUCCESS, OEMCrypto_Generic_Verify( + session_.session_id(), clear_buffer_.data(), + clear_buffer_.size(), OEMCrypto_HMAC_SHA256, + signature.data(), signature.size())); wvcdm::TestSleep::Sleep(kLongSleep + kShortSleep); // Should be expired key. OEMCryptoResult status = OEMCrypto_Generic_Verify( @@ -4827,7 +4817,8 @@ class LicenseWithUsageEntry { generic_crypto_(false), time_license_received_(0), time_first_decrypt_(0), - time_last_decrypt_(0) { + time_last_decrypt_(0), + active_(true) { license_messages_.set_pst(pst); } @@ -4888,6 +4879,15 @@ class LicenseWithUsageEntry { if (time_first_decrypt_ == 0) time_first_decrypt_ = time_last_decrypt_; } + void DeactivateUsageEntry() { + active_ = false; + ASSERT_EQ( + OEMCrypto_SUCCESS, + OEMCrypto_DeactivateUsageEntry( + session_.session_id(), + reinterpret_cast(pst().c_str()), pst().length())); + } + void GenerateVerifyReport(OEMCrypto_Usage_Entry_Status status) { ASSERT_NO_FATAL_FAILURE(session_.GenerateReport(pst())); Test_PST_Report expected(pst(), status); @@ -4897,7 +4897,7 @@ class LicenseWithUsageEntry { // The PST report was signed above. Below we verify that the entire message // that is sent to the server will be signed by the right mac keys. RenewalRoundTrip renewal_messages(&license_messages_); - renewal_messages.set_is_release(true); + renewal_messages.set_is_release(!active_); ASSERT_NO_FATAL_FAILURE(renewal_messages.SignAndVerifyRequest()); } @@ -4921,6 +4921,7 @@ class LicenseWithUsageEntry { int64_t time_license_received_; int64_t time_first_decrypt_; int64_t time_last_decrypt_; + bool active_; }; class OEMCryptoUsageTableTest : public OEMCryptoGenericCryptoTest { @@ -4973,7 +4974,7 @@ TEST_P(OEMCryptoUsageTableTest, OnlineLicense) { ASSERT_NO_FATAL_FAILURE(s.UpdateUsageEntry(&encrypted_usage_header_)); ASSERT_NO_FATAL_FAILURE(entry.GenerateVerifyReport(kActive)); // Flag the entry as inactive. - ASSERT_NO_FATAL_FAILURE(s.DeactivateUsageEntry(entry.pst())); + ASSERT_NO_FATAL_FAILURE(entry.DeactivateUsageEntry()); ASSERT_NO_FATAL_FAILURE(s.UpdateUsageEntry(&encrypted_usage_header_)); // It should report as inactive. ASSERT_NO_FATAL_FAILURE(entry.GenerateVerifyReport(kInactiveUsed)); @@ -4982,7 +4983,7 @@ TEST_P(OEMCryptoUsageTableTest, OnlineLicense) { entry.TestDecryptCTR(false, OEMCrypto_ERROR_UNKNOWN_FAILURE)); // We could call DeactivateUsageEntry multiple times. The state should not // change. - ASSERT_NO_FATAL_FAILURE(s.DeactivateUsageEntry(entry.pst())); + ASSERT_NO_FATAL_FAILURE(entry.DeactivateUsageEntry()); ASSERT_NO_FATAL_FAILURE(s.UpdateUsageEntry(&encrypted_usage_header_)); // It should report as inactive. ASSERT_NO_FATAL_FAILURE(entry.GenerateVerifyReport(kInactiveUsed)); @@ -4999,7 +5000,7 @@ TEST_P(OEMCryptoUsageTableTest, OnlineLicenseUnused) { // No decrypt. We do not use this license. ASSERT_NO_FATAL_FAILURE(entry.GenerateVerifyReport(kUnused)); // Flag the entry as inactive. - ASSERT_NO_FATAL_FAILURE(s.DeactivateUsageEntry(entry.pst())); + ASSERT_NO_FATAL_FAILURE(entry.DeactivateUsageEntry()); ASSERT_NO_FATAL_FAILURE(s.UpdateUsageEntry(&encrypted_usage_header_)); // It should report as inactive. ASSERT_NO_FATAL_FAILURE(entry.GenerateVerifyReport(kInactiveUnused)); @@ -5008,7 +5009,7 @@ TEST_P(OEMCryptoUsageTableTest, OnlineLicenseUnused) { entry.TestDecryptCTR(false, OEMCrypto_ERROR_UNKNOWN_FAILURE)); // We could call DeactivateUsageEntry multiple times. The state should not // change. - ASSERT_NO_FATAL_FAILURE(s.DeactivateUsageEntry(entry.pst())); + ASSERT_NO_FATAL_FAILURE(entry.DeactivateUsageEntry()); ASSERT_NO_FATAL_FAILURE(s.UpdateUsageEntry(&encrypted_usage_header_)); // It should report as inactive. ASSERT_NO_FATAL_FAILURE(entry.GenerateVerifyReport(kInactiveUnused)); @@ -5031,7 +5032,7 @@ TEST_P(OEMCryptoUsageTableTest, ForbidReportWithNoUpdate) { // Now it's OK. ASSERT_NO_FATAL_FAILURE(entry.GenerateVerifyReport(kActive)); // Flag the entry as inactive. - ASSERT_NO_FATAL_FAILURE(s.DeactivateUsageEntry(entry.pst())); + ASSERT_NO_FATAL_FAILURE(entry.DeactivateUsageEntry()); // Cannot generate a report without first updating the file. ASSERT_NO_FATAL_FAILURE( s.GenerateReport(entry.pst(), OEMCrypto_ERROR_ENTRY_NEEDS_UPDATE)); @@ -5201,7 +5202,7 @@ TEST_P(OEMCryptoUsageTableTest, GenericCryptoEncrypt) { EXPECT_EQ(expected_encrypted, encrypted); ASSERT_NO_FATAL_FAILURE(s.UpdateUsageEntry(&encrypted_usage_header_)); ASSERT_NO_FATAL_FAILURE(entry.GenerateVerifyReport(kActive)); - ASSERT_NO_FATAL_FAILURE(s.DeactivateUsageEntry(entry.pst())); + ASSERT_NO_FATAL_FAILURE(entry.DeactivateUsageEntry()); ASSERT_NO_FATAL_FAILURE(s.UpdateUsageEntry(&encrypted_usage_header_)); ASSERT_NO_FATAL_FAILURE(entry.GenerateVerifyReport(kInactiveUsed)); encrypted.assign(clear_buffer_.size(), 0); @@ -5236,7 +5237,7 @@ TEST_P(OEMCryptoUsageTableTest, GenericCryptoDecrypt) { EXPECT_EQ(clear_buffer_, resultant); ASSERT_NO_FATAL_FAILURE(s.UpdateUsageEntry(&encrypted_usage_header_)); ASSERT_NO_FATAL_FAILURE(entry.GenerateVerifyReport(kActive)); - ASSERT_NO_FATAL_FAILURE(s.DeactivateUsageEntry(entry.pst())); + ASSERT_NO_FATAL_FAILURE(entry.DeactivateUsageEntry()); ASSERT_NO_FATAL_FAILURE(s.UpdateUsageEntry(&encrypted_usage_header_)); ASSERT_NO_FATAL_FAILURE(entry.GenerateVerifyReport(kInactiveUsed)); resultant.assign(encrypted.size(), 0); @@ -5279,7 +5280,7 @@ TEST_P(OEMCryptoUsageTableTest, GenericCryptoSign) { ASSERT_NO_FATAL_FAILURE(s.UpdateUsageEntry(&encrypted_usage_header_)); ASSERT_NO_FATAL_FAILURE(entry.GenerateVerifyReport(kActive)); - ASSERT_NO_FATAL_FAILURE(s.DeactivateUsageEntry(entry.pst())); + ASSERT_NO_FATAL_FAILURE(entry.DeactivateUsageEntry()); ASSERT_NO_FATAL_FAILURE(s.UpdateUsageEntry(&encrypted_usage_header_)); ASSERT_NO_FATAL_FAILURE(entry.GenerateVerifyReport(kInactiveUsed)); signature.assign(SHA256_DIGEST_LENGTH, 0); @@ -5314,7 +5315,7 @@ TEST_P(OEMCryptoUsageTableTest, GenericCryptoVerify) { ASSERT_EQ(OEMCrypto_SUCCESS, sts); ASSERT_NO_FATAL_FAILURE(s.UpdateUsageEntry(&encrypted_usage_header_)); ASSERT_NO_FATAL_FAILURE(entry.GenerateVerifyReport(kActive)); - ASSERT_NO_FATAL_FAILURE(s.DeactivateUsageEntry(entry.pst())); + ASSERT_NO_FATAL_FAILURE(entry.DeactivateUsageEntry()); ASSERT_NO_FATAL_FAILURE(s.UpdateUsageEntry(&encrypted_usage_header_)); ASSERT_NO_FATAL_FAILURE(entry.GenerateVerifyReport(kInactiveUsed)); sts = OEMCrypto_Generic_Verify(s.session_id(), clear_buffer_.data(), @@ -5505,8 +5506,7 @@ TEST_P(OEMCryptoUsageTableTest, DeactivateOfflineLicense) { ASSERT_NO_FATAL_FAILURE(entry.OpenAndReload(this)); ASSERT_NO_FATAL_FAILURE( entry.TestDecryptCTR()); // Should be able to decrypt. - ASSERT_NO_FATAL_FAILURE( - s.DeactivateUsageEntry(entry.pst())); // Then deactivate. + ASSERT_NO_FATAL_FAILURE(entry.DeactivateUsageEntry()); // Then deactivate. // After deactivate, should not be able to decrypt. ASSERT_NO_FATAL_FAILURE( entry.TestDecryptCTR(false, OEMCrypto_ERROR_UNKNOWN_FAILURE)); @@ -5531,7 +5531,7 @@ TEST_P(OEMCryptoUsageTableTest, DeactivateOfflineLicense) { ASSERT_NO_FATAL_FAILURE(entry.GenerateVerifyReport(kInactiveUsed)); // We could call DeactivateUsageEntry multiple times. The state should not // change. - ASSERT_NO_FATAL_FAILURE(s.DeactivateUsageEntry(entry.pst())); + ASSERT_NO_FATAL_FAILURE(entry.DeactivateUsageEntry()); ASSERT_NO_FATAL_FAILURE(s.UpdateUsageEntry(&encrypted_usage_header_)); ASSERT_NO_FATAL_FAILURE(entry.GenerateVerifyReport(kInactiveUsed)); } @@ -5545,8 +5545,7 @@ TEST_P(OEMCryptoUsageTableTest, DeactivateOfflineLicenseUnused) { Session& s = entry.session(); // No Decrypt. This license is unused. ASSERT_NO_FATAL_FAILURE(entry.OpenAndReload(this)); - ASSERT_NO_FATAL_FAILURE( - s.DeactivateUsageEntry(entry.pst())); // Then deactivate. + ASSERT_NO_FATAL_FAILURE(entry.DeactivateUsageEntry()); // Then deactivate. // After deactivate, should not be able to decrypt. ASSERT_NO_FATAL_FAILURE( entry.TestDecryptCTR(false, OEMCrypto_ERROR_UNKNOWN_FAILURE)); @@ -5571,11 +5570,30 @@ TEST_P(OEMCryptoUsageTableTest, DeactivateOfflineLicenseUnused) { ASSERT_NO_FATAL_FAILURE(entry.GenerateVerifyReport(kInactiveUnused)); // We could call DeactivateUsageEntry multiple times. The state should not // change. - ASSERT_NO_FATAL_FAILURE(s.DeactivateUsageEntry(entry.pst())); + ASSERT_NO_FATAL_FAILURE(entry.DeactivateUsageEntry()); ASSERT_NO_FATAL_FAILURE(s.UpdateUsageEntry(&encrypted_usage_header_)); ASSERT_NO_FATAL_FAILURE(entry.GenerateVerifyReport(kInactiveUnused)); } +TEST_P(OEMCryptoUsageTableTest, SecureStop) { + LicenseWithUsageEntry entry; + entry.license_messages().set_api_version(license_api_version_); + entry.MakeAndLoadOnline(this); + Session& s = entry.session(); + ASSERT_NO_FATAL_FAILURE(s.UpdateUsageEntry(&encrypted_usage_header_)); + ASSERT_NO_FATAL_FAILURE(entry.TestDecryptCTR()); + ASSERT_NO_FATAL_FAILURE(s.UpdateUsageEntry(&encrypted_usage_header_)); + ASSERT_NO_FATAL_FAILURE(s.close()); + // When we generate a secure stop without loading the license first, it + // should assume the server does not support core messages. + ASSERT_NO_FATAL_FAILURE(s.open()); + ASSERT_NO_FATAL_FAILURE(entry.ReloadUsageEntry()); + ASSERT_NO_FATAL_FAILURE(entry.DeactivateUsageEntry()); + ASSERT_NO_FATAL_FAILURE(s.UpdateUsageEntry(&encrypted_usage_header_)); + // It should report as inactive. + ASSERT_NO_FATAL_FAILURE(entry.GenerateVerifyReport(kInactiveUsed)); +} + // Test update usage table fails when passed a null pointer. TEST_P(OEMCryptoUsageTableTest, UpdateFailsWithNullPtr) { LicenseWithUsageEntry entry; @@ -5638,8 +5656,7 @@ class OEMCryptoUsageTableDefragTest : public OEMCryptoUsageTableTest { ASSERT_LT(0u, header_buffer_length); encrypted_usage_header_.resize(header_buffer_length); sts = OEMCrypto_ShrinkUsageTableHeader( - new_size, encrypted_usage_header_.data(), - &header_buffer_length); + new_size, encrypted_usage_header_.data(), &header_buffer_length); ASSERT_EQ(expected_result, sts); } }; @@ -5895,26 +5912,23 @@ TEST_P(OEMCryptoUsageTableTest, ReloadUsageTableWithSkew) { nullptr, old_usage_header_2_.size())); ASSERT_NO_FATAL_FAILURE(s.open()); // Cannot load an entry if header didn't load. - ASSERT_EQ( - OEMCrypto_ERROR_UNKNOWN_FAILURE, - OEMCrypto_LoadUsageEntry(s.session_id(), s.usage_entry_number(), - s.encrypted_usage_entry().data(), - s.encrypted_usage_entry().size())); + ASSERT_EQ(OEMCrypto_ERROR_UNKNOWN_FAILURE, + OEMCrypto_LoadUsageEntry(s.session_id(), s.usage_entry_number(), + s.encrypted_usage_entry().data(), + s.encrypted_usage_entry().size())); ASSERT_NO_FATAL_FAILURE(s.close()); // Modified header generates error. vector bad_header = encrypted_usage_header_; bad_header[3] ^= 42; - ASSERT_NE(OEMCrypto_SUCCESS, - OEMCrypto_LoadUsageTableHeader(bad_header.data(), - bad_header.size())); + ASSERT_NE(OEMCrypto_SUCCESS, OEMCrypto_LoadUsageTableHeader( + bad_header.data(), bad_header.size())); ASSERT_NO_FATAL_FAILURE(s.open()); // Cannot load an entry if header didn't load. - ASSERT_EQ( - OEMCrypto_ERROR_UNKNOWN_FAILURE, - OEMCrypto_LoadUsageEntry(s.session_id(), s.usage_entry_number(), - s.encrypted_usage_entry().data(), - s.encrypted_usage_entry().size())); + ASSERT_EQ(OEMCrypto_ERROR_UNKNOWN_FAILURE, + OEMCrypto_LoadUsageEntry(s.session_id(), s.usage_entry_number(), + s.encrypted_usage_entry().data(), + s.encrypted_usage_entry().size())); ASSERT_NO_FATAL_FAILURE(s.close()); // Old by 2 generation numbers is error. @@ -5923,11 +5937,10 @@ TEST_P(OEMCryptoUsageTableTest, ReloadUsageTableWithSkew) { old_usage_header_2_.size())); ASSERT_NO_FATAL_FAILURE(s.open()); // Cannot load an entry if header didn't load. - ASSERT_NE( - OEMCrypto_SUCCESS, - OEMCrypto_LoadUsageEntry(s.session_id(), s.usage_entry_number(), - s.encrypted_usage_entry().data(), - s.encrypted_usage_entry().size())); + ASSERT_NE(OEMCrypto_SUCCESS, + OEMCrypto_LoadUsageEntry(s.session_id(), s.usage_entry_number(), + s.encrypted_usage_entry().data(), + s.encrypted_usage_entry().size())); ASSERT_NO_FATAL_FAILURE(s.close()); // Old by 1 generation numbers is just warning. @@ -5936,11 +5949,10 @@ TEST_P(OEMCryptoUsageTableTest, ReloadUsageTableWithSkew) { old_usage_header_1_.size())); // Everything else should still work. Skew by 1 is just a warning. ASSERT_NO_FATAL_FAILURE(s.open()); - ASSERT_EQ( - OEMCrypto_WARNING_GENERATION_SKEW, - OEMCrypto_LoadUsageEntry(s.session_id(), s.usage_entry_number(), - s.encrypted_usage_entry().data(), - s.encrypted_usage_entry().size())); + ASSERT_EQ(OEMCrypto_WARNING_GENERATION_SKEW, + OEMCrypto_LoadUsageEntry(s.session_id(), s.usage_entry_number(), + s.encrypted_usage_entry().data(), + s.encrypted_usage_entry().size())); ASSERT_NO_FATAL_FAILURE(InstallTestRSAKey(&s)); ASSERT_EQ(OEMCrypto_SUCCESS, entry.license_messages().LoadResponse()); } @@ -5952,8 +5964,8 @@ TEST_P(OEMCryptoUsageTableTest, GenerateReportWrongPST) { entry.MakeAndLoadOnline(this); Session& s = entry.session(); ASSERT_NO_FATAL_FAILURE(s.UpdateUsageEntry(&encrypted_usage_header_)); - ASSERT_NO_FATAL_FAILURE(s.GenerateReport("wrong_pst", - OEMCrypto_ERROR_WRONG_PST)); + ASSERT_NO_FATAL_FAILURE( + s.GenerateReport("wrong_pst", OEMCrypto_ERROR_WRONG_PST)); } // Test usage table timing. @@ -5992,7 +6004,7 @@ TEST_P(OEMCryptoUsageTableTest, TimingTest) { ASSERT_NO_FATAL_FAILURE(entry2.TestDecryptCTR()); wvcdm::TestSleep::Sleep(kLongSleep); - ASSERT_NO_FATAL_FAILURE(s1.DeactivateUsageEntry(entry1.pst())); + ASSERT_NO_FATAL_FAILURE(entry1.DeactivateUsageEntry()); ASSERT_NO_FATAL_FAILURE(s1.UpdateUsageEntry(&encrypted_usage_header_)); ASSERT_NO_FATAL_FAILURE(s2.UpdateUsageEntry(&encrypted_usage_header_)); ASSERT_NO_FATAL_FAILURE(s1.close()); @@ -6105,7 +6117,7 @@ TEST_P(OEMCryptoUsageTableTest, VerifyUsageTimes) { // |<------------------------------------| = seconds_since_license_received ASSERT_NO_FATAL_FAILURE(s.UpdateUsageEntry(&encrypted_usage_header_)); ASSERT_NO_FATAL_FAILURE(entry.GenerateVerifyReport(kActive)); - ASSERT_NO_FATAL_FAILURE(s.DeactivateUsageEntry(entry.pst())); + ASSERT_NO_FATAL_FAILURE(entry.DeactivateUsageEntry()); ASSERT_NO_FATAL_FAILURE(s.UpdateUsageEntry(&encrypted_usage_header_)); ASSERT_NO_FATAL_FAILURE(entry.GenerateVerifyReport(kInactiveUsed)); ASSERT_NO_FATAL_FAILURE( @@ -6195,8 +6207,8 @@ TEST_P(OEMCryptoUsageTableTestWallClock, TimeRollbackPrevention) { // Rollback the wall clock time. cout << "Rolling the system time back..." << endl; - ASSERT_NO_FATAL_FAILURE(SetWallTimeDelta( - -static_cast(kLongDuration) * 10)); + ASSERT_NO_FATAL_FAILURE( + SetWallTimeDelta(-static_cast(kLongDuration) * 10)); // Try to playback again. ASSERT_NO_FATAL_FAILURE(entry.OpenAndReload(this)); @@ -6210,8 +6222,8 @@ TEST_P(OEMCryptoUsageTableTestWallClock, TimeRollbackPrevention) { // not report negative times. const auto test_duration = third_decrypt_monotonic - first_decrypt_monotonic; cout << "Rolling the system time forward to the absolute time..." << endl; - ASSERT_NO_FATAL_FAILURE(SetWallTimeDelta( - test_duration / std::chrono::seconds(1))); + ASSERT_NO_FATAL_FAILURE( + SetWallTimeDelta(test_duration / std::chrono::seconds(1))); // Need to update time created since the verification checks the time of PST // report creation. expected.time_created = UnixTime(wall_clock.now()); @@ -6232,8 +6244,7 @@ TEST_P(OEMCryptoUsageTableTest, PSTLargeBuffer) { ASSERT_NO_FATAL_FAILURE(entry.OpenAndReload(this)); ASSERT_NO_FATAL_FAILURE( entry.TestDecryptCTR()); // Should be able to decrypt. - ASSERT_NO_FATAL_FAILURE( - s.DeactivateUsageEntry(entry.pst())); // Then deactivate. + ASSERT_NO_FATAL_FAILURE(entry.DeactivateUsageEntry()); // Then deactivate. // After deactivate, should not be able to decrypt. ASSERT_NO_FATAL_FAILURE( entry.TestDecryptCTR(false, OEMCrypto_ERROR_UNKNOWN_FAILURE));