From 726f2d51e971a389ec2c39815d98138cc2f2fa1f Mon Sep 17 00:00:00 2001 From: Vicky Min Date: Tue, 21 Nov 2023 14:21:06 -0800 Subject: [PATCH] ODK unit tests for release request PiperOrigin-RevId: 584427947 Change-Id: I7a131739c5ea0d27c2f9e9c5ecb7b138176ce049 --- libwvdrmengine/oemcrypto/odk/include/odk.h | 45 ++++++++++++-- .../oemcrypto/odk/include/odk_structs.h | 2 +- libwvdrmengine/oemcrypto/odk/src/odk.c | 42 ++++++++++++-- .../oemcrypto/odk/src/odk_structs_priv.h | 2 +- .../oemcrypto/odk/test/odk_test.cpp | 58 +++++++++++++++++++ .../oemcrypto/odk/test/odk_test_helper.cpp | 22 +++++++ .../oemcrypto/odk/test/odk_test_helper.h | 4 +- 7 files changed, 160 insertions(+), 15 deletions(-) diff --git a/libwvdrmengine/oemcrypto/odk/include/odk.h b/libwvdrmengine/oemcrypto/odk/include/odk.h index 92cfc953..7ecaa4ab 100644 --- a/libwvdrmengine/oemcrypto/odk/include/odk.h +++ b/libwvdrmengine/oemcrypto/odk/include/odk.h @@ -295,8 +295,6 @@ OEMCryptoResult ODK_PrepareCoreLicenseRequest( * of the message. (in) size of buffer reserved for the core message, in * bytes. (out) actual length of the core message, in bytes. * @param[in] nonce_values: pointer to the session's nonce data. - * @param[in] message_count_info: information used for server-side anomaly - * detection * @param[in] status: the enumeration of OEMCrypto_Usage_Entry_Status * @param[in] clock_security_level: the enumeration of * OEMCryto_Clock_Security_Level @@ -304,6 +302,9 @@ OEMCryptoResult ODK_PrepareCoreLicenseRequest( * being requested and the release being generated in seconds * @param[in] seconds_since_first_decrypt: The time since playback has started * in seconds + * @param[in,out] clock_values: the session's clock values. + * @param[in] system_time_seconds: the current time on OEMCrypto's clock, in + * seconds. * * @retval OEMCrypto_SUCCESS * @retval OEMCrypto_ERROR_SHORT_BUFFER: core_message_size is too small @@ -314,10 +315,10 @@ OEMCryptoResult ODK_PrepareCoreLicenseRequest( */ OEMCryptoResult ODK_PrepareCoreReleaseRequest( uint8_t* message, size_t message_length, size_t* core_message_size, - const ODK_NonceValues* nonce_values, - const ODK_MessageCounterInfo* counter_info, uint32_t status, + ODK_NonceValues* nonce_values, uint32_t status, uint32_t clock_security_level, int64_t seconds_since_license_requested, - int64_t seconds_since_first_decrypt); + int64_t seconds_since_first_decrypt, ODK_ClockValues* clock_values, + uint64_t system_time_seconds); /** * Modifies the message to include a core renewal request at the beginning of @@ -666,6 +667,7 @@ OEMCryptoResult ODK_ParseLicense( * value is ODK_SET_TIMER. This must be non-null if OEMCrypto uses a * hardware timer. * + * @retval OEMCrypto_SUCCESS * @retval ODK_ERROR_CORE_MESSAGE: the message did not parse correctly, or there * were other incorrect values. An error should be returned to the CDM * layer. @@ -690,6 +692,39 @@ OEMCryptoResult ODK_ParseRenewal(const uint8_t* message, size_t message_length, ODK_ClockValues* clock_values, uint64_t* timer_value); +/** + * The function ODK_ParseRelease will parse the message and verify its + * header contents. If the message does not parse correctly, an error of + * ODK_ERROR_CORE_MESSAGE is returned. This function is mostly a placeholder + * function since there is no information needed in the release response. + * + * @param[in] message: pointer to the message buffer. + * @param[in] message_length: length of the entire message buffer. + * @param[in] core_message_size: length of the core message, at the beginning of + * the message buffer. + * @param[in,out] nonce_values: pointer to the session's nonce data. These might + * be updated if the server returns a lower API version. + * @param[in] seconds_since_license_requested: the time between the license + * being requested and the release being generated in seconds. + * @param[in] seconds_since_first_decrypt: The time since playback has started + * in seconds. + * + * @retval OEMCrypto_SUCCESS + * @retval ODK_ERROR_CORE_MESSAGE: the message did not parse correctly, or there + * were other incorrect values. An error should be returned to the CDM + * layer. + * @retval ODK_UNSUPPORTED_API + * @retval OEMCrypto_ERROR_INVALID_NONCE + * + * @version + * This method is new in version 19 of the API. + */ +OEMCryptoResult ODK_ParseRelease(const uint8_t* message, size_t message_length, + size_t core_message_length, + ODK_NonceValues* nonce_values, + int64_t seconds_since_license_requested, + int64_t seconds_since_first_decrypt); + /** * The function ODK_ParseProvisioning will parse the message and verify the * nonce values match those in the license. diff --git a/libwvdrmengine/oemcrypto/odk/include/odk_structs.h b/libwvdrmengine/oemcrypto/odk/include/odk_structs.h index 82ca1410..2ff0cef8 100644 --- a/libwvdrmengine/oemcrypto/odk/include/odk_structs.h +++ b/libwvdrmengine/oemcrypto/odk/include/odk_structs.h @@ -19,7 +19,7 @@ extern "C" { #define ODK_MINOR_VERSION 0 /* ODK Version string. Date changed automatically on each release. */ -#define ODK_RELEASE_DATE "ODK v19.0 2023-11-10" +#define ODK_RELEASE_DATE "ODK v19.0 2023-11-21" /* The lowest version number for an ODK message. */ #define ODK_FIRST_VERSION 16 diff --git a/libwvdrmengine/oemcrypto/odk/src/odk.c b/libwvdrmengine/oemcrypto/odk/src/odk.c index dc52b0ab..59737db9 100644 --- a/libwvdrmengine/oemcrypto/odk/src/odk.c +++ b/libwvdrmengine/oemcrypto/odk/src/odk.c @@ -235,12 +235,12 @@ OEMCryptoResult ODK_PrepareCoreLicenseRequest( OEMCryptoResult ODK_PrepareCoreReleaseRequest( uint8_t* message, size_t message_length, size_t* core_message_size, - const ODK_NonceValues* nonce_values, - const ODK_MessageCounterInfo* counter_info, uint32_t status, + ODK_NonceValues* nonce_values, uint32_t status, uint32_t clock_security_level, int64_t seconds_since_license_requested, - int64_t seconds_since_first_decrypt) { + int64_t seconds_since_first_decrypt, ODK_ClockValues* clock_values, + uint64_t system_time_seconds) { if (core_message_size == NULL || nonce_values == NULL || - counter_info == NULL) { + clock_values == NULL) { return ODK_ERROR_CORE_MESSAGE; } if (nonce_values->api_major_version >= 19) { @@ -251,8 +251,9 @@ OEMCryptoResult ODK_PrepareCoreReleaseRequest( } else { // If the version is pre 19 when license release isn't supported, create a // license request. - return ODK_PrepareCoreLicenseRequest( - message, message_length, core_message_size, nonce_values, counter_info); + return ODK_PrepareCoreRenewalRequest(message, message_length, + core_message_size, nonce_values, + clock_values, system_time_seconds); } } @@ -521,6 +522,35 @@ OEMCryptoResult ODK_ParseRenewal(const uint8_t* message, size_t message_length, timer_value); } +OEMCryptoResult ODK_ParseRelease(const uint8_t* message, size_t message_length, + size_t core_message_length, + ODK_NonceValues* nonce_values, + int64_t seconds_since_license_requested, + int64_t seconds_since_first_decrypt) { + if (message == NULL || nonce_values == NULL) { + return ODK_ERROR_CORE_MESSAGE; + } + + const OEMCryptoResult err = + ODK_ParseCoreHeader(message, message_length, core_message_length, + ODK_Release_Response_Type, nonce_values); + if (err != OEMCrypto_SUCCESS) { + return err; + } + + ODK_ReleaseResponse release_response = {0}; + ODK_Message msg = ODK_Message_Create((uint8_t*)message, message_length); + ODK_Message_SetSize(&msg, core_message_length); + Unpack_ODK_ReleaseResponse(&msg, &release_response); + + if (ODK_Message_GetStatus(&msg) != MESSAGE_STATUS_OK || + ODK_Message_GetOffset(&msg) != core_message_length) { + return ODK_ERROR_CORE_MESSAGE; + } + + return OEMCrypto_SUCCESS; +} + OEMCryptoResult ODK_ParseProvisioning( const uint8_t* message, size_t message_length, size_t core_message_length, ODK_NonceValues* nonce_values, const uint8_t* device_id, diff --git a/libwvdrmengine/oemcrypto/odk/src/odk_structs_priv.h b/libwvdrmengine/oemcrypto/odk/src/odk_structs_priv.h index b9bd4657..208ce2c4 100644 --- a/libwvdrmengine/oemcrypto/odk/src/odk_structs_priv.h +++ b/libwvdrmengine/oemcrypto/odk/src/odk_structs_priv.h @@ -132,7 +132,7 @@ typedef struct { #define ODK_CORE_MESSAGE_SIZE 20u #define ODK_LICENSE_REQUEST_SIZE 90u #define ODK_LICENSE_REQUEST_SIZE_V17 20u -#define ODK_RELEASE_REQUEST_SIZE 90u +#define ODK_RELEASE_REQUEST_SIZE 20u #define ODK_RENEWAL_REQUEST_SIZE 28u #define ODK_PROVISIONING_REQUEST_SIZE 94u #define ODK_PROVISIONING_REQUEST_SIZE_V17 88u diff --git a/libwvdrmengine/oemcrypto/odk/test/odk_test.cpp b/libwvdrmengine/oemcrypto/odk/test/odk_test.cpp index f89483b4..083ac72c 100644 --- a/libwvdrmengine/oemcrypto/odk/test/odk_test.cpp +++ b/libwvdrmengine/oemcrypto/odk/test/odk_test.cpp @@ -32,12 +32,14 @@ using oemcrypto_core_message::ODK_LicenseRequest; using oemcrypto_core_message::ODK_MessageCounter; using oemcrypto_core_message::ODK_Provisioning40Request; using oemcrypto_core_message::ODK_ProvisioningRequest; +using oemcrypto_core_message::ODK_ReleaseRequest; using oemcrypto_core_message::ODK_RenewalRequest; using oemcrypto_core_message::deserialize::CoreCommonRequestFromMessage; using oemcrypto_core_message::deserialize::CoreLicenseRequestFromMessage; using oemcrypto_core_message::deserialize::CoreProvisioning40RequestFromMessage; using oemcrypto_core_message::deserialize::CoreProvisioningRequestFromMessage; +using oemcrypto_core_message::deserialize::CoreReleaseRequestFromMessage; using oemcrypto_core_message::deserialize::CoreRenewalRequestFromMessage; using oemcrypto_core_message::deserialize:: CoreRenewedProvisioningRequestFromMessage; @@ -49,6 +51,7 @@ using oemcrypto_core_message::serialize::CreateCoreProvisioning40Response; using oemcrypto_core_message::serialize::CreateCoreProvisioningResponse; using oemcrypto_core_message::serialize:: CreateCoreProvisioningResponseFromProto; +using oemcrypto_core_message::serialize::CreateCoreReleaseResponse; using oemcrypto_core_message::serialize::CreateCoreRenewalResponse; constexpr uint32_t kExtraPayloadSize = 128u; @@ -686,6 +689,35 @@ TEST(OdkTest, RenewalRequestRoundtrip) { odk_prepare_func, kdo_parse_func); } +TEST(OdkTest, ReleaseRequestRoundTrip) { + const uint32_t clock_security_level = 1; + const uint32_t status = 1; + constexpr uint64_t system_time_seconds = 0xBADDCAFE000FF1CE; + uint64_t playback_time = 0xCAFE00000000; + const int64_t seconds_since_license_requested = 1; + const int64_t seconds_since_first_decrypt = + static_cast(system_time_seconds - playback_time); + ODK_ClockValues clock_values; + memset(&clock_values, 0, sizeof(clock_values)); + clock_values.time_of_first_decrypt = seconds_since_first_decrypt; + std::vector extra_fields = {}; + auto odk_prepare_func = [&](uint8_t* const buf, size_t* size, + ODK_NonceValues* nonce_values) { + return ODK_PrepareCoreReleaseRequest( + buf, SIZE_MAX, size, nonce_values, status, clock_security_level, + seconds_since_license_requested, seconds_since_first_decrypt, + &clock_values, system_time_seconds); + }; + auto kdo_parse_func = [&](const std::string& oemcrypto_core_message, + ODK_ReleaseRequest* core_release_request) { + bool ok = CoreReleaseRequestFromMessage(oemcrypto_core_message, + core_release_request); + return ok; + }; + ValidateRequest(ODK_Release_Request_Type, extra_fields, + odk_prepare_func, kdo_parse_func); +} + TEST(OdkTest, ProvisionRequestRoundtrip) { ODK_MessageCounterInfo counter_info; counter_info.master_generation_number = 0x12345678abcdffff; @@ -1131,6 +1163,32 @@ TEST_P(OdkVersionTest, RenewalResponseRoundtrip) { kdo_prepare_func); } +TEST_P(OdkVersionTest, ReleaseResponseRoundtrip) { + ODK_ReleaseResponseParams params; + ODK_SetDefaultReleaseResponseParams(¶ms); + SetRequestVersion(¶ms); + const int64_t seconds_since_license_requested = + params.seconds_since_license_requested; + const int64_t seconds_since_first_decrypt = + params.seconds_since_first_decrypt; + auto odk_parse_func = [&](const uint8_t* buf, size_t size) { + OEMCryptoResult err = + ODK_ParseRelease(buf, size, size, &(params.core_message.nonce_values), + params.seconds_since_license_requested, + params.seconds_since_first_decrypt); + return err; + }; + auto kdo_prepare_func = [&](ODK_ReleaseRequest& core_request, + std::string* oemcrypto_core_message) { + return CreateCoreReleaseResponse( + features_, core_request, seconds_since_license_requested, + seconds_since_first_decrypt, oemcrypto_core_message); + }; + ValidateResponse(GetParam(), &(params.core_message), + params.extra_fields, odk_parse_func, + kdo_prepare_func); +} + TEST_P(OdkVersionTest, ProvisionResponseRoundtrip) { ODK_ProvisioningResponseParams params; ODK_SetDefaultProvisioningResponseParams(¶ms, diff --git a/libwvdrmengine/oemcrypto/odk/test/odk_test_helper.cpp b/libwvdrmengine/oemcrypto/odk/test/odk_test_helper.cpp index 49b04824..2c95cfe1 100644 --- a/libwvdrmengine/oemcrypto/odk/test/odk_test_helper.cpp +++ b/libwvdrmengine/oemcrypto/odk/test/odk_test_helper.cpp @@ -358,6 +358,8 @@ size_t ODK_FieldLength(ODK_FieldType type) { return sizeof(uint32_t); case ODK_UINT64: return sizeof(uint64_t); + case ODK_INT64: + return sizeof(uint64_t); case ODK_BOOL: // Booleans are stored in the message as 32 bit ints. return sizeof(uint32_t); case ODK_SUBSTRING: @@ -414,6 +416,12 @@ OEMCryptoResult ODK_WriteSingleField(uint8_t* buf, const ODK_Field* field) { memcpy(buf, &u64, sizeof(u64)); break; } + case ODK_INT64: { + const int64_t i64 = + oemcrypto_htobe64(*static_cast(field->value)); + memcpy(buf, &i64, sizeof(i64)); + break; + } case ODK_BOOL: { const bool value = *static_cast(field->value); const uint32_t u32 = oemcrypto_htobe32(value ? 1 : 0); @@ -493,6 +501,12 @@ OEMCryptoResult ODK_ReadSingleField(const uint8_t* buf, *u64p = oemcrypto_be64toh(*u64p); break; } + case ODK_INT64: { + memcpy(field->value, buf, sizeof(int64_t)); + int64_t* i64p = static_cast(field->value); + *i64p = oemcrypto_be64toh(*i64p); + break; + } case ODK_BOOL: { uint32_t value; memcpy(&value, buf, sizeof(uint32_t)); @@ -612,6 +626,14 @@ OEMCryptoResult ODK_DumpSingleField(const uint8_t* buf, << "\n"; break; } + case ODK_INT64: { + int64_t val; + memcpy(&val, buf, sizeof(int64_t)); + val = oemcrypto_be64toh(val); + std::cerr << field->name << ": " << val << " = 0x" << std::hex << val + << "\n"; + break; + } case ODK_SUBSTRING: { uint32_t off = 0; uint32_t len = 0; diff --git a/libwvdrmengine/oemcrypto/odk/test/odk_test_helper.h b/libwvdrmengine/oemcrypto/odk/test/odk_test_helper.h index 559f004f..7357a2fe 100644 --- a/libwvdrmengine/oemcrypto/odk/test/odk_test_helper.h +++ b/libwvdrmengine/oemcrypto/odk/test/odk_test_helper.h @@ -19,6 +19,7 @@ enum ODK_FieldType { ODK_UINT16, ODK_UINT32, ODK_UINT64, + ODK_INT64, ODK_SUBSTRING, ODK_DEVICEID, ODK_DEVICEINFO, @@ -98,8 +99,7 @@ void ODK_SetDefaultCoreFields(ODK_CoreMessage* core_message, ODK_MessageType message_type); void ODK_SetDefaultLicenseResponseParams(ODK_LicenseResponseParams* params, uint32_t odk_major_version); -void ODK_SetDefaultReleaseResponseParams(ODK_ReleaseResponseParams* params, - uint32_t odk_major_version); +void ODK_SetDefaultReleaseResponseParams(ODK_ReleaseResponseParams* params); void ODK_SetDefaultRenewalResponseParams(ODK_RenewalResponseParams* params); void ODK_SetDefaultProvisioningResponseParams( ODK_ProvisioningResponseParams* params, uint32_t odk_major_version);