OEMCrypto v16.2

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

This is the unit tests, reference code, and documentation for
OEMCrypto v16.2. Backwards compatibility should work for a v15
OEMCrypto.

Some review comments will be addressed in future CLs.

Bug: 141247171
Test: Unit tests
Test: Media GTS tests on bonito
Change-Id: I9d427c07580e180c0a4cfdc4a68f538d351c0ddd
This commit is contained in:
Fred Gylys-Colwell
2020-01-18 10:18:50 -08:00
parent 7665614b2e
commit db2050dff1
62 changed files with 2947 additions and 2286 deletions

View File

@@ -1,4 +1,6 @@
// 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.
// ----------------------------------------------------------------
// Builds libwv_odk.a, The ODK Library (libwv_odk) is used by
@@ -16,6 +18,7 @@ cc_library_static {
"src/odk_overflow.c",
"src/odk_serialize.c",
"src/odk_timer.c",
"src/odk_util.c",
"src/serialization_base.c",
],
proprietary: true,

View File

@@ -106,6 +106,21 @@ typedef enum OEMCrypto_Usage_Entry_Status {
kInactiveUnused = 4,
} OEMCrypto_Usage_Entry_Status;
/*
* OEMCrypto_LicenseType is used in the license message to indicate if the key
* objects are for content keys, or for entitlement keys.
*/
typedef enum OEMCrypto_LicenseType {
OEMCrypto_ContentLicense = 0,
OEMCrypto_EntitlementLicense = 1
} OEMCrypto_LicenseType;
/* Private key type used in the provisioning response. */
typedef enum OEMCrypto_PrivateKeyType {
OEMCrypto_RSA_Private_Key = 0,
OEMCrypto_ECC_Private_Key = 1,
} OEMCrypto_PrivateKeyType;
/*
* OEMCrypto_Substring
*

View File

@@ -30,10 +30,12 @@ namespace serialize {
* Parameters:
* [in] parsed_lic
* [in] core_request
* [in] core_request_sha256
* [out] oemcrypto_core_message
*/
bool CreateCoreLicenseResponse(const ODK_ParsedLicense& parsed_lic,
const ODK_LicenseRequest& core_request,
const std::string& core_request_sha256,
std::string* oemcrypto_core_message);
/**
@@ -41,9 +43,11 @@ bool CreateCoreLicenseResponse(const ODK_ParsedLicense& parsed_lic,
*
* Parameters:
* [in] core_request
* [in] renewal_duration_seconds
* [out] oemcrypto_core_message
*/
bool CreateCoreRenewalResponse(const ODK_RenewalRequest& core_request,
uint64_t renewal_duration_seconds,
std::string* oemcrypto_core_message);
/**

View File

@@ -30,13 +30,16 @@ namespace serialize {
*
* Parameters:
* [in] serialized_license
* serialized video_widevine::License
* [in] core_request
* [out] oemcrypto_core_message
serialized video_widevine::License
* [in] core_request oemcrypto core message from request.
* [in] core_request_sha256 - hash of serialized core request.
* [in] nonce_required - if the device should require a nonce match.
* [out] oemcrypto_core_message - the serialized oemcrypto core response.
*/
bool CreateCoreLicenseResponseFromProto(const std::string& serialized_license,
const ODK_LicenseRequest& core_request,
const std::string& core_request_sha256,
const bool nonce_required,
std::string* oemcrypto_core_message);
/**

View File

@@ -64,7 +64,8 @@ namespace oemcrypto_core_message {
* Input structure for CreateCoreLicenseResponse
*/
struct ODK_LicenseRequest {
uint32_t api_version;
uint16_t api_minor_version;
uint16_t api_major_version;
uint32_t nonce;
uint32_t session_id;
};
@@ -74,7 +75,8 @@ struct ODK_LicenseRequest {
* Input structure for CreateCoreRenewalResponse
*/
struct ODK_RenewalRequest {
uint32_t api_version;
uint16_t api_minor_version;
uint16_t api_major_version;
uint32_t nonce;
uint32_t session_id;
uint64_t playback_time_seconds;
@@ -85,7 +87,8 @@ struct ODK_RenewalRequest {
* Input structure for CreateCoreProvisioningResponse
*/
struct ODK_ProvisioningRequest {
uint32_t api_version;
uint16_t api_minor_version;
uint16_t api_major_version;
uint32_t nonce;
uint32_t session_id;
std::string device_id;

View File

@@ -65,7 +65,7 @@ extern "C" {
* [out] timer_limits: the session's timer limits.
* [out] clock_values: the session's clock values.
* [out] nonce_values: the session's ODK nonce values.
* [in] api_version: the API version of OEMCrypto.
* [in] api_major_version: the API version of OEMCrypto.
* [in] session_id: the session id of the newly created session.
*
* Returns:
@@ -78,7 +78,7 @@ extern "C" {
OEMCryptoResult ODK_InitializeSessionValues(ODK_TimerLimits* timer_limits,
ODK_ClockValues* clock_values,
ODK_NonceValues* nonce_values,
uint32_t api_version,
uint32_t api_major_version,
uint32_t session_id);
/*
@@ -127,7 +127,9 @@ OEMCryptoResult ODK_InitializeClockValues(ODK_ClockValues* clock_values,
*
* Description:
* This function sets the values in the clock_values structure. It shall be
* called from OEMCrypto_LoadUsageEntry.
* called from OEMCrypto_LoadUsageEntry. When a usage entry from a v15 or
* earlier license is loaded, the value time_of_license_loaded shall be used
* in place of time_of_license_signed.
*
* Parameters:
* [in/out] clock_values: the session's clock data.
@@ -231,7 +233,7 @@ OEMCryptoResult ODK_UpdateLastPlaybackTime(uint64_t system_time_seconds,
*
* Description:
* This function modifies the session's clock values to indicate that the
* license has been deactiviated. It shall be called from
* license has been deactivated. It shall be called from
* OEMCrypto_DeactivateUsageEntry
*
* Parameters:
@@ -256,8 +258,8 @@ OEMCryptoResult ODK_DeactivateUsageEntry(ODK_ClockValues* clock_values);
*
* This shall be called by OEMCrypto from OEMCrypto_PrepAndSignLicenseRequest.
*
* NOTE: if message pointer is null and/or input core_message_size is zero,
* this function returns OEMCrypto_ERROR_SHORT_BUFFER and sets output
* NOTE: if the message pointer is null and/or input core_message_size is
* zero, this function returns OEMCrypto_ERROR_SHORT_BUFFER and sets output
* core_message_size to the size needed.
*
* Parameters:
@@ -271,7 +273,7 @@ OEMCryptoResult ODK_DeactivateUsageEntry(ODK_ClockValues* clock_values);
*
* Returns:
* OEMCrypto_SUCCESS
* OEMCrypto_ERROR_SHORT_BUFFER if core_message_size is too small
* OEMCrypto_ERROR_SHORT_BUFFER: core_message_size is too small
* OEMCrypto_ERROR_INVALID_CONTEXT
*
* Version:
@@ -292,8 +294,14 @@ OEMCryptoResult ODK_PrepareCoreLicenseRequest(
*
* This shall be called by OEMCrypto from OEMCrypto_PrepAndSignRenewalRequest.
*
* NOTE: if message pointer is null and/or input core_message_size is zero,
* this function returns OEMCrypto_ERROR_SHORT_BUFFER and sets output
* If status in clock_values indicates that a license has not been loaded,
* then this is a license release. The ODK library will change the value of
* nonce_values.api_major_version to 15. This will make
* OEMCrypto_PrepAndSignRenewalRequest sign just the message body, as it does
* for all legacy licenses.
*
* NOTE: if the message pointer is null and/or input core_message_size is
* zero, this function returns OEMCrypto_ERROR_SHORT_BUFFER and sets output
* core_message_size to the size needed.
*
* Parameters:
@@ -303,31 +311,33 @@ OEMCryptoResult ODK_PrepareCoreLicenseRequest(
* [in/out] core_message_size: length of the core message at the beginning of
* the message. (in) size of buffer reserved for the core message, in
* bytes. (out) actual length of the core message, in bytes.
* [in] nonce_values: pointer to the session's nonce data.
* [in] clock_values: the session's clock values.
* [in/out] nonce_values: pointer to the session's nonce data.
* [in/out] clock_values: the session's clock values.
* [in] system_time_seconds: the current time on OEMCrypto's clock, in
* seconds.
*
* Returns:
* OEMCrypto_SUCCESS
* OEMCrypto_ERROR_SHORT_BUFFER if core_message_size is too small
* OEMCrypto_ERROR_SHORT_BUFFER: core_message_size is too small
* OEMCrypto_ERROR_INVALID_CONTEXT
*
* Version:
* This method is new in version 16 of the API.
*/
OEMCryptoResult ODK_PrepareCoreRenewalRequest(
uint8_t* message, size_t message_length, size_t* core_message_size,
const ODK_NonceValues* nonce_values, ODK_ClockValues* clock_values,
uint64_t system_time_seconds);
OEMCryptoResult ODK_PrepareCoreRenewalRequest(uint8_t* message,
size_t message_length,
size_t* core_message_size,
ODK_NonceValues* nonce_values,
ODK_ClockValues* clock_values,
uint64_t system_time_seconds);
/*
* ODK_PrepareCoreProvisioningRequest
*
* Description:
* Modifies the message to include a core provisioning request at the
* beginning of the message buffer. The values in nonce_values, clock_values
* and system_time_seconds are used to populate the message.
* beginning of the message buffer. The values in nonce_values are used to
* populate the message.
*
* This shall be called by OEMCrypto from
* OEMCrypto_PrepAndSignProvisioningRequest.
@@ -336,8 +346,8 @@ OEMCryptoResult ODK_PrepareCoreRenewalRequest(
* OEMCrypto_GetDeviceID. The device ID shall be unique to the device, and
* stable across reboots and factory resets for an L1 device.
*
* NOTE: if message pointer is null and/or input core_message_size is zero,
* this function returns OEMCrypto_ERROR_SHORT_BUFFER and sets output
* NOTE: if the message pointer is null and/or input core_message_size is
* zero, this function returns OEMCrypto_ERROR_SHORT_BUFFER and sets output
* core_message_size to the size needed.
*
* Parameters:
@@ -356,7 +366,7 @@ OEMCryptoResult ODK_PrepareCoreRenewalRequest(
*
* Returns:
* OEMCrypto_SUCCESS
* OEMCrypto_ERROR_SHORT_BUFFER if core_message_size is too small
* OEMCrypto_ERROR_SHORT_BUFFER: core_message_size is too small
* OEMCrypto_ERROR_INVALID_CONTEXT
*
* Version:
@@ -373,7 +383,7 @@ OEMCryptoResult ODK_PrepareCoreProvisioningRequest(
* Description:
* This function sets all limits in the timer_limits struct to the
* key_duration and initializes the other values. The field
* nonce_values.api_level will be set to 15. It shall be called from
* nonce_values.api_major_version will be set to 15. It shall be called from
* OEMCrypto_LoadKeys when loading a legacy license.
*
* Parameters:
@@ -403,8 +413,12 @@ OEMCryptoResult ODK_InitializeV15Values(ODK_TimerLimits* timer_limits,
* ODK_RefreshV15Values
*
* Description:
* This function updates the clock_values as needed if the renewal is
* accepted. The field nonce_values.api_level is verified to be 15.
* This function updates the clock_values as needed if a v15 renewal is
* accepted. The field nonce_values.api_major_version is verified to be 15.
*
* This is called from OEMCrypto_RefreshKeys for a valid license renewal.
* OEMCrypto shall pass in the current system time, and the key duration from
* the first object in the OEMCrypto_KeyRefreshObject.
*
* Parameters:
* [in] timer_limits: The session's timer limits.
@@ -412,6 +426,8 @@ OEMCryptoResult ODK_InitializeV15Values(ODK_TimerLimits* timer_limits,
* [in] nonce_values: The session's ODK nonce values.
* [in] system_time_seconds: The current time on the system clock, as
* described in the document "License Duration and Renewal".
* [in] new_key_duration: The duration from the first
* OEMCrypto_KeyRefreshObject in key_array.
* [out] timer_value: set to the new timer value. Only used if the return
* value is ODK_SET_TIMER. This must be non-null if OEMCrypto uses a
* hardware timer.
@@ -432,27 +448,43 @@ OEMCryptoResult ODK_RefreshV15Values(const ODK_TimerLimits* timer_limits,
ODK_ClockValues* clock_values,
const ODK_NonceValues* nonce_values,
uint64_t system_time_seconds,
uint32_t new_key_duration,
uint64_t* timer_value);
/*
* ODK_ParseLicense
*
* Description:
* The function ODK_ParseLicense will parse the message and verify
*
* 1. Either the nonce matches the one passed in or the license does not
* require a nonce.
* 2. The API version of the message matches.
* 3. The session id matches.
* The function ODK_ParseLicense will parse the message and set each
* substring pointer to point to a location in the message body. The message
* body is the buffer starting at message + core_message_length with size
* message_length - core_message_length.
* The function ODK_ParseLicense will parse the message and verify fields in
* the message.
*
* If the message does not parse correctly, ODK_VerifyAndParseLicense will
* return ODK_ERROR_CORE_MESSAGE that OEMCrypto should return to the CDM
* layer above. If the api in the message is larger than 16, then
* ODK_UNSUPPORTED_API is returned.
* layer above.
*
* If the API in the message is not 16, then ODK_UNSUPPORTED_API is returned.
*
* If initial_license_load is true, and nonce_required in the license is
* true, then the ODK library shall verify that nonce_values->nonce and
* nonce_values->session_id are the same as those in the message. If
* verification fails, then it shall return OEMCrypto_ERROR_INVALID_NONCE.
*
* If initial_license_load is false, and nonce_required is true, then
* ODK_ParseLicense will set the values in nonce_values from those in the
* message.
*
* The function ODK_ParseLicense will verify that each substring points to a
* location in the message body. The message body is the buffer starting at
* message + core_message_length with size message_length -
* core_message_length.
*
* If initial_license_load is true, then ODK_ParseLicense shall verify that
* the parameter request_hash matches request_hash in the parsed license. If
* verification fails, then it shall return ODK_ERROR_CORE_MESSAGE. This was
* computed by OEMCrypto when the license was requested.
*
* If usage_entry_present is true, then ODK_ParseLicense shall verify that
* the pst in the license has a nonzero length.
*
* Parameters:
* [in] message: pointer to the message buffer.
@@ -463,6 +495,8 @@ OEMCryptoResult ODK_RefreshV15Values(const ODK_TimerLimits* timer_limits,
* false when called for OEMCrypto_ReloadLicense.
* [in] usage_entry_present: true if the session has a new usage entry
* associated with it created via OEMCrypto_CreateNewUsageEntry.
* [in] request_hash: the hash of the license request core message. This was
* computed by OEMCrypto when the license request was signed.
* [in/out] timer_limits: The session's timer limits. These will be updated.
* [in/out] clock_values: The session's clock values. These will be updated.
* [in/out] nonce_values: The session's nonce values. These will be updated.
@@ -470,7 +504,7 @@ OEMCryptoResult ODK_RefreshV15Values(const ODK_TimerLimits* timer_limits,
*
* Returns:
* OEMCrypto_SUCCESS
* ODK_ERROR_CORE_MESSAGE if the message did not parse correctly, or there
* ODK_ERROR_CORE_MESSAGE: if the message did not parse correctly, or there
* were other incorrect values. An error should be returned to the CDM
* layer.
* ODK_UNSUPPORTED_API
@@ -490,9 +524,12 @@ OEMCryptoResult ODK_ParseLicense(
* ODK_ParseRenewal
*
* Description:
* The function ODK_ParseRenewal will parse the message and verify that the
* nonce values match those in the license. If the message does not parse
* correctly, an error of ODK_ERROR_CORE_MESSAGE is returned.
* The function ODK_ParseRenewal will parse the message and verify its
* contents. If the message does not parse correctly, an error of
* ODK_ERROR_CORE_MESSAGE is returned.
*
* ODK_ParseRenewal shall verify that all fields in nonce_values match those
* in the license. Otherwise it shall return OEMCrypto_ERROR_INVALID_NONCE.
*
* After parsing the message, this function updates the clock_values based on
* the timer_limits and the current system time. If playback may not
@@ -504,8 +541,8 @@ OEMCryptoResult ODK_ParseLicense(
* ODK_DISABLE_TIMER, then playback time is not limited.
*
* If OEMCrypto uses a hardware timer, and this function returns
* ODK_SET_TIMER, then the timer should be set to the value pointed to by
* timer_value.
* ODK_SET_TIMER, then OEMCrypto shall set the timer to the value pointed to
* by timer_value.
*
* Parameters:
* [in] message: pointer to the message buffer.
@@ -522,15 +559,16 @@ OEMCryptoResult ODK_ParseLicense(
* hardware timer.
*
* Returns:
* ODK_ERROR_CORE_MESSAGE if the message did not parse correctly, or there
* were other incorrect values. An error should be returned to the CDM
* layer.
* 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.
* ODK_SET_TIMER: Success. The timer should be reset to the specified timer
* value.
* ODK_DISABLE_TIMER: Success, but disable timer. Unlimited playback is
* allowed.
* ODK_TIMER_EXPIRED: Set timer as disabled. Playback is not allowed.
* ODK_UNSUPPORTED_API
* ODK_STALE_RENEWAL: This renewal is not the most recently signed. It is
* rejected.
* OEMCrypto_ERROR_INVALID_NONCE
*
* Version:
@@ -551,14 +589,21 @@ OEMCryptoResult ODK_ParseRenewal(const uint8_t* message, size_t message_length,
* The function ODK_ParseProvisioning will parse the message and verify the
* nonce values match those in the license.
*
* The function ODK_ParseProvisioning will parse the message and set each
* substring pointer to point to a location in the message body. The message
* body is the buffer starting at message + core_message_length with size
* message_length - core_message_length.
*
* If the message does not parse correctly, ODK_ParseProvisioning will return
* an error that OEMCrypto should return to the CDM layer above.
*
* If the API in the message is larger than 16, then ODK_UNSUPPORTED_API is
* returned.
*
* ODK_ParseProvisioning shall verify that nonce_values->nonce and
* nonce_values->session_id are the same as those in the message. Otherwise
* it shall return OEMCrypto_ERROR_INVALID_NONCE.
*
* The function ODK_ParseProvisioning will verify that each substring points
* to a location in the message body. The message body is the buffer starting
* at message + core_message_length with size message_length -
* core_message_length.
*
* Parameters:
* [in] message: pointer to the message buffer.
* [in] message_length: length of the entire message buffer.
@@ -572,9 +617,8 @@ OEMCryptoResult ODK_ParseRenewal(const uint8_t* message, size_t message_length,
*
* Returns:
* OEMCrypto_SUCCESS
* ODK_ERROR_CORE_MESSAGE if the message did not parse correctly, or there
* were other incorrect values. An error should be returned to the CDM
* layer.
* 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.
* ODK_UNSUPPORTED_API
* OEMCrypto_ERROR_INVALID_NONCE
*

View File

@@ -1,27 +0,0 @@
/*
* 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 ODK_ASSERT_H_
#define ODK_ASSERT_H_
#ifdef __cplusplus
extern "C" {
#endif
#if (__STDC_VERSION__ >= 201112L)
# include <assert.h>
# define odk_static_assert static_assert
#else
# define odk_static_assert(msg, e) \
enum { odk_static_assert = 1 / (!!((msg) && (e))) };
#endif
#ifdef __cplusplus
}
#endif
#endif /* ODK_ASSERT_H_ */

View File

@@ -1,33 +0,0 @@
/*
* 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 ODK_OVERFLOW_H_
#define ODK_OVERFLOW_H_
#ifdef __cplusplus
extern "C" {
#endif
#ifndef __has_builtin
# define __has_builtin(x) 0
#endif
#if (defined(__GNUC__) && __GNUC__ >= 5) || \
__has_builtin(__builtin_add_overflow)
# define odk_sub_overflow_u64 __builtin_sub_overflow
# define odk_add_overflow_u64 __builtin_add_overflow
# define odk_add_overflow_ux __builtin_add_overflow
#else
int odk_sub_overflow_u64(uint64_t a, uint64_t b, uint64_t* c);
int odk_add_overflow_u64(uint64_t a, uint64_t b, uint64_t* c);
int odk_add_overflow_ux(size_t a, size_t b, size_t* c);
#endif
#ifdef __cplusplus
}
#endif
#endif /* ODK_OVERFLOW_H_ */

View File

@@ -1,44 +0,0 @@
/*
* 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.
*/
/*
* This code is auto-generated, do not edit
*/
#ifndef ODKITEE_SERIALIZER_H_
#define ODKITEE_SERIALIZER_H_
#include "odk_structs_priv.h"
#include "serialization_base.h"
#ifdef __cplusplus
extern "C" {
#endif
/* odk pack */
void Pack_ODK_PreparedLicense(Message* msg, ODK_PreparedLicense const* obj);
void Pack_ODK_RenewalMessage(Message* msg, ODK_RenewalMessage const* obj);
void Pack_ODK_ProvisioningMessage(Message* msg,
ODK_ProvisioningMessage const* obj);
/* odk unpack */
void Unpack_ODK_LicenseResponse(Message* msg, ODK_LicenseResponse* obj);
void Unpack_ODK_RenewalMessage(Message* msg, ODK_RenewalMessage* obj);
void Unpack_ODK_ProvisioningResponse(Message* msg,
ODK_ProvisioningResponse* obj);
/* kdo pack */
void Pack_ODK_LicenseResponse(Message* msg, ODK_LicenseResponse const* obj);
void Pack_ODK_ProvisioningResponse(Message* msg,
ODK_ProvisioningResponse const* obj);
/* kdo unpack */
void Unpack_ODK_PreparedLicense(Message* msg, ODK_PreparedLicense* obj);
void Unpack_ODK_ProvisioningMessage(Message* msg, ODK_ProvisioningMessage* obj);
#ifdef __cplusplus
} // extern "C"
#endif
#endif /* ODKITEE_SERIALIZER_H_ */

View File

@@ -8,63 +8,103 @@
#include <stdint.h>
#include "OEMCryptoCENCCommon.h"
#include "odk_target.h"
#define ODK_MAX_NUM_KEYS 32
/* The version of this library. */
#define ODK_MAJOR_VERSION 16
#define ODK_MINOR_VERSION 2
/* Some useful constants. */
#define ODK_DEVICE_ID_LEN_MAX 64
#define ODK_SHA256_HASH_SIZE 32
/*
* ODK_TimerLimits is filled out by the function ODK_ParseLicense.
* ODK_TimerLimits Structure
*
* The fields in this structure are defined in the core license response
* message. This structure should be kept as part of the session and used
* when calling the ODK timer functions described in the document "License
* Duration and Renewal" distributed as part of the OEMCrypto v16 design.
* Description:
* Timer limits are specified in a license and are used to determine when
* playback is allowed. See the document "License Duration and Renewal" for a
* discussion on the time restrictions that may be placed on a license. The
* fields in this structure are directly related to the fields in the core
* license message. The fields are set when OEMCrypto calls the function
* ODK_ParseLicense or ODK_InitializeV15Values.
*
* Fields:
* soft_enforce_rental_duration: A boolean controlling the soft or hard
* enforcement of rental duration.
* soft_enforce_playback_duration: A boolean controlling the soft or hard
* enforcement of playback duration.
* earliest_playback_start_seconds: The earliest time that the first playback
* is allowed. Measured in seconds since the license request was signed. For
* most use cases, this is zero.
* rental_duration_seconds: Window of time for the allowed first playback.
* Measured in seconds since the earliest playback start. If
* soft_enforce_rental_duration is true, this applies only to the first
* playback. If soft_enforce_rental_duration is false, then this restricts
* any playback. A value of zero means no limit.
* total_playback_duration_seconds: Window of time for allowed playback.
* Measured in seconds since the first playback start. If
* soft_enforce_playback_duration is true, this applies only to the start of
* playback for any session. If soft_enforce_playback_duration is false, then
* this restricts any playback. A value of zero means no limit.
* initial_renewal_duration_seconds: Window of time for allowed playback.
* Measured in seconds since the first playback start. This value is only
* used to start the renewal timer. After a renewal message is loaded, the
* timer will be reset. A value of zero means no limit.
*
* Version:
* This struct changed in API version 16.2.
*/
typedef struct {
uint32_t /*boolean*/ soft_expiry;
uint64_t earliest_playback_start_seconds; /* since license signed. */
uint64_t latest_playback_start_seconds; /* since license signed. */
uint64_t initial_playback_duration_seconds; /* since playback start. */
uint64_t renewal_playback_duration_seconds; /* since renewal signed. */
uint64_t license_duration_seconds; /* since license signed. */
bool soft_enforce_rental_duration;
bool soft_enforce_playback_duration;
uint64_t earliest_playback_start_seconds;
uint64_t rental_duration_seconds;
uint64_t total_playback_duration_seconds;
uint64_t initial_renewal_duration_seconds;
} ODK_TimerLimits;
/*
* ODK_ParsedLicense holds fields from the core license response.
*/
typedef struct {
OEMCrypto_Substring enc_mac_keys_iv;
OEMCrypto_Substring enc_mac_keys;
OEMCrypto_Substring pst;
OEMCrypto_Substring srm_restriction_data;
uint32_t /* OEMCrypto_LicenseType */ license_type;
uint32_t nonce_required;
ODK_TimerLimits timer_limits;
uint8_t request_hash[ODK_SHA256_HASH_SIZE];
uint32_t key_array_length; /* num_keys */
OEMCrypto_KeyObject key_array[ODK_MAX_NUM_KEYS];
} ODK_ParsedLicense;
/*
* ODK_ParsedProvisioning holds fields from the core provisioning response.
*/
typedef struct {
uint32_t key_type;
OEMCrypto_Substring enc_private_key;
OEMCrypto_Substring enc_private_key_iv;
OEMCrypto_Substring encrypted_message_key; /* Used for Prov 3.0 */
} ODK_ParsedProvisioning;
/*
* ODK_ClockValues keeps information about a session's current clock values
* and timers.
* ODK_ClockValues Structure
*
* Most of the fields in this structure are saved in the usage entry for each
* session. This structure should be initialized when a usage entry is
* created or loaded, and should be used to save a usage entry. It is
* updated using ODK functions listed in the document "License Duration and
* Renewal". The time values are based on OEMCryptos system clock.
* Description:
* Clock values are modified when decryption occurs or when a renewal is
* processed. They are used to track the current status of the license --
* i.e. has playback started? When does the timer expire? See the section
* "Complete ODK API" of the document "Widevine Core Message Serialization"
* for a complete list of all fields in this structure. Most of these values
* shall be saved with the usage entry.
*
* All times are in seconds. Most of the fields in this structure are saved
* in the usage entry. This structure should be initialized when a usage
* entry is created or loaded, and should be used to save a usage entry. It
* is updated using the ODK functions listed below. The time values are based
* on OEMCrypto's system clock, as described in the document "License
* Duration and Renewal".
*
* Fields:
* time_of_license_signed: Time that the license request was signed, based on
* OEMCrypto's system clock. This value shall be stored and reloaded with
* usage entry as time_of_license_received.
* time_of_first_decrypt: Time of the first decrypt or call select key, based
* on OEMCrypto's system clock. This is 0 if the license has not been used to
* decrypt any data. This value shall be stored and reloaded with usage entry.
* time_of_last_decrypt: Time of the most recent decrypt call, based on
* OEMCrypto's system clock. This value shall be stored and reloaded with
* usage entry.
* time_of_renewal_request: Time of the most recent renewal request, based on
* OEMCrypto's system clock. This is used to verify that a renewal is not
* stale.
* time_when_timer_expires: Time that the current timer expires, based on
* OEMCrypto's system clock. If the timer is active, this is used by the ODK
* library to determine if it has expired.
* timer_status: Used internally by the ODK library to indicate the current
* timer status.
* status: The license or usage entry status. This value shall be stored and
* reloaded with usage entry.
*
* Version:
* This struct changed in API version 16.2.
*/
typedef struct {
uint64_t time_of_license_signed;
@@ -77,20 +117,99 @@ typedef struct {
} ODK_ClockValues;
/*
* ODK_NonceValues are used to match a license or provisioning request to a
* license or provisioning response. For this reason, the api_version might be
* lower than that supported by OEMCrypto. The api_version matches the version
* of the license. Similarly the nonce and session_id match the session that
* generated the license request. For an offline license, these might not match
* the session that is loading the license. We use the nonce to prevent a
* license from being replayed. By also including a session_id in the license
* request and license response, we prevent an attack using the birthday paradox
* to generate nonce collisions on a single device.
* ODK_NonceValues Structure
*
* Description:
* Nonce values are used to match a license or provisioning request to a
* license or provisioning response. They are also used to match a renewal
* request and response to a license. For this reason, the api_version might
* be lower than that supported by OEMCrypto. The api_version matches the
* version of the license. Similarly the nonce and session_id match the
* session that generated the license request. For an offline license, these
* might not match the session that is loading the license. We use the nonce
* to prevent a license from being replayed. By also including a session_id
* in the license request and license response, we prevent an attack using
* the birthday paradox to generate nonce collisions on a single device.
*
* Fields:
* api_major_version: the API version of the license. This is initialized to
* the API version of the ODK library, but may be lower.
* api_minor_version: the minor version of the ODK library. This is used by
* the server to verify that device is not using an obsolete version of the
* ODK library.
* nonce: a randomly generated number used to prevent replay attacks.
* session_id: the session id of the session which signed the license or
* provisioning request. It is used to prevent replay attacks from one
* session to another.
*
* Version:
* This struct changed in API version 16.2.
*/
typedef struct {
uint32_t api_version;
uint16_t api_minor_version;
uint16_t api_major_version;
uint32_t nonce;
uint32_t session_id;
} ODK_NonceValues;
/*
* ODK_ParsedLicense Structure
*
* Description:
* The parsed license structure contains information from the license
* message. The function ODK_ParseLicense will fill in the fields of this
* message. All substrings are contained within the message body.
*
* Fields:
* enc_mac_keys_iv: IV for decrypting new mac_key. Size is 128 bits.
* enc_mac_keys: encrypted mac_keys for generating new mac_keys. Size is 512
* bits.
* pst: the Provider Session Token.
* srm_restriction_data: optional data specifying the minimum SRM version.
* license_type: specifies if the license contains content keys or
* entitlement keys.
* nonce_required: indicates if the license requires a nonce.
* timer_limits: time limits of the for the license.
* key_array_length: number of keys present.
* key_array: set of keys to be installed.
*
* Version:
* This struct changed in API version 16.2.
*/
typedef struct {
OEMCrypto_Substring enc_mac_keys_iv;
OEMCrypto_Substring enc_mac_keys;
OEMCrypto_Substring pst;
OEMCrypto_Substring srm_restriction_data;
OEMCrypto_LicenseType license_type;
bool nonce_required;
ODK_TimerLimits timer_limits;
uint32_t key_array_length;
OEMCrypto_KeyObject key_array[ODK_MAX_NUM_KEYS];
} ODK_ParsedLicense;
/*
* ODK_ParsedProvisioning Structure
*
* Description:
* The parsed provisioning structure contains information from the license
* message. The function ODK_ParseProvisioning will fill in the fields of
* this message. All substrings are contained within the message body.
*
* Fields:
* key_type: indicates if this key is an RSA or ECC private key.
* enc_private_key: encrypted private key for the DRM certificate.
* enc_private_key_iv: IV for decrypting new private key. Size is 128 bits.
* encrypted_message_key: used for provisioning 3.0 to derive keys.
*
* Version:
* This struct changed in API version 16.2.
*/
typedef struct {
OEMCrypto_PrivateKeyType key_type;
OEMCrypto_Substring enc_private_key;
OEMCrypto_Substring enc_private_key_iv;
OEMCrypto_Substring encrypted_message_key; /* Used for Prov 3.0 */
} ODK_ParsedProvisioning;
#endif /* WIDEVINE_ODK_INCLUDE_ODK_STRUCTS_H_ */

View File

@@ -1,54 +0,0 @@
/*
* 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 ODK_STRUCTS_PRIV_H_
#define ODK_STRUCTS_PRIV_H_
#include <stdint.h>
#include "OEMCryptoCENCCommon.h"
#include "odk_structs.h"
typedef enum {
ODK_License_Request_Type = 1,
ODK_License_Response_Type = 2,
ODK_Renewal_Request_Type = 3,
ODK_Renewal_Response_Type = 4,
ODK_Provisioning_Request_Type = 5,
ODK_Provisioning_Response_Type = 6,
} ODK_MessageType;
typedef struct {
uint32_t message_type;
uint32_t message_length;
ODK_NonceValues nonce_values;
} ODK_CoreMessage;
typedef struct {
ODK_CoreMessage core_message;
} ODK_PreparedLicense;
typedef struct {
ODK_CoreMessage core_message;
uint64_t playback_time;
} ODK_RenewalMessage;
typedef struct {
ODK_CoreMessage core_message;
uint32_t device_id_length;
uint8_t device_id[ODK_DEVICE_ID_LEN_MAX];
} ODK_ProvisioningMessage;
typedef struct {
ODK_CoreMessage core_message;
ODK_ParsedLicense* parsed_license;
} ODK_LicenseResponse;
typedef struct {
ODK_ProvisioningMessage core_provisioning;
ODK_ParsedProvisioning* parsed_provisioning;
} ODK_ProvisioningResponse;
#endif // ODK_STRUCTS_PRIV_H_

View File

@@ -0,0 +1,13 @@
/* Copyright 2019 Google LLC. All rights reserved. This file is distributed */
/* under the Widevine Master License Agreement. */
/* Partners are expected to edit this file to support target specific code */
/* and limits. */
#ifndef WIDEVINE_ODK_INCLUDE_ODK_TARGET_H_
#define WIDEVINE_ODK_INCLUDE_ODK_TARGET_H_
/* Maximum number of keys can be modified to suit target's resource tier. */
#define ODK_MAX_NUM_KEYS 32
#endif /* WIDEVINE_ODK_INCLUDE_ODK_TARGET_H_ */

View File

@@ -1,91 +0,0 @@
/*
* 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 ODKITEE_SERIALIZATION_BASE_H_
#define ODKITEE_SERIALIZATION_BASE_H_
#ifdef __cplusplus
extern "C" {
#endif
#include <stddef.h>
#include <stdint.h>
#include "OEMCryptoCENCCommon.h"
#define SIZE_OF_MESSAGE_STRUCT 64
/*
* Description:
* Point |msg| to stack-array |blk|.
* |blk| is guaranteed large enough to hold a |Message| struct.
* |blk| cannot be used in the same scope as a variable name.
* |msg| points to valid memory in the same scope |AllocateMessage| is used.
* Parameters:
* msg: pointer to pointer to |Message| struct
* blk: variable name for stack-array
*/
#define AllocateMessage(msg, blk) \
uint8_t blk[SIZE_OF_MESSAGE_STRUCT]; \
*(msg) = (Message*)(blk);
typedef struct _Message Message;
bool ValidMessage(Message* message);
void Pack_uint32_t(Message* message, const uint32_t* value);
void Pack_uint64_t(Message* message, const uint64_t* value);
void PackArray(Message* message, const uint8_t* base, size_t size);
void Pack_OEMCrypto_Substring(Message* msg, const OEMCrypto_Substring* obj);
void Unpack_uint32_t(Message* message, uint32_t* value);
void Unpack_uint64_t(Message* message, uint64_t* value);
void UnpackArray(Message* message, uint8_t* base, 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
* that the buffer remains allocated for the lifetime of the message.
*/
Message* CreateMessage(uint8_t* buffer, size_t buffer_size);
/*
* Initialize a message structure to reference a separate buffer. The caller
* is responsible for ensuring that the buffer remains allocated for the
* lifetime of the message.
*/
void InitMessage(Message* message, uint8_t* buffer, size_t capacity);
/*
* Reset an existing the message to an empty state
*/
void ResetMessage(Message* message);
uint8_t* GetBase(Message* message);
size_t GetCapacity(Message* message);
size_t GetSize(Message* message);
void SetSize(Message* message, size_t size);
MessageStatus GetStatus(Message* message);
void SetStatus(Message* message, MessageStatus status);
size_t GetOffset(Message* message);
size_t SizeOfMessageStruct();
#ifdef __cplusplus
} // extern "C"
#endif
#endif // ODKITEE_SERIALIZATION_BASE_H_

View File

@@ -1,172 +0,0 @@
/*
* 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.
*/
// clang-format off
/*********************************************************************
* oec_util.h
*
* OEMCrypto v16 Core Message Serialization library counterpart (a.k.a. KDO)
*
* For Widevine Modular DRM, there are six message types between a server and
* a client device: license request and response, provisioning request and
* response, and renewal request and response.
*
* In OEMCrypto v15 and earlier, messages from the server were parsed by the
* CDM layer above OEMCrypto; the CDM in turn gave OEMCrypto a collection of
* pointers to protected data within the message. However, the pointers
* themselves were not signed by the server.
*
* Starting from OEMCrypto v16, all fields used by OEMCrypto in each of these
* messages have been identified in the document "Widevine Core Message
* Serialization". These fields are called the core of the message. Core
* message fields are (de)serialized using the ODK, a C library provided by
* Widevine. OEMCrypto will parse and verify the core of the message with
* help from the ODK.
*
* The KDO library is the counterpart of ODK used in the CDM & Widevine
* servers. For each message type generated by the ODK, KDO provides a
* corresponding parser. For each message type to be parsed by the ODK,
* KDO provides a corresponding writer.
*
* Table: ODK vs KDO (s: serialize; d: deserialize)
* +----------------------------------------+------------------------------------+
* | ODK | KDO |
* +---+------------------------------------+---+--------------------------------+
* | s | ODK_PrepareCoreLicenseRequest | d | ParseLicenseRequest |
* | +------------------------------------+ +--------------------------------+
* | | ODK_PrepareCoreRenewalRequest | | ParseRenewalRequest |
* | +------------------------------------+ +--------------------------------+
* | | ODK_PrepareCoreProvisioningRequest | | ParseProvisioningRequest |
* +---+------------------------------------+---+--------------------------------+
* | d | ODK_ParseLicense | s | CreateCoreLicenseResponse |
* | +------------------------------------+ +--------------------------------+
* | | ODK_ParseRenewal | | CreateCoreRenewalResponse |
* | +------------------------------------+ +--------------------------------+
* | | ODK_ParseProvisioning | | CreateCoreProvisioningResponse |
* +---+------------------------------------+---+--------------------------------+
*
*********************************************************************/
// clang-format on
#ifndef OEC_UTIL_H_
#define OEC_UTIL_H_
#include <cstdint>
#include <string>
#include "odk_structs.h"
using namespace std;
namespace oec_util {
// @ input/output structs
/**
* Output structure for ParseLicenseRequest
* Input structure for CreateCoreLicenseResponse
*/
struct ODK_LicenseRequest {
uint32_t api_version;
uint32_t nonce;
uint32_t session_id;
};
/**
* Output structure for ParseRenewalRequest
* Input structure for CreateCoreRenewalResponse
*/
struct ODK_RenewalRequest {
uint32_t api_version;
uint32_t nonce;
uint32_t session_id;
uint64_t playback_time;
};
/**
* Output structure for ParseProvisioningRequest
* Input structure for CreateCoreProvisioningResponse
*/
struct ODK_ProvisioningRequest {
uint32_t api_version;
uint32_t nonce;
uint32_t session_id;
string device_id;
};
// @ public parse request (deserializer) functions
/**
* Counterpart (deserializer) of ODK_PrepareCoreLicenseRequest (serializer)
*
* Parameters:
* [in] oemcrypto_core_message
* [out] core_license_request
*/
bool ParseLicenseRequest(const string& oemcrypto_core_message,
ODK_LicenseRequest* core_license_request);
/**
* Counterpart (deserializer) of ODK_PrepareCoreRenewalRequest (serializer)
*
* Parameters:
* [in] oemcrypto_core_message
* [out] core_renewal_request
*/
bool ParseRenewalRequest(const string& oemcrypto_core_message,
ODK_RenewalRequest* core_renewal_request);
/**
* Counterpart (deserializer) of ODK_PrepareCoreProvisioningRequest (serializer)
*
* Parameters:
* [in] oemcrypto_core_message
* [out] core_provisioning_request
*/
bool ParseProvisioningRequest(
const string& oemcrypto_core_message,
ODK_ProvisioningRequest* core_provisioning_request);
// @ public create response (serializer) functions
/**
* Counterpart (serializer) of ODK_ParseLicense (deserializer)
* struct-input variant
*
* Parameters:
* [in] parsed_lic
* [in] core_request
* [out] oemcrypto_core_message
*/
bool CreateCoreLicenseResponse(const ODK_ParsedLicense& parsed_lic,
const ODK_LicenseRequest& core_request,
string* oemcrypto_core_message);
/**
* Counterpart (serializer) of ODK_ParseRenewal (deserializer)
*
* Parameters:
* [in] core_request
* [out] oemcrypto_core_message
*/
bool CreateCoreRenewalResponse(const ODK_RenewalRequest& core_request,
string* oemcrypto_core_message);
/**
* Counterpart (serializer) of ODK_ParseProvisioning (deserializer)
* struct-input variant
*
* Parameters:
* [in] parsed_prov
* [in] core_request
* [out] oemcrypto_core_message
*/
bool CreateCoreProvisioningResponse(const ODK_ParsedProvisioning& parsed_prov,
const ODK_ProvisioningRequest& core_request,
string* oemcrypto_core_message);
} // namespace oec_util
#endif // OEC_UTIL_H_

View File

@@ -1,59 +0,0 @@
/*
* 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.
*/
/*********************************************************************
* oec_util_proto.h
*
* These functions are an extension of those found in oec_util.h. The
* difference is that these use the license and provisioning messages
* in protobuf format to create the core message.
*********************************************************************/
#ifndef OEC_UTIL_PROTO_H_
#define OEC_UTIL_PROTO_H_
#include <cstdint>
#include <string>
#include "license_protocol.pb.h"
#include "oec_util.h"
using namespace std;
using video_widevine::License;
using video_widevine::License_KeyContainer;
namespace oec_util {
// @ public create response (serializer) functions
/**
* Counterpart (serializer) of ODK_ParseLicense (deserializer)
*
* Parameters:
* [in] license
* [in] core_request
* [out] oemcrypto_core_message
*/
bool CreateCoreLicenseResponse(const video_widevine::License& license,
const ODK_LicenseRequest& core_request,
string* oemcrypto_core_message);
/**
* Counterpart (serializer) of ODK_ParseProvisioning (deserializer)
*
* Parameters:
* [in] provisioning_response
* [in] core_request
* [out] oemcrypto_core_message
*/
bool CreateCoreProvisioningResponse(
const video_widevine::ProvisioningResponse& provisioning_response,
const ODK_ProvisioningRequest& core_request,
string* oemcrypto_core_message);
} // namespace oec_util
#endif // OEC_UTIL_PROTO_H_

View File

@@ -19,8 +19,8 @@ namespace oemcrypto_core_message {
namespace deserialize {
namespace {
const int EARLIEST_OEMCRYPTO_VERSION_WITH_ODK = 16;
const int LATEST_OEMCRYPTO_VERSION = 16;
constexpr int EARLIEST_OEMCRYPTO_VERSION_WITH_ODK = 16;
constexpr int LATEST_OEMCRYPTO_VERSION = 16;
/**
* Template for parsing requests
@@ -53,29 +53,43 @@ bool ParseRequest(uint32_t message_type,
}
const auto& core_message = prepared->core_message;
core_request->api_version = core_message.nonce_values.api_version;
core_request->api_major_version = core_message.nonce_values.api_major_version;
core_request->api_minor_version = core_message.nonce_values.api_minor_version;
core_request->nonce = core_message.nonce_values.nonce;
core_request->session_id = core_message.nonce_values.session_id;
// Verify that the minor version matches the released version for the given
// major version.
if ((core_request->api_major_version < EARLIEST_OEMCRYPTO_VERSION_WITH_ODK) ||
(core_request->api_major_version > LATEST_OEMCRYPTO_VERSION)) {
// Non existing and future versions are not supported.
return false;
} else if (core_request->api_major_version == 16) {
// For version 16, we demand a minor version of at least 2.
if (core_request->api_major_version < 2) return false;
} else {
// Other versions do not (yet) have a restriction on minor number.
}
return core_message.message_type == message_type &&
core_message.message_length == GetOffset(msg) &&
core_request->api_version >= EARLIEST_OEMCRYPTO_VERSION_WITH_ODK &&
core_request->api_version <= LATEST_OEMCRYPTO_VERSION;
core_request->api_major_version >=
EARLIEST_OEMCRYPTO_VERSION_WITH_ODK &&
core_request->api_major_version <= LATEST_OEMCRYPTO_VERSION;
}
} // namespace
bool CoreLicenseRequestFromMessage(const std::string& oemcrypto_core_message,
ODK_LicenseRequest* core_license_request) {
const auto unpacker = Unpack_ODK_PreparedLicense;
ODK_PreparedLicense prepared_license = {};
const auto unpacker = Unpack_ODK_PreparedLicenseRequest;
ODK_PreparedLicenseRequest prepared_license = {};
return ParseRequest(ODK_License_Request_Type, oemcrypto_core_message,
core_license_request, &prepared_license, unpacker);
}
bool CoreRenewalRequestFromMessage(const std::string& oemcrypto_core_message,
ODK_RenewalRequest* core_renewal_request) {
const auto unpacker = Unpack_ODK_RenewalMessage;
ODK_RenewalMessage prepared_renewal = {};
const auto unpacker = Unpack_ODK_PreparedRenewalRequest;
ODK_PreparedRenewalRequest prepared_renewal = {};
if (!ParseRequest(ODK_Renewal_Request_Type, oemcrypto_core_message,
core_renewal_request, &prepared_renewal, unpacker)) {
return false;
@@ -87,8 +101,8 @@ bool CoreRenewalRequestFromMessage(const std::string& oemcrypto_core_message,
bool CoreProvisioningRequestFromMessage(
const std::string& oemcrypto_core_message,
ODK_ProvisioningRequest* core_provisioning_request) {
const auto unpacker = Unpack_ODK_ProvisioningMessage;
ODK_ProvisioningMessage prepared_provision = {};
const auto unpacker = Unpack_ODK_PreparedProvisioningRequest;
ODK_PreparedProvisioningRequest prepared_provision = {};
if (!ParseRequest(ODK_Provisioning_Request_Type, oemcrypto_core_message,
core_provisioning_request, &prepared_provision, unpacker)) {
return false;

View File

@@ -4,7 +4,6 @@
#include "core_message_serialize.h"
#include <algorithm>
#include <cstddef>
#include <cstdint>
#include <cstring>
@@ -38,11 +37,12 @@ bool CreateResponse(uint32_t message_type, const S& core_request,
auto* header = reinterpret_cast<ODK_CoreMessage*>(&response);
header->message_type = message_type;
header->nonce_values.api_version = core_request.api_version;
header->nonce_values.api_major_version = core_request.api_major_version;
header->nonce_values.api_minor_version = core_request.api_minor_version;
header->nonce_values.nonce = core_request.nonce;
header->nonce_values.session_id = core_request.session_id;
const size_t BUF_CAPACITY = 2048;
static constexpr size_t BUF_CAPACITY = 2048;
std::vector<uint8_t> buf(BUF_CAPACITY, 0);
Message* msg = nullptr;
AllocateMessage(&msg, message_block);
@@ -63,16 +63,14 @@ bool CreateResponse(uint32_t message_type, const S& core_request,
bool CopyDeviceId(const ODK_ProvisioningRequest& src,
ODK_ProvisioningResponse* dest) {
auto& core_provisioning = dest->core_provisioning;
auto& request = dest->request;
const std::string& device_id = src.device_id;
core_provisioning.device_id_length = device_id.size();
if (core_provisioning.device_id_length >
sizeof(core_provisioning.device_id)) {
if (request.device_id_length > sizeof(request.device_id)) {
return false;
}
memset(core_provisioning.device_id, 0, sizeof(core_provisioning.device_id));
memcpy(core_provisioning.device_id, device_id.data(),
core_provisioning.device_id_length);
request.device_id_length = device_id.size();
memset(request.device_id, 0, sizeof(request.device_id));
memcpy(request.device_id, device_id.data(), request.device_id_length);
return true;
}
@@ -80,21 +78,28 @@ bool CopyDeviceId(const ODK_ProvisioningRequest& src,
bool CreateCoreLicenseResponse(const ODK_ParsedLicense& parsed_lic,
const ODK_LicenseRequest& core_request,
const std::string& core_request_sha256,
std::string* oemcrypto_core_message) {
ODK_LicenseResponse license_response{
{}, const_cast<ODK_ParsedLicense*>(&parsed_lic)};
{}, const_cast<ODK_ParsedLicense*>(&parsed_lic), {0}};
if (core_request_sha256.size() != sizeof(license_response.request_hash))
return false;
memcpy(license_response.request_hash, core_request_sha256.data(),
sizeof(license_response.request_hash));
return CreateResponse(ODK_License_Response_Type, core_request,
oemcrypto_core_message, license_response,
Pack_ODK_LicenseResponse);
}
bool CreateCoreRenewalResponse(const ODK_RenewalRequest& core_request,
uint64_t renewal_duration_seconds,
std::string* oemcrypto_core_message) {
ODK_RenewalMessage renewal{{}, core_request.playback_time_seconds};
renewal.playback_time = core_request.playback_time_seconds;
ODK_RenewalResponse renewal_response{{}, core_request.playback_time_seconds};
renewal_response.request.playback_time = core_request.playback_time_seconds;
renewal_response.renewal_duration_seconds = renewal_duration_seconds;
return CreateResponse(ODK_Renewal_Response_Type, core_request,
oemcrypto_core_message, renewal,
Pack_ODK_RenewalMessage);
oemcrypto_core_message, renewal_response,
Pack_ODK_RenewalResponse);
}
bool CreateCoreProvisioningResponse(const ODK_ParsedProvisioning& parsed_prov,

View File

@@ -67,6 +67,7 @@ OEMCrypto_KeyObject KeyContainerToOecKey(
bool CreateCoreLicenseResponseFromProto(const std::string& serialized_license,
const ODK_LicenseRequest& core_request,
const std::string& core_request_sha256,
const bool nonce_required,
std::string* oemcrypto_core_message) {
video_widevine::License lic;
if (!lic.ParseFromString(serialized_license)) {
@@ -74,25 +75,33 @@ bool CreateCoreLicenseResponseFromProto(const std::string& serialized_license,
}
ODK_ParsedLicense parsed_lic{};
if (core_request_sha256.size() != ODK_SHA256_HASH_SIZE) {
return false;
}
std::memcpy(parsed_lic.request_hash, core_request_sha256.data(),
ODK_SHA256_HASH_SIZE);
bool any_content = false;
bool any_entitlement = false;
for (int i = 0; i < lic.key_size(); ++i) {
const auto& k = lic.key(i);
switch (k.type()) {
case video_widevine::License_KeyContainer::SIGNING: {
if (!k.has_key()) {
continue;
}
parsed_lic.enc_mac_keys_iv =
GetOecSubstring(serialized_license, k.iv());
// Strip off PKCS#5 padding
const size_t MAC_KEY_SIZE = 32;
std::string mac_keys(k.key(), 2 * MAC_KEY_SIZE);
std::string mac_keys(k.key(), k.key().size());
parsed_lic.enc_mac_keys = GetOecSubstring(serialized_license, mac_keys);
break;
}
case video_widevine::License_KeyContainer::CONTENT: {
any_content = true;
if (parsed_lic.key_array_length >= ODK_MAX_NUM_KEYS) {
return false;
}
uint32_t& n = parsed_lic.key_array_length;
parsed_lic.key_array[n++] = KeyContainerToOecKey(serialized_license, k);
break;
}
case video_widevine::License_KeyContainer::ENTITLEMENT: {
any_entitlement = true;
if (parsed_lic.key_array_length >= ODK_MAX_NUM_KEYS) {
return false;
}
@@ -105,7 +114,16 @@ bool CreateCoreLicenseResponseFromProto(const std::string& serialized_license,
}
}
}
if (any_content && any_entitlement) {
// TODO(b/147513335): this should be logged -- both type of keys.
return false;
}
if (!any_content && !any_entitlement) {
// TODO(b/147513335): this should be logged -- no keys?
return false;
}
parsed_lic.license_type =
any_content ? OEMCrypto_ContentLicense : OEMCrypto_EntitlementLicense;
const auto& lid = lic.id();
if (lid.has_provider_session_token()) {
parsed_lic.pst =
@@ -117,22 +135,25 @@ bool CreateCoreLicenseResponseFromProto(const std::string& serialized_license,
GetOecSubstring(serialized_license, lic.srm_requirement());
}
parsed_lic.license_type = lid.type();
// todo(robertshih): nonce_required
parsed_lic.nonce_required = nonce_required;
const auto& policy = lic.policy();
ODK_TimerLimits& timer_limits = parsed_lic.timer_limits;
timer_limits.soft_expiry = policy.soft_enforce_playback_duration();
// 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_playback_duration =
policy.soft_enforce_playback_duration();
timer_limits.earliest_playback_start_seconds = 0;
timer_limits.latest_playback_start_seconds =
policy.license_duration_seconds();
timer_limits.initial_playback_duration_seconds =
timer_limits.rental_duration_seconds = policy.rental_duration_seconds();
timer_limits.total_playback_duration_seconds =
policy.playback_duration_seconds();
timer_limits.renewal_playback_duration_seconds =
policy.playback_duration_seconds();
timer_limits.license_duration_seconds = policy.license_duration_seconds();
timer_limits.initial_renewal_duration_seconds =
policy.renewal_delay_seconds() +
policy.renewal_recovery_duration_seconds();
return CreateCoreLicenseResponse(parsed_lic, core_request,
oemcrypto_core_message);
core_request_sha256, oemcrypto_core_message);
}
bool CreateCoreProvisioningResponseFromProto(
@@ -145,7 +166,8 @@ bool CreateCoreProvisioningResponseFromProto(
return false;
}
parsed_prov.key_type = 0; // todo(robertshih): ECC or RSA
parsed_prov.key_type =
OEMCrypto_RSA_Private_Key; // TODO(b/148404408): ECC or RSA
if (prov.has_device_rsa_key()) {
parsed_prov.enc_private_key =
GetOecSubstring(serialized_provisioning_resp, prov.device_rsa_key());

View File

@@ -12,12 +12,12 @@
#include "odk_serialize.h"
#include "odk_structs.h"
#include "odk_structs_priv.h"
#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
#define OEC_API_VERSION 16
/* @ private odk functions */
@@ -26,8 +26,8 @@ static OEMCryptoResult ODK_PrepareRequest(uint8_t* buffer, size_t buffer_length,
uint32_t message_type,
const ODK_NonceValues* nonce_values,
ODK_CoreMessage* core_message) {
if (!nonce_values || !core_message_length || !core_message ||
*core_message_length > buffer_length) {
if (nonce_values == NULL || core_message_length == NULL ||
core_message == NULL || *core_message_length > buffer_length) {
return ODK_ERROR_CORE_MESSAGE;
}
@@ -43,17 +43,20 @@ static OEMCryptoResult ODK_PrepareRequest(uint8_t* buffer, size_t buffer_length,
switch (message_type) {
case ODK_License_Request_Type: {
core_message->message_length = ODK_LICENSE_REQUEST_SIZE;
Pack_ODK_PreparedLicense(msg, (ODK_PreparedLicense*)core_message);
Pack_ODK_PreparedLicenseRequest(
msg, (ODK_PreparedLicenseRequest*)core_message);
break;
}
case ODK_Renewal_Request_Type: {
core_message->message_length = ODK_RENEWAL_REQUEST_SIZE;
Pack_ODK_RenewalMessage(msg, (ODK_RenewalMessage*)core_message);
Pack_ODK_PreparedRenewalRequest(
msg, (ODK_PreparedRenewalRequest*)core_message);
break;
}
case ODK_Provisioning_Request_Type: {
core_message->message_length = ODK_PROVISIONING_REQUEST_SIZE;
Pack_ODK_ProvisioningMessage(msg, (ODK_ProvisioningMessage*)core_message);
Pack_ODK_PreparedProvisioningRequest(
msg, (ODK_PreparedProvisioningRequest*)core_message);
break;
}
default: {
@@ -93,7 +96,7 @@ static OEMCryptoResult ODK_ParseResponse(const uint8_t* buf,
break;
}
case ODK_Renewal_Response_Type: {
Unpack_ODK_RenewalMessage(msg, (ODK_RenewalMessage*)core_message);
Unpack_ODK_RenewalResponse(msg, (ODK_RenewalResponse*)core_message);
break;
}
case ODK_Provisioning_Response_Type: {
@@ -114,7 +117,10 @@ static OEMCryptoResult ODK_ParseResponse(const uint8_t* buf,
if (nonce_values) {
/* always verify nonce_values for Renewal and Provisioning responses */
if (nonce_values->api_version != core_message->nonce_values.api_version ||
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) {
return OEMCrypto_ERROR_INVALID_NONCE;
@@ -131,7 +137,7 @@ 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) {
ODK_PreparedLicense license_request = {
ODK_PreparedLicenseRequest license_request = {
{0},
};
return ODK_PrepareRequest(message, message_length, core_message_length,
@@ -139,13 +145,17 @@ OEMCryptoResult ODK_PrepareCoreLicenseRequest(
&license_request.core_message);
}
OEMCryptoResult ODK_PrepareCoreRenewalRequest(
uint8_t* message, size_t message_length, size_t* core_message_size,
const ODK_NonceValues* nonce_values, ODK_ClockValues* clock_values,
uint64_t system_time_seconds) {
ODK_RenewalMessage renewal_request = {
{0},
};
OEMCryptoResult ODK_PrepareCoreRenewalRequest(uint8_t* message,
size_t message_length,
size_t* core_message_size,
ODK_NonceValues* nonce_values,
ODK_ClockValues* clock_values,
uint64_t system_time_seconds) {
if (nonce_values == NULL || clock_values == NULL)
return ODK_ERROR_CORE_MESSAGE;
ODK_PreparedRenewalRequest renewal_request = {{0}, 0};
/* First, we compute the time this request was made relative to the playback
* clock. */
if (clock_values->time_of_first_decrypt == 0) {
/* It is OK to preemptively request a renewal before playback starts.
* We'll treat this as asking for a renewal at playback time 0. */
@@ -158,7 +168,9 @@ OEMCryptoResult ODK_PrepareCoreRenewalRequest(
return ODK_ERROR_CORE_MESSAGE;
}
}
/* Save time for this request so that we can verify the response. */
/* 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,
@@ -169,7 +181,9 @@ 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) {
ODK_ProvisioningMessage provisioning_request = {
ODK_PreparedProvisioningRequest provisioning_request = {
{0},
0,
{0},
};
if (device_id_length > sizeof(provisioning_request.device_id)) {
@@ -192,55 +206,61 @@ OEMCryptoResult ODK_ParseLicense(
const uint8_t* request_hash, ODK_TimerLimits* timer_limits,
ODK_ClockValues* clock_values, ODK_NonceValues* nonce_values,
ODK_ParsedLicense* parsed_license) {
if (!message || !request_hash || !timer_limits || !clock_values ||
!nonce_values || !parsed_license) {
if (message == NULL || request_hash == NULL || timer_limits == NULL ||
clock_values == NULL || nonce_values == NULL || parsed_license == NULL) {
return ODK_ERROR_CORE_MESSAGE;
}
ODK_LicenseResponse license_response = {{0}, parsed_license};
OEMCryptoResult err = ODK_ParseResponse(
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.core_message);
NULL, &license_response.request.core_message);
if (err != OEMCrypto_SUCCESS) {
return err;
}
/* This function should not be used for legacy licenses. */
if (license_response.core_message.nonce_values.api_version !=
OEC_API_VERSION) {
if (license_response.request.core_message.nonce_values.api_major_version !=
ODK_MAJOR_VERSION) {
return ODK_UNSUPPORTED_API;
}
/* If the license has a provider session token (pst), then OEMCrypto should
* have a usage entry loaded. The opposite is also an error. */
if ((usage_entry_present && parsed_license->pst.length == 0) ||
(!usage_entry_present && parsed_license->pst.length > 0)) {
return ODK_ERROR_CORE_MESSAGE;
}
if (parsed_license->nonce_required) {
if (initial_license_load) {
if (nonce_values->nonce !=
license_response.core_message.nonce_values.nonce ||
license_response.request.core_message.nonce_values.nonce ||
nonce_values->session_id !=
license_response.core_message.nonce_values.session_id) {
license_response.request.core_message.nonce_values.session_id) {
return OEMCrypto_ERROR_INVALID_NONCE;
}
} else { /* !initial_license_load */
nonce_values->nonce = license_response.core_message.nonce_values.nonce;
nonce_values->nonce =
license_response.request.core_message.nonce_values.nonce;
nonce_values->session_id =
license_response.core_message.nonce_values.session_id;
license_response.request.core_message.nonce_values.session_id;
}
}
/* For v16, in order to be backwards compatible with a v15 license server,
* OEMCrypto stores a hash of the core license request and only signs the
* message body. Here, when we process the license response, we verify that
* the server has the same hash of the core request. */
if (initial_license_load && memcmp(request_hash, parsed_license->request_hash,
ODK_SHA256_HASH_SIZE)) {
return ODK_ERROR_CORE_MESSAGE;
}
/* If the license has a provider session token (pst), then OEMCrypto should
* have a usage entry loaded. */
if (usage_entry_present && parsed_license->pst.length == 0) {
if (initial_license_load &&
crypto_memcmp(request_hash, license_response.request_hash,
ODK_SHA256_HASH_SIZE)) {
return ODK_ERROR_CORE_MESSAGE;
}
*timer_limits = parsed_license->timer_limits;
return err;
/* And update the clock values state. */
clock_values->timer_status = ODK_CLOCK_TIMER_STATUS_LICENSE_LOADED;
return OEMCrypto_SUCCESS;
}
OEMCryptoResult ODK_ParseRenewal(const uint8_t* message, size_t message_length,
@@ -250,16 +270,18 @@ OEMCryptoResult ODK_ParseRenewal(const uint8_t* message, size_t message_length,
const ODK_TimerLimits* timer_limits,
ODK_ClockValues* clock_values,
uint64_t* timer_value) {
if (!message || !nonce_values || !timer_limits || !clock_values) {
if (message == NULL || nonce_values == NULL || timer_limits == NULL ||
clock_values == NULL) {
return ODK_ERROR_CORE_MESSAGE;
}
ODK_RenewalMessage renewal_response = {
{0},
ODK_RenewalResponse renewal_response = {
{{0}, 0},
0,
};
OEMCryptoResult err = ODK_ParseResponse(
message, message_length, core_message_length, ODK_Renewal_Response_Type,
nonce_values, &renewal_response.core_message);
nonce_values, &renewal_response.request.core_message);
if (err) {
return err;
@@ -269,69 +291,51 @@ OEMCryptoResult ODK_ParseRenewal(const uint8_t* message, size_t message_length,
* Doc: License Duration and Renewal (Changes for OEMCrypto v16)
* Section: Renewal Message
*/
/* If a renewal request is lost in transit, we should throw it out and create
* a new one. We use the timestamp to make sure we have the latest request.
*/
if (clock_values->time_of_renewal_request < renewal_response.playback_time) {
if (clock_values->time_of_renewal_request <
renewal_response.request.playback_time) {
return ODK_STALE_RENEWAL;
}
/* The timer value should be set to the renewal duration. */
if (timer_value) {
*timer_value = timer_limits->renewal_playback_duration_seconds;
}
if (timer_limits->renewal_playback_duration_seconds == 0) {
clock_values->time_when_timer_expires = 0;
clock_values->timer_status = ODK_DISABLE_TIMER;
return ODK_DISABLE_TIMER;
}
if (odk_add_overflow_u64(system_time,
timer_limits->renewal_playback_duration_seconds,
&clock_values->time_when_timer_expires)) {
return ODK_ERROR_CORE_MESSAGE;
}
clock_values->timer_status = ODK_SET_TIMER;
return ODK_SET_TIMER;
return ODK_ComputeRenewalDuration(timer_limits, clock_values, system_time,
renewal_response.renewal_duration_seconds,
timer_value);
}
OEMCryptoResult ODK_ParseProvisioning(
const 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, ODK_ParsedProvisioning* parsed_response) {
if (!message || !nonce_values || !device_id || !parsed_response) {
if (message == NULL || nonce_values == NULL || device_id == NULL ||
parsed_response == NULL) {
return ODK_ERROR_CORE_MESSAGE;
}
ODK_ProvisioningResponse provisioning_response = {{
{0},
},
ODK_ProvisioningResponse provisioning_response = {{{0}, 0, {0}},
parsed_response};
if (device_id_length > ODK_DEVICE_ID_LEN_MAX) {
return ODK_ERROR_CORE_MESSAGE;
}
const OEMCryptoResult err =
OEMCryptoResult err =
ODK_ParseResponse(message, message_length, core_message_length,
ODK_Provisioning_Response_Type, nonce_values,
&provisioning_response.core_provisioning.core_message);
&provisioning_response.request.core_message);
if (err) {
return err;
}
if (memcmp(device_id, provisioning_response.core_provisioning.device_id,
if (memcmp(device_id, provisioning_response.request.device_id,
device_id_length)) {
return ODK_ERROR_CORE_MESSAGE;
}
uint8_t zero[ODK_DEVICE_ID_LEN_MAX] = {0};
/* check bytes beyond device_id_length are 0 */
if (memcmp(
zero,
provisioning_response.core_provisioning.device_id + device_id_length,
ODK_DEVICE_ID_LEN_MAX - device_id_length)) {
if (memcmp(zero, provisioning_response.request.device_id + device_id_length,
ODK_DEVICE_ID_LEN_MAX - device_id_length)) {
return ODK_ERROR_CORE_MESSAGE;
}

View File

@@ -5,6 +5,9 @@
#ifndef WIDEVINE_ODK_SRC_ODK_OVERFLOW_H_
#define WIDEVINE_ODK_SRC_ODK_OVERFLOW_H_
#include <stddef.h>
#include <stdint.h>
#ifdef __cplusplus
extern "C" {
#endif

View File

@@ -14,7 +14,8 @@
/* @@ private serialize */
static void Pack_ODK_NonceValues(Message* msg, ODK_NonceValues const* obj) {
Pack_uint32_t(msg, &obj->api_version);
Pack_uint16_t(msg, &obj->api_minor_version);
Pack_uint16_t(msg, &obj->api_major_version);
Pack_uint32_t(msg, &obj->nonce);
Pack_uint32_t(msg, &obj->session_id);
}
@@ -35,12 +36,12 @@ static void Pack_OEMCrypto_KeyObject(Message* msg,
}
static void Pack_ODK_TimerLimits(Message* msg, ODK_TimerLimits const* obj) {
Pack_uint32_t(msg, &obj->soft_expiry);
Pack_bool(msg, &obj->soft_enforce_rental_duration);
Pack_bool(msg, &obj->soft_enforce_playback_duration);
Pack_uint64_t(msg, &obj->earliest_playback_start_seconds);
Pack_uint64_t(msg, &obj->latest_playback_start_seconds);
Pack_uint64_t(msg, &obj->initial_playback_duration_seconds);
Pack_uint64_t(msg, &obj->renewal_playback_duration_seconds);
Pack_uint64_t(msg, &obj->license_duration_seconds);
Pack_uint64_t(msg, &obj->rental_duration_seconds);
Pack_uint64_t(msg, &obj->total_playback_duration_seconds);
Pack_uint64_t(msg, &obj->initial_renewal_duration_seconds);
}
static void Pack_ODK_ParsedLicense(Message* msg, ODK_ParsedLicense const* obj) {
@@ -53,10 +54,9 @@ static void Pack_ODK_ParsedLicense(Message* msg, ODK_ParsedLicense const* obj) {
Pack_OEMCrypto_Substring(msg, &obj->enc_mac_keys);
Pack_OEMCrypto_Substring(msg, &obj->pst);
Pack_OEMCrypto_Substring(msg, &obj->srm_restriction_data);
Pack_uint32_t(msg, &obj->license_type);
Pack_uint32_t(msg, &obj->nonce_required);
Pack_enum(msg, obj->license_type);
Pack_bool(msg, &obj->nonce_required);
Pack_ODK_TimerLimits(msg, &obj->timer_limits);
PackArray(msg, &obj->request_hash[0], sizeof(obj->request_hash));
Pack_uint32_t(msg, &obj->key_array_length);
size_t i;
for (i = 0; i < (size_t)obj->key_array_length; i++) {
@@ -66,7 +66,7 @@ static void Pack_ODK_ParsedLicense(Message* msg, ODK_ParsedLicense const* obj) {
static void Pack_ODK_ParsedProvisioning(Message* msg,
ODK_ParsedProvisioning const* obj) {
Pack_uint32_t(msg, &obj->key_type);
Pack_enum(msg, obj->key_type);
Pack_OEMCrypto_Substring(msg, &obj->enc_private_key);
Pack_OEMCrypto_Substring(msg, &obj->enc_private_key_iv);
Pack_OEMCrypto_Substring(msg, &obj->encrypted_message_key);
@@ -74,17 +74,19 @@ static void Pack_ODK_ParsedProvisioning(Message* msg,
/* @@ odk serialize */
void Pack_ODK_PreparedLicense(Message* msg, ODK_PreparedLicense const* obj) {
void Pack_ODK_PreparedLicenseRequest(Message* msg,
ODK_PreparedLicenseRequest const* obj) {
Pack_ODK_CoreMessage(msg, &obj->core_message);
}
void Pack_ODK_RenewalMessage(Message* msg, ODK_RenewalMessage const* obj) {
void Pack_ODK_PreparedRenewalRequest(Message* msg,
ODK_PreparedRenewalRequest const* obj) {
Pack_ODK_CoreMessage(msg, &obj->core_message);
Pack_uint64_t(msg, &obj->playback_time);
}
void Pack_ODK_ProvisioningMessage(Message* msg,
ODK_ProvisioningMessage const* obj) {
void Pack_ODK_PreparedProvisioningRequest(
Message* msg, ODK_PreparedProvisioningRequest const* obj) {
Pack_ODK_CoreMessage(msg, &obj->core_message);
Pack_uint32_t(msg, &obj->device_id_length);
PackArray(msg, &obj->device_id[0], sizeof(obj->device_id));
@@ -93,13 +95,19 @@ void Pack_ODK_ProvisioningMessage(Message* msg,
/* @@ kdo serialize */
void Pack_ODK_LicenseResponse(Message* msg, ODK_LicenseResponse const* obj) {
Pack_ODK_CoreMessage(msg, &obj->core_message);
Pack_ODK_PreparedLicenseRequest(msg, &obj->request);
Pack_ODK_ParsedLicense(msg, (const ODK_ParsedLicense*)obj->parsed_license);
PackArray(msg, &obj->request_hash[0], sizeof(obj->request_hash));
}
void Pack_ODK_RenewalResponse(Message* msg, ODK_RenewalResponse const* obj) {
Pack_ODK_PreparedRenewalRequest(msg, &obj->request);
Pack_uint64_t(msg, &obj->renewal_duration_seconds);
}
void Pack_ODK_ProvisioningResponse(Message* msg,
ODK_ProvisioningResponse const* obj) {
Pack_ODK_ProvisioningMessage(msg, &obj->core_provisioning);
Pack_ODK_PreparedProvisioningRequest(msg, &obj->request);
Pack_ODK_ParsedProvisioning(
msg, (const ODK_ParsedProvisioning*)obj->parsed_provisioning);
}
@@ -109,7 +117,8 @@ void Pack_ODK_ProvisioningResponse(Message* msg,
/* @@ private deserialize */
static void Unpack_ODK_NonceValues(Message* msg, ODK_NonceValues* obj) {
Unpack_uint32_t(msg, &obj->api_version);
Unpack_uint16_t(msg, &obj->api_minor_version);
Unpack_uint16_t(msg, &obj->api_major_version);
Unpack_uint32_t(msg, &obj->nonce);
Unpack_uint32_t(msg, &obj->session_id);
}
@@ -129,12 +138,12 @@ static void Unpack_OEMCrypto_KeyObject(Message* msg, OEMCrypto_KeyObject* obj) {
}
static void Unpack_ODK_TimerLimits(Message* msg, ODK_TimerLimits* obj) {
Unpack_uint32_t(msg, &obj->soft_expiry);
Unpack_bool(msg, &obj->soft_enforce_rental_duration);
Unpack_bool(msg, &obj->soft_enforce_playback_duration);
Unpack_uint64_t(msg, &obj->earliest_playback_start_seconds);
Unpack_uint64_t(msg, &obj->latest_playback_start_seconds);
Unpack_uint64_t(msg, &obj->initial_playback_duration_seconds);
Unpack_uint64_t(msg, &obj->renewal_playback_duration_seconds);
Unpack_uint64_t(msg, &obj->license_duration_seconds);
Unpack_uint64_t(msg, &obj->rental_duration_seconds);
Unpack_uint64_t(msg, &obj->total_playback_duration_seconds);
Unpack_uint64_t(msg, &obj->initial_renewal_duration_seconds);
}
static void Unpack_ODK_ParsedLicense(Message* msg, ODK_ParsedLicense* obj) {
@@ -142,10 +151,9 @@ static void Unpack_ODK_ParsedLicense(Message* msg, ODK_ParsedLicense* obj) {
Unpack_OEMCrypto_Substring(msg, &obj->enc_mac_keys);
Unpack_OEMCrypto_Substring(msg, &obj->pst);
Unpack_OEMCrypto_Substring(msg, &obj->srm_restriction_data);
Unpack_uint32_t(msg, &obj->license_type);
Unpack_uint32_t(msg, &obj->nonce_required);
obj->license_type = Unpack_enum(msg);
Unpack_bool(msg, &obj->nonce_required);
Unpack_ODK_TimerLimits(msg, &obj->timer_limits);
UnpackArray(msg, &obj->request_hash[0], sizeof(obj->request_hash));
Unpack_uint32_t(msg, &obj->key_array_length);
if (obj->key_array_length > ODK_MAX_NUM_KEYS) {
SetStatus(msg, MESSAGE_STATUS_OVERFLOW_ERROR);
@@ -159,7 +167,7 @@ static void Unpack_ODK_ParsedLicense(Message* msg, ODK_ParsedLicense* obj) {
static void Unpack_ODK_ParsedProvisioning(Message* msg,
ODK_ParsedProvisioning* obj) {
Unpack_uint32_t(msg, &obj->key_type);
obj->key_type = Unpack_enum(msg);
Unpack_OEMCrypto_Substring(msg, &obj->enc_private_key);
Unpack_OEMCrypto_Substring(msg, &obj->enc_private_key_iv);
Unpack_OEMCrypto_Substring(msg, &obj->encrypted_message_key);
@@ -167,12 +175,19 @@ static void Unpack_ODK_ParsedProvisioning(Message* msg,
/* @ kdo deserialize */
void Unpack_ODK_PreparedLicense(Message* msg, ODK_PreparedLicense* obj) {
void Unpack_ODK_PreparedLicenseRequest(Message* msg,
ODK_PreparedLicenseRequest* obj) {
Unpack_ODK_CoreMessage(msg, &obj->core_message);
}
void Unpack_ODK_ProvisioningMessage(Message* msg,
ODK_ProvisioningMessage* obj) {
void Unpack_ODK_PreparedRenewalRequest(Message* msg,
ODK_PreparedRenewalRequest* obj) {
Unpack_ODK_CoreMessage(msg, &obj->core_message);
Unpack_uint64_t(msg, &obj->playback_time);
}
void Unpack_ODK_PreparedProvisioningRequest(
Message* msg, ODK_PreparedProvisioningRequest* obj) {
Unpack_ODK_CoreMessage(msg, &obj->core_message);
Unpack_uint32_t(msg, &obj->device_id_length);
UnpackArray(msg, &obj->device_id[0], sizeof(obj->device_id));
@@ -181,17 +196,18 @@ void Unpack_ODK_ProvisioningMessage(Message* msg,
/* @@ odk deserialize */
void Unpack_ODK_LicenseResponse(Message* msg, ODK_LicenseResponse* obj) {
Unpack_ODK_CoreMessage(msg, &obj->core_message);
Unpack_ODK_PreparedLicenseRequest(msg, &obj->request);
Unpack_ODK_ParsedLicense(msg, obj->parsed_license);
UnpackArray(msg, &obj->request_hash[0], sizeof(obj->request_hash));
}
void Unpack_ODK_RenewalMessage(Message* msg, ODK_RenewalMessage* obj) {
Unpack_ODK_CoreMessage(msg, &obj->core_message);
Unpack_uint64_t(msg, &obj->playback_time);
void Unpack_ODK_RenewalResponse(Message* msg, ODK_RenewalResponse* obj) {
Unpack_ODK_PreparedRenewalRequest(msg, &obj->request);
Unpack_uint64_t(msg, &obj->renewal_duration_seconds);
}
void Unpack_ODK_ProvisioningResponse(Message* msg,
ODK_ProvisioningResponse* obj) {
Unpack_ODK_ProvisioningMessage(msg, &obj->core_provisioning);
Unpack_ODK_PreparedProvisioningRequest(msg, &obj->request);
Unpack_ODK_ParsedProvisioning(msg, obj->parsed_provisioning);
}

View File

@@ -16,25 +16,32 @@ extern "C" {
#endif
/* odk pack */
void Pack_ODK_PreparedLicense(Message* msg, ODK_PreparedLicense const* obj);
void Pack_ODK_RenewalMessage(Message* msg, ODK_RenewalMessage const* obj);
void Pack_ODK_ProvisioningMessage(Message* msg,
ODK_ProvisioningMessage const* obj);
void Pack_ODK_PreparedLicenseRequest(Message* msg,
const ODK_PreparedLicenseRequest* obj);
void Pack_ODK_PreparedRenewalRequest(Message* msg,
const ODK_PreparedRenewalRequest* obj);
void Pack_ODK_PreparedProvisioningRequest(
Message* msg, const ODK_PreparedProvisioningRequest* obj);
/* odk unpack */
void Unpack_ODK_LicenseResponse(Message* msg, ODK_LicenseResponse* obj);
void Unpack_ODK_RenewalMessage(Message* msg, ODK_RenewalMessage* obj);
void Unpack_ODK_RenewalResponse(Message* msg, ODK_RenewalResponse* obj);
void Unpack_ODK_ProvisioningResponse(Message* msg,
ODK_ProvisioningResponse* obj);
/* kdo pack */
void Pack_ODK_LicenseResponse(Message* msg, ODK_LicenseResponse const* obj);
void Pack_ODK_LicenseResponse(Message* msg, const ODK_LicenseResponse* obj);
void Pack_ODK_RenewalResponse(Message* msg, const ODK_RenewalResponse* obj);
void Pack_ODK_ProvisioningResponse(Message* msg,
ODK_ProvisioningResponse const* obj);
const ODK_ProvisioningResponse* obj);
/* kdo unpack */
void Unpack_ODK_PreparedLicense(Message* msg, ODK_PreparedLicense* obj);
void Unpack_ODK_ProvisioningMessage(Message* msg, ODK_ProvisioningMessage* obj);
void Unpack_ODK_PreparedLicenseRequest(Message* msg,
ODK_PreparedLicenseRequest* obj);
void Unpack_ODK_PreparedRenewalRequest(Message* msg,
ODK_PreparedRenewalRequest* obj);
void Unpack_ODK_PreparedProvisioningRequest(
Message* msg, ODK_PreparedProvisioningRequest* obj);
#ifdef __cplusplus
} /* extern "C" */

View File

@@ -10,6 +10,10 @@
#include "OEMCryptoCENCCommon.h"
#include "odk_structs.h"
#ifdef __cplusplus
extern "C" {
#endif
typedef enum {
ODK_License_Request_Type = 1,
ODK_License_Response_Type = 2,
@@ -27,27 +31,61 @@ typedef struct {
typedef struct {
ODK_CoreMessage core_message;
} ODK_PreparedLicense;
} ODK_PreparedLicenseRequest;
typedef struct {
ODK_CoreMessage core_message;
uint64_t playback_time;
} ODK_RenewalMessage;
} ODK_PreparedRenewalRequest;
typedef struct {
ODK_CoreMessage core_message;
uint32_t device_id_length;
uint8_t device_id[ODK_DEVICE_ID_LEN_MAX];
} ODK_ProvisioningMessage;
} ODK_PreparedProvisioningRequest;
typedef struct {
ODK_CoreMessage core_message;
ODK_PreparedLicenseRequest request;
ODK_ParsedLicense* parsed_license;
uint8_t request_hash[ODK_SHA256_HASH_SIZE];
} ODK_LicenseResponse;
typedef struct {
ODK_ProvisioningMessage core_provisioning;
ODK_PreparedRenewalRequest request;
uint64_t renewal_duration_seconds;
} ODK_RenewalResponse;
typedef struct {
ODK_PreparedProvisioningRequest request;
ODK_ParsedProvisioning* parsed_provisioning;
} ODK_ProvisioningResponse;
/* 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. */
#define ODK_CLOCK_TIMER_STATUS_LICENSE_NOT_LOADED 1
/* After the license is loaded, before a successful decrypt. */
#define ODK_CLOCK_TIMER_STATUS_LICENSE_LOADED 2
/* After the license is loaded, if a renewal has also been loaded. */
#define ODK_CLOCK_TIMER_STATUS_RENEWAL_LOADED 3
/* The first decrypt has occurred and the timer is active. */
#define ODK_CLOCK_TIMER_STATUS_ACTIVE 4
/* The first decrypt has occurred and the timer is unlimited. */
#define ODK_CLOCK_TIMER_STATUS_UNLIMITED 5
/* The timer has transitioned from active to expired. */
#define ODK_CLOCK_TIMER_STATUS_EXPIRED 6
/* The license has been marked as inactive. */
#define ODK_CLOCK_TIMER_STATUS_LICENSE_INACTIVE 7
/* A helper function for computing timer limits when a renewal is loaded. */
OEMCryptoResult ODK_ComputeRenewalDuration(const ODK_TimerLimits* timer_limits,
ODK_ClockValues* clock_values,
uint64_t system_time_seconds,
uint64_t new_renewal_duration,
uint64_t* timer_value);
#ifdef __cplusplus
}
#endif
#endif /* WIDEVINE_ODK_SRC_ODK_STRUCTS_PRIV_H_ */

View File

@@ -6,41 +6,279 @@
#include <string.h>
#include "odk.h"
#include "odk_overflow.h"
#include "odk_structs_priv.h"
/* 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. */
static OEMCryptoResult ODK_LicenseActive(const ODK_TimerLimits* timer_limits,
ODK_ClockValues* clock_values) {
/* Check some basic errors. */
if (clock_values == NULL || timer_limits == NULL) {
return OEMCrypto_ERROR_INVALID_CONTEXT;
}
/* Check if the license has not been loaded yet. */
if (clock_values->timer_status == ODK_CLOCK_TIMER_STATUS_UNDEFINED ||
clock_values->timer_status == ODK_CLOCK_TIMER_STATUS_LICENSE_NOT_LOADED) {
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;
}
/* Private function. Sets the timer_value to be the min(timer_value, new_value),
* with the convention that 0 means infinite. The convention that 0 means
* infinite is used for all Widevine license and duration values. */
static void ComputeMinimum(uint64_t* timer_value, uint64_t new_value) {
if (timer_value == NULL) return;
if (new_value > 0) {
if (*timer_value == 0 || *timer_value > new_value) {
*timer_value = new_value;
}
}
}
/* Private function. Check to see if the rental window restricts playback. If
* the rental enforcement is hard, or if this is the first playback, then we
* verify that system_time_seconds is within the rental window. If the
* enforcement is soft and we have already started playback, then there is no
* restriction.
* Return ODK_TIMER_EXPIRED if out of the window.
* Return ODK_TIMER_ACTIVE if within the window, and there is a hard limit.
* Return ODK_DISABLE_TIMER if no there should be no limit.
* Return other error on error.
* Also, if this function does compute a limit, the timer_value is reduced to
* obey that limit. If the limit is less restrictive than the current
* timer_value, then timer_value is not changed. */
static OEMCryptoResult ODK_CheckRentalWindow(
const ODK_TimerLimits* timer_limits, ODK_ClockValues* clock_values,
uint64_t system_time_seconds, uint64_t* timer_value) {
if (clock_values == NULL || timer_limits == NULL || timer_value == NULL) {
return OEMCrypto_ERROR_INVALID_CONTEXT;
}
/* If playback has already started, and rental duration enforcement is soft,
* then there is no restriction. */
if (clock_values->time_of_first_decrypt > 0 &&
timer_limits->soft_enforce_rental_duration) {
return ODK_DISABLE_TIMER;
}
/* rental_clock = time since license signed. */
uint64_t rental_clock = 0;
if (odk_sub_overflow_u64(system_time_seconds,
clock_values->time_of_license_signed,
&rental_clock)) {
return OEMCrypto_ERROR_INVALID_CONTEXT;
}
/* Check if it is before license is valid. This is an unusual case. First
* playback may still work if it occurs after the rental window opens. */
if (rental_clock < timer_limits->earliest_playback_start_seconds) {
return ODK_TIMER_EXPIRED;
}
/* If the rental duration is 0, there is no limit. */
if (timer_limits->rental_duration_seconds == 0) {
return ODK_DISABLE_TIMER;
}
/* End of rental window, based on rental clock (not system time). */
uint64_t end_of_rental_window = 0;
if (odk_add_overflow_u64(timer_limits->earliest_playback_start_seconds,
timer_limits->rental_duration_seconds,
&end_of_rental_window)) {
return OEMCrypto_ERROR_INVALID_CONTEXT;
}
if (end_of_rental_window <= rental_clock) {
return ODK_TIMER_EXPIRED;
}
/* At this point system_time is within the rental window. */
if (timer_limits->soft_enforce_rental_duration) {
/* For soft enforcement, we allow playback, and do not adjust the timer. */
return ODK_DISABLE_TIMER;
}
uint64_t time_left = 0;
if (odk_sub_overflow_u64(end_of_rental_window, rental_clock, &time_left)) {
return OEMCrypto_ERROR_INVALID_CONTEXT;
}
ComputeMinimum(timer_value, time_left);
return ODK_SET_TIMER;
}
/* Private function. Check to see if the playback window restricts
* playback. This should only be called if playback has started, so that
* clock_values->time_of_first_decrypt is nonzero.
* Return ODK_TIMER_EXPIRED if out of the window.
* Return ODK_SET_TIMER if within the window, and there is a hard limit.
* Return ODK_DISABLE_TIMER if no limit.
* Return other error on error.
* Also, if this function does compute a limit, the timer_value is reduced to
* obey that limit. If the limit is less restrictive than the current
* timer_value, then timer_value is not changed. */
static OEMCryptoResult ODK_CheckPlaybackWindow(
const ODK_TimerLimits* timer_limits, ODK_ClockValues* clock_values,
uint64_t system_time_seconds, uint64_t* timer_value) {
if (clock_values == NULL || timer_limits == NULL || timer_value == NULL) {
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
}
/* if the playback duration is 0, there is no limit. */
if (timer_limits->total_playback_duration_seconds == 0) {
return ODK_DISABLE_TIMER;
}
uint64_t end_of_playback_window = 0;
if (odk_add_overflow_u64(timer_limits->total_playback_duration_seconds,
clock_values->time_of_first_decrypt,
&end_of_playback_window)) {
return OEMCrypto_ERROR_INVALID_CONTEXT;
}
if (end_of_playback_window <= system_time_seconds) {
return ODK_TIMER_EXPIRED;
}
/* At this point, system_time is within the total playback window. */
if (timer_limits->soft_enforce_playback_duration) {
/* For soft enforcement, we allow playback, and do not adjust the timer. */
return ODK_DISABLE_TIMER;
}
uint64_t time_left = 0;
if (odk_sub_overflow_u64(end_of_playback_window, system_time_seconds,
&time_left)) {
return OEMCrypto_ERROR_INVALID_CONTEXT;
}
ComputeMinimum(timer_value, time_left);
return ODK_SET_TIMER;
}
/* Update the timer status. If playback has already started, we use the given
* status. However, if playback has not yet started, then we expect a call to
* ODK_AttemptFirstPlayback in the future, and we need to signal to it that we
* 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->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;
} else {
clock_values->timer_status = new_status;
}
}
/* Private function, but accessed from odk.c so cannot be static. This checks to
* see if a renewal message should restart the playback timer and sets the value
* appropriately. */
OEMCryptoResult ODK_ComputeRenewalDuration(const ODK_TimerLimits* timer_limits,
ODK_ClockValues* clock_values,
uint64_t system_time_seconds,
uint64_t new_renewal_duration,
uint64_t* timer_value) {
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)
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;
/* We start with the new renewal duration as the new timer limit. */
uint64_t new_timer_value = new_renewal_duration;
/* Then we factor in the rental window restrictions. This might decrease
* 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))
return rental_status;
/* If playback has already started and it has hard enforcement, then check
* total playback window. */
if (clock_values->time_of_first_decrypt > 0 &&
!timer_limits->soft_enforce_playback_duration) {
/* This might decrease new_timer_value. */
const OEMCryptoResult playback_status = ODK_CheckPlaybackWindow(
timer_limits, clock_values, system_time_seconds, &new_timer_value);
/* 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))
return playback_status;
}
/* If new_timer_value is infinite (represented by 0), then there are no
* limits, so we can return now. */
if (new_timer_value == 0) {
clock_values->time_when_timer_expires = 0;
ODK_UpdateTimerStatusForRenewal(clock_values,
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;
}
if (odk_add_overflow_u64(system_time_seconds, new_timer_value,
&clock_values->time_when_timer_expires)) {
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
}
ODK_UpdateTimerStatusForRenewal(clock_values, ODK_CLOCK_TIMER_STATUS_ACTIVE);
return ODK_SET_TIMER;
}
/************************************************************************/
/************************************************************************/
/* Public functions, declared in odk.h. */
/* This is called when OEMCrypto opens a new session. */
OEMCryptoResult ODK_InitializeSessionValues(ODK_TimerLimits* timer_limits,
ODK_ClockValues* clock_values,
ODK_NonceValues* nonce_values,
uint32_t api_version,
uint32_t api_major_version,
uint32_t session_id) {
if (clock_values == NULL || clock_values == NULL || nonce_values == NULL)
return OEMCrypto_ERROR_INVALID_CONTEXT;
timer_limits->soft_expiry = false;
/* Check that the API version passed in from OEMCrypto matches the version of
* this ODK library. */
if (api_major_version != ODK_MAJOR_VERSION) {
return OEMCrypto_ERROR_INVALID_CONTEXT;
}
timer_limits->soft_enforce_rental_duration = false;
timer_limits->soft_enforce_playback_duration = false;
timer_limits->earliest_playback_start_seconds = 0;
timer_limits->latest_playback_start_seconds = 0;
timer_limits->initial_playback_duration_seconds = 0;
timer_limits->renewal_playback_duration_seconds = 0;
timer_limits->license_duration_seconds = 0;
timer_limits->rental_duration_seconds = 0;
timer_limits->total_playback_duration_seconds = 0;
timer_limits->initial_renewal_duration_seconds = 0;
clock_values->time_of_license_signed = 0;
clock_values->time_of_first_decrypt = 0;
clock_values->time_of_last_decrypt = 0;
clock_values->time_when_timer_expires = 0;
clock_values->timer_status = 0;
clock_values->status = kUnused;
ODK_InitializeClockValues(clock_values, 0);
nonce_values->api_version = api_version;
nonce_values->api_major_version = ODK_MAJOR_VERSION;
nonce_values->api_minor_version = ODK_MINOR_VERSION;
nonce_values->nonce = 0;
nonce_values->session_id = session_id;
return OEMCrypto_SUCCESS;
}
/* This is called when OEMCrypto generates a new nonce in
* OEMCrypto_GenerateNonce. */
OEMCryptoResult ODK_SetNonceValues(ODK_NonceValues* nonce_values,
uint32_t nonce) {
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;
}
nonce_values->nonce = nonce;
return OEMCrypto_SUCCESS;
}
/* 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;
@@ -48,12 +286,12 @@ OEMCryptoResult ODK_InitializeClockValues(ODK_ClockValues* clock_values,
clock_values->time_of_first_decrypt = 0;
clock_values->time_of_last_decrypt = 0;
clock_values->time_when_timer_expires = 0;
/* TODO(b/142415188): document this. */
clock_values->timer_status = 0;
clock_values->timer_status = ODK_CLOCK_TIMER_STATUS_LICENSE_NOT_LOADED;
clock_values->status = kUnused;
return OEMCrypto_SUCCESS;
}
/* This is called when OEMCrypto reloads a usage entry. */
OEMCryptoResult ODK_ReloadClockValues(ODK_ClockValues* clock_values,
uint64_t time_of_license_signed,
uint64_t time_of_first_decrypt,
@@ -65,114 +303,84 @@ OEMCryptoResult ODK_ReloadClockValues(ODK_ClockValues* clock_values,
clock_values->time_of_first_decrypt = time_of_first_decrypt;
clock_values->time_of_last_decrypt = time_of_last_decrypt;
clock_values->time_when_timer_expires = 0;
clock_values->timer_status = 0;
clock_values->timer_status = ODK_CLOCK_TIMER_STATUS_LICENSE_NOT_LOADED;
clock_values->status = status;
return OEMCrypto_SUCCESS;
}
/* This is called on the first playback for a session. */
uint32_t ODK_AttemptFirstPlayback(uint64_t system_time_seconds,
const ODK_TimerLimits* timer_limits,
ODK_ClockValues* clock_values,
uint64_t* timer_value) {
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)
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
/* All times are relative to when the license was signed. */
const uint64_t rental_time =
system_time_seconds - clock_values->time_of_license_signed;
uint64_t rental_time = 0;
if (odk_sub_overflow_u64(system_time_seconds,
clock_values->time_of_license_signed,
&rental_time)) {
return OEMCrypto_ERROR_INVALID_CONTEXT;
}
if (rental_time < timer_limits->earliest_playback_start_seconds) {
clock_values->timer_status = ODK_TIMER_EXPIRED;
return ODK_TIMER_EXPIRED;
}
/* If the clock status is already marked as inactive, then playback is
* not allowed. */
/* TODO(b/142415188): add helper function. */
if (clock_values->status > kActive) {
clock_values->timer_status = ODK_TIMER_EXPIRED;
return ODK_TIMER_EXPIRED;
}
/* If this license is still inactive (never used) then we just look at the
* rental window. This is the first playback for the license, not just this
* session. */
if (clock_values->status == kUnused) {
/* If the rental clock has expired, the license has expired. */
if (rental_time > timer_limits->latest_playback_start_seconds &&
timer_limits->latest_playback_start_seconds > 0) {
clock_values->timer_status = ODK_TIMER_EXPIRED;
/* 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;
/* We start with the initial renewal duration as the timer limit. */
uint64_t new_timer_value = timer_limits->initial_renewal_duration_seconds;
/* However, if a renewal was loaded before this first playback, use the
* previously computed limit. */
if (clock_values->timer_status == ODK_CLOCK_TIMER_STATUS_RENEWAL_LOADED) {
if (clock_values->time_when_timer_expires <= system_time_seconds) {
return ODK_TIMER_EXPIRED;
}
/* The timer should be limited by the playback duration. */
uint64_t time_left = timer_limits->initial_playback_duration_seconds;
/* If there is a license duration, it also limits the timer. Remeber, a
* limit of 0 means no limit, or infinite. */
if (timer_limits->license_duration_seconds > 0) {
if (timer_limits->license_duration_seconds < rental_time) {
/* If the license duration has expired. This is unusual, because this
* can only happen if the license duration is less than the rental
* window. */
clock_values->timer_status = ODK_TIMER_EXPIRED;
return ODK_TIMER_EXPIRED;
}
if (timer_limits->license_duration_seconds - rental_time < time_left ||
time_left == 0) {
time_left = timer_limits->license_duration_seconds - rental_time;
}
if (odk_sub_overflow_u64(clock_values->time_when_timer_expires,
system_time_seconds, &new_timer_value)) {
return OEMCrypto_ERROR_INVALID_CONTEXT;
}
/* This is a new license, and we can start playback. */
clock_values->status = kActive;
}
/* Then we factor in the rental window restrictions. This might decrease
* 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 playback has not already started, then this is the first playback. */
if (clock_values->time_of_first_decrypt == 0) {
clock_values->time_of_first_decrypt = system_time_seconds;
clock_values->time_of_last_decrypt = system_time_seconds;
if (time_left == 0 || timer_limits->soft_expiry) { /* Unlimited. */
clock_values->time_when_timer_expires = 0;
clock_values->timer_status = ODK_DISABLE_TIMER;
return ODK_DISABLE_TIMER;
}
/* Set timer to limit playback. */
if (timer_value) *timer_value = time_left;
clock_values->time_when_timer_expires = system_time_seconds + time_left;
clock_values->timer_status = ODK_SET_TIMER;
return ODK_SET_TIMER;
clock_values->status = kActive;
}
/* Otherwise, this is the second loading of a persistent license. In this
* case, we ignore the rental window. */
const uint64_t time_since_first_decrypt =
system_time_seconds - clock_values->time_of_first_decrypt;
uint64_t time_left = 0;
/* If there is an initial playback duration, the we use that as a limit.
* This ignores any license renewals. If renewals are allowed, then the last
* one can be reloaded to reset the timer. */
if (timer_limits->initial_playback_duration_seconds > 0) {
if (timer_limits->initial_playback_duration_seconds <=
time_since_first_decrypt) {
clock_values->timer_status = ODK_TIMER_EXPIRED;
return ODK_TIMER_EXPIRED;
}
time_left = timer_limits->initial_playback_duration_seconds -
time_since_first_decrypt;
}
/* If there is a license duration, it also limits the timer. */
if (timer_limits->license_duration_seconds > 0) {
if (timer_limits->license_duration_seconds < rental_time) {
/* The license duration has expired. */
clock_values->timer_status = ODK_TIMER_EXPIRED;
return ODK_TIMER_EXPIRED;
}
if (timer_limits->license_duration_seconds - rental_time < time_left ||
time_left == 0) {
time_left = timer_limits->license_duration_seconds - rental_time;
}
}
/* We can restart playback for this license. Update last playback time. */
/* Similar to the rental window, we check the playback window
* 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;
/* We know we are allowed to decrypt. The rest computes the timer duration. */
clock_values->time_of_last_decrypt = system_time_seconds;
if (time_left == 0 || timer_limits->soft_expiry) { /* Unlimited. */
/* If new_timer_value is infinite (represented by 0), then there are no
* limits, so we can return now. */
if (new_timer_value == 0) {
clock_values->time_when_timer_expires = 0;
clock_values->timer_status = ODK_DISABLE_TIMER;
clock_values->timer_status = ODK_CLOCK_TIMER_STATUS_UNLIMITED;
return ODK_DISABLE_TIMER;
}
/* Set timer. */
if (timer_value) *timer_value = time_left;
clock_values->time_when_timer_expires = system_time_seconds + time_left;
clock_values->timer_status = ODK_SET_TIMER;
/* If the caller gave us a pointer to store the new timer value. Fill it. */
if (timer_value) {
*timer_value = new_timer_value;
}
if (odk_add_overflow_u64(system_time_seconds, new_timer_value,
&clock_values->time_when_timer_expires)) {
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
}
clock_values->timer_status = ODK_CLOCK_TIMER_STATUS_ACTIVE;
return ODK_SET_TIMER;
}
@@ -181,27 +389,29 @@ uint32_t ODK_AttemptFirstPlayback(uint64_t system_time_seconds,
OEMCryptoResult ODK_UpdateLastPlaybackTime(uint64_t system_time_seconds,
const ODK_TimerLimits* timer_limits,
ODK_ClockValues* clock_values) {
if (clock_values == NULL || timer_limits == NULL)
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
if (clock_values->timer_status == ODK_TIMER_EXPIRED) {
return ODK_TIMER_EXPIRED;
}
/* If the clock status is already marked as inactive, then playback is
* not allowed. */
/* TODO(b/142415188): add helper function. */
if (clock_values->status > kActive) {
clock_values->timer_status = ODK_TIMER_EXPIRED;
return ODK_TIMER_EXPIRED;
}
if (clock_values->time_when_timer_expires > 0 &&
system_time_seconds > clock_values->time_when_timer_expires) {
clock_values->timer_status = ODK_TIMER_EXPIRED;
return ODK_TIMER_EXPIRED;
OEMCryptoResult status = ODK_LicenseActive(timer_limits, clock_values);
if (status != OEMCrypto_SUCCESS) return status;
switch (clock_values->timer_status) {
case ODK_CLOCK_TIMER_STATUS_UNLIMITED:
break;
case ODK_CLOCK_TIMER_STATUS_ACTIVE:
/* Note: we allow playback at the time when the timer expires, but not
* after. This is not important for business cases, but it makes it
* easier to write tests. */
if (clock_values->time_when_timer_expires > 0 &&
system_time_seconds > clock_values->time_when_timer_expires) {
clock_values->timer_status = ODK_CLOCK_TIMER_STATUS_EXPIRED;
return ODK_TIMER_EXPIRED;
}
break;
default: /* Expired, error state, or never started. */
return ODK_TIMER_EXPIRED;
}
clock_values->time_of_last_decrypt = system_time_seconds;
return OEMCrypto_SUCCESS;
}
/* 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->status == kUnused) {
@@ -209,9 +419,12 @@ OEMCryptoResult ODK_DeactivateUsageEntry(ODK_ClockValues* clock_values) {
} else if (clock_values->status == kActive) {
clock_values->status = kInactiveUsed;
}
clock_values->timer_status = ODK_CLOCK_TIMER_STATUS_LICENSE_INACTIVE;
return OEMCrypto_SUCCESS;
}
/* This is called when OEMCrypto loads a legacy v15 license, from
* OEMCrypto_LoadKeys. */
OEMCryptoResult ODK_InitializeV15Values(ODK_TimerLimits* timer_limits,
ODK_ClockValues* clock_values,
ODK_NonceValues* nonce_values,
@@ -219,67 +432,41 @@ OEMCryptoResult ODK_InitializeV15Values(ODK_TimerLimits* timer_limits,
uint64_t system_time_seconds) {
if (clock_values == NULL || clock_values == NULL || nonce_values == NULL)
return OEMCrypto_ERROR_INVALID_CONTEXT;
timer_limits->soft_expiry = false;
timer_limits->soft_enforce_playback_duration = false;
timer_limits->soft_enforce_rental_duration = false;
timer_limits->earliest_playback_start_seconds = 0;
timer_limits->latest_playback_start_seconds = 0;
timer_limits->initial_playback_duration_seconds = key_duration;
timer_limits->renewal_playback_duration_seconds = key_duration;
timer_limits->license_duration_seconds = 0;
nonce_values->api_version = 15;
timer_limits->rental_duration_seconds = 0;
timer_limits->total_playback_duration_seconds = 0;
timer_limits->initial_renewal_duration_seconds = key_duration;
nonce_values->api_major_version = 15;
nonce_values->api_minor_version = 0;
if (key_duration > 0) {
clock_values->time_when_timer_expires = system_time_seconds + key_duration;
} else {
clock_values->time_when_timer_expires = 0;
}
clock_values->timer_status = ODK_CLOCK_TIMER_STATUS_LICENSE_LOADED;
return OEMCrypto_SUCCESS;
}
/* This is called when OEMCrypto loads a legacy license renewal in
* OEMCrypto_RefreshKeys. */
OEMCryptoResult ODK_RefreshV15Values(const ODK_TimerLimits* timer_limits,
ODK_ClockValues* clock_values,
const ODK_NonceValues* nonce_values,
uint64_t system_time_seconds,
uint32_t new_key_duration,
uint64_t* timer_value) {
if (timer_limits == NULL || clock_values == NULL || nonce_values == NULL)
return OEMCrypto_ERROR_INVALID_CONTEXT;
if (nonce_values->api_version != 15) return OEMCrypto_ERROR_INVALID_NONCE;
if (nonce_values->api_major_version != 15)
return OEMCrypto_ERROR_INVALID_NONCE;
if (clock_values->status > kActive) {
clock_values->timer_status = ODK_TIMER_EXPIRED;
return ODK_TIMER_EXPIRED;
}
/* If this is before the license was signed, something is odd. Return an
* error. */
if (system_time_seconds < clock_values->time_of_license_signed)
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
/* All times are relative to when the license was signed. */
const uint64_t rental_time =
system_time_seconds - clock_values->time_of_license_signed;
/* The timer should be limited by the renewal playback duration. This is
* similar to code in AttemptFirstPlayback, above, except we use the
* renewal_playback_duration here, and we do not change clock_values->status.
*/
uint64_t time_left = timer_limits->renewal_playback_duration_seconds;
/* If there is a license duration, it also limits the timer. Remember, a
* limit of 0 means no limit, or infinite. */
if (timer_limits->license_duration_seconds > 0) {
if (timer_limits->license_duration_seconds < rental_time) {
clock_values->timer_status = ODK_TIMER_EXPIRED;
return ODK_TIMER_EXPIRED;
}
if (timer_limits->license_duration_seconds - rental_time < time_left ||
time_left == 0) {
time_left = timer_limits->license_duration_seconds - rental_time;
}
}
if (time_left == 0 || timer_limits->soft_expiry) { /* Unlimited. */
clock_values->time_when_timer_expires = 0;
clock_values->timer_status = ODK_DISABLE_TIMER;
return ODK_DISABLE_TIMER;
}
/* Set timer to limit playback. */
if (timer_value) *timer_value = time_left;
clock_values->time_when_timer_expires = system_time_seconds + time_left;
clock_values->timer_status = ODK_SET_TIMER;
return ODK_SET_TIMER;
return ODK_ComputeRenewalDuration(timer_limits, clock_values,
system_time_seconds, new_key_duration,
timer_value);
}

View File

@@ -0,0 +1,25 @@
/* 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_util.h"
int crypto_memcmp(const void* in_a, const void* in_b, size_t len) {
if (len == 0) {
return 0;
}
/* Only valid pointers are allowed. */
if (in_a == NULL || in_b == NULL) {
return -1;
}
const uint8_t* a = in_a;
const uint8_t* b = in_b;
uint8_t x = 0;
for (size_t i = 0; i < len; i++) {
x |= a[i] ^ b[i];
}
return x;
}

View File

@@ -0,0 +1,24 @@
/* 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_SRC_ODK_UTIL_H_
#define WIDEVINE_ODK_SRC_ODK_UTIL_H_
#include <stddef.h>
#include <stdint.h>
#ifdef __cplusplus
extern "C" {
#endif
/* crypto_memcmp returns zero iff the |len| bytes at |a| and |b| are equal. It
* takes an amount of time dependent on |len|, but independent of the contents
* of |a| and |b|. Unlike memcmp, it cannot be used to order elements as the
* return value when a != b is undefined, other than being non-zero. */
int crypto_memcmp(const void* a, const void* b, size_t len);
#ifdef __cplusplus
} /* extern "C" */
#endif
#endif /* WIDEVINE_ODK_SRC_ODK_UTIL_H_ */

View File

@@ -51,6 +51,26 @@ static void PackBytes(Message* message, const uint8_t* ptr, size_t count) {
}
}
void Pack_enum(Message* message, int value) {
uint32_t v32 = value;
Pack_uint32_t(message, &v32);
}
void Pack_bool(Message* message, const bool* value) {
if (!ValidMessage(message)) return;
uint8_t data[4] = {0};
data[3] = *value ? 1 : 0;
PackBytes(message, data, sizeof(data));
}
void Pack_uint16_t(Message* message, const uint16_t* value) {
if (!ValidMessage(message)) return;
uint8_t data[2] = {0};
data[0] = *value >> 8;
data[1] = *value >> 0;
PackBytes(message, data, sizeof(data));
}
void Pack_uint32_t(Message* message, const uint32_t* value) {
if (!ValidMessage(message)) return;
uint8_t data[4] = {0};
@@ -90,6 +110,27 @@ static void UnpackBytes(Message* message, uint8_t* ptr, size_t count) {
}
}
int Unpack_enum(Message* message) {
uint32_t v32;
Unpack_uint32_t(message, &v32);
return v32;
}
void Unpack_bool(Message* message, bool* value) {
if (!ValidMessage(message)) return;
uint8_t data[4] = {0};
UnpackBytes(message, data, sizeof(data));
*value = (0 != data[3]);
}
void Unpack_uint16_t(Message* message, uint16_t* value) {
if (!ValidMessage(message)) return;
uint8_t data[2] = {0};
UnpackBytes(message, data, sizeof(data));
*value = data[0];
*value = *value << 8 | data[1];
}
void Unpack_uint32_t(Message* message, uint32_t* value) {
if (!ValidMessage(message)) return;
uint8_t data[4] = {0};
@@ -195,7 +236,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;
message->size = size;
else
message->size = size;
}
MessageStatus GetStatus(Message* message) { return message->status; }

View File

@@ -34,11 +34,17 @@ typedef struct _Message Message;
bool ValidMessage(Message* message);
void Pack_enum(Message* message, int value);
void Pack_bool(Message* message, const bool* value);
void Pack_uint16_t(Message* message, const uint16_t* value);
void Pack_uint32_t(Message* message, const uint32_t* value);
void Pack_uint64_t(Message* message, const uint64_t* value);
void PackArray(Message* message, const uint8_t* base, size_t size);
void Pack_OEMCrypto_Substring(Message* msg, const OEMCrypto_Substring* obj);
int Unpack_enum(Message* message);
void Unpack_bool(Message* message, bool* value);
void Unpack_uint16_t(Message* message, uint16_t* value);
void Unpack_uint32_t(Message* message, uint32_t* value);
void Unpack_uint64_t(Message* message, uint64_t* value);
void UnpackArray(Message* message, uint8_t* address,

View File

@@ -115,7 +115,7 @@ static bool kdo_fun_LicenseResponse(const ODK_ParseLicense_Args* args,
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_RenewalMessage& renewal_msg) {
ODK_PreparedRenewalRequest& renewal_msg) {
uint64_t timer_value = 0;
OEMCryptoResult err =
ODK_ParseRenewal(buf, len, api_version, nonce, session_id, a->system_time,
@@ -125,15 +125,16 @@ static OEMCryptoResult odk_fun_RenewalResponse(
AllocateMessage(&msg, message_block);
InitMessage(msg, const_cast<uint8_t*>(buf), len);
SetSize(msg, len);
Unpack_ODK_RenewalMessage(msg, &renewal_msg);
Unpack_ODK_PreparedRenewalRequest(msg, &renewal_msg);
assert(ValidMessage(msg));
}
return err;
}
static bool kdo_fun_RenewalResponse(const ODK_ParseRenewal_Args* args,
const ODK_RenewalMessage& renewal_msg,
std::string* oemcrypto_core_message) {
static bool kdo_fun_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,
common.session_id, renewal_msg.playback_time};
@@ -217,7 +218,7 @@ extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
odk_kdo<ODK_ParseLicense_Args, ODK_ParsedLicense>(
odk_fun_LicenseResponse, kdo_fun_LicenseResponse));
verify_roundtrip(data, size,
odk_kdo<ODK_ParseRenewal_Args, ODK_RenewalMessage>(
odk_kdo<ODK_ParseRenewal_Args, ODK_PreparedRenewalRequest>(
odk_fun_RenewalResponse, kdo_fun_RenewalResponse));
verify_roundtrip(
data, size,

View File

@@ -4,6 +4,8 @@
#include "odk.h"
#include <endian.h> // TODO(b/147944591): use this one? Or odk_endian.h?
#include <cstdint>
#include <cstdio>
#include <cstdlib>
@@ -37,6 +39,7 @@ using oemcrypto_core_message::serialize::CreateCoreProvisioningResponse;
using oemcrypto_core_message::serialize::CreateCoreRenewalResponse;
enum ODK_FieldType {
ODK_UINT16,
ODK_UINT32,
ODK_UINT64,
ODK_SUBSTRING,
@@ -59,6 +62,8 @@ struct ODK_Field {
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:
@@ -87,6 +92,11 @@ OEMCryptoResult ODK_WriteSingleField(uint8_t* const buf,
return ODK_ERROR_CORE_MESSAGE;
}
switch (field->type) {
case ODK_UINT16: {
uint16_t u16 = htobe16(*static_cast<uint16_t*>(field->value));
memcpy(buf, &u16, sizeof(u16));
break;
}
case ODK_UINT32: {
uint32_t u32 = htobe32(*static_cast<uint32_t*>(field->value));
memcpy(buf, &u32, sizeof(u32));
@@ -125,6 +135,12 @@ OEMCryptoResult ODK_ReadSingleField(const uint8_t* const buf,
return ODK_ERROR_CORE_MESSAGE;
}
switch (field->type) {
case ODK_UINT16: {
memcpy(field->value, buf, sizeof(uint16_t));
uint16_t* u16p = static_cast<uint16_t*>(field->value);
*u16p = be16toh(*u16p);
break;
}
case ODK_UINT32: {
memcpy(field->value, buf, sizeof(uint32_t));
uint32_t* u32p = static_cast<uint32_t*>(field->value);
@@ -166,6 +182,14 @@ OEMCryptoResult ODK_DumpSingleField(const uint8_t* const buf,
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));
@@ -230,14 +254,18 @@ OEMCryptoResult ODK_IterFields(ODK_FieldMode mode, uint8_t* const buf,
return ODK_ERROR_CORE_MESSAGE;
}
uint8_t* const buf_off = buf + off;
if (mode == ODK_WRITE) {
ODK_WriteSingleField(buf_off, &fields[i]);
} else if (mode == ODK_READ) {
ODK_ReadSingleField(buf_off, &fields[i]);
} else if (mode == ODK_DUMP) {
ODK_DumpSingleField(buf_off, &fields[i]);
} else {
return ODK_ERROR_CORE_MESSAGE;
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;
}
@@ -280,14 +308,17 @@ void ValidateRequest(uint32_t message_type,
const std::vector<ODK_Field>& extra_fields,
const F& odk_prepare_func, const G& kdo_parse_func) {
uint32_t message_size = 0;
uint32_t api_version = 16;
uint16_t api_major_version = ODK_MAJOR_VERSION;
uint16_t api_minor_version = ODK_MINOR_VERSION;
uint32_t nonce = 0xdeadbeef;
uint32_t session_id = 0xcafebabe;
ODK_NonceValues nonce_values{api_version, nonce, session_id};
ODK_NonceValues nonce_values{api_minor_version, api_major_version, nonce,
session_id};
std::vector<ODK_Field> total_fields = {
{ODK_UINT32, &message_type, "message_type"},
{ODK_UINT32, &message_size, "message_size"},
{ODK_UINT32, &api_version, "api_version"},
{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"},
};
@@ -317,7 +348,8 @@ void ValidateRequest(uint32_t message_type,
std::string oemcrypto_core_message(reinterpret_cast<char*>(buf),
message_size);
EXPECT_TRUE(kdo_parse_func(oemcrypto_core_message, &t));
nonce_values.api_version = t.api_version;
nonce_values.api_minor_version = t.api_minor_version;
nonce_values.api_major_version = t.api_major_version;
nonce_values.nonce = t.nonce;
nonce_values.session_id = t.session_id;
EXPECT_EQ(OEMCrypto_SUCCESS,
@@ -340,13 +372,15 @@ void ValidateResponse(uint32_t message_type,
const std::vector<ODK_Field>& extra_fields,
const F& odk_parse_func, const G& kdo_prepare_func) {
uint32_t message_size = 0;
uint32_t api_version = 16;
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<ODK_Field> total_fields = {
{ODK_UINT32, &message_type, "message_type"},
{ODK_UINT32, &message_size, "message_size"},
{ODK_UINT32, &api_version, "api_version"},
{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"},
};
@@ -367,7 +401,8 @@ void ValidateResponse(uint32_t message_type,
size_t bytes_read = 0, bytes_written = 0;
T t = {};
t.api_version = api_version;
t.api_minor_version = api_minor_version;
t.api_major_version = api_major_version;
t.nonce = nonce;
t.session_id = session_id;
@@ -383,7 +418,8 @@ void ValidateResponse(uint32_t message_type,
bytes_written - bytes_read == header_size);
// parse buf with odk
ODK_NonceValues nonce_values{api_version, nonce, session_id};
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));
@@ -465,16 +501,16 @@ TEST(OdkTest, LicenseRequest) {
}
TEST(OdkTest, RenewalRequest) {
const uint64_t system_time_seconds = 0xBADDCAFE000FF1CE;
constexpr uint64_t system_time_seconds = 0xBADDCAFE000FF1CE;
uint64_t playback_time = 0xCAFE00000000;
const uint64_t playback_start = system_time_seconds - playback_time;
std::vector<ODK_Field> extra_fields = {
const std::vector<ODK_Field> extra_fields = {
{ODK_UINT64, &playback_time, "playback_time"},
};
ODK_ClockValues clock_values = {0};
clock_values.time_of_first_decrypt = playback_start;
auto odk_prepare_func = [&](uint8_t* const buf, size_t* size,
const ODK_NonceValues* nonce_values) {
ODK_NonceValues* nonce_values) {
return ODK_PrepareCoreRenewalRequest(buf, SIZE_MAX, size, nonce_values,
&clock_values, system_time_seconds);
};
@@ -528,20 +564,17 @@ TEST(OdkTest, LicenseResponse) {
.enc_mac_keys = {.offset = 2, .length = 3},
.pst = {.offset = 4, .length = 5},
.srm_restriction_data = {.offset = 6, .length = 7},
.license_type = 8,
.nonce_required = 0xDEADC0DE,
.license_type = OEMCrypto_EntitlementLicense,
.nonce_required = true,
.timer_limits =
{
.soft_expiry = 9,
.soft_enforce_rental_duration = true,
.soft_enforce_playback_duration = false,
.earliest_playback_start_seconds = 10,
.latest_playback_start_seconds = 11,
.initial_playback_duration_seconds = 12,
.renewal_playback_duration_seconds = 13,
.license_duration_seconds = 14,
.rental_duration_seconds = 11,
.total_playback_duration_seconds = 12,
.initial_renewal_duration_seconds = 13,
},
.request_hash = {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},
.key_array_length = 3,
.key_array =
{
@@ -569,6 +602,11 @@ TEST(OdkTest, LicenseResponse) {
},
};
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};
uint8_t request_hash_read[ODK_SHA256_HASH_SIZE];
memcpy(request_hash_read, request_hash, sizeof(request_hash));
std::vector<ODK_Field> extra_fields = {
{ODK_SUBSTRING, &parsed_license.enc_mac_keys_iv, ".enc_mac_keys_iv"},
{ODK_SUBSTRING, &parsed_license.enc_mac_keys, ".enc_mac_keys"},
@@ -577,20 +615,19 @@ TEST(OdkTest, LicenseResponse) {
".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_expiry, ".soft_expiry"},
{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.latest_playback_start_seconds,
".latest_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_playback_duration_seconds,
".initial_playback_duration_seconds"},
{ODK_UINT64,
&parsed_license.timer_limits.renewal_playback_duration_seconds,
".renewal_playback_duration_seconds"},
{ODK_UINT64, &parsed_license.timer_limits.license_duration_seconds,
".license_duration_seconds"},
{ODK_HASH, &parsed_license.request_hash, ".request_hash"},
&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"},
@@ -610,21 +647,24 @@ TEST(OdkTest, LicenseResponse) {
{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"},
};
uint8_t request_hash[ODK_SHA256_HASH_SIZE] = {};
memcpy(request_hash, parsed_license.request_hash, ODK_SHA256_HASH_SIZE);
const std::string request_hash_string(
reinterpret_cast<const char*>(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;
return ODK_ParseLicense(buf, size + 128, size, true, false, request_hash,
&timer_limits, &clock_values, nonce_values,
&parsed_license);
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);
};
auto kdo_prepare_func = [&](const ODK_LicenseRequest& core_request,
std::string* oemcrypto_core_message) {
return CreateCoreLicenseResponse(parsed_license, core_request,
request_hash_string,
oemcrypto_core_message);
};
ValidateResponse<ODK_LicenseRequest>(ODK_License_Response_Type, extra_fields,
@@ -636,26 +676,29 @@ TEST(OdkTest, RenewalResponse) {
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<ODK_Field> extra_fields = {
{ODK_UINT64, &message_playback_clock, "message_playback_clock"},
{ODK_UINT64, &var_renewal_duration, "renewal_duration"},
};
ODK_TimerLimits timer_limits = {
.soft_expiry = 0,
.soft_enforce_rental_duration = false,
.soft_enforce_playback_duration = false,
.earliest_playback_start_seconds = 0,
.latest_playback_start_seconds = 100,
.initial_playback_duration_seconds = 10,
.renewal_playback_duration_seconds = 20,
.license_duration_seconds = 100,
.rental_duration_seconds = 1000,
.total_playback_duration_seconds = 2000,
.initial_renewal_duration_seconds = 30,
};
ODK_ClockValues clock_values = {
.time_of_license_signed = 0,
.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 = 0,
.timer_status = ODK_CLOCK_TIMER_STATUS_LICENSE_LOADED,
.status = kUnused,
};
@@ -666,7 +709,7 @@ TEST(OdkTest, RenewalResponse) {
&timer_limits, &clock_values, &playback_timer);
EXPECT_EQ(ODK_SET_TIMER, err);
EXPECT_EQ(timer_limits.renewal_playback_duration_seconds, playback_timer);
EXPECT_EQ(renewal_duration, playback_timer);
EXPECT_EQ(clock_values.time_when_timer_expires,
system_time + playback_timer);
@@ -678,7 +721,8 @@ TEST(OdkTest, RenewalResponse) {
auto kdo_prepare_func = [&](ODK_RenewalRequest& core_request,
std::string* oemcrypto_core_message) {
core_request.playback_time_seconds = message_playback_clock;
return CreateCoreRenewalResponse(core_request, oemcrypto_core_message);
return CreateCoreRenewalResponse(core_request, renewal_duration,
oemcrypto_core_message);
};
ValidateResponse<ODK_RenewalRequest>(ODK_Renewal_Response_Type, extra_fields,
odk_parse_func, kdo_prepare_func);
@@ -732,10 +776,12 @@ TEST(OdkSizeTest, LicenseRequest) {
uint8_t* message = nullptr;
size_t message_length = 0;
size_t core_message_length = 0;
uint32_t api_version = 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_NonceValues nonce_values{api_version, nonce, session_id};
ODK_NonceValues nonce_values{api_minor_version, api_major_version, nonce,
session_id};
EXPECT_EQ(OEMCrypto_ERROR_SHORT_BUFFER,
ODK_PrepareCoreLicenseRequest(message, message_length,
&core_message_length, &nonce_values));
@@ -748,13 +794,15 @@ TEST(OdkSizeTest, RenewalRequest) {
uint8_t* message = nullptr;
size_t message_length = 0;
size_t core_message_length = 0;
uint32_t api_version = 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;
uint64_t system_time_seconds = 15;
ODK_NonceValues nonce_values{api_version, nonce, session_id};
ODK_NonceValues nonce_values{api_minor_version, api_major_version, nonce,
session_id};
EXPECT_EQ(OEMCrypto_ERROR_SHORT_BUFFER,
ODK_PrepareCoreRenewalRequest(message, message_length,
&core_message_length, &nonce_values,
@@ -768,11 +816,13 @@ TEST(OdkSizeTest, ProvisioningRequest) {
uint8_t* message = nullptr;
size_t message_length = 0;
size_t core_message_length = 0;
uint32_t api_version = 0;
uint16_t api_minor_version = ODK_MINOR_VERSION;
uint16_t api_major_version = 0;
uint32_t nonce = 0;
uint32_t session_id = 0;
uint32_t device_id_length = 0;
ODK_NonceValues nonce_values{api_version, nonce, session_id};
ODK_NonceValues nonce_values{api_minor_version, api_major_version, nonce,
session_id};
EXPECT_EQ(OEMCrypto_ERROR_SHORT_BUFFER,
ODK_PrepareCoreProvisioningRequest(
message, message_length, &core_message_length, &nonce_values,

View File

@@ -1,64 +0,0 @@
/*
* 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 ODK_TEST_H_
#define ODK_TEST_H_
#include "OEMCryptoCENCCommon.h"
typedef enum {
ODK_License_Request_Type = 1,
ODK_License_Response_Type = 2,
ODK_Renewal_Request_Type = 3,
ODK_Renewal_Response_Type = 4,
ODK_Provisioning_Request_Type = 5,
ODK_Provisioning_Response_Type = 6,
} ODK_MessageType;
typedef enum {
ODK_UINT32,
ODK_UINT64,
ODK_SUBSTRING,
ODK_DEVICEID,
ODK_HASH,
ODK_NUMTYPES,
} ODK_FieldType;
typedef enum {
ODK_READ,
ODK_WRITE,
} ODK_FieldMode;
typedef struct {
ODK_FieldType type;
void* value;
} ODK_Field;
#define DEVICE_ID_MAX (64)
#ifdef __cplusplus
extern "C" {
#endif
size_t ODK_FieldLength(ODK_FieldType type);
OEMCryptoResult ODK_WriteSingleField(uint8_t* const buf,
const ODK_Field* const field);
OEMCryptoResult ODK_ReadSingleField(const uint8_t* const buf,
const ODK_Field* const field);
OEMCryptoResult ODK_ReadFields(const uint8_t* const buf, const size_t size_in,
size_t* size_out, const size_t n,
const ODK_Field* const fields);
OEMCryptoResult ODK_WriteFields(uint8_t* const buf, const size_t size_in,
size_t* size_out, const size_t n,
const ODK_Field* const fields);
#ifdef __cplusplus
}
#endif
#endif // ODK_TEST_H_

File diff suppressed because it is too large Load Diff