Merge recent doc changes for OEMCrypto

This is a cherry pick of recent changes to OEMCrypto and ODK. Most of
these are part of the document migration to doxygen.

See http://go/wvgerrit/106005 and its parents for code reviews.

Bug: 144715340
Bug: 148232693
Bug: 167580674
Change-Id: I658f99c8117b974faed97322d61fac0f382283af
This commit is contained in:
Fred Gylys-Colwell
2020-09-11 13:30:58 -07:00
parent 28b13ef65e
commit 20bb84ffee
75 changed files with 5717 additions and 4488 deletions

View File

@@ -34,6 +34,7 @@ LOCAL_C_INCLUDES := \
vendor/widevine/libwvdrmengine/cdm/util/test \
vendor/widevine/libwvdrmengine/oemcrypto/include \
vendor/widevine/libwvdrmengine/oemcrypto/test \
vendor/widevine/libwvdrmengine/oemcrypto/test/fuzz_tests \
vendor/widevine/libwvdrmengine/oemcrypto/odk/include \
vendor/widevine/libwvdrmengine/oemcrypto/odk/kdo/include \

View File

@@ -31,6 +31,7 @@ LOCAL_C_INCLUDES := \
vendor/widevine/libwvdrmengine/cdm/util/test \
vendor/widevine/libwvdrmengine/oemcrypto/include \
vendor/widevine/libwvdrmengine/oemcrypto/test \
vendor/widevine/libwvdrmengine/oemcrypto/test/fuzz_tests \
vendor/widevine/libwvdrmengine/oemcrypto/odk/include \
vendor/widevine/libwvdrmengine/oemcrypto/odk/kdo/include \

File diff suppressed because it is too large Load Diff

View File

@@ -20,6 +20,7 @@
namespace wvoec3 {
// clang-format off
#ifdef DYNAMIC_ADAPTER
#define Level3_IsInApp _lcc00
#define Level3_Initialize _lcc01
@@ -91,6 +92,7 @@ namespace wvoec3 {
#define Level3_LoadEntitledContentKeys _lcc92
#define Level3_CopyBuffer _lcc93
#else
#define Level3_IsInApp _oecc00
#define Level3_Initialize _oecc01
#define Level3_Terminate _oecc02
#define Level3_InstallKeyboxOrOEMCert _oecc03
@@ -162,6 +164,7 @@ namespace wvoec3 {
#endif
#define Level3_GetInitializationState _oecl3o01
// clang-format on
extern "C" {

View File

@@ -1,6 +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. */
// 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.
/*********************************************************************
* OEMCryptoCENCCommon.h
@@ -20,7 +20,11 @@
extern "C" {
#endif
/// @addtogroup common_types
/// @{
/* clang-format off */
/** Error and result codes returned by OEMCrypto functions. */
typedef enum OEMCryptoResult {
OEMCrypto_SUCCESS = 0,
OEMCrypto_ERROR_INIT_FAILED = 1,
@@ -94,8 +98,7 @@ typedef enum OEMCryptoResult {
} OEMCryptoResult;
/* clang-format on */
/*
* OEMCrypto_Usage_Entry_Status.
/**
* Valid values for status in the usage table.
*/
typedef enum OEMCrypto_Usage_Entry_Status {
@@ -106,13 +109,14 @@ 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_EntitlementLicense = 1,
OEMCrypto_LicenstType_MaxValue = OEMCrypto_EntitlementLicense,
} OEMCrypto_LicenseType;
/* Private key type used in the provisioning response. */
@@ -121,9 +125,7 @@ typedef enum OEMCrypto_PrivateKeyType {
OEMCrypto_ECC_Private_Key = 1,
} OEMCrypto_PrivateKeyType;
/*
* OEMCrypto_Substring
*
/**
* Used to indicate a substring of a signed message in OEMCrypto_LoadKeys and
* other functions which must verify that a parameter is contained within a
* signed message.
@@ -133,23 +135,22 @@ typedef struct {
size_t length;
} OEMCrypto_Substring;
/*
* OEMCrypto_KeyObject
/**
* Points to the relevant fields for a content key. The fields are extracted
* from the License Response message offered to OEMCrypto_LoadKeys(). Each
* field points to one of the components of the key. Key data, key control,
* and both IV fields are 128 bits (16 bytes):
* key_id - the unique id of this key.
* key_id_length - the size of key_id. OEMCrypto may assume this is at
* @param key_id: the unique id of this key.
* @param key_id_length: the size of key_id. OEMCrypto may assume this is at
* most 16. However, OEMCrypto shall correctly handle key id lengths
* from 1 to 16 bytes.
* key_data_iv - the IV for performing AES-128-CBC decryption of the
* @param key_data_iv: the IV for performing AES-128-CBC decryption of the
* key_data field.
* key_data - the key data. It is encrypted (AES-128-CBC) with the
* @param key_data - the key data. It is encrypted (AES-128-CBC) with the
* session's derived encrypt key and the key_data_iv.
* key_control_iv - the IV for performing AES-128-CBC decryption of the
* @param key_control_iv: the IV for performing AES-128-CBC decryption of the
* key_control field.
* key_control - the key control block. It is encrypted (AES-128-CBC) with
* @param key_control: the key control block. It is encrypted (AES-128-CBC) with
* the content key from the key_data field.
*
* The memory for the OEMCrypto_KeyObject fields is allocated and freed
@@ -163,8 +164,10 @@ typedef struct {
OEMCrypto_Substring key_control;
} OEMCrypto_KeyObject;
/// @}
#ifdef __cplusplus
}
#endif
#endif /* WIDEVINE_ODK_INCLUDE_OEMCRYPTOCENCCOMMON_H_ */
#endif // WIDEVINE_ODK_INCLUDE_OEMCRYPTOCENCCOMMON_H_

View File

@@ -1,6 +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. */
// 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.
/*********************************************************************
* core_message_deserialize.h
@@ -53,7 +53,17 @@ bool CoreProvisioningRequestFromMessage(
const std::string& oemcrypto_core_message,
ODK_ProvisioningRequest* core_provisioning_request);
} /* namespace deserialize */
} /* namespace oemcrypto_core_message */
/**
* Serializer counterpart is not used and is therefore not implemented.
*
* Parameters:
* [in] oemcrypto_core_message
* [out] core_common_request
*/
bool CoreCommonRequestFromMessage(const std::string& oemcrypto_core_message,
ODK_CommonRequest* core_common_request);
#endif /* WIDEVINE_ODK_INCLUDE_CORE_MESSAGE_DESERIALIZE_H_ */
} // namespace deserialize
} // namespace oemcrypto_core_message
#endif // WIDEVINE_ODK_INCLUDE_CORE_MESSAGE_DESERIALIZE_H_

View File

@@ -1,6 +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. */
// 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.
/*********************************************************************
* core_message_serialize.h
@@ -62,7 +62,7 @@ bool CreateCoreRenewalResponse(const ODK_RenewalRequest& core_request,
bool CreateCoreProvisioningResponse(const ODK_ParsedProvisioning& parsed_prov,
const ODK_ProvisioningRequest& core_request,
std::string* oemcrypto_core_message);
} /* namespace serialize */
} /* namespace oemcrypto_core_message */
} // namespace serialize
} // namespace oemcrypto_core_message
#endif /* WIDEVINE_ODK_INCLUDE_CORE_MESSAGE_SERIALIZE_H_ */
#endif // WIDEVINE_ODK_INCLUDE_CORE_MESSAGE_SERIALIZE_H_

View File

@@ -1,6 +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. */
// 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.
/*********************************************************************
* core_message_serialize_proto.h
@@ -23,7 +23,7 @@
namespace oemcrypto_core_message {
namespace serialize {
/* @ public create response (serializer) functions accepting proto input */
// @ public create response (serializer) functions accepting proto input
/**
* Counterpart (serializer) of ODK_ParseLicense (deserializer)
@@ -56,7 +56,7 @@ bool CreateCoreProvisioningResponseFromProto(
const ODK_ProvisioningRequest& core_request,
std::string* oemcrypto_core_message);
} /* namespace serialize */
} /* namespace oemcrypto_core_message */
} // namespace serialize
} // namespace oemcrypto_core_message
#endif /* WIDEVINE_ODK_INCLUDE_CORE_MESSAGE_SERIALIZE_PROTO_H_ */
#endif // WIDEVINE_ODK_INCLUDE_CORE_MESSAGE_SERIALIZE_PROTO_H_

View File

@@ -1,8 +1,8 @@
/* 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. */
// 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 */
// clang-format off
/*********************************************************************
* core_message_types.h
*
@@ -38,6 +38,8 @@
* | | ODK_PrepareCoreRenewalRequest | | CoreRenewalRequestFromMessage |
* | +------------------------------------+ +-----------------------------------+
* | | ODK_PrepareCoreProvisioningRequest | | CoreProvisioningRequestFromMessage|
* | +------------------------------------+ +-----------------------------------+
* | | ODK_PrepareCommonRequest | | CoreCommonRequestFromMessage |
* +---+------------------------------------+---+-----------------------------------+
* | d | ODK_ParseLicense | s | CreateCoreLicenseResponse |
* | +------------------------------------+ +-----------------------------------+
@@ -47,7 +49,7 @@
* +---+------------------------------------+---+-----------------------------------+
*
*********************************************************************/
/* clang-format on */
// clang-format on
#ifndef WIDEVINE_ODK_INCLUDE_CORE_MESSAGE_TYPES_H_
#define WIDEVINE_ODK_INCLUDE_CORE_MESSAGE_TYPES_H_
@@ -57,7 +59,18 @@
namespace oemcrypto_core_message {
/* @ input/output structs */
// @ input/output structs
/**
* Output structure for CommonRequestFromMessage
* Input structure for CreateCommonResponse
*/
struct ODK_CommonRequest {
uint16_t api_minor_version;
uint16_t api_major_version;
uint32_t nonce;
uint32_t session_id;
};
/**
* Output structure for CoreLicenseRequestFromMessage
@@ -94,6 +107,6 @@ struct ODK_ProvisioningRequest {
std::string device_id;
};
} /* namespace oemcrypto_core_message */
} // namespace oemcrypto_core_message
#endif /* WIDEVINE_ODK_INCLUDE_CORE_MESSAGE_TYPES_H_ */
#endif // WIDEVINE_ODK_INCLUDE_CORE_MESSAGE_TYPES_H_

View File

@@ -1,11 +1,9 @@
/* 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. */
// 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.
/*********************************************************************
* odk.h
*
* OEMCrypto v16 Core Message Serialization library
/**
* @mainpage OEMCrypto v16 Core Message Serialization library
*
* For Widevine Modular DRM, there are six message types between a server and
* a client device: license request and response, provisioning request and
@@ -34,12 +32,30 @@
* ODK library shall be sanitized by the OEMCrypto implementer to prevent
* modification by any process running the REE.
*
* See the documents "Widevine Core Message Serialization" and "License
* Duration and Renewal" for a detailed description of the ODK API. You can
* See the documents
* <a href="../odk">Widevine Core Message Serialization</a>
* and
* <a href="../../lic_duration_and_renewal">License Duration and Renewal</a>
* for a detailed description of the ODK API. You can
* find these documents in the widevine repository as
* docs/Widevine_Core_Message_Serialization.pdf and
* docs/License_Duration_and_Renewal.pdf
*
* @defgroup odk_parser Core Message Parsing and Verification
* Functions that parse core messages and verify they are valid.
* TODO(fredgc): add documentation for parsing functions.
*
* @defgroup odk_packer Core Message Creation
* Functions that create core messages.
* TODO(fredgc): add documentation for packing functions.
*
* @defgroup odk_timer Timer and Clock Functions
* Functions related to enforcing timer and duration restrictions.
* TODO(fredgc): add documentation for timers and clocks.
*
* @defgroup common_types Common Types
* Enumerations and structures that are used by several OEMCrypto and ODK
* functions.
*********************************************************************/
#ifndef WIDEVINE_ODK_INCLUDE_ODK_H_
@@ -54,26 +70,24 @@
extern "C" {
#endif
/*
* ODK_InitializeSessionValues
/// @addtogroup odk_timer
/// @{
/**
* This function initializes the session's data structures. It shall be
* called from OEMCrypto_OpenSession.
*
* Description:
* This function initializes the session's data structures. It shall be
* called from OEMCrypto_OpenSession.
* @param[out] timer_limits: the session's timer limits.
* @param[out] clock_values: the session's clock values.
* @param[out] nonce_values: the session's ODK nonce values.
* @param[in] api_major_version: the API version of OEMCrypto.
* @param[in] session_id: the session id of the newly created session.
*
* Parameters:
* [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_major_version: the API version of OEMCrypto.
* [in] session_id: the session id of the newly created session.
* @retval OEMCrypto_SUCCESS
* @retval OEMCrypto_ERROR_INVALID_CONTEXT
*
* Returns:
* OEMCrypto_SUCCESS
* OEMCrypto_ERROR_INVALID_CONTEXT
*
* Version:
* This method is new in version 16 of the API.
* @version
* This method is new in version 16 of the API.
*/
OEMCryptoResult ODK_InitializeSessionValues(ODK_TimerLimits* timer_limits,
ODK_ClockValues* clock_values,
@@ -81,73 +95,61 @@ OEMCryptoResult ODK_InitializeSessionValues(ODK_TimerLimits* timer_limits,
uint32_t api_major_version,
uint32_t session_id);
/*
* ODK_SetNonceValues
/**
* This function sets the nonce value in the session's nonce structure. It
* shall be called from OEMCrypto_GenerateNonce.
*
* Description:
* This function sets the nonce value in the session's nonce structure. It
* shall be called from OEMCrypto_GenerateNonce.
* @param[in,out] nonce_values: the session's nonce data.
* @param[in] nonce: the new nonce that was just generated.
*
* Parameters:
* [in/out] nonce_values: the session's nonce data.
* [in] nonce: the new nonce that was just generated.
* @retval true on success
*
* Returns:
* true on success
*
* Version:
* This method is new in version 16 of the API.
* @version
* This method is new in version 16 of the API.
*/
OEMCryptoResult ODK_SetNonceValues(ODK_NonceValues* nonce_values,
uint32_t nonce);
/*
* ODK_InitializeClockValues
*
* Description:
* This function initializes the clock values in the session clock_values
* structure. It shall be called from OEMCrypto_PrepAndSignLicenseRequest.
/**
* This function initializes the clock values in the session clock_values
* structure. It shall be called from OEMCrypto_PrepAndSignLicenseRequest.
*
* Parameters:
* [in/out] clock_values: the session's clock data.
* [in] system_time_seconds: the current time on OEMCrypto's monotonic clock.
* @param[in,out] clock_values: the session's clock data.
* @param[in] system_time_seconds: the current time on OEMCrypto's monotonic
* clock.
*
* Returns:
* OEMCrypto_SUCCESS
* OEMCrypto_ERROR_INVALID_CONTEXT
* @retval OEMCrypto_SUCCESS
* @retval OEMCrypto_ERROR_INVALID_CONTEXT
*
* Version:
* This method is new in version 16 of the API.
* @version
* This method is new in version 16 of the API.
*/
OEMCryptoResult ODK_InitializeClockValues(ODK_ClockValues* clock_values,
uint64_t system_time_seconds);
/*
* ODK_ReloadClockValues
/**
* This function sets the values in the clock_values structure. It shall be
* 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.
*
* Description:
* This function sets the values in the clock_values structure. It shall be
* 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.
* @param[in,out] clock_values: the session's clock data.
* @param[in] time_of_license_signed: the value time_license_received from the
* loaded usage entry.
* @param[in] time_of_first_decrypt: the value time_of_first_decrypt from the
* loaded usage entry.
* @param[in] time_of_last_decrypt: the value time_of_last_decrypt from the
* loaded usage entry.
* @param[in] status: the value status from the loaded usage entry.
* @param[in] system_time_seconds: the current time on OEMCrypto's monotonic
* clock.
*
* Parameters:
* [in/out] clock_values: the session's clock data.
* [in] time_of_license_signed: the value time_license_received from the
* loaded usage entry.
* [in] time_of_first_decrypt: the value time_of_first_decrypt from the
* loaded usage entry.
* [in] time_of_last_decrypt: the value time_of_last_decrypt from the loaded
* usage entry.
* [in] status: the value status from the loaded usage entry.
* [in] system_time_seconds: the current time on OEMCrypto's monotonic clock.
* @retval OEMCrypto_SUCCESS
* @retval OEMCrypto_ERROR_INVALID_CONTEXT
*
* Returns:
* OEMCrypto_SUCCESS
* OEMCrypto_ERROR_INVALID_CONTEXT
*
* Version:
* This method is new in version 16 of the API.
* @version
* This method is new in version 16 of the API.
*/
OEMCryptoResult ODK_ReloadClockValues(ODK_ClockValues* clock_values,
uint64_t time_of_license_signed,
@@ -156,173 +158,154 @@ OEMCryptoResult ODK_ReloadClockValues(ODK_ClockValues* clock_values,
enum OEMCrypto_Usage_Entry_Status status,
uint64_t system_time_seconds);
/*
* ODK_AttemptFirstPlayback
/**
* This updates the clock values, and determines if playback may start based
* on the given system time. It uses the values in clock_values to determine
* if this is the first playback for the license or the first playback for
* just this session.
*
* Description:
* This updates the clock values, and determines if playback may start based
* on the given system time. It uses the values in clock_values to determine
* if this is the first playback for the license or the first playback for
* just this session.
* This shall be called from the first call in a session to any of
* OEMCrypto_DecryptCENC or any of the OEMCrypto_Generic* functions.
*
* This shall be called from the first call in a session to any of
* OEMCrypto_DecryptCENC or any of the OEMCrypto_Generic* functions.
* 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.
*
* 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.
* @param[in] system_time_seconds: the current time on OEMCrypto's monotonic
* clock, in seconds.
* @param[in] timer_limits: timer limits specified in the license.
* @param[in,out] clock_values: the sessions clock values.
* @param[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.
*
* Parameters:
* [in] system_time_seconds: the current time on OEMCrypto's monotonic clock,
* in seconds.
* [in] timer_limits: timer limits specified in the license.
* [in/out] clock_values: the sessions clock values.
* [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.
*
* Returns:
* ODK_SET_TIMER: Success. The timer should be reset to the specified value
* and playback is allowed.
* ODK_DISABLE_TIMER: Success, but disable timer. Unlimited playback is
* @retval ODK_SET_TIMER: Success. The timer should be reset to the specified
* value and playback is allowed.
* @retval ODK_DISABLE_TIMER: Success, but disable timer. Unlimited playback is
* allowed.
* ODK_TIMER_EXPIRED: Set timer as disabled. Playback is not allowed.
* @retval ODK_TIMER_EXPIRED: Set timer as disabled. Playback is not allowed.
*
* Version:
* This method is new in version 16 of the API.
* @version
* This method is new in version 16 of the API.
*/
OEMCryptoResult ODK_AttemptFirstPlayback(uint64_t system_time_seconds,
const ODK_TimerLimits* timer_limits,
ODK_ClockValues* clock_values,
uint64_t* timer_value);
/*
* ODK_UpdateLastPlaybackTime
/**
* Vendors that do not implement their own timer should call
* ODK_UpdateLastPlaybackTime regularly during playback. This updates the
* clock values, and determines if playback may continue based on the given
* system time. This shall be called from any of OEMCrypto_DecryptCENC or any
* of the OEMCrypto_Generic* functions.
*
* Description:
* Vendors that do not implement their own timer should call
* ODK_UpdateLastPlaybackTime regularly during playback. This updates the
* clock values, and determines if playback may continue based on the given
* system time. This shall be called from any of OEMCrypto_DecryptCENC or any
* of the OEMCrypto_Generic* functions.
* All Vendors (i.e. those that do or do not implement their own timer) shall
* call ODK_UpdateLastPlaybackTime from the function
* OEMCrypto_UpdateUsageEntry before updating the usage entry so that the
* clock values are accurate.
*
* All Vendors (i.e. those that do or do not implement their own timer) shall
* call ODK_UpdateLastPlaybackTime from the function
* OEMCrypto_UpdateUsageEntry before updating the usage entry so that the
* clock values are accurate.
* @param[in] system_time_seconds: the current time on OEMCrypto's monotonic
* clock, in seconds.
* @param[in] timer_limits: timer limits specified in the license.
* @param[in,out] clock_values: the sessions clock values.
*
* Parameters:
* [in] system_time_seconds: the current time on OEMCrypto's monotonic clock,
* in seconds.
* [in] timer_limits: timer limits specified in the license.
* [in/out] clock_values: the sessions clock values.
* @retval OEMCrypto_SUCCESS: Success. Playback is allowed.
* @retval ODK_TIMER_EXPIRED: Set timer as disabled. Playback is not allowed.
*
* Returns:
* OEMCrypto_SUCCESS: Success. Playback is allowed.
* ODK_TIMER_EXPIRED: Set timer as disabled. Playback is not allowed.
*
* Version:
* This method is new in version 16 of the API.
* @version
* This method is new in version 16 of the API.
*/
OEMCryptoResult ODK_UpdateLastPlaybackTime(uint64_t system_time_seconds,
const ODK_TimerLimits* timer_limits,
ODK_ClockValues* clock_values);
/*
* ODK_DeactivateUsageEntry
*
* Description:
* This function modifies the session's clock values to indicate that the
* license has been deactivated. It shall be called from
* OEMCrypto_DeactivateUsageEntry
/**
* This function modifies the session's clock values to indicate that the
* license has been deactivated. It shall be called from
* OEMCrypto_DeactivateUsageEntry
*
* Parameters:
* [in/out] clock_values: the sessions clock values.
* @param[in,out] clock_values: the sessions clock values.
*
* Returns:
* OEMCrypto_SUCCESS
* OEMCrypto_ERROR_INVALID_CONTEXT
* @retval OEMCrypto_SUCCESS
* @retval OEMCrypto_ERROR_INVALID_CONTEXT
*
* Version:
* This method is new in version 16 of the API.
* @version
* This method is new in version 16 of the API.
*/
OEMCryptoResult ODK_DeactivateUsageEntry(ODK_ClockValues* clock_values);
/*
* ODK_PrepareCoreLicenseRequest
/// @}
/// @addtogroup odk_packer
/// @{
/**
* Modifies the message to include a core license request at the beginning of
* the message buffer. The values in nonce_values are used to populate the
* message.
*
* Description:
* Modifies the message to include a core license request at the beginning of
* the message buffer. The values in nonce_values are used to populate the
* message.
* This shall be called by OEMCrypto from OEMCrypto_PrepAndSignLicenseRequest.
*
* This shall be called by OEMCrypto from OEMCrypto_PrepAndSignLicenseRequest.
* 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.
*
* 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.
* @param[in,out] message: Pointer to memory for the entire message. Modified by
* the ODK library.
* @param[in] message_length: length of the entire message buffer.
* @param[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.
* @param[in] nonce_values: pointer to the session's nonce data.
*
* Parameters:
* [in/out] message: Pointer to memory for the entire message. Modified by
* the ODK library.
* [in] message_length: length of the entire message buffer.
* [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.
* @retval OEMCrypto_SUCCESS
* @retval OEMCrypto_ERROR_SHORT_BUFFER: core_message_size is too small
* @retval OEMCrypto_ERROR_INVALID_CONTEXT
*
* Returns:
* OEMCrypto_SUCCESS
* 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.
* @version
* This method is new in version 16 of the API.
*/
OEMCryptoResult ODK_PrepareCoreLicenseRequest(
uint8_t* message, size_t message_length, size_t* core_message_size,
const ODK_NonceValues* nonce_values);
/*
* ODK_PrepareCoreRenewalRequest
/**
* Modifies the message to include a core renewal 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. The nonce_values
* should match those from the license.
*
* Description:
* Modifies the message to include a core renewal 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. The nonce_values
* should match those from the license.
* This shall be called by OEMCrypto from OEMCrypto_PrepAndSignRenewalRequest.
*
* This shall be called by OEMCrypto from OEMCrypto_PrepAndSignRenewalRequest.
* 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.
*
* 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.
*
* 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.
* @param[in,out] message: Pointer to memory for the entire message. Modified by
* the ODK library.
* @param[in] message_length: length of the entire message buffer.
* @param[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.
* @param[in,out] nonce_values: pointer to the session's nonce data.
* @param[in,out] clock_values: the session's clock values.
* @param[in] system_time_seconds: the current time on OEMCrypto's clock, in
* seconds.
*
* Parameters:
* [in/out] message: Pointer to memory for the entire message. Modified by
* the ODK library.
* [in] message_length: length of the entire message buffer.
* [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/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.
* @retval OEMCrypto_SUCCESS
* @retval OEMCrypto_ERROR_SHORT_BUFFER: core_message_size is too small
* @retval OEMCrypto_ERROR_INVALID_CONTEXT
*
* Returns:
* OEMCrypto_SUCCESS
* 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.
* @version
* This method is new in version 16 of the API.
*/
OEMCryptoResult ODK_PrepareCoreRenewalRequest(uint8_t* message,
size_t message_length,
@@ -331,77 +314,72 @@ OEMCryptoResult ODK_PrepareCoreRenewalRequest(uint8_t* message,
ODK_ClockValues* clock_values,
uint64_t system_time_seconds);
/*
* ODK_PrepareCoreProvisioningRequest
/**
* Modifies the message to include a core provisioning request at the
* beginning of the message buffer. The values in nonce_values are used to
* populate the message.
*
* Description:
* Modifies the message to include a core provisioning request at the
* 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.
*
* This shall be called by OEMCrypto from
* OEMCrypto_PrepAndSignProvisioningRequest.
* The buffer device_id shall be the same string returned by
* OEMCrypto_GetDeviceID. The device ID shall be unique to the device, and
* stable across reboots and factory resets for an L1 device.
*
* The buffer device_id shall be the same string returned by
* OEMCrypto_GetDeviceID. The device ID shall be unique to the device, and
* stable across reboots and factory resets for an L1 device.
* 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.
*
* 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.
* @param[in,out] message: Pointer to memory for the entire message. Modified by
* the ODK library.
* @param[in] message_length: length of the entire message buffer.
* @param[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.
* @param[in] nonce_values: pointer to the session's nonce data.
* @param[in] device_id: For devices with a keybox, this is the device ID from
* the keybox. For devices with an OEM Certificate, this is a device
* unique id string.
* @param[in] device_id_length: length of device_id. The device ID can be at
* most 64 bytes.
*
* Parameters:
* [in/out] message: Pointer to memory for the entire message. Modified by
* the ODK library.
* [in] message_length: length of the entire message buffer.
* [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] device_id: For devices with a keybox, this is the device ID from the
* keybox. For devices with an OEM Certificate, this is a device unique
* id string.
* [in] device_id_length: length of device_id. The device ID can be at most
* 64 bytes.
* @retval OEMCrypto_SUCCESS
* @retval OEMCrypto_ERROR_SHORT_BUFFER: core_message_size is too small
* @retval OEMCrypto_ERROR_INVALID_CONTEXT
*
* Returns:
* OEMCrypto_SUCCESS
* 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.
* @version
* This method is new in version 16 of the API.
*/
OEMCryptoResult ODK_PrepareCoreProvisioningRequest(
uint8_t* message, size_t message_length, size_t* core_message_size,
const ODK_NonceValues* nonce_values, const uint8_t* device_id,
size_t device_id_length);
/*
* ODK_InitializeV15Values
/// @}
/// @addtogroup odk_timer
/// @{
/**
* This function sets all limits in the timer_limits struct to the
* key_duration and initializes the other values. The field
* nonce_values.api_major_version will be set to 15. It shall be called from
* OEMCrypto_LoadKeys when loading a legacy license.
*
* 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_major_version will be set to 15. It shall be called from
* OEMCrypto_LoadKeys when loading a legacy license.
* @param[out] timer_limits: The session's timer limits.
* @param[in,out] clock_values: The session's clock values.
* @param[in,out] nonce_values: The session's ODK nonce values.
* @param[in] key_duration: The duration from the first key's key control
* block. In practice, the key duration is the same for all keys and is
* the same as the license duration.
* @param[in] system_time_seconds: The current time on the system clock, as
* described in the document "License Duration and Renewal".
*
* Parameters:
* [out] timer_limits: The session's timer limits.
* [in/out] clock_values: The session's clock values.
* [in/out] nonce_values: The session's ODK nonce values.
* [in] key_duration: The duration from the first key's key control block. In
* practice, the key duration is the same for all keys and is the same
* as the license duration.
* [in] system_time_seconds: The current time on the system clock, as
* described in the document "License Duration and Renewal".
* @retval OEMCrypto_SUCCESS
* @retval OEMCrypto_ERROR_INVALID_CONTEXT
*
* Returns:
* OEMCrypto_SUCCESS
* OEMCrypto_ERROR_INVALID_CONTEXT
*
* Version:
* This method is new in version 16 of the API.
* @version
* This method is new in version 16 of the API.
*/
OEMCryptoResult ODK_InitializeV15Values(ODK_TimerLimits* timer_limits,
ODK_ClockValues* clock_values,
@@ -409,40 +387,35 @@ OEMCryptoResult ODK_InitializeV15Values(ODK_TimerLimits* timer_limits,
uint32_t key_duration,
uint64_t system_time_seconds);
/*
* ODK_RefreshV15Values
/**
* 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.
*
* Description:
* 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.
*
* 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.
* @param[in] timer_limits: The session's timer limits.
* @param[in,out] clock_values: The session's clock values.
* @param[in] nonce_values: The session's ODK nonce values.
* @param[in] system_time_seconds: The current time on the system clock, as
* described in the document "License Duration and Renewal".
* @param[in] new_key_duration: The duration from the first
* OEMCrypto_KeyRefreshObject in key_array.
* @param[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.
*
* Parameters:
* [in] timer_limits: The session's timer limits.
* [in/out] clock_values: The session's clock values.
* [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.
*
* Returns:
* OEMCrypto_SUCCESS
* OEMCrypto_ERROR_UNKNOWN_FAILURE
* ODK_SET_TIMER: Success. The timer should be reset to the specified value
* and playback is allowed.
* ODK_DISABLE_TIMER: Success, but disable timer. Unlimited playback is
* @retval OEMCrypto_SUCCESS
* @retval OEMCrypto_ERROR_UNKNOWN_FAILURE
* @retval ODK_SET_TIMER: Success. The timer should be reset to the specified
* value and playback is allowed.
* @retval ODK_DISABLE_TIMER: Success, but disable timer. Unlimited playback is
* allowed.
* ODK_TIMER_EXPIRED: Set timer as disabled. Playback is not allowed.
* @retval ODK_TIMER_EXPIRED: Set timer as disabled. Playback is not allowed.
*
* Version:
* This method is new in version 16 of the API.
* @version
* This method is new in version 16 of the API.
*/
OEMCryptoResult ODK_RefreshV15Values(const ODK_TimerLimits* timer_limits,
ODK_ClockValues* clock_values,
@@ -451,67 +424,70 @@ OEMCryptoResult ODK_RefreshV15Values(const ODK_TimerLimits* timer_limits,
uint32_t new_key_duration,
uint64_t* timer_value);
/*
* ODK_ParseLicense
/// @}
/// @addtogroup odk_parser
/// @{
/**
* The function ODK_ParseLicense will parse the message and verify fields in
* the message.
*
* Description:
* 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 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 not 16, then ODK_UNSUPPORTED_API is returned.
*
* 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 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.
*
* 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.
*
* 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 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.
*
* If usage_entry_present is true, then ODK_ParseLicense shall verify that
* the pst in the license has a nonzero length.
* @param[in] message: pointer to the message buffer.
* @param[in] message_length: length of the entire message buffer.
* @param[in] core_message_size: length of the core message, at the beginning of
* the message buffer.
* @param[in] initial_license_load: true when called for OEMCrypto_LoadLicense
* and false when called for OEMCrypto_ReloadLicense.
* @param[in] usage_entry_present: true if the session has a new usage entry
* associated with it created via OEMCrypto_CreateNewUsageEntry.
* @param[in] request_hash: the hash of the license request core message. This
* was computed by OEMCrypto when the license request was signed.
* @param[in,out] timer_limits: The session's timer limits. These will be
* updated.
* @param[in,out] clock_values: The session's clock values. These will be
* updated.
* @param[in,out] nonce_values: The session's nonce values. These will be
* updated.
* @param[out] parsed_license: the destination for the data.
*
* Parameters:
* [in] message: pointer to the message buffer.
* [in] message_length: length of the entire message buffer.
* [in] core_message_size: length of the core message, at the beginning of
* the message buffer.
* [in] initial_license_load: true when called for OEMCrypto_LoadLicense and
* 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.
* [out] parsed_license: the destination for the data.
* @retval OEMCrypto_SUCCESS
* @retval 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.
* @retval ODK_UNSUPPORTED_API
* @retval OEMCrypto_ERROR_INVALID_NONCE
*
* 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_UNSUPPORTED_API
* OEMCrypto_ERROR_INVALID_NONCE
*
* Version:
* This method is new in version 16 of the API.
* @version
* This method is new in version 16 of the API.
*/
OEMCryptoResult ODK_ParseLicense(
const uint8_t* message, size_t message_length, size_t core_message_length,
@@ -520,59 +496,55 @@ OEMCryptoResult ODK_ParseLicense(
ODK_TimerLimits* timer_limits, ODK_ClockValues* clock_values,
ODK_NonceValues* nonce_values, ODK_ParsedLicense* parsed_license);
/*
* ODK_ParseRenewal
/**
* 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.
*
* Description:
* 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.
*
* 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
* continue, then ODK_TIMER_EXPIRED is returned.
*
* After parsing the message, this function updates the clock_values based on
* the timer_limits and the current system time. If playback may not
* continue, then ODK_TIMER_EXPIRED is returned.
* If playback may continue, a return value of ODK_SET_TIMER or
* ODK_TIMER_EXPIRED is returned. If the return value is ODK_SET_TIMER, then
* playback may continue until the timer expires. If the return value is
* ODK_DISABLE_TIMER, then playback time is not limited.
*
* If playback may continue, a return value of ODK_SET_TIMER or
* ODK_TIMER_EXPIRED is returned. If the return value is ODK_SET_TIMER, then
* playback may continue until the timer expires. If the return value is
* ODK_DISABLE_TIMER, then playback time is not limited.
* If OEMCrypto uses a hardware timer, and this function returns
* ODK_SET_TIMER, then OEMCrypto shall set the timer to the value pointed to
* by timer_value.
*
* If OEMCrypto uses a hardware timer, and this function returns
* ODK_SET_TIMER, then OEMCrypto shall set the timer to the value pointed to
* by timer_value.
* @param[in] message: pointer to the message buffer.
* @param[in] message_length: length of the entire message buffer.
* @param[in] core_message_size: length of the core message, at the beginning of
* the message buffer.
* @param[in] nonce_values: pointer to the session's nonce data.
* @param[in] system_time_seconds: the current time on OEMCrypto's clock, in
* seconds.
* @param[in] timer_limits: timer limits specified in the license.
* @param[in,out] clock_values: the sessions clock values.
* @param[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.
*
* Parameters:
* [in] message: pointer to the message buffer.
* [in] message_length: length of the entire message buffer.
* [in] core_message_size: length of the core message, at the beginning of
* the message buffer.
* [in] nonce_values: pointer to the session's nonce data.
* [in] system_time_seconds: the current time on OEMCrypto's clock, in
* seconds.
* [in] timer_limits: timer limits specified in the license.
* [in/out] clock_values: the sessions clock values.
* [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.
*
* Returns:
* 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
* @retval ODK_ERROR_CORE_MESSAGE: the message did not parse correctly, or there
* were other incorrect values. An error should be returned to the CDM
* layer.
* @retval ODK_SET_TIMER: Success. The timer should be reset to the specified
* timer value.
* @retval 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
* @retval ODK_TIMER_EXPIRED: Set timer as disabled. Playback is not allowed.
* @retval ODK_UNSUPPORTED_API
* @retval ODK_STALE_RENEWAL: This renewal is not the most recently signed. It
* is rejected.
* @retval OEMCrypto_ERROR_INVALID_NONCE
*
* Version:
* This method is new in version 16 of the API.
* @version
* This method is new in version 16 of the API.
*/
OEMCryptoResult ODK_ParseRenewal(const uint8_t* message, size_t message_length,
size_t core_message_length,
@@ -582,56 +554,54 @@ OEMCryptoResult ODK_ParseRenewal(const uint8_t* message, size_t message_length,
ODK_ClockValues* clock_values,
uint64_t* timer_value);
/*
* ODK_ParseProvisioning
/**
* The function ODK_ParseProvisioning will parse the message and verify the
* nonce values match those in the license.
*
* Description:
* The function ODK_ParseProvisioning will parse the message and verify the
* nonce values match those in the license.
* If the message does not parse correctly, ODK_ParseProvisioning will return
* an error that OEMCrypto should return to the CDM layer above.
*
* 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.
*
* 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.
*
* 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.
*
* 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.
* @param[in] message: pointer to the message buffer.
* @param[in] message_length: length of the entire message buffer.
* @param[in] core_message_size: length of the core message, at the beginning of
* the message buffer.
* @param[in] nonce_values: pointer to the session's nonce data.
* @param[in] device_id: a pointer to a buffer containing the device ID of the
* device. The ODK function will verify it matches that in the message.
* @param[in] device_id_length: the length of the device ID.
* @param[out] parsed_response: destination for the parse data.
*
* Parameters:
* [in] message: pointer to the message buffer.
* [in] message_length: length of the entire message buffer.
* [in] core_message_size: length of the core message, at the beginning of
* the message buffer.
* [in] nonce_values: pointer to the session's nonce data.
* [in] device_id: a pointer to a buffer containing the device ID of the
* device. The ODK function will verify it matches that in the message.
* [in] device_id_length: the length of the device ID.
* [out] parsed_response: destination for the parse data.
* @retval OEMCrypto_SUCCESS
* @retval ODK_ERROR_CORE_MESSAGE: the message did not parse correctly, or there
* were other incorrect values. An error should be returned to the CDM
* layer.
* @retval ODK_UNSUPPORTED_API
* @retval OEMCrypto_ERROR_INVALID_NONCE
*
* Returns:
* OEMCrypto_SUCCESS
* 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
*
* Version:
* This method is new in version 16 of the API.
* @version
* This method is new in version 16 of the API.
*/
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);
/// @}
#ifdef __cplusplus
}
#endif
#endif /* WIDEVINE_ODK_INCLUDE_ODK_H_ */
#endif // WIDEVINE_ODK_INCLUDE_ODK_H_

View File

@@ -0,0 +1,14 @@
// 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_INCLUDE_ODK_ATTRIBUTES_H_
#define WIDEVINE_ODK_INCLUDE_ODK_ATTRIBUTES_H_
#if defined(__GNUC__) || defined(__clang__)
#define UNUSED __attribute__((__unused__))
#else
#define UNUSED
#endif
#endif // WIDEVINE_ODK_INCLUDE_ODK_ATTRIBUTES_H_

View File

@@ -1,6 +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. */
// 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_INCLUDE_ODK_STRUCTS_H_
#define WIDEVINE_ODK_INCLUDE_ODK_STRUCTS_H_
@@ -15,51 +15,51 @@
#define ODK_MINOR_VERSION 3
/* ODK Version string. Date changed automatically on each release. */
#define ODK_RELEASE_DATE "ODK v16.3 2020-06-02"
#define ODK_RELEASE_DATE "ODK v16.3 2020-08-18"
/* The lowest version number for an ODK message. */
/* The lowest version number for an ODK message. */
#define ODK_FIRST_VERSION 16
/* Some useful constants. */
#define ODK_DEVICE_ID_LEN_MAX 64
#define ODK_SHA256_HASH_SIZE 32
/*
* ODK_TimerLimits Structure
/// @addtogroup odk_timer
/// @{
/**
* 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.
*
* 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.
* @param soft_enforce_rental_duration: A boolean controlling the soft or hard
* enforcement of rental duration.
* @param soft_enforce_playback_duration: A boolean controlling the soft or hard
* enforcement of playback duration.
* @param 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.
* @param 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.
* @param 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.
* @param 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.
*
* 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.
* @version
* This struct changed in API version 16.2.
*/
typedef struct {
bool soft_enforce_rental_duration;
@@ -70,47 +70,44 @@ typedef struct {
uint64_t initial_renewal_duration_seconds;
} ODK_TimerLimits;
/*
* ODK_ClockValues Structure
/**
* 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.
*
* 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".
*
* 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".
* @param 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.
* @param 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.
* @param 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.
* @param 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.
* @param 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.
* @param timer_status: Used internally by the ODK library to indicate the
* current timer status.
* @param status: The license or usage entry status. This value shall be stored
* and reloaded with usage entry.
*
* 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.
* @version
* This struct changed in API version 16.2.
*/
typedef struct {
uint64_t time_of_license_signed;
@@ -122,34 +119,30 @@ typedef struct {
enum OEMCrypto_Usage_Entry_Status status;
} ODK_ClockValues;
/*
* ODK_NonceValues Structure
/**
* 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.
*
* 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.
* @param api_major_version: the API version of the license. This is initialized
* to the API version of the ODK library, but may be lower.
* @param 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.
* @param nonce: a randomly generated number used to prevent replay attacks.
* @param 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.
*
* 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.
* @version
* This struct changed in API version 16.2.
*/
typedef struct {
uint16_t api_minor_version;
@@ -158,29 +151,31 @@ typedef struct {
uint32_t session_id;
} ODK_NonceValues;
/*
* ODK_ParsedLicense Structure
/// @}
/// @addtogroup odk_parser
/// @{
/**
* 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.
*
* 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.
* @param enc_mac_keys_iv: IV for decrypting new mac_key. Size is 128 bits.
* @param enc_mac_keys: encrypted mac_keys for generating new mac_keys. Size is
* 512 bits.
* @param pst: the Provider Session Token.
* @param srm_restriction_data: optional data specifying the minimum SRM
* version.
* @param license_type: specifies if the license contains content keys or
* entitlement keys.
* @param nonce_required: indicates if the license requires a nonce.
* @param timer_limits: time limits of the for the license.
* @param key_array_length: number of keys present.
* @param key_array: set of keys to be installed.
*
* 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.
* @version
* This struct changed in API version 16.2.
*/
typedef struct {
OEMCrypto_Substring enc_mac_keys_iv;
@@ -194,22 +189,19 @@ typedef struct {
OEMCrypto_KeyObject key_array[ODK_MAX_NUM_KEYS];
} ODK_ParsedLicense;
/*
* ODK_ParsedProvisioning Structure
/**
* 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.
*
* 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.
* @param key_type: indicates if this key is an RSA or ECC private key.
* @param enc_private_key: encrypted private key for the DRM certificate.
* @param enc_private_key_iv: IV for decrypting new private key. Size is 128
* bits.
* @param encrypted_message_key: used for provisioning 3.0 to derive keys.
*
* 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.
* @version
* This struct changed in API version 16.2.
*/
typedef struct {
OEMCrypto_PrivateKeyType key_type;
@@ -218,4 +210,6 @@ typedef struct {
OEMCrypto_Substring encrypted_message_key; /* Used for Prov 3.0 */
} ODK_ParsedProvisioning;
#endif /* WIDEVINE_ODK_INCLUDE_ODK_STRUCTS_H_ */
/// @}
#endif // WIDEVINE_ODK_INCLUDE_ODK_STRUCTS_H_

View File

@@ -1,13 +1,13 @@
/* Copyright 2019 Google LLC. All rights reserved. This file is distributed */
/* under the Widevine Master License Agreement. */
// 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. */
// 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. */
// 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_ */
#endif // WIDEVINE_ODK_INCLUDE_ODK_TARGET_H_

View File

@@ -39,8 +39,8 @@ bool ParseRequest(uint32_t message_type,
reinterpret_cast<const uint8_t*>(oemcrypto_core_message.c_str());
const size_t buf_length = oemcrypto_core_message.size();
Message* msg = nullptr;
AllocateMessage(&msg, message_block);
uint8_t blk[SIZE_OF_MESSAGE_STRUCT];
Message* msg = reinterpret_cast<Message*>(blk);
InitMessage(msg, const_cast<uint8_t*>(buf), buf_length);
SetSize(msg, buf_length);
@@ -70,7 +70,8 @@ bool ParseRequest(uint32_t message_type,
// For v16, a release and a renewal use the same message structure.
// However, for future API versions, the release might be a separate
// message. Otherwise, we expect an exact match of message types.
if (core_message.message_type != message_type &&
if (message_type != ODK_Common_Request_Type &&
core_message.message_type != message_type &&
!(message_type == ODK_Renewal_Request_Type &&
core_message.message_type == ODK_Release_Request_Type)) {
return false;
@@ -129,5 +130,13 @@ bool CoreProvisioningRequestFromMessage(
return true;
}
bool CoreCommonRequestFromMessage(const std::string& oemcrypto_core_message,
ODK_CommonRequest* common_request) {
const auto unpacker = Unpack_ODK_PreparedCommonRequest;
ODK_PreparedCommonRequest prepared_common = {};
return ParseRequest(ODK_Common_Request_Type, oemcrypto_core_message,
common_request, &prepared_common, unpacker);
}
} // namespace deserialize
} // namespace oemcrypto_core_message

View File

@@ -50,8 +50,8 @@ bool CreateResponse(uint32_t message_type, const S& core_request,
static constexpr size_t BUF_CAPACITY = 2048;
std::vector<uint8_t> buf(BUF_CAPACITY, 0);
Message* msg = nullptr;
AllocateMessage(&msg, message_block);
uint8_t blk[SIZE_OF_MESSAGE_STRUCT];
Message* msg = reinterpret_cast<Message*>(blk);
InitMessage(msg, buf.data(), buf.capacity());
packer(msg, &response);
if (!ValidMessage(msg)) {

View File

@@ -90,17 +90,14 @@ bool CreateCoreLicenseResponseFromProto(const std::string& serialized_license,
parsed_lic.enc_mac_keys = GetOecSubstring(serialized_license, k.key());
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::CONTENT:
case video_widevine::License_KeyContainer::OPERATOR_SESSION:
case video_widevine::License_KeyContainer::ENTITLEMENT: {
any_entitlement = true;
if (k.type() == video_widevine::License_KeyContainer::ENTITLEMENT) {
any_entitlement = true;
} else {
any_content = true;
}
if (parsed_lic.key_array_length >= ODK_MAX_NUM_KEYS) {
return false;
}

View File

@@ -1,6 +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. */
// 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.h"
@@ -19,7 +19,7 @@
static OEMCryptoResult ODK_PrepareRequest(
uint8_t* message, size_t message_length, size_t* core_message_length,
uint32_t message_type, const ODK_NonceValues* nonce_values,
ODK_MessageType message_type, const ODK_NonceValues* nonce_values,
void* prepared_request_buffer, size_t prepared_request_buffer_length) {
if (nonce_values == NULL || core_message_length == NULL ||
prepared_request_buffer == NULL ||
@@ -27,8 +27,8 @@ static OEMCryptoResult ODK_PrepareRequest(
return ODK_ERROR_CORE_MESSAGE;
}
Message* msg = NULL;
AllocateMessage(&msg, message_block);
uint8_t blk[SIZE_OF_MESSAGE_STRUCT];
Message* msg = (Message*)blk;
InitMessage(msg, message, *core_message_length);
/* The core message should be at the beginning of the buffer, and with a
@@ -95,18 +95,24 @@ static OEMCryptoResult ODK_PrepareRequest(
static OEMCryptoResult ODK_ParseResponse(
const uint8_t* message, size_t message_length, size_t core_message_length,
uint32_t message_type, const ODK_NonceValues* nonce_values,
ODK_MessageType message_type, const ODK_NonceValues* nonce_values,
void* response_buffer, uint32_t response_buffer_length) {
if (message == NULL || response_buffer == NULL ||
core_message_length > message_length) {
return ODK_ERROR_CORE_MESSAGE;
}
Message* msg = NULL;
AllocateMessage(&msg, message_block);
uint8_t blk[SIZE_OF_MESSAGE_STRUCT];
Message* msg = (Message*)blk;
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wcast-qual"
/* We initialize the message buffer with a size of the entire message
* length. */
/* TODO(b/164486737): Fix the cast-qual warning */
InitMessage(msg, (uint8_t*)message, message_length);
#pragma GCC diagnostic pop
/* The core message should be at the beginning of the buffer, and with a
* shorter length. The core message is the part we are parsing. */
SetSize(msg, core_message_length);
@@ -168,7 +174,7 @@ OEMCryptoResult ODK_PrepareCoreLicenseRequest(
return ODK_ERROR_CORE_MESSAGE;
}
ODK_PreparedLicenseRequest license_request = {
{0},
{0, 0, {}},
};
return ODK_PrepareRequest(
message, message_length, core_message_length, ODK_License_Request_Type,
@@ -197,7 +203,7 @@ OEMCryptoResult ODK_PrepareCoreRenewalRequest(uint8_t* message,
return OEMCrypto_SUCCESS;
}
ODK_PreparedRenewalRequest renewal_request = {{0}, 0};
ODK_PreparedRenewalRequest renewal_request = {{0, 0, {}}, 0};
/* First, we compute the time this request was made relative to the playback
* clock. */
if (clock_values->time_of_first_decrypt == 0) {
@@ -231,14 +237,14 @@ OEMCryptoResult ODK_PrepareCoreProvisioningRequest(
return ODK_ERROR_CORE_MESSAGE;
}
ODK_PreparedProvisioningRequest provisioning_request = {
{0},
{0, 0, {}},
0,
{0},
};
if (device_id_length > sizeof(provisioning_request.device_id)) {
return ODK_ERROR_CORE_MESSAGE;
}
provisioning_request.device_id_length = device_id_length;
provisioning_request.device_id_length = (uint32_t)device_id_length;
if (device_id) {
memcpy(provisioning_request.device_id, device_id, device_id_length);
}
@@ -261,7 +267,9 @@ OEMCryptoResult ODK_ParseLicense(
return ODK_ERROR_CORE_MESSAGE;
}
ODK_LicenseResponse license_response = {{{0}}, parsed_license, {0}};
ODK_LicenseResponse license_response = {{{0, 0, {}}}, NULL, {0}};
license_response.parsed_license = parsed_license;
const OEMCryptoResult err = ODK_ParseResponse(
message, message_length, core_message_length, ODK_License_Response_Type,
NULL, &license_response, sizeof(ODK_LicenseResponse));
@@ -342,7 +350,7 @@ OEMCryptoResult ODK_ParseRenewal(const uint8_t* message, size_t message_length,
}
ODK_RenewalResponse renewal_response = {
{{0}, 0},
{{0, 0, {}}, 0},
0,
};
const OEMCryptoResult err = ODK_ParseResponse(
@@ -378,8 +386,9 @@ OEMCryptoResult ODK_ParseProvisioning(
return ODK_ERROR_CORE_MESSAGE;
}
ODK_ProvisioningResponse provisioning_response = {{{0}, 0, {0}},
parsed_response};
ODK_ProvisioningResponse provisioning_response = {{{0, 0, {}}, 0, {0}}, NULL};
provisioning_response.parsed_provisioning = parsed_response;
if (device_id_length > ODK_DEVICE_ID_LEN_MAX) {
return ODK_ERROR_CORE_MESSAGE;
}

View File

@@ -1,6 +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. */
// 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_ASSERT_H_
#define WIDEVINE_ODK_SRC_ODK_ASSERT_H_
@@ -21,4 +21,4 @@ extern "C" {
}
#endif
#endif /* WIDEVINE_ODK_SRC_ODK_ASSERT_H_ */
#endif // WIDEVINE_ODK_SRC_ODK_ASSERT_H_

View File

@@ -1,6 +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. */
// 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_ENDIAN_H_
#define WIDEVINE_ODK_SRC_ODK_ENDIAN_H_
@@ -15,7 +15,7 @@ extern "C" {
#define oemcrypto_be32toh be32toh
#define oemcrypto_htobe64 htobe64
#define oemcrypto_be64toh be64toh
#else /* defined(__linux__) || defined(__ANDROID__) */
#else /* defined(__linux__) || defined(__ANDROID__) */
uint32_t oemcrypto_htobe32(uint32_t u32);
uint32_t oemcrypto_be32toh(uint32_t u32);
uint64_t oemcrypto_htobe64(uint64_t u64);
@@ -26,4 +26,4 @@ uint64_t oemcrypto_be64toh(uint64_t u64);
}
#endif
#endif /* WIDEVINE_ODK_SRC_ODK_ENDIAN_H_ */
#endif // WIDEVINE_ODK_SRC_ODK_ENDIAN_H_

View File

@@ -1,6 +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. */
// 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 <stddef.h>
#include <stdint.h>

View File

@@ -1,6 +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. */
// 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_OVERFLOW_H_
#define WIDEVINE_ODK_SRC_ODK_OVERFLOW_H_
@@ -20,4 +20,4 @@ int odk_add_overflow_ux(size_t a, size_t b, size_t* c);
}
#endif
#endif /* WIDEVINE_ODK_SRC_ODK_OVERFLOW_H_ */
#endif // WIDEVINE_ODK_SRC_ODK_OVERFLOW_H_

View File

@@ -1,6 +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. */
// 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
@@ -193,6 +193,10 @@ void Unpack_ODK_PreparedProvisioningRequest(
UnpackArray(msg, &obj->device_id[0], sizeof(obj->device_id));
}
void Unpack_ODK_PreparedCommonRequest(Message* msg,
ODK_PreparedCommonRequest* obj) {
Unpack_ODK_CoreMessage(msg, &obj->core_message);
}
/* @@ odk deserialize */
void Unpack_ODK_LicenseResponse(Message* msg, ODK_LicenseResponse* obj) {

View File

@@ -1,6 +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. */
// 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
@@ -43,7 +43,10 @@ void Unpack_ODK_PreparedRenewalRequest(Message* msg,
void Unpack_ODK_PreparedProvisioningRequest(
Message* msg, ODK_PreparedProvisioningRequest* obj);
void Unpack_ODK_PreparedCommonRequest(Message* msg,
ODK_PreparedCommonRequest* obj);
#ifdef __cplusplus
} /* extern "C" */
} // extern "C"
#endif
#endif /* WIDEVINE_ODK_SRC_ODK_SERIALIZE_H_ */
#endif // WIDEVINE_ODK_SRC_ODK_SERIALIZE_H_

View File

@@ -1,6 +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. */
// 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_STRUCTS_PRIV_H_
#define WIDEVINE_ODK_SRC_ODK_STRUCTS_PRIV_H_
@@ -22,9 +22,11 @@ typedef enum {
ODK_Provisioning_Request_Type = 5,
ODK_Provisioning_Response_Type = 6,
/* Reserve future message types to support forward compatibility. */
// Reserve future message types to support forward compatibility.
ODK_Release_Request_Type = 7,
ODK_Release_Response_Type = 8,
ODK_Common_Request_Type = 9,
ODK_Common_Response_Type = 10,
} ODK_MessageType;
typedef struct {
@@ -48,6 +50,10 @@ typedef struct {
uint8_t device_id[ODK_DEVICE_ID_LEN_MAX];
} ODK_PreparedProvisioningRequest;
typedef struct {
ODK_CoreMessage core_message;
} ODK_PreparedCommonRequest;
typedef struct {
ODK_PreparedLicenseRequest request;
ODK_ParsedLicense* parsed_license;
@@ -64,33 +70,32 @@ typedef struct {
ODK_ParsedProvisioning* parsed_provisioning;
} ODK_ProvisioningResponse;
/* These are the sum of sizeof of each individual member of the request structs
*/
/* without any padding added by the compiler. Make sure they get updated when */
/* request structs change. Refer to test suite OdkSizeTest in */
/* ../test/odk_test.cpp for validations of each of the defined request sizes. */
// These are the sum of sizeof of each individual member of the request structs
// without any padding added by the compiler. Make sure they get updated when
// request structs change. Refer to test suite OdkSizeTest in
// ../test/odk_test.cpp for validations of each of the defined request sizes.
#define ODK_LICENSE_REQUEST_SIZE 20
#define ODK_RENEWAL_REQUEST_SIZE 28
#define ODK_PROVISIONING_REQUEST_SIZE 88
/* These are the possible timer status values. */
#define ODK_CLOCK_TIMER_STATUS_UNDEFINED 0 /* Should not happen. */
/* When the structure has been initialized, but no license is loaded. */
// 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. */
// 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. */
// 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. */
// 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. */
// 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. */
// The timer has transitioned from active to expired.
#define ODK_CLOCK_TIMER_STATUS_EXPIRED 6
/* The license has been marked as inactive. */
// 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. */
// 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,
@@ -101,4 +106,4 @@ OEMCryptoResult ODK_ComputeRenewalDuration(const ODK_TimerLimits* timer_limits,
}
#endif
#endif /* WIDEVINE_ODK_SRC_ODK_STRUCTS_PRIV_H_ */
#endif // WIDEVINE_ODK_SRC_ODK_STRUCTS_PRIV_H_

View File

@@ -1,11 +1,12 @@
/* 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. */
// 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 <stdint.h>
#include <string.h>
#include "odk.h"
#include "odk_attributes.h"
#include "odk_overflow.h"
#include "odk_structs_priv.h"
@@ -311,7 +312,7 @@ OEMCryptoResult ODK_ReloadClockValues(ODK_ClockValues* clock_values,
uint64_t time_of_first_decrypt,
uint64_t time_of_last_decrypt,
enum OEMCrypto_Usage_Entry_Status status,
uint64_t system_time_seconds) {
uint64_t system_time_seconds UNUSED) {
if (clock_values == NULL) {
return OEMCrypto_ERROR_INVALID_CONTEXT;
}

View File

@@ -1,6 +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. */
// 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"

View File

@@ -1,6 +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. */
// 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_
@@ -23,6 +23,6 @@ int crypto_memcmp(const void* a, const void* b, size_t len);
bool ODK_NonceValuesEqual(const ODK_NonceValues* a, const ODK_NonceValues* b);
#ifdef __cplusplus
} /* extern "C" */
} // extern "C"
#endif
#endif /* WIDEVINE_ODK_SRC_ODK_UTIL_H_ */
#endif // WIDEVINE_ODK_SRC_ODK_UTIL_H_

View File

@@ -1,6 +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. */
// 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 "serialization_base.h"
@@ -9,7 +9,6 @@
#include <string.h>
#include "OEMCryptoCENCCommon.h"
#include "odk_assert.h"
#include "odk_overflow.h"
struct _Message {
@@ -20,13 +19,6 @@ struct _Message {
MessageStatus status;
};
/* TODO(b/150776214): this can be removed once AllocateMessage gets cleaned up
*/
/*
* odk_static_assert(SIZE_OF_MESSAGE_STRUCT >= sizeof(struct _Message),
* "SIZE_OF_MESSAGE_STRUCT too small");
*/
bool ValidMessage(Message* message) {
if (message == NULL) {
return false;
@@ -99,8 +91,8 @@ void PackArray(Message* message, const uint8_t* base, size_t size) {
}
void Pack_OEMCrypto_Substring(Message* msg, const OEMCrypto_Substring* obj) {
uint32_t offset = obj->offset;
uint32_t length = obj->length;
uint32_t offset = (uint32_t)obj->offset;
uint32_t length = (uint32_t)obj->length;
Pack_uint32_t(msg, &offset);
Pack_uint32_t(msg, &length);
}
@@ -198,21 +190,6 @@ void InitMessage(Message* message, uint8_t* buffer, size_t capacity) {
message->status = MESSAGE_STATUS_OK;
}
/*
* The message structure is in the first sizeof(Memory) bytes
* of the buffer
*/
Message* CreateMessage(uint8_t* buffer, size_t buffer_size) {
if (buffer == NULL || buffer_size < sizeof(Message)) return NULL;
Message* message = (Message*)buffer;
message->base = buffer + sizeof(Message);
message->capacity = buffer_size - sizeof(Message);
message->size = 0;
message->read_offset = 0;
message->status = MESSAGE_STATUS_OK;
return message;
}
/*
* Set the message to an empty state
*/

View File

@@ -1,6 +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. */
// 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_SERIALIZATION_BASE_H_
#define WIDEVINE_ODK_SRC_SERIALIZATION_BASE_H_
@@ -61,13 +61,6 @@ void UnpackArray(Message* message, uint8_t* address,
size_t size); /* copy out */
void Unpack_OEMCrypto_Substring(Message* msg, OEMCrypto_Substring* obj);
/*
* 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
@@ -90,7 +83,7 @@ size_t GetOffset(Message* message);
size_t SizeOfMessageStruct();
#ifdef __cplusplus
} /* extern "C" */
} // extern "C"
#endif
#endif /* WIDEVINE_ODK_SRC_SERIALIZATION_BASE_H_ */
#endif // WIDEVINE_ODK_SRC_SERIALIZATION_BASE_H_

View File

@@ -62,6 +62,9 @@ library and need to preload that before OEMCrypto unit tests run
* Preload the shared library before running OEMCrypto unit tests
```shell
$ cd oemcrypto/odk/test/fuzzing/corpus
$ mkdir license_request_corpus license_response_corpus renewal_request_corpus renewal_response_corpus provisioning_request_corpus provisioning_response_corpus
$ cd /path/to/cdm/repo
$ LD_PRELOAD=out/Default/lib/libodk_corpus_generator.so ./out/Default/oemcrypto_unittests
```

View File

@@ -1,8 +1,8 @@
/* Copyright 2020 Google LLC. All rights reserved. This file and proprietary */
/* source code may only be used and distributed under the Widevine Master */
/* License Agreement. */
// Copyright 2020 Google LLC. All rights reserved. This file and proprietary
// source code may only be used and distributed under the Widevine Master
// License Agreement.
/* We must define this macro to get RTLD_NEXT definition from <dlfcn.h> */
// We must define this macro to get RTLD_NEXT definition from <dlfcn.h>
#define _GNU_SOURCE
#include <dlfcn.h>
@@ -21,7 +21,7 @@ OEMCryptoResult ODK_PrepareCoreLicenseRequest(
message, message_length, core_message_length, nonce_values);
char* file_name = GetFileName("license_request_corpus");
/* License Request format expected by fuzzer - [Core License Request] */
// License Request format expected by fuzzer - [Core License Request]
AppendToFile(file_name, (const char*)message, *core_message_length);
free(file_name);
return oem_crypto_result;
@@ -50,9 +50,8 @@ OEMCryptoResult ODK_ParseLicense(
nonce_values, parsed_license);
char* file_name = GetFileName("license_response_corpus");
/* License Response format expected by fuzzer - [ODK_ParseLicense_Args][Core
*/
/* License Response] */
// License Response format expected by fuzzer - [ODK_ParseLicense_Args][Core
// License Response]
AppendToFile(file_name, (const char*)&parse_license_args,
sizeof(struct ODK_ParseLicense_Args));
AppendToFile(file_name, (const char*)message, core_message_length);
@@ -74,8 +73,8 @@ OEMCryptoResult ODK_PrepareCoreRenewalRequest(uint8_t* message,
nonce_values, clock_values, system_time_seconds);
char* file_name = GetFileName("renewal_request_corpus");
/* License Request format expected by fuzzer - [ODK_ClockValues][Core */
/* License Request] */
// License Request format expected by fuzzer - [ODK_ClockValues][Core
// License Request]
AppendToFile(file_name, (const char*)clock_values, sizeof(ODK_ClockValues));
AppendToFile(file_name, (const char*)message, *core_message_size);
free(file_name);
@@ -103,9 +102,8 @@ OEMCryptoResult ODK_ParseRenewal(const uint8_t* message, size_t message_length,
timer_limits, clock_values, timer_value);
char* file_name = GetFileName("renewal_response_corpus");
/* Renewal Response format expected by fuzzer - [ODK_ParseRenewal_Args][Core
*/
/* Renewal Response] */
// Renewal Response format expected by fuzzer - [ODK_ParseRenewal_Args][Core
// Renewal Response]
AppendToFile(file_name, (const char*)&parse_renewal_args,
sizeof(struct ODK_ParseRenewal_Args));
AppendToFile(file_name, (const char*)message, core_message_length);
@@ -126,8 +124,8 @@ OEMCryptoResult ODK_PrepareCoreProvisioningRequest(
nonce_values, device_id, device_id_length);
char* file_name = GetFileName("provisioning_request_corpus");
/* Provisioning Request format expected by fuzzer - [Core Provisioning */
/* Request] */
// Provisioning Request format expected by fuzzer - [Core Provisioning
// Request]
AppendToFile(file_name, (const char*)message, *core_message_length);
free(file_name);
return oem_crypto_result;
@@ -150,8 +148,8 @@ OEMCryptoResult ODK_ParseProvisioning(
device_id_length, parsed_response);
char* file_name = GetFileName("provisioning_response_corpus");
/* Provisioning Response format expected by fuzzer - */
/* [ODK_ParseProvisioning_Args][Core Provisioning Response] */
// Provisioning Response format expected by fuzzer -
// [ODK_ParseProvisioning_Args][Core Provisioning Response]
AppendToFile(file_name, (const char*)&parse_provisioning_args,
sizeof(struct ODK_ParseProvisioning_Args));
AppendToFile(file_name, (const char*)message, core_message_length);

View File

@@ -1,6 +1,6 @@
/* Copyright 2020 Google LLC. All rights reserved. This file and proprietary */
/* source code may only be used and distributed under the Widevine Master */
/* License Agreement. */
// Copyright 2020 Google LLC. All rights reserved. This file and proprietary
// source code may only be used and distributed under the Widevine Master
// License Agreement.
#include "fuzzing/corpus_generator/odk_corpus_generator_helper.h"
void AppendToFile(const char* file_name, const char* message,

View File

@@ -1,6 +1,6 @@
/* Copyright 2020 Google LLC. All rights reserved. This file and proprietary */
/* source code may only be used and distributed under the Widevine Master */
/* License Agreement. */
// Copyright 2020 Google LLC. All rights reserved. This file and proprietary
// source code may only be used and distributed under the Widevine Master
// License Agreement.
#ifndef WIDEVINE_ODK_TEST_FUZZING_CORPUS_GENERATOR_ODK_CORPUS_GENERATOR_HELPER_H_
#define WIDEVINE_ODK_TEST_FUZZING_CORPUS_GENERATOR_ODK_CORPUS_GENERATOR_HELPER_H_
@@ -15,5 +15,4 @@ void AppendToFile(const char* file_name, const char* message,
char* GetFileName(const char* directory);
#endif /* WIDEVINE_ODK_TEST_FUZZING_CORPUS_GENERATOR_ODK_CORPUS_GENERATOR_HELPER_H_ \
*/
#endif // WIDEVINE_ODK_TEST_FUZZING_CORPUS_GENERATOR_ODK_CORPUS_GENERATOR_HELPER_H_

View File

@@ -28,13 +28,13 @@ void ConvertDataToValidBools(ODK_ParsedLicense* t) {
&t->timer_limits.soft_enforce_rental_duration);
}
void ConvertDataToValidBools(ODK_PreparedRenewalRequest* t) {}
void ConvertDataToValidBools(ODK_PreparedRenewalRequest* t UNUSED) {}
void ConvertDataToValidBools(ODK_ParsedProvisioning* t) {}
void ConvertDataToValidBools(ODK_ParsedProvisioning* t UNUSED) {}
OEMCryptoResult odk_serialize_LicenseRequest(
const void* in, uint8_t* out, size_t* size,
const ODK_LicenseRequest& core_license_request,
const void* in UNUSED, uint8_t* out, size_t* size,
const ODK_LicenseRequest& core_license_request UNUSED,
const ODK_NonceValues* nonce_values) {
return ODK_PrepareCoreLicenseRequest(out, SIZE_MAX, size, nonce_values);
}
@@ -50,7 +50,7 @@ OEMCryptoResult odk_serialize_RenewalRequest(
}
OEMCryptoResult odk_serialize_ProvisioningRequest(
const void* in, uint8_t* out, size_t* size,
const void* in UNUSED, uint8_t* out, size_t* size,
const ODK_ProvisioningRequest& core_provisioning,
const ODK_NonceValues* nonce_values) {
const std::string& device_id = core_provisioning.device_id;
@@ -99,8 +99,8 @@ OEMCryptoResult odk_deserialize_RenewalResponse(
// odk_kdo method, we call Unpack_ODK_PreparedRenewalRequest private method.
// playback_time cannot be captured from publicly exposed API
// ODK_ParseRenewal.
Message* msg = nullptr;
AllocateMessage(&msg, message_block);
uint8_t blk[SIZE_OF_MESSAGE_STRUCT];
Message* msg = reinterpret_cast<Message*>(blk);
InitMessage(msg, const_cast<uint8_t*>(buf), len);
SetSize(msg, len);
Unpack_ODK_PreparedRenewalRequest(msg, renewal_msg);

View File

@@ -1,6 +1,6 @@
/* Copyright 2020 Google LLC. All rights reserved. This file and proprietary */
/* source code may only be used and distributed under the Widevine Master */
/* License Agreement. */
// Copyright 2020 Google LLC. All rights reserved. This file and proprietary
// source code may only be used and distributed under the Widevine Master
// License Agreement.
#ifndef WIDEVINE_ODK_TEST_FUZZING_ODK_FUZZ_HELPER_H_
#define WIDEVINE_ODK_TEST_FUZZING_ODK_FUZZ_HELPER_H_
@@ -9,6 +9,7 @@
#include "core_message_serialize.h"
#include "fuzzing/odk_fuzz_structs.h"
#include "odk_attributes.h"
#include "odk_serialize.h"
namespace oemcrypto_core_message {
@@ -56,24 +57,23 @@ bool kdo_serialize_ProvisioningResponse(
const ODK_ParsedProvisioning& parsed_prov,
std::string* oemcrypto_core_message);
/* Idea behind having three different functions is: */
/* Only ODK_ParseLicense structure had fields which needed additional */
/* procession. Having a single function with templated parameter T was */
/* failing during compile time because other two structures doesn't have */
/* fields that need additional processing. Hence to reduce code redundance and
*/
/* make us of common FuzzerMutateResponse across three response fuzzers, */
/* three independent functions were defined and renewal and provisioning */
/* functions would be empty as no additional processing is needed for them. */
// Idea behind having three different functions is:
// Only ODK_ParseLicense structure had fields which needed additional
// procession. Having a single function with templated parameter T was
// failing during compile time because other two structures doesn't have
// fields that need additional processing. Hence to reduce code redundance and
// make us of common FuzzerMutateResponse across three response fuzzers,
// three independent functions were defined and renewal and provisioning
// functions would be empty as no additional processing is needed for them.
void ConvertDataToValidBools(ODK_ParsedLicense* t);
void ConvertDataToValidBools(ODK_PreparedRenewalRequest* t);
void ConvertDataToValidBools(ODK_ParsedProvisioning* t);
/* Forward-declare the libFuzzer's mutator callback. Mark it weak so that */
/* the program links successfully even outside of --config=asan-fuzzer */
/* (apparently the only config in which LLVM uses our custom mutator). */
// Forward-declare the libFuzzer's mutator callback. Mark it weak so that
// the program links successfully even outside of --config=asan-fuzzer
// (apparently the only config in which LLVM uses our custom mutator).
extern "C" size_t LLVMFuzzerMutate(uint8_t* Data, size_t Size, size_t MaxSize)
__attribute__((weak));
@@ -85,8 +85,8 @@ size_t FuzzerMutateResponse(uint8_t* data, size_t size, size_t max_size,
const size_t kCoreResponseSize = sizeof(T);
const size_t kTotalResponseSize = kArgsSize + kCoreResponseSize;
/* Deserializing data in order to make sure it deserializes properly. */
/* Input byte array format: [function arguments][data to parse]. */
// Deserializing data in order to make sure it deserializes properly.
// Input byte array format: [function arguments][data to parse].
std::shared_ptr<A> _args(new A());
A* args = _args.get();
memcpy(args, data, kArgsSize);
@@ -97,17 +97,16 @@ size_t FuzzerMutateResponse(uint8_t* data, size_t size, size_t max_size,
OEMCryptoResult result =
odk_deserialize_fun(buf, size - kArgsSize, args, &nonce_values, &t);
/* If data doesn't deserialize successfully, We copy random bytes into */
/* T and serialize using kdo function */
/* which will create a valid oemcrypto core message using */
/* nonce and request hash from function args. OEMCrypto core message acts as
*/
/* input to odk_kdo. */
// If data doesn't deserialize successfully, We copy random bytes into
// T and serialize using kdo function
// which will create a valid oemcrypto core message using
// nonce and request hash from function args. OEMCrypto core message acts as
// input to odk_kdo.
if (result != OEMCrypto_SUCCESS) {
if (max_size < kTotalResponseSize) {
return 0;
}
/* Initialize remaining bytes needed in data to zero. */
// Initialize remaining bytes needed in data to zero.
if (size < kTotalResponseSize) {
memset(data + size, 0, kTotalResponseSize - size);
}
@@ -115,24 +114,24 @@ size_t FuzzerMutateResponse(uint8_t* data, size_t size, size_t max_size,
memcpy(&t, buf, kCoreResponseSize);
}
/* Ask LLVM to run its usual mutations, hopefully giving us interesting */
/* inputs. We copy deserialized data into pointer data, run mutations */
/* and copy back the mutated data to args and t */
// Ask LLVM to run its usual mutations, hopefully giving us interesting
// inputs. We copy deserialized data into pointer data, run mutations
// and copy back the mutated data to args and t
memcpy(data + kArgsSize, &t, kCoreResponseSize);
LLVMFuzzerMutate(data, kTotalResponseSize, kTotalResponseSize);
memcpy(args, data, kArgsSize);
memcpy(&t, data + kArgsSize, kCoreResponseSize);
/* Convert boolean flags in parsed message to valid bytes to */
/* avoid errors from msan. Only needed for parsed license. */
// Convert boolean flags in parsed message to valid bytes to
// avoid errors from msan. Only needed for parsed license.
ConvertDataToValidBools(&t);
/* Serialize the data after mutation. */
// Serialize the data after mutation.
std::string oemcrypto_core_message;
if (!kdo_serialize_fun(args, t, &oemcrypto_core_message)) {
return 0;
}
/* Copy mutated and serialized oemcrypto_core_message to data */
/* so that it acts as input to odk_kdo function. */
// Copy mutated and serialized oemcrypto_core_message to data
// so that it acts as input to odk_kdo function.
memcpy(data + kArgsSize, oemcrypto_core_message.data(),
oemcrypto_core_message.size());
return kArgsSize + oemcrypto_core_message.size();
@@ -149,9 +148,9 @@ size_t FuzzerMutateResponse(uint8_t* data, size_t size, size_t max_size,
*/
template <typename A, typename T, typename F, typename G>
void odk_kdo(const F& odk_fun, const G& kdo_fun, const uint8_t* in,
const size_t size, const size_t args_size, uint8_t* out) {
const size_t size, const size_t args_size, uint8_t* out UNUSED) {
T t = {};
/* Input byte array format: [function arguments][data to parse] */
// Input byte array format: [function arguments][data to parse]
if (size < args_size) {
return;
}
@@ -187,8 +186,8 @@ static void kdo_odk(const F& kdo_fun, const G& odk_fun, const uint8_t* in,
if (size <= clock_value_size) {
return;
}
/* Input byte array format: [Clock Values][data to parse]. */
/* Only Renewal Request expects clock values to be present. */
// Input byte array format: [Clock Values][data to parse].
// Only Renewal Request expects clock values to be present.
std::string input(reinterpret_cast<const char*>(in) + clock_value_size,
size - clock_value_size);
T t = {};
@@ -202,5 +201,5 @@ static void kdo_odk(const F& kdo_fun, const G& odk_fun, const uint8_t* in,
return;
}
}
} /* namespace oemcrypto_core_message */
#endif /* WIDEVINE_ODK_TEST_FUZZING_ODK_FUZZ_HELPER_H_ */
} // namespace oemcrypto_core_message
#endif // WIDEVINE_ODK_TEST_FUZZING_ODK_FUZZ_HELPER_H_

View File

@@ -1,6 +1,6 @@
/* Copyright 2020 Google LLC. All rights reserved. This file and proprietary */
/* source code may only be used and distributed under the Widevine Master */
/* License Agreement. */
// Copyright 2020 Google LLC. All rights reserved. This file and proprietary
// source code may only be used and distributed under the Widevine Master
// License Agreement.
#ifndef WIDEVINE_ODK_TEST_FUZZING_ODK_FUZZ_STRUCTS_H_
#define WIDEVINE_ODK_TEST_FUZZING_ODK_FUZZ_STRUCTS_H_
@@ -25,4 +25,4 @@ struct ODK_ParseProvisioning_Args {
size_t device_id_length;
uint8_t device_id[64];
};
#endif /* WIDEVINE_ODK_TEST_FUZZING_ODK_FUZZ_STRUCTS_H_ */
#endif // WIDEVINE_ODK_TEST_FUZZING_ODK_FUZZ_STRUCTS_H_

View File

@@ -12,7 +12,8 @@ namespace oemcrypto_core_message {
// The custom mutator: Ensure that each input can be deserialized properly
// by ODK function after mutation.
extern "C" size_t LLVMFuzzerCustomMutator(uint8_t* data, size_t size,
size_t max_size, unsigned int seed) {
size_t max_size,
unsigned int seed UNUSED) {
const size_t kLicenseResponseArgsSize = sizeof(ODK_ParseLicense_Args);
if (size < kLicenseResponseArgsSize) {
return 0;

View File

@@ -6,13 +6,15 @@
#include <vector>
#include "fuzzing/odk_fuzz_helper.h"
#include "odk_attributes.h"
namespace oemcrypto_core_message {
// The custom mutator: Ensure that each input can be deserialized properly
// by ODK function after mutation.
extern "C" size_t LLVMFuzzerCustomMutator(uint8_t* data, size_t size,
size_t max_size, unsigned int seed) {
size_t max_size,
unsigned int seed UNUSED) {
const size_t kProvisioningResponseArgsSize =
sizeof(ODK_ParseProvisioning_Args);
if (size < kProvisioningResponseArgsSize) {

View File

@@ -6,13 +6,15 @@
#include <vector>
#include "fuzzing/odk_fuzz_helper.h"
#include "odk_attributes.h"
namespace oemcrypto_core_message {
// The custom mutator: Ensure that each input can be deserialized properly
// by ODK function after mutation.
extern "C" size_t LLVMFuzzerCustomMutator(uint8_t* data, size_t size,
size_t max_size, unsigned int seed) {
size_t max_size,
unsigned int seed UNUSED) {
const size_t kRenewalResponseArgsSize = sizeof(ODK_ParseRenewal_Args);
if (size < kRenewalResponseArgsSize) {
return 0;

View File

@@ -0,0 +1,37 @@
// Copyright 2020 Google LLC. All rights reserved. This file and proprietary
// source code may only be used and distributed under the Widevine Master
// License Agreement.
#include "OEMCryptoCENCCommon.h"
#include "gtest/gtest.h"
#include "odk.h"
#include "third_party/absl/strings/escaping.h"
namespace wvodk_test {
TEST(CoreMessageTest, RenwalRequest) {
std::string oem =
"abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrst"
"uvwxyzabcdefghijklmnopqrstuvwxyz";
const uint8_t* buf = reinterpret_cast<const uint8_t*>(oem.c_str());
uint8_t* message = const_cast<uint8_t*>(buf);
size_t message_length = 88;
size_t core_message_length = 88;
uint16_t api_minor_version = 16;
uint16_t api_major_version = 16;
uint32_t nonce = 0;
uint32_t timer_status = 2;
uint64_t time = 10;
enum OEMCrypto_Usage_Entry_Status status = kInactiveUsed;
ODK_NonceValues nonce_values{api_minor_version, api_major_version, nonce};
ODK_ClockValues clock_values{time, time, time, time,
time, timer_status, status};
uint64_t system_time_seconds = 100;
EXPECT_EQ(OEMCrypto_SUCCESS,
ODK_PrepareCoreRenewalRequest(message, message_length,
&core_message_length, &nonce_values,
&clock_values, system_time_seconds));
// All messages have at least a five 4-byte fields.
char* m = reinterpret_cast<char*>(message);
VLOG(0) << absl::BytesToHexString(std::string(m, core_message_length));
}
} // namespace wvodk_test

View File

@@ -1,326 +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.
*/
#include <cassert>
#include <cstddef>
#include <cstdint>
#include <cstring>
#include <functional>
#include <memory>
#include <vector>
#include "OEMCryptoCENCCommon.h"
#include "core_message_deserialize.h"
#include "core_message_serialize.h"
#include "core_message_types.h"
#include "odk.h"
#include "odk_serialize.h"
#include "odk_structs.h"
#include "odk_structs_priv.h"
typedef std::function<void(const uint8_t*, uint8_t*, size_t, size_t)>
roundtrip_fun;
using oemcrypto_core_message::ODK_LicenseRequest;
using oemcrypto_core_message::ODK_ProvisioningRequest;
using oemcrypto_core_message::ODK_RenewalRequest;
using oemcrypto_core_message::deserialize::CoreLicenseRequestFromMessage;
using oemcrypto_core_message::deserialize::CoreProvisioningRequestFromMessage;
using oemcrypto_core_message::deserialize::CoreRenewalRequestFromMessage;
using oemcrypto_core_message::serialize::CreateCoreLicenseResponse;
using oemcrypto_core_message::serialize::CreateCoreProvisioningResponse;
using oemcrypto_core_message::serialize::CreateCoreRenewalResponse;
// @ kdo deserialize; odk serialize
static OEMCryptoResult odk_serialize_LicenseRequest(
const void* in, uint8_t* out, size_t* size,
const ODK_LicenseRequest& core_license_request,
const ODK_NonceValues* nonce_values) {
return ODK_PrepareCoreLicenseRequest(out, SIZE_MAX, size, nonce_values);
}
static OEMCryptoResult odk_serialize_RenewalRequest(
const void* in, uint8_t* out, size_t* size,
const ODK_RenewalRequest& core_renewal, ODK_NonceValues* nonce_values) {
ODK_ClockValues clock{};
memcpy(&clock, in, sizeof(ODK_ClockValues));
uint64_t system_time_seconds = core_renewal.playback_time_seconds;
return ODK_PrepareCoreRenewalRequest(out, SIZE_MAX, size, nonce_values,
&clock, system_time_seconds);
}
static OEMCryptoResult odk_serialize_ProvisioningRequest(
const void* in, uint8_t* out, size_t* size,
const ODK_ProvisioningRequest& core_provisioning,
const ODK_NonceValues* nonce_values) {
const std::string& device_id = core_provisioning.device_id;
return ODK_PrepareCoreProvisioningRequest(
out, SIZE_MAX, size, nonce_values,
reinterpret_cast<const uint8_t*>(device_id.data()), device_id.size());
}
/**
* Template arguments:
* T: kdo deserialize output/odk serialize input structure
* F: kdo deserialize function
* G: odk serialize function
*
* raw bytes -> F deserialize -> struct T -> G serialize -> raw bytes
*/
template <typename T, typename F, typename G>
static roundtrip_fun kdo_odk(const F& kdo_fun, const G& odk_fun) {
auto roundtrip = [&](const uint8_t* in, uint8_t* out, size_t size,
size_t clock_value_size) -> void {
if (size <= clock_value_size) {
return;
}
// Input byte array format: [Clock Values][data to parse]
std::string input(reinterpret_cast<const char*>(in) + clock_value_size,
size - clock_value_size);
T t = {};
if (!kdo_fun(input, &t)) {
return;
}
ODK_NonceValues nonce_values = {t.api_minor_version, t.api_major_version,
t.nonce, t.session_id};
OEMCryptoResult err = odk_fun(in, out, &size, t, &nonce_values);
if (OEMCrypto_SUCCESS != err) {
return;
}
assert(0 == memcmp(in + clock_value_size, out, size));
};
return roundtrip;
}
// @ odk deserialize; kdo serialize
namespace {
struct ODK_ParseLicense_Args {
ODK_NonceValues nonce_values;
uint8_t initial_license_load;
uint8_t usage_entry_present;
uint8_t request_hash[32];
ODK_TimerLimits timer_limits;
ODK_ClockValues clock_values;
};
struct ODK_ParseRenewal_Args {
ODK_NonceValues nonce_values;
uint64_t system_time;
ODK_TimerLimits timer_limits;
ODK_ClockValues clock_values;
};
struct ODK_ParseProvisioning_Args {
ODK_NonceValues nonce_values;
size_t device_id_length;
uint8_t device_id[64];
};
} // namespace
bool convert_byte_to_valid_boolean(const bool* in) {
const int value = *reinterpret_cast<const int*>(in);
return value != 0;
}
static OEMCryptoResult odk_deserialize_LicenseResponse(
const uint8_t* message, size_t core_message_length,
ODK_ParseLicense_Args* a, ODK_NonceValues* nonce_values,
ODK_ParsedLicense* parsed_lic) {
return ODK_ParseLicense(message, SIZE_MAX, core_message_length,
static_cast<bool>(a->initial_license_load),
static_cast<bool>(a->usage_entry_present),
a->request_hash, &a->timer_limits, &a->clock_values,
nonce_values, parsed_lic);
}
static bool kdo_serialize_LicenseResponse(const ODK_ParseLicense_Args* args,
const ODK_ParsedLicense& parsed_lic,
std::string* oemcrypto_core_message) {
const auto& nonce_values = args->nonce_values;
ODK_LicenseRequest core_request{nonce_values.api_minor_version,
nonce_values.api_major_version,
nonce_values.nonce, nonce_values.session_id};
std::string core_request_sha_256(
reinterpret_cast<const char*>(args->request_hash), 32);
return CreateCoreLicenseResponse(
parsed_lic, core_request, core_request_sha_256, oemcrypto_core_message);
}
static OEMCryptoResult odk_deserialize_RenewalResponse(
const uint8_t* buf, size_t len, ODK_ParseRenewal_Args* a,
ODK_NonceValues* nonce_values, ODK_PreparedRenewalRequest* renewal_msg) {
/* Address Sanitizer doesn't like values other than 0 OR 1 for boolean
* variables. Input from fuzzer can be parsed and any random bytes can be
* assigned to boolean variables. Using the workaround to mitigate sanitizer
* errors in fuzzer code and converting random bytes to 0 OR 1.
* This has no negative security impact*/
a->timer_limits.soft_enforce_playback_duration =
convert_byte_to_valid_boolean(
&a->timer_limits.soft_enforce_playback_duration);
a->timer_limits.soft_enforce_rental_duration = convert_byte_to_valid_boolean(
&a->timer_limits.soft_enforce_rental_duration);
uint64_t timer_value = 0;
OEMCryptoResult err =
ODK_ParseRenewal(buf, SIZE_MAX, len, nonce_values, a->system_time,
&a->timer_limits, &a->clock_values, &timer_value);
if (OEMCrypto_SUCCESS == err) {
Message* msg = nullptr;
AllocateMessage(&msg, message_block);
InitMessage(msg, const_cast<uint8_t*>(buf), len);
SetSize(msg, len);
Unpack_ODK_PreparedRenewalRequest(msg, renewal_msg);
assert(ValidMessage(msg));
}
return err;
}
static bool kdo_serialize_RenewalResponse(
const ODK_ParseRenewal_Args* args,
const ODK_PreparedRenewalRequest& renewal_msg,
std::string* oemcrypto_core_message) {
const auto& nonce_values = args->nonce_values;
ODK_RenewalRequest core_request{
nonce_values.api_minor_version, nonce_values.api_major_version,
nonce_values.nonce, nonce_values.session_id, renewal_msg.playback_time};
return CreateCoreRenewalResponse(
core_request, args->timer_limits.initial_renewal_duration_seconds,
oemcrypto_core_message);
}
static OEMCryptoResult odk_deserialize_ProvisioningResponse(
const uint8_t* buf, size_t len, ODK_ParseProvisioning_Args* a,
ODK_NonceValues* nonce_values, ODK_ParsedProvisioning* parsed_prov) {
return ODK_ParseProvisioning(buf, SIZE_MAX, len, nonce_values, a->device_id,
a->device_id_length, parsed_prov);
}
static bool kdo_serialize_ProvisioningResponse(
const ODK_ParseProvisioning_Args* args,
const ODK_ParsedProvisioning& parsed_prov,
std::string* oemcrypto_core_message) {
const auto& nonce_values = args->nonce_values;
if (args->device_id_length > sizeof(args->device_id)) {
return false;
}
ODK_ProvisioningRequest core_request{
nonce_values.api_minor_version, nonce_values.api_major_version,
nonce_values.nonce, nonce_values.session_id,
std::string(reinterpret_cast<const char*>(args->device_id),
args->device_id_length)};
return CreateCoreProvisioningResponse(parsed_prov, core_request,
oemcrypto_core_message);
}
/**
* Template arguments:
* A: struct holding function arguments
* T: odk deserialize output/kdo serialize input structure
* F: odk deserialize function
* G: kdo serialize function
*
* raw bytes -> F deserialize -> struct T -> G serialize -> raw bytes
*/
template <typename A, typename T, typename F, typename G>
static roundtrip_fun odk_kdo(const F& odk_fun, const G& kdo_fun) {
auto roundtrip = [&](const uint8_t* in, uint8_t* out, size_t size,
size_t args_size) -> void {
// Input byte array format: [function arguments][data to parse]
if (args_size > size) {
return;
}
T t = {};
const uint8_t* buf = in + args_size;
std::shared_ptr<A> _args(new A());
A* args = _args.get();
memcpy(args, in, args_size);
args->nonce_values.api_major_version = ODK_MAJOR_VERSION;
args->nonce_values.api_minor_version = ODK_MINOR_VERSION;
/*
* Input random bytes from autofuzz are interpreted by this script as
* [function args][data to parse]. Odk deserialize functions
* expect the nonce values in function args to match with those
* in data to parse which is not possible with random bytes.
* We follow two pass approach.
*
* 1st pass - We copy random bytes into struct t and call kdo serialize
* with function args which will create oemcrypto core message using nonce
* from function args. Now we have a valid oemcrypto core message which is
* formed using nonce_values from function args which acts as input bytes
* for 2nd pass
*
* 2nd pass - oemcrypto core message from 1st pass guarantees that
* nonce_values in [function args] and core message match. we call
* odk_deserialize using nonce from function args and oemcrypto core message
* from 1st pass. Then we call kdo function which generates oemcrypto core
* message2, which should be equal to oemcrypto_core_message which was input
* to 2nd pass
*/
// TODO(ellurubharath): Use structure aware fuzzing
// 1st pass
memcpy(&t, buf, sizeof(t));
std::string oemcrypto_core_message;
if (!kdo_fun(args, t, &oemcrypto_core_message)) {
return;
}
assert(oemcrypto_core_message.size() <= size);
// 2nd pass
ODK_NonceValues nonce_values = args->nonce_values;
OEMCryptoResult result =
odk_fun(reinterpret_cast<const uint8_t*>(oemcrypto_core_message.data()),
oemcrypto_core_message.size(), args, &nonce_values, &t);
if (result != OEMCrypto_SUCCESS) {
return;
}
std::string oemcrypto_core_message2;
if (!kdo_fun(args, t, &oemcrypto_core_message2)) {
return;
}
assert(oemcrypto_core_message == oemcrypto_core_message2);
};
return roundtrip;
}
// @ fuzz raw -> parsed -> raw
static void verify_roundtrip(const uint8_t* in, size_t size,
roundtrip_fun roundtrip, size_t args_size) {
std::vector<uint8_t> _out(size);
auto out = _out.data();
roundtrip(in, out, size, args_size);
}
// Entry point for fuzzer, data is random bytes program gets from autofuzzer
extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
verify_roundtrip(data, size,
kdo_odk<ODK_LicenseRequest>(CoreLicenseRequestFromMessage,
odk_serialize_LicenseRequest),
0);
verify_roundtrip(data, size,
kdo_odk<ODK_RenewalRequest>(CoreRenewalRequestFromMessage,
odk_serialize_RenewalRequest),
sizeof(ODK_ClockValues));
verify_roundtrip(
data, size,
kdo_odk<ODK_ProvisioningRequest>(CoreProvisioningRequestFromMessage,
odk_serialize_ProvisioningRequest),
0);
verify_roundtrip(
data, size,
odk_kdo<ODK_ParseLicense_Args, ODK_ParsedLicense>(
odk_deserialize_LicenseResponse, kdo_serialize_LicenseResponse),
sizeof(ODK_ParseLicense_Args));
verify_roundtrip(
data, size,
odk_kdo<ODK_ParseRenewal_Args, ODK_PreparedRenewalRequest>(
odk_deserialize_RenewalResponse, kdo_serialize_RenewalResponse),
sizeof(ODK_ParseRenewal_Args));
verify_roundtrip(data, size,
odk_kdo<ODK_ParseProvisioning_Args, ODK_ParsedProvisioning>(
odk_deserialize_ProvisioningResponse,
kdo_serialize_ProvisioningResponse),
sizeof(ODK_ParseProvisioning_Args));
return 0;
}

View File

@@ -209,8 +209,10 @@ TEST(OdkTest, SerializeFieldsStress) {
TEST(OdkTest, NullRequestTest) {
size_t core_message_length = 0;
ODK_NonceValues nonce_values{0};
ODK_ClockValues clock_values{0};
ODK_NonceValues nonce_values;
memset(&nonce_values, 0, sizeof(nonce_values));
ODK_ClockValues clock_values;
memset(&clock_values, 0, sizeof(clock_values));
// Assert that nullptr does not cause a core dump.
EXPECT_EQ(ODK_ERROR_CORE_MESSAGE, ODK_PrepareCoreLicenseRequest(
@@ -250,10 +252,12 @@ TEST(OdkTest, NullResponseTest) {
uint8_t message[message_size] = {0};
size_t core_message_length = message_size;
uint8_t request_hash[ODK_SHA256_HASH_SIZE] = {0};
ODK_TimerLimits timer_limits{0};
ODK_TimerLimits timer_limits;
ODK_ParsedLicense parsed_license;
ODK_NonceValues nonce_values{0};
ODK_ClockValues clock_values{0};
ODK_NonceValues nonce_values;
memset(&nonce_values, 0, sizeof(nonce_values));
ODK_ClockValues clock_values;
memset(&clock_values, 0, sizeof(clock_values));
// Assert that nullptr does not cause a core dump.
EXPECT_EQ(ODK_ERROR_CORE_MESSAGE,
@@ -322,7 +326,8 @@ TEST(OdkTest, NullResponseTest) {
TEST(OdkTest, PrepareCoreLicenseRequest) {
uint8_t license_message[ODK_LICENSE_REQUEST_SIZE] = {0};
size_t core_message_length = sizeof(license_message);
ODK_NonceValues nonce_values{0};
ODK_NonceValues nonce_values;
memset(&nonce_values, 0, sizeof(nonce_values));
EXPECT_EQ(OEMCrypto_SUCCESS, ODK_PrepareCoreLicenseRequest(
license_message, sizeof(license_message),
&core_message_length, &nonce_values));
@@ -331,7 +336,8 @@ TEST(OdkTest, PrepareCoreLicenseRequest) {
TEST(OdkTest, PrepareCoreLicenseRequestSize) {
uint8_t license_message[ODK_LICENSE_REQUEST_SIZE] = {0};
size_t core_message_length = sizeof(license_message);
ODK_NonceValues nonce_values{0};
ODK_NonceValues nonce_values;
memset(&nonce_values, 0, sizeof(nonce_values));
// message length smaller than core message length
size_t core_message_length_invalid = core_message_length + 1;
EXPECT_EQ(ODK_ERROR_CORE_MESSAGE,
@@ -349,8 +355,10 @@ TEST(OdkTest, PrepareCoreLicenseRequestSize) {
TEST(OdkTest, PrepareCoreRenewalRequest) {
uint8_t renewal_message[ODK_RENEWAL_REQUEST_SIZE] = {0};
size_t core_message_length = sizeof(renewal_message);
ODK_NonceValues nonce_values{0};
ODK_ClockValues clock_values{0};
ODK_NonceValues nonce_values;
memset(&nonce_values, 0, sizeof(nonce_values));
ODK_ClockValues clock_values;
memset(&clock_values, 0, sizeof(clock_values));
constexpr uint64_t system_time_seconds = 10;
EXPECT_EQ(OEMCrypto_SUCCESS,
ODK_PrepareCoreRenewalRequest(
@@ -363,7 +371,8 @@ TEST(OdkTest, PrepareCoreRenewalRequestTimer) {
size_t core_message_length = sizeof(renewal_message);
ODK_NonceValues nonce_values{2, 16, 0, 0};
constexpr uint64_t system_time_seconds = 10;
ODK_ClockValues clock_values_updated{0};
ODK_ClockValues clock_values_updated;
memset(&clock_values_updated, 0, sizeof(clock_values_updated));
// system time smaller than first decrypt time
clock_values_updated.time_of_first_decrypt = system_time_seconds + 1;
EXPECT_EQ(ODK_ERROR_CORE_MESSAGE,
@@ -383,7 +392,8 @@ TEST(OdkTest, PrepareCoreRenewalRequestTimer) {
TEST(OdkTest, PrepareCoreProvisioningRequest) {
uint8_t provisioning_message[ODK_PROVISIONING_REQUEST_SIZE] = {0};
size_t core_message_length = sizeof(provisioning_message);
ODK_NonceValues nonce_values{0};
ODK_NonceValues nonce_values;
memset(&nonce_values, 0, sizeof(nonce_values));
uint8_t device_id[ODK_DEVICE_ID_LEN_MAX] = {0};
EXPECT_EQ(
OEMCrypto_SUCCESS,
@@ -395,7 +405,8 @@ TEST(OdkTest, PrepareCoreProvisioningRequest) {
TEST(OdkTest, PrepareCoreProvisioningRequestDeviceId) {
uint8_t provisioning_message[ODK_PROVISIONING_REQUEST_SIZE] = {0};
size_t core_message_length = sizeof(provisioning_message);
ODK_NonceValues nonce_values{0};
ODK_NonceValues nonce_values;
memset(&nonce_values, 0, sizeof(nonce_values));
uint8_t device_id_invalid[ODK_DEVICE_ID_LEN_MAX + 1] = {0};
EXPECT_EQ(ODK_ERROR_CORE_MESSAGE,
ODK_PrepareCoreProvisioningRequest(
@@ -423,7 +434,8 @@ TEST(OdkTest, RenewalRequestRoundtrip) {
const std::vector<ODK_Field> extra_fields = {
{ODK_UINT64, &playback_time, "playback_time"},
};
ODK_ClockValues clock_values = {0};
ODK_ClockValues clock_values;
memset(&clock_values, 0, sizeof(clock_values));
clock_values.time_of_first_decrypt = playback_start;
auto odk_prepare_func = [&](uint8_t* const buf, size_t* size,
ODK_NonceValues* nonce_values) {
@@ -713,6 +725,17 @@ TEST(OdkSizeTest, ProvisioningRequest) {
EXPECT_EQ(ODK_PROVISIONING_REQUEST_SIZE, core_message_length);
}
// Verify the version string contains the right version numbers.
TEST(OdkTest, CheckReleaseVersion) {
// Here are the version numbers.
std::string expected_version = std::to_string(ODK_MAJOR_VERSION) + "." +
std::to_string(ODK_MINOR_VERSION);
// Here is the version string.
EXPECT_NE(std::string(ODK_RELEASE_DATE).find(expected_version),
std::string::npos)
<< "Version mismatch in odk_structs.h";
}
} // namespace
} // namespace wvodk_test

View File

@@ -1,6 +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. */
// Copyright 2019 Google LLC. All rights reserved. This file and proprietary
// source code may only be used and distributed under the Widevine Master
// License Agreement.
#ifndef WIDEVINE_ODK_TEST_ODK_TEST_HELPER_H_
#define WIDEVINE_ODK_TEST_ODK_TEST_HELPER_H_
@@ -66,7 +66,7 @@ struct ODK_ProvisioningResponseParams {
std::vector<ODK_Field> extra_fields;
};
/* Default values in core_message for testing */
// Default values in core_message for testing
void ODK_SetDefaultCoreFields(ODK_CoreMessage* core_message,
uint32_t message_type);
void ODK_SetDefaultLicenseResponseParams(ODK_LicenseResponseParams* params);
@@ -77,9 +77,9 @@ void ODK_SetDefaultProvisioningResponseParams(
size_t ODK_FieldLength(ODK_FieldType type);
size_t ODK_AllocSize(ODK_FieldType type);
/* Copy ODK_Field to buf */
// Copy ODK_Field to buf
OEMCryptoResult ODK_WriteSingleField(uint8_t* buf, const ODK_Field* field);
/* Load buf to ODK_Field */
// Load buf to ODK_Field
OEMCryptoResult ODK_ReadSingleField(const uint8_t* buf, const ODK_Field* field);
OEMCryptoResult ODK_DumpSingleField(const uint8_t* buf, const ODK_Field* field);
OEMCryptoResult ODK_IterFields(ODK_FieldMode mode, uint8_t* buf,
@@ -89,11 +89,11 @@ void ODK_ExpectEqualBuf(const void* s1, const void* s2, size_t n,
const std::vector<ODK_Field>& fields);
void ODK_ResetOdkFields(std::vector<ODK_Field>* fields);
/* Serialize core_message and extra_fields into buf */
// Serialize core_message and extra_fields into buf
void ODK_BuildMessageBuffer(ODK_CoreMessage* core_message,
const std::vector<ODK_Field>& extra_fields,
uint8_t** buf, uint32_t* buf_size);
} /* namespace wvodk_test */
} // namespace wvodk_test
#endif /* WIDEVINE_ODK_TEST_ODK_TEST_HELPER_H_ */
#endif // WIDEVINE_ODK_TEST_ODK_TEST_HELPER_H_

View File

@@ -35,6 +35,7 @@ TEST(OdkTimerBasicTest, NullTest) {
TEST(OdkTimerBasicTest, Init) {
// Verify that basic initialization sets all of the fields.
ODK_ClockValues clock_values;
memset(&clock_values, 0, sizeof(clock_values));
uint64_t time = 42;
ODK_InitializeClockValues(&clock_values, time);
EXPECT_EQ(clock_values.time_of_license_signed, time);
@@ -50,6 +51,7 @@ TEST(OdkTimerBasicTest, Reload) {
// Verify that reloading clock values uses the same values
// for fields that can be saved, and sets others to 0.
ODK_ClockValues clock_values;
memset(&clock_values, 0, sizeof(clock_values));
uint64_t time = 42u;
uint64_t lic_signed = 1u;
uint64_t first_decrypt = 2u;
@@ -1223,6 +1225,7 @@ TEST_F(ODKUseCase_LimitedDurationLicense, Case5) {
TEST_F(RenewalTest, V15Test) {
const uint32_t key_duration = 25;
ODK_NonceValues nonce_values;
memset(&nonce_values, 0, sizeof(nonce_values));
const uint64_t license_loaded = GetSystemTime(10);
EXPECT_EQ(
ODK_InitializeV15Values(&timer_limits_, &clock_values_, &nonce_values,

View File

@@ -23,6 +23,14 @@
#include "oemcrypto_rsa_key_shared.h"
#include "string_conversions.h"
namespace {
// Lower bits in SessionId are actual session id. The rest higher bits are
// session type.
const uint32_t kSessionIdTypeShift = 28;
const uint32_t kSessionIdMask = (1u << kSessionIdTypeShift) - 1u;
} // namespace
namespace wvoec_ref {
// Note: The class CryptoEngine is configured at compile time by compiling in
@@ -64,6 +72,12 @@ SessionId CryptoEngine::OpenSession() {
std::unique_lock<std::mutex> lock(session_table_lock_);
static OEMCrypto_SESSION unique_id = 1;
SessionId id = ++unique_id;
// Check if too many sessions have been opened.
if (SessionTypeBits(id) != 0) {
return 0;
}
// Apply session type to higher bits.
id = (kSessionTypeOEMCrypto << kSessionIdTypeShift) | (id & kSessionIdMask);
sessions_[id] = MakeSession(id);
return id;
}
@@ -251,4 +265,8 @@ OEMCryptoResult CryptoEngine::SetDestination(
return OEMCrypto_SUCCESS;
}
uint32_t CryptoEngine::SessionTypeBits(SessionId sid) {
return sid >> kSessionIdTypeShift;
}
} // namespace wvoec_ref

View File

@@ -22,8 +22,8 @@
#include "oemcrypto_key_ref.h"
#include "oemcrypto_rsa_key_shared.h"
#include "oemcrypto_session.h"
#include "oemcrypto_usage_table_ref.h"
#include "oemcrypto_types.h"
#include "oemcrypto_usage_table_ref.h"
namespace wvoec_ref {
@@ -41,10 +41,16 @@ typedef struct {
uint8_t padding[16 - (2 * sizeof(time_t)) % 16];
} TimeInfo;
// Session types are higher (32 - kSessionIdTypeShift) bits in SessionId.
typedef enum SessionType {
kSessionTypeOEMCrypto = 0,
kSessionTypeEntitledKey = 1,
} SessionType;
class CryptoEngine {
public:
static const uint32_t kApiVersion = 16;
static const uint32_t kMinorApiVersion = 0;
static const uint32_t kMinorApiVersion = 3;
static const int64_t kTimeInfoUpdateWindowInSeconds = 300;
// This is like a factory method, except we choose which version to use at
@@ -83,9 +89,7 @@ class CryptoEngine {
size_t DeviceRootTokenLength() { return root_of_trust_.DeviceTokenLength(); }
const uint8_t* DeviceRootToken() {
return root_of_trust_.DeviceToken();
}
const uint8_t* DeviceRootToken() { return root_of_trust_.DeviceToken(); }
virtual void Terminate();
@@ -116,9 +120,7 @@ class CryptoEngine {
virtual OEMCrypto_HDCP_Capability config_maximum_hdcp_capability();
// Return true if there might be analog video output enabled.
virtual bool analog_display_active() {
return !config_local_display_only();
}
virtual bool analog_display_active() { return !config_local_display_only(); }
// Return true if there is an analog display, and CGMS A is turned on.
virtual bool cgms_a_active() { return false; }
@@ -222,6 +224,9 @@ class CryptoEngine {
return OEMCrypto_SUCCESS;
}
// Get the session type bits from |sid|.
static uint32_t SessionTypeBits(SessionId sid);
protected:
// System clock, measuring time in seconds, including anti-rollback offset.
int64_t MonotonicTime();

View File

@@ -39,6 +39,9 @@
namespace {
const uint8_t kBakedInCertificateMagicBytes[] = {0xDE, 0xAD, 0xBE, 0xEF};
// Maximum context key length used for performance reasons, not mandated by
// specification.
const size_t kMaxContextKeyLength = 1024 * 1024;
// Return uint32 referenced through a potentially unaligned pointer.
// If the pointer is nullptr, return 0.
@@ -109,6 +112,10 @@ OEMCRYPTO_API OEMCryptoResult OEMCrypto_OpenSession(
return OEMCrypto_ERROR_TOO_MANY_SESSIONS;
}
SessionId sid = crypto_engine->OpenSession();
if (sid == 0) {
LOGE("OEMCrypto_OpenSession: invalid session id returned.");
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
}
*session = (OEMCrypto_SESSION)sid;
return OEMCrypto_SUCCESS;
}
@@ -141,6 +148,11 @@ OEMCRYPTO_API OEMCryptoResult OEMCrypto_GenerateDerivedKeys(
LOGE("[OEMCrypto_GenerateDerivedKeys(): ERROR_KEYBOX_INVALID]");
return OEMCrypto_ERROR_KEYBOX_INVALID;
}
if (mac_key_context_length > kMaxContextKeyLength ||
enc_key_context_length > kMaxContextKeyLength) {
LOGE("[OEMCrypto_GenerateDerivedKeys(): ERROR_BUFFER_TOO_LARGE]");
return OEMCrypto_ERROR_BUFFER_TOO_LARGE;
}
SessionContext* session_ctx = crypto_engine->FindSession(session);
if (session_ctx == nullptr || !session_ctx->isValid()) {
@@ -723,8 +735,10 @@ OEMCRYPTO_API OEMCryptoResult OEMCrypto_LoadTestKeybox(const uint8_t* buffer,
if (crypto_engine->config_provisioning_method() != OEMCrypto_Keybox) {
return OEMCrypto_ERROR_NOT_IMPLEMENTED;
}
crypto_engine->UseTestKeybox(buffer, length);
return OEMCrypto_SUCCESS;
if (crypto_engine->UseTestKeybox(buffer, length)) {
return OEMCrypto_SUCCESS;
}
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
}
OEMCRYPTO_API OEMCryptoResult OEMCrypto_IsKeyboxOrOEMCertValid(void) {
@@ -887,17 +901,6 @@ static OEMCryptoResult OEMCrypto_RewrapDeviceRSAKey30(
LOGE("[OEMCrypto_RewrapDeviceRSAKey30(): OEMCrypto_ERROR_INVALID_CONTEXT]");
return OEMCrypto_ERROR_INVALID_CONTEXT;
}
// For the reference implementation, the wrapped key and the encrypted
// key are the same size -- just encrypted with different keys.
// We add 32 bytes for a context, 32 for iv, and 32 bytes for a signature.
// Important: This layout must match OEMCrypto_LoadDRMPrivateKey below.
const size_t buffer_size = enc_rsa_key_length + sizeof(WrappedRSAKey);
if (wrapped_rsa_key == nullptr || *wrapped_rsa_key_length < buffer_size) {
*wrapped_rsa_key_length = buffer_size;
return OEMCrypto_ERROR_SHORT_BUFFER;
}
*wrapped_rsa_key_length = buffer_size; // Tell caller how much space we used.
if (!crypto_engine->ValidRootOfTrust()) {
LOGE("[OEMCrypto_RewrapDeviceRSAKey30(): ERROR_KEYBOX_INVALID]");
return OEMCrypto_ERROR_KEYBOX_INVALID;
@@ -933,13 +936,7 @@ static OEMCryptoResult OEMCrypto_RewrapDeviceRSAKey30(
enc_rsa_key_iv, &pkcs8_rsa_key[0])) {
return OEMCrypto_ERROR_INVALID_RSA_KEY;
}
size_t padding = pkcs8_rsa_key[enc_rsa_key_length - 1];
if (padding > 16) {
// Do not return an error at this point, to avoid a padding oracle attack.
padding = 0;
}
size_t rsa_key_length = enc_rsa_key_length - padding;
if (!session_ctx->LoadRSAKey(&pkcs8_rsa_key[0], rsa_key_length)) {
if (!session_ctx->LoadRSAKey(&pkcs8_rsa_key[0], enc_rsa_key_length)) {
LOGE("[OEMCrypto_RewrapDeviceRSAKey30(): Failed to LoadRSAKey.");
return OEMCrypto_ERROR_INVALID_RSA_KEY;
}
@@ -970,6 +967,7 @@ static OEMCryptoResult OEMCrypto_RewrapDeviceRSAKey30(
LOGE("[_RewrapDeviceRSAKey30(): EncrypteRSAKey failed.");
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
}
const size_t buffer_size = enc_rsa_key_length + sizeof(WrappedRSAKey);
// The wrapped keybox must be signed with the same key we verify with. I'll
// pick the server key, so I don't have to modify LoadRSAKey.
unsigned int sig_length = sizeof(wrapped->signature);
@@ -1004,17 +1002,6 @@ static OEMCryptoResult OEMCrypto_RewrapDeviceRSAKey(
LOGE("[OEMCrypto_RewrapDeviceRSAKey(): OEMCrypto_ERROR_INVALID_CONTEXT]");
return OEMCrypto_ERROR_INVALID_CONTEXT;
}
// For the reference implementation, the wrapped key and the encrypted
// key are the same size -- just encrypted with different keys.
// We add 32 bytes for a context, 32 for iv, and 32 bytes for a signature.
// Important: This layout must match OEMCrypto_LoadDRMPrivateKey below.
const size_t buffer_size = enc_rsa_key_length + sizeof(WrappedRSAKey);
if (wrapped_rsa_key == nullptr || *wrapped_rsa_key_length < buffer_size) {
*wrapped_rsa_key_length = buffer_size;
return OEMCrypto_ERROR_SHORT_BUFFER;
}
*wrapped_rsa_key_length = buffer_size; // Tell caller how much space we used.
if (!crypto_engine->ValidRootOfTrust()) {
LOGE("[OEMCrypto_RewrapDeviceRSAKey(): ERROR_KEYBOX_INVALID]");
return OEMCrypto_ERROR_KEYBOX_INVALID;
@@ -1051,14 +1038,7 @@ static OEMCryptoResult OEMCrypto_RewrapDeviceRSAKey(
enc_rsa_key_iv, &pkcs8_rsa_key[0])) {
return OEMCrypto_ERROR_INVALID_RSA_KEY;
}
size_t padding = pkcs8_rsa_key[enc_rsa_key_length - 1];
if (padding > 16) {
// Do not return an error at this point, to avoid a padding oracle attack.
padding = 0;
}
size_t rsa_key_length = enc_rsa_key_length - padding;
if (!session_ctx->LoadRSAKey(&pkcs8_rsa_key[0], rsa_key_length)) {
if (!session_ctx->LoadRSAKey(&pkcs8_rsa_key[0], enc_rsa_key_length)) {
return OEMCrypto_ERROR_INVALID_RSA_KEY;
}
@@ -1084,6 +1064,7 @@ static OEMCryptoResult OEMCrypto_RewrapDeviceRSAKey(
wrapped->iv, wrapped->enc_rsa_key)) {
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
}
const size_t buffer_size = enc_rsa_key_length + sizeof(WrappedRSAKey);
// The wrapped keybox must be signed with the same key we verify with. I'll
// pick the server key, so I don't have to modify LoadRSAKey.
unsigned int sig_length = sizeof(wrapped->signature);
@@ -1302,6 +1283,12 @@ OEMCRYPTO_API OEMCryptoResult OEMCrypto_DeriveKeysFromSessionKey(
LOGE("[OEMCrypto_GenerateDerivedKeys(): ERROR_KEYBOX_INVALID]");
return OEMCrypto_ERROR_KEYBOX_INVALID;
}
if (mac_key_context_length > kMaxContextKeyLength ||
enc_key_context_length > kMaxContextKeyLength ||
enc_session_key_length > kMaxContextKeyLength) {
LOGE("[OEMCrypto_GenerateDerivedKeys(): ERROR_BUFFER_TOO_LARGE]");
return OEMCrypto_ERROR_BUFFER_TOO_LARGE;
}
SessionContext* session_ctx = crypto_engine->FindSession(session);
if (session_ctx == nullptr || !session_ctx->isValid()) {

View File

@@ -86,8 +86,7 @@ class ContentKeysContext : public SessionContextKeys {
OEMCrypto_LicenseType type() override { return OEMCrypto_ContentLicense; }
bool SetContentKey(const KeyId& entitlement_id,
const KeyId& content_key_id,
bool SetContentKey(const KeyId& entitlement_id, const KeyId& content_key_id,
const std::vector<uint8_t>& content_key) override;
EntitlementKey* GetEntitlementKey(const KeyId& entitlement_id) override;
@@ -139,8 +138,7 @@ class EntitlementKeysContext : public SessionContextKeys {
Key* FirstKey() override;
void Remove(const KeyId& key_id) override;
void UpdateDuration(const KeyControlBlock& control) override;
bool SetContentKey(const KeyId& entitlement_id,
const KeyId& content_key_id,
bool SetContentKey(const KeyId& entitlement_id, const KeyId& content_key_id,
const std::vector<uint8_t>& content_key) override;
EntitlementKey* GetEntitlementKey(const KeyId& entitlement_id) override;
@@ -210,8 +208,7 @@ SessionContext::SessionContext(CryptoEngine* ce, SessionId sid,
CryptoEngine::kApiVersion, sid);
}
SessionContext::~SessionContext() {
}
SessionContext::~SessionContext() {}
// Internal utility function to derive key using CMAC-128
bool SessionContext::DeriveKey(const std::vector<uint8_t>& key,
@@ -366,8 +363,10 @@ OEMCryptoResult SessionContext::PrepAndSignLicenseRequest(
const size_t message_body_length = message_length - *core_message_length;
result = GenerateCertSignature(message_body, message_body_length, signature,
signature_length);
if (result == OEMCrypto_SUCCESS) state_request_signed_ = true;
ODK_InitializeClockValues(&clock_values_, ce_->SystemTime());
if (result == OEMCrypto_SUCCESS) {
state_request_signed_ = true;
result = ODK_InitializeClockValues(&clock_values_, ce_->SystemTime());
}
return result;
}
@@ -677,11 +676,13 @@ OEMCryptoResult SessionContext::CheckStatusOffline(uint32_t nonce,
OEMCryptoResult SessionContext::CheckNonceOrEntry(
const KeyControlBlock& key_control_block) {
switch (key_control_block.control_bits() & wvoec::kControlReplayMask) {
case wvoec::kControlNonceRequired: // Online license. Nonce always required.
case wvoec::kControlNonceRequired: // Online license. Nonce always
// required.
return CheckStatusOnline(key_control_block.nonce(),
key_control_block.control_bits());
break;
case wvoec::kControlNonceOrEntry: // Offline license. Nonce required on first use.
case wvoec::kControlNonceOrEntry: // Offline license. Nonce required on
// first use.
return CheckStatusOffline(key_control_block.nonce(),
key_control_block.control_bits());
break;
@@ -844,9 +845,8 @@ OEMCryptoResult SessionContext::LoadKeysNoSignature(
message + key_array[i].key_control_iv.offset,
message + key_array[i].key_control_iv.offset + wvoec::KEY_IV_SIZE);
OEMCryptoResult result =
InstallKey(key_id, enc_key_data, key_data_iv, key_control,
key_control_iv);
OEMCryptoResult result = InstallKey(key_id, enc_key_data, key_data_iv,
key_control, key_control_iv);
if (result != OEMCrypto_SUCCESS) {
status = result;
break;
@@ -868,7 +868,12 @@ OEMCryptoResult SessionContext::LoadKeysNoSignature(
LOGE("Failed to update mac keys.");
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
}
} else {
// If the mac keys are not updated, we will not need them again.
mac_key_server_.resize(0);
mac_key_client_.resize(0);
}
if (usage_entry_) {
OEMCryptoResult result = OEMCrypto_SUCCESS;
switch (usage_entry_status_) {
@@ -1122,6 +1127,10 @@ bool SessionContext::DecryptRSAKey(const uint8_t* enc_rsa_key,
size_t enc_rsa_key_length,
const uint8_t* enc_rsa_key_iv,
uint8_t* pkcs8_rsa_key) {
if (enc_rsa_key_length % AES_BLOCK_SIZE != 0) {
LOGE("[DecryptRSAKey(): bad buffer size]");
return false;
}
// Decrypt rsa key with keybox.
uint8_t iv_buffer[wvoec::KEY_IV_SIZE];
memcpy(iv_buffer, enc_rsa_key_iv, wvoec::KEY_IV_SIZE);
@@ -1136,6 +1145,10 @@ bool SessionContext::EncryptRSAKey(const uint8_t* pkcs8_rsa_key,
size_t enc_rsa_key_length,
const uint8_t* enc_rsa_key_iv,
uint8_t* enc_rsa_key) {
if (enc_rsa_key_length % AES_BLOCK_SIZE != 0) {
LOGE("[EncryptRSAKey(): bad buffer size]");
return false;
}
// Encrypt rsa key with keybox.
uint8_t iv_buffer[wvoec::KEY_IV_SIZE];
memcpy(iv_buffer, enc_rsa_key_iv, wvoec::KEY_IV_SIZE);
@@ -1227,7 +1240,7 @@ OEMCryptoResult SessionContext::CheckKeyUse(const std::string& log_string,
LOGE("[%s(): CGMS required, but buffer is clear", log_string.c_str());
return OEMCrypto_ERROR_ANALOG_OUTPUT;
}
if ( ce_->analog_display_active() && !ce_->cgms_a_active()) {
if (ce_->analog_display_active() && !ce_->cgms_a_active()) {
LOGE("[%s(): control bit says CGMS required", log_string.c_str());
return OEMCrypto_ERROR_ANALOG_OUTPUT;
}
@@ -1252,8 +1265,9 @@ OEMCryptoResult SessionContext::Generic_Encrypt(const uint8_t* in_buffer,
LOGE("[Generic_Encrypt(): CONTENT_KEY has wrong size: %zu", key.size());
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
}
OEMCryptoResult result = CheckKeyUse("Generic_Encrypt", wvoec::kControlAllowEncrypt,
OEMCrypto_BufferType_Clear);
OEMCryptoResult result =
CheckKeyUse("Generic_Encrypt", wvoec::kControlAllowEncrypt,
OEMCrypto_BufferType_Clear);
if (result != OEMCrypto_SUCCESS) return result;
if (algorithm != OEMCrypto_AES_CBC_128_NO_PADDING) {
LOGE("[Generic_Encrypt(): algorithm bad");
@@ -1292,8 +1306,9 @@ OEMCryptoResult SessionContext::Generic_Decrypt(const uint8_t* in_buffer,
LOGE("[Generic_Decrypt(): CONTENT_KEY has wrong size");
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
}
OEMCryptoResult result = CheckKeyUse("Generic_Decrypt", wvoec::kControlAllowDecrypt,
OEMCrypto_BufferType_Clear);
OEMCryptoResult result =
CheckKeyUse("Generic_Decrypt", wvoec::kControlAllowDecrypt,
OEMCrypto_BufferType_Clear);
if (result != OEMCrypto_SUCCESS) return result;
if (algorithm != OEMCrypto_AES_CBC_128_NO_PADDING) {
@@ -1373,8 +1388,8 @@ OEMCryptoResult SessionContext::Generic_Verify(const uint8_t* in_buffer,
LOGE("[Generic_Verify(): CONTENT_KEY has wrong size: %zu", key.size());
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
}
OEMCryptoResult result = CheckKeyUse("Generic_Verify", wvoec::kControlAllowVerify,
OEMCrypto_BufferType_Clear);
OEMCryptoResult result = CheckKeyUse(
"Generic_Verify", wvoec::kControlAllowVerify, OEMCrypto_BufferType_Clear);
if (result != OEMCrypto_SUCCESS) return result;
if (algorithm != OEMCrypto_HMAC_SHA256) {
LOGE("[Generic_Verify(): bad algorithm");
@@ -1523,6 +1538,11 @@ bool SessionContext::DecryptMessage(const std::vector<uint8_t>& key,
LOGE("[DecryptMessage(): OEMCrypto_ERROR_INVALID_CONTEXT]");
return false;
}
if (message.size() % AES_BLOCK_SIZE != 0) {
LOGE("[DecryptMessage(): bad buffer size]");
return false;
}
decrypted->resize(message.size());
uint8_t iv_buffer[16];
memcpy(iv_buffer, &iv[0], 16);
@@ -1714,7 +1734,7 @@ OEMCryptoResult SessionContext::PatternDecryptCBC(
const bool skip_block = (pattern_offset >= pattern->encrypt);
pattern_offset = (pattern_offset + 1) % pattern_length;
if (skip_block || (size < AES_BLOCK_SIZE)) {
// If we are decrypting in-place, then this byte is already correct and
// If we are decrypting in-place, then this block is already correct and
// can be skipped.
if (clear_data != cipher_data) {
memcpy(&clear_data[l], &cipher_data[l], size);

View File

@@ -121,7 +121,8 @@ OEMCryptoResult UsageTableEntry::ReportUsage(const std::vector<uint8_t>& pst,
return OEMCrypto_ERROR_WRONG_PST;
}
if (CRYPTO_memcmp(&pst[0], data_.pst, data_.pst_length)) {
LOGE("ReportUsage: wrong pst %s, should be %s.", wvcdm::b2a_hex(pst).c_str(),
LOGE("ReportUsage: wrong pst %s, should be %s.",
wvcdm::b2a_hex(pst).c_str(),
wvcdm::HexEncode(data_.pst, data_.pst_length).c_str());
return OEMCrypto_ERROR_WRONG_PST;
}
@@ -283,7 +284,7 @@ OEMCryptoResult UsageTableEntry::LoadData(CryptoEngine* ce, uint32_t index,
wvcdm::HexEncode(clear->verification, kMagicLength).c_str(),
clear->verification,
wvcdm::HexEncode(reinterpret_cast<const uint8_t*>(kEntryVerification),
kMagicLength)
kMagicLength)
.c_str(),
reinterpret_cast<const uint8_t*>(kEntryVerification));
return OEMCrypto_ERROR_BAD_MAGIC;
@@ -378,7 +379,7 @@ OEMCryptoResult UsageTable::LoadUsageEntry(
uint32_t index, const std::vector<uint8_t>& buffer,
ODK_ClockValues* clock_values) {
if (!header_loaded_) {
LOGE("CreateNewUsageEntry: Header not loaded.");
LOGE("LoadUsageEntry: Header not loaded.");
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
}
if (!entry) return OEMCrypto_ERROR_UNKNOWN_FAILURE;
@@ -557,7 +558,7 @@ OEMCryptoResult UsageTable::LoadUsageTableHeader(
wvcdm::HexEncode(clear->verification, kMagicLength).c_str(),
clear->verification,
wvcdm::HexEncode(reinterpret_cast<const uint8_t*>(kHeaderVerification),
kMagicLength)
kMagicLength)
.c_str(),
reinterpret_cast<const uint8_t*>(kHeaderVerification));
return OEMCrypto_ERROR_BAD_MAGIC;

View File

@@ -13,6 +13,7 @@ LOCAL_SRC_FILES:= \
oec_decrypt_fallback_chain.cpp \
oec_key_deriver.cpp \
oec_session_util.cpp \
oemcrypto_corpus_generator_helper.cpp \
oemcrypto_session_tests_helper.cpp \
oemcrypto_test.cpp \
oemcrypto_test_android.cpp \
@@ -21,6 +22,7 @@ LOCAL_SRC_FILES:= \
../../cdm/util/test/test_sleep.cpp \
LOCAL_C_INCLUDES += \
$(LOCAL_PATH)/fuzz_tests \
$(LOCAL_PATH)/../include \
$(LOCAL_PATH)/../odk/include \
$(LOCAL_PATH)/../odk/kdo/include \

View File

@@ -0,0 +1,247 @@
# OEMCRYPTO Fuzzing
## Objective
* Run fuzzing on OEMCrypto public APIs on linux using google supported
clusterfuzz infrastructure to find security vulnerabilities.
Design Document - https://docs.google.com/document/d/1mdSV2irJZz5Y9uYb5DmSIddBjrAIZU9q8G5Q_BGpA4I/edit?usp=sharing
Fuzzing at google -
[go/fuzzing](https://g3doc.corp.google.com/security/fuzzing/g3doc/fuzzing_resources.md?cl=head)
## Monitoring
### Cluster fuzz statistics
* Performance of OEMCrypto fuzz binaries running continuously using cluster
fuzz infrastructure can be monitored
[here](https://clusterfuzz.corp.google.com/fuzzer-stats).
The options to select are `Job type: libfuzzer_asan_oemcrypto` and `Fuzzer:
fuzzer name you are looking for`
Example: [load_license_fuzz](https://clusterfuzz.corp.google.com/fuzzer-stats?group_by=by-day&date_start=2020-07-11&date_end=2020-07-17&fuzzer=libFuzzer_oemcrypto_load_license_fuzz&job=libfuzzer_asan_oemcrypto)
### Issues filed by clusterfuzz - Fixing those issues
* Any issues found with the fuzz target under test are reported by clusterfuzz
[here](https://b.corp.google.com/hotlists/2442954).
* The bug will have a link to the test case that generated the bug. Download
the test case and follow the steps from
[testing fuzzer locally](#testing-fuzzer-locally) section to run the fuzzer
locally using the test case that caused the crash.
* Once the issue is fixed, consider adding the test case that caused the crash
to the seed corpus zip file. Details about seed corpus and their location
are mentioned in
[this section](#build-oemcrypto-unit-tests-to-generate-corpus).
## Corpus
* Once the fuzzer scripts are ready and running continuously using clusterfuzz
or android infrastructure, we can measure the efficiency of fuzzers by
looking at code coverage and number of new features that have been
discovered by fuzzer scripts here Fuzz script statistics.
A fuzzer which tries to start from random inputs and figure out intelligent
inputs to crash the libraries can be time consuming and not effective. A way
to make fuzzers more effective is by providing a set of valid and invalid
inputs of the library so that fuzzer can use those as a starting point.
These sets of valid and invalid inputs are called corpus.
The idea is to run OEMCrypto unit tests and read required data into binary
corpus files before calling into respective OEMCrypto APIs under test.
Writing corpus data to binary files is controlled by --generate_corpus flag.
### Build OEMCrypto unit tests to generate corpus
* Install Pre-requisites
```shell
$ sudo apt-get install gyp ninja-build
```
* download cdm source code (including ODK & OEMCrypto unit tests):
```shell
$ git clone sso://widevine-internal/cdm
```
* Build OEMCrypto unit tests and run with --generate_corpus flag to generate
corpus files:
```shell
$ cd /path/to/cdm/repo
$ export CDM_DIR=/path/to/cdm/repo
$ export PATH_TO_CDM_DIR=..
$ gyp --format=ninja --depth=$(pwd) oemcrypto/oemcrypto_unittests.gyp
$ ninja -C out/Default/
$ ./out/Default/oemcrypto_unittests --generate_corpus
```
* To avoid uploading huge binary files to git repository, the corpus files
will be saved in fuzzername_seed_corpus.zip format in blockbuster project's
oemcrypto_fuzzing_corpus GCS bucket using gsutil. If you need permissions
for blockbuster project, contact widevine-engprod@google.com.
```shell
$ gsutil cp gs://oemcrypto_fuzzing_corpus/<fuzzername_seed_corpus.zip> \
<destination_path>
```
## Testing fuzzer locally
* Corpus needed to run fuzz tests locally are available in blockbuster
project's oemcrypto_fuzzing_corpus GCS bucket. If you need permissions for
this project, contact widevine-engprod@google.com. Download corpus.
```shell
$ gsutil cp gs://oemcrypto_fuzzing_corpus/<fuzzername_seed_corpus.zip> \
<destination_path>
```
* Add flags to generate additional debugging information. Add '-g3' flag to
oemcrypto_fuzztests.gypi cflags_cc in order to generate additional debug
information locally.
* Build and test fuzz scripts locally using:
```shell
$ export CXX=clang++
$ export CC=clang
$ export GYP_DEFINES="clang=1"
$ cd /path/to/cdm/repo
$ export PATH_TO_CDM_DIR=.
$ gyp --format=ninja --depth=$(pwd) \
oemcrypto/test/fuzz_tests/oemcrypto_fuzztests.gyp
$ ninja -C out/Default/
$ mkdir /tmp/new_interesting_corpus
$ ./out/Default/fuzzer_binary /tmp/new_interesting_corpus \
/path/to/fuzz/seed/corpus/folder
```
* In order to run fuzz script against a crash input, follow the above steps
and run the fuzz binary against crash input rather than seed corpus.
```shell
$ ./out/Default/fuzzer_binary crash_input_file
```
## Adding a new OEMCrypto fuzz script
* In order to fuzz a new OEMCrypto API in future, a fuzz script can be added to
oemcrypto/test/fuzz_tests folder which ends with _fuzz.cc.
* In the program, define the function LLVMFuzzerTestOneInput with the following signature:
```
extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
<your test code goes here>
return 0;
}
```
*Note*: Make sure LLVMFuzzerTestOneInput calls the function you want to fuzz.
* Add a new target to oemcrypto_fuzztests.gyp file and follow instructions in
[testing fuzzer locally](#testing-fuzzer-locally) to build and test locally.
## Building OEMCrypto fuzz scripts and uploading them to Google Cloud Storage:
* We are using Google Cloud Buid (GCB) in order to setup continuous
integration which uploads OEMCrypto fuzz binaries to Google Cloud Storage.
GCB expects build script in form of a docker image that is uploaded to
Google Container Registry(GCR).
The cloud build scripts (docker images) for widevine projects are
[here](https://widevine-internal.googlesource.com/cloud/+/refs/heads/master/docker/README.md)
Refer to README of the project to setup a new docker image and uploading
the image to GCR.
* Git on borg repository needs to be integrated with GCB and a git trigger
needs to be set up in order to achieve continuous integration. Git trigger
will mention which docker image the GCB needs to use in order to build fuzz
binaries. GCB searches for docker images from GCR.
Design document lists the steps to create a git trigger.
### Adding a new fuzz script to the build script:
* In order to update build script such as adding a new fuzzer to build script,
we need to update the build script in docker image from cloud repository.
[Build script.](https://widevine-internal.googlesource.com/cloud/+/refs/heads/master/docker
/cloud_build/oemcrypto/release/ubuntu/fuzz/build.sh)
Add the new fuzz script name to fuzzers variable and follow steps in README
to upload new docker image. Make sure you update the tag to be higher than
latest version in GCR.
Run the following command from your machine to update the docker image tag
in the git trigger.
```shell
stubby call --rpc_creds_file=/tmp/mint.txt \
blade:alphasource-ci-proctor-metadata-service-prod \
ProctorMetadataService.UpdateTrigger --proto2 <<EOF
trigger {
cloud_project_number: 257246079067
name: "cdm-git-trigger"
id: "e8939c9a-d971-4c05-91b5-e0544abf872b"
state: LIVE
git_trigger {
url: "https://widevine-internal.googlesource.com/cdm"
branch_name: "master"
}
build_configs {
build {
steps {
name: "gcr.io/google.com/blockbuster-1154/
cloud-build-oemcrypto-release-ubuntu-fuzz:LATEST_TAG_VERSION"
}
}
}
result_config {
email_config {
notify_condition {
condition: ON_FAILURE
}
to_address: "wideving-engprod@google.com"
}
}
}
EOF
```
## Generate code coverage reports locally
* Code coverage is a means of measuring fuzzer performance. We want to make
sure that our fuzzer covers all the paths in our code and make any tweeks to
fuzzer logic so we can maximize coverage to get better results.
Coverage reports for git on borg project is not automated and needs to be
generated manually. Future plan is to build a dashboard for git on borg
coverage reports.
### Generate code coverage reports using script from Google cloud build
* A docker image with script to generate code coverage reports for oemcrypto
fuzz scripts is linked with a GCB trigger
`oemcrypto-fuzzing-code-coverage-git-trigger`. More information about clang
source based coverage can be found
[here](https://clang.llvm.org/docs/SourceBasedCodeCoverage.html).
* This trigger when invoked compiles oemcrypto fuzz scripts with clang source
based code coverage enabled, downloads latest corpus from cluster fuzz
for the respective fuzzer, generates and uploads code coverage html reports
to [GCS](https://pantheon.corp.google.com/storage/browser/oemcrypto_fuzzing_code_coverage_reports;tab=objects?forceOnBucketsSortingFiltering=false&project=google.com:blockbuster-1154&prefix=).
* The trigger can be invoked manually using cloud scheduler
`oemcrypto_fuzzing_code_coverage_reports`.
* In order to generate latest code coverage reports from master branch,
go to pantheon->cloud scheduler->oemcrypto_fuzzing_code_coverage_reports and
click on `RUN NOW` button.
* The above step should invoke a google cloud build. Go to cloud build console
and find latest build job with Trigger Name
`oemcrypto-fuzzing-code-coverage-git-trigger`.
* Once the build job is successful, latest code coverage reports can be
downloaded from [GCS](https://pantheon.corp.google.com/storage/browser/oemcrypto_fuzzing_code_coverage_reports;tab=objects?forceOnBucketsSortingFiltering=false&project=google.com:blockbuster-1154&prefix=).
The coverage report folder uploaded to GCS is appended with timestamp.

View File

@@ -0,0 +1,182 @@
// Copyright 2020 Google LLC. All Rights Reserved. This file and proprietary
// source code may only be used and distributed under the Widevine Master
// License Agreement.
#include "FuzzedDataProvider.h"
#include "OEMCryptoCENC.h"
#include "log.h"
#include "oemcrypto_fuzz_helper.h"
#include "oemcrypto_fuzz_structs.h"
namespace wvoec {
const size_t MAX_FUZZ_SAMPLE_SIZE = 5 * MB;
// Free dynamic memory allocated by fuzzer script.
void FreeOutputBuffers(OEMCrypto_SESSION session_id,
OEMCrypto_SampleDescription* sample_description,
size_t sample_index, int* secure_fd_array) {
for (size_t i = 0; i < sample_index; i++) {
OEMCrypto_DestBufferDesc fuzzed_output_descriptor =
sample_description[i].buffers.output_descriptor;
switch (fuzzed_output_descriptor.type) {
case OEMCrypto_BufferType_Clear: {
delete[] fuzzed_output_descriptor.buffer.clear.address;
break;
}
case OEMCrypto_BufferType_Secure: {
OEMCrypto_FreeSecureBuffer(session_id, &fuzzed_output_descriptor,
secure_fd_array[i]);
break;
}
case OEMCrypto_BufferType_Direct: {
break;
}
}
}
}
// Function to initialize output buffer pointers by allocating memory.
// Limiting output buffer size to 5 MB as 4 MB is maximum size specified
// by resource rating tier documentation.
bool InitializeOutputBuffers(OEMCrypto_SESSION session_id,
OEMCrypto_DestBufferDesc& output_descriptor,
size_t sample_index,
vector<int>& secure_fd_array) {
switch (output_descriptor.type) {
case OEMCrypto_BufferType_Clear: {
output_descriptor.buffer.clear
.address = new OEMCrypto_SharedMemory[std::min(
MAX_FUZZ_SAMPLE_SIZE, output_descriptor.buffer.clear.address_length)];
return true;
}
case OEMCrypto_BufferType_Secure: {
int* secure_fd;
OEMCryptoResult sts = OEMCrypto_AllocateSecureBuffer(
session_id,
std::min(MAX_FUZZ_SAMPLE_SIZE,
output_descriptor.buffer.secure.handle_length),
&output_descriptor, secure_fd);
if (sts == OEMCrypto_SUCCESS) secure_fd_array[sample_index] = *secure_fd;
return sts == OEMCrypto_SUCCESS;
}
case OEMCrypto_BufferType_Direct: {
return true;
}
}
}
extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
// Redirect printf and log statements from oemcrypto functions to a file to
// reduce noise
RedirectStdoutToFile();
size_t samples_length;
// Split data using separator.
auto inputs = SplitInput(data, size);
if (inputs.size() < 2) {
return 0;
}
OEMCrypto_Decrypt_Cenc_Fuzz fuzzed_structure;
if (inputs[0].size() < sizeof(fuzzed_structure)) {
return 0;
}
// Copy OEMCrypto_Decrypt_Cenc_Fuzz from input data.
memcpy(&fuzzed_structure, data, sizeof(fuzzed_structure));
ConvertDataToValidEnum(OEMCrypto_CipherMode_MaxValue,
&fuzzed_structure.cipher_mode);
size_t remaining_size_for_samples =
inputs[0].size() - sizeof(fuzzed_structure);
// Initialize FDP structures to read data using inbuilt functions.
FuzzedDataProvider fuzzed_sample_data(data + sizeof(fuzzed_structure),
remaining_size_for_samples);
FuzzedDataProvider fuzzed_subsample_data(inputs[1].data(), inputs[1].size());
// Read subsamples from fuzzed data.
vector<OEMCrypto_SubSampleDescription> subsamples;
while (fuzzed_subsample_data.remaining_bytes() >
sizeof(OEMCrypto_SubSampleDescription)) {
OEMCrypto_SubSampleDescription subsample;
fuzzed_subsample_data.ConsumeData(&subsample,
sizeof(OEMCrypto_SubSampleDescription));
subsamples.push_back(subsample);
}
if (subsamples.size() == 0) {
return 0;
}
// Infer samples_length from fuzzed data.
size_t sample_description_size = sizeof(OEMCrypto_SampleDescription);
samples_length =
fuzzed_sample_data.remaining_bytes() / sample_description_size;
if (samples_length == 0) {
return 0;
}
// Initialize sample_descriptions array.
vector<OEMCrypto_SampleDescription> sample_descriptions(samples_length);
// Create array to maintain secure_fd buffer values for secure buffers.
vector<int> secure_fd_array(samples_length);
OEMCryptoLicenseAPIFuzz license_api_fuzz;
Session* session = license_api_fuzz.session();
// Copy samples from fuzzed data.
size_t input_subsample_index = 0;
size_t total_input_data_length = 0;
for (size_t i = 0; i < samples_length; i++) {
fuzzed_sample_data.ConsumeData(&sample_descriptions[i],
sample_description_size);
ConvertDataToValidEnum(
OEMCrypto_BufferType_MaxValue,
&sample_descriptions[i].buffers.output_descriptor.type);
// Copy random data into input sample data. Cap input data length at 5 MB,
// 1 MB higher than that described by resource rating tier.
total_input_data_length += std::min(
MAX_FUZZ_SAMPLE_SIZE, sample_descriptions[i].buffers.input_data_length);
// Copy sub sample data.
sample_descriptions[i].subsamples = &subsamples[input_subsample_index];
input_subsample_index += sample_descriptions[i].subsamples_length;
if (input_subsample_index > subsamples.size()) return 0;
} // Sample loop.
// Allocate input/output buffers for each sample description.
vector<OEMCrypto_SharedMemory> input_buffer(total_input_data_length);
RAND_bytes(input_buffer.data(), total_input_data_length);
size_t input_buffer_index = 0;
for (size_t i = 0; i < samples_length; i++) {
sample_descriptions[i].buffers.input_data =
&input_buffer[input_buffer_index];
input_buffer_index += std::min(
MAX_FUZZ_SAMPLE_SIZE, sample_descriptions[i].buffers.input_data_length);
// Create output buffer pointers. If secure buffer is not supported, we
// explicitly convert to clear buffer and fuzz.
if (!InitializeOutputBuffers(
session->session_id(),
sample_descriptions[i].buffers.output_descriptor, i,
secure_fd_array)) {
LOGI(
"[OEMCrypto decrypt CENC fuzz] Secure buffers are not supported. Use "
"clear buffer instead.");
sample_descriptions[i].buffers.output_descriptor.type =
OEMCrypto_BufferType_Clear;
InitializeOutputBuffers(session->session_id(),
sample_descriptions[i].buffers.output_descriptor,
i, secure_fd_array);
}
}
// Load license and call decrypt_cenc API.
license_api_fuzz.LoadLicense();
OEMCrypto_SelectKey(session->session_id(), session->license().keys[0].key_id,
session->license().keys[0].key_id_length,
fuzzed_structure.cipher_mode);
OEMCrypto_DecryptCENC(session->session_id(), sample_descriptions.data(),
samples_length, &fuzzed_structure.pattern);
FreeOutputBuffers(session->session_id(), sample_descriptions.data(),
samples_length, secure_fd_array.data());
return 0;
}
} // namespace wvoec

View File

@@ -0,0 +1,25 @@
// Copyright 2020 Google LLC. All Rights Reserved. This file and proprietary
// source code may only be used and distributed under the Widevine Master
// License Agreement.
#include "oemcrypto_fuzz_helper.h"
namespace wvoec {
void RedirectStdoutToFile() { freopen("log.txt", "a", stdout); }
std::vector<std::vector<uint8_t>> SplitInput(const uint8_t* data, size_t size) {
std::vector<std::vector<uint8_t>> result;
auto current_pos = data;
auto end = data + size;
// Using memmem to find separator
while (const uint8_t* pos = reinterpret_cast<const uint8_t*>(
memmem(current_pos, end - current_pos, kFuzzDataSeparator,
sizeof(kFuzzDataSeparator)))) {
result.push_back({current_pos, pos});
current_pos = pos + sizeof(kFuzzDataSeparator);
}
if (current_pos < end) {
result.push_back({current_pos, end});
}
return result;
}
} // namespace wvoec

View File

@@ -0,0 +1,106 @@
// Copyright 2020 Google LLC. All Rights Reserved. This file and proprietary
// source code may only be used and distributed under the Widevine Master
// License Agreement.
#ifndef OEMCRYPTO_FUZZ_HELPER_H_
#define OEMCRYPTO_FUZZ_HELPER_H_
#include <vector>
#include "FuzzedDataProvider.h"
#include "OEMCryptoCENC.h"
#include "oec_device_features.h"
#include "oemcrypto_corpus_generator_helper.h"
#include "oemcrypto_session_tests_helper.h"
namespace wvoec {
// Initial setup to create a valid OEMCrypto state such as initializing crypto
// firmware/hardware, installing golden key box etc. in order to fuzz
// OEMCrypto APIs.
class InitializeFuzz : public SessionUtil {
public:
InitializeFuzz() {
wvoec::global_features.Initialize();
OEMCrypto_SetSandbox(kTestSandbox, sizeof(kTestSandbox));
OEMCrypto_Initialize();
EnsureTestKeys();
}
~InitializeFuzz() { OEMCrypto_Terminate(); }
};
class OEMCryptoLicenseAPIFuzz : public InitializeFuzz {
public:
OEMCryptoLicenseAPIFuzz() : license_messages_(&session_) {
session_.open();
InstallTestRSAKey(&session_);
session_.GenerateNonce();
}
~OEMCryptoLicenseAPIFuzz() {
session_.close();
}
LicenseRoundTrip& license_messages() { return license_messages_; }
Session* session() { return &session_; }
void LoadLicense() {
license_messages_.SignAndVerifyRequest();
license_messages_.CreateDefaultResponse();
license_messages_.EncryptAndSignResponse();
license_messages_.LoadResponse();
}
private:
Session session_;
LicenseRoundTrip license_messages_;
};
class OEMCryptoProvisioningAPIFuzz : public InitializeFuzz {
public:
OEMCryptoProvisioningAPIFuzz()
: provisioning_messages_(&session_, encoded_rsa_key_) {
// Opens a session and Generates Nonce.
provisioning_messages_.PrepareSession(keybox_);
}
~OEMCryptoProvisioningAPIFuzz() { session_.close(); }
ProvisioningRoundTrip& provisioning_messages() {
return provisioning_messages_;
}
private:
Session session_;
ProvisioningRoundTrip provisioning_messages_;
};
// Initial setup to create a valid state such as creating session, installing
// golden key box etc. in order to fuzz Load Renewal API.
class OEMCryptoRenewalAPIFuzz : public OEMCryptoLicenseAPIFuzz {
public:
OEMCryptoRenewalAPIFuzz() : renewal_messages_(&license_messages()) {}
RenewalRoundTrip& renewal_messages() { return renewal_messages_; }
private:
RenewalRoundTrip renewal_messages_;
};
// Convert data to valid enum value.
template <typename T>
void ConvertDataToValidEnum(T max_enum_value, T* t) {
FuzzedDataProvider fuzzed_enum_data(reinterpret_cast<uint8_t*>(t), sizeof(T));
*t = static_cast<T>(fuzzed_enum_data.ConsumeIntegralInRange<uint32_t>(
0, static_cast<uint32_t>(max_enum_value)));
}
// Redirect printf and log statements from oemcrypto functions to a file to
// reduce noise
void RedirectStdoutToFile();
// Function to split fuzzer input using delimiter "-_^_".
std::vector<std::vector<uint8_t>> SplitInput(const uint8_t* data, size_t size);
} // namespace wvoec
#endif // OEMCRYPTO_FUZZ_HELPER_H_

View File

@@ -0,0 +1,42 @@
// Copyright 2020 Google LLC. All Rights Reserved. This file and proprietary
// source code may only be used and distributed under the Widevine Master
// License Agreement.
#ifndef OEMCRYPTO_FUZZ_STRUCTS_H_
#define OEMCRYPTO_FUZZ_STRUCTS_H_
namespace wvoec {
struct OEMCrypto_Renewal_Response_Fuzz {
// Timer limits in core license response needs to be fuzzed as load renewal
// depends on timer limits loaded from license response.
ODK_TimerLimits timer_limits;
// message(core_response + license_renewal_response) which mimics
// response from license renewal server needs to be fuzzed. core_request
// will be used to generate serialized core response.
oemcrypto_core_message::ODK_RenewalRequest core_request;
// Renewal duration seconds needs to be fuzzed which is part of serialized
// core message from license renewal server.
uint64_t renewal_duration_seconds;
// license_renewal_response is of variable length and not included in this
// structure.
};
struct OEMCrypto_Request_Fuzz {
// We would like to fuzz computed signature_length, input core_message_length
// that ODK parses and actual message buffer to the request APIs.
size_t signature_length;
size_t core_message_length;
// Request message is of variable length and not included in this structure.
};
struct OEMCrypto_Decrypt_Cenc_Fuzz {
// Corpus format is as below, let | be separator.
// cipher_mode + pattern + sample_data for all samples |
// subsample_data for all samples
OEMCryptoCipherMode cipher_mode;
OEMCrypto_CENCEncryptPatternDesc pattern;
// Sample data and subsample data are of variable length and not included in
// this structure.
};
} // namespace wvoec
#endif // OEMCRYPTO_FUZZ_STRUCTS_H_

View File

@@ -0,0 +1,36 @@
#include "properties.h"
#include "oemcrypto_session_tests_helper.h"
using namespace wvoec;
static bool is_init = false;
extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
SessionUtil session_helper;
if (!is_init) {
wvoec::global_features.Initialize();
wvoec::global_features.RestrictFilter("*");
wvcdm::Properties::Init();
is_init = true;
}
OEMCrypto_Initialize();
session_helper.EnsureTestKeys();
Session s;
s.open();
s.GenerateDerivedKeysFromKeybox(session_helper.keybox_);
static const uint32_t SignatureBufferMaxLength = size;
vector<uint8_t> signature(SignatureBufferMaxLength);
size_t signature_length = signature.size();
OEMCryptoResult sts;
sts = OEMCrypto_GenerateSignature(s.session_id(), data, size,
&signature[0], &signature_length);
s.close();
OEMCrypto_Terminate();
return 0;
}

View File

@@ -0,0 +1,28 @@
// Copyright 2020 Google LLC. All Rights Reserved. This file and proprietary
// source code may only be used and distributed under the Widevine Master
// License Agreement.
#include "oemcrypto_fuzz_helper.h"
#include "oemcrypto_fuzz_structs.h"
namespace wvoec {
extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
// Redirect printf and log statements from oemcrypto functions to a file to
// reduce noise
RedirectStdoutToFile();
// Reject the input if it is less than fuzz data structure size.
if (size < sizeof(OEMCrypto_Request_Fuzz)) {
return 0;
}
// Input for license request API will be modified by OEMCrypto, hence it
// cannot be a const. Fuzzer complains if const identifier is removed of data,
// hence copying data into a non const pointer.
uint8_t* input = new uint8_t[size];
memcpy(input, data, size);
OEMCryptoLicenseAPIFuzz license_api_fuzz;
license_api_fuzz.license_messages().InjectFuzzedRequestData(input, size);
delete[] input;
return 0;
}
} // namespace wvoec

View File

@@ -0,0 +1,66 @@
// Copyright 2020 Google LLC. All Rights Reserved. This file and proprietary
// source code may only be used and distributed under the Widevine Master
// License Agreement.
#include "FuzzedDataProvider.h"
#include "oemcrypto_fuzz_helper.h"
#include "oemcrypto_fuzz_structs.h"
namespace wvoec {
extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
// Redirect printf and log statements from oemcrypto functions to a file to
// reduce noise
RedirectStdoutToFile();
// Corpus format is as below, let | be separator.
// message buffer with key data | entitled content key object array with
// offsets and lengths to read key data from message buffer.
// Split data using separator.
auto inputs = SplitInput(data, size);
if (inputs.size() < 2) {
return 0;
}
FuzzedDataProvider fuzzed_entitled_content_key_array(inputs[1].data(),
inputs[1].size());
// Message to be verified. Return 0 if key data buffer is empty.
if (inputs[0].size() == 0) {
return 0;
}
// Copy data to OEMCrypto_EntitledContentKeyObject array.
size_t entitled_content_key_object_size =
sizeof(OEMCrypto_EntitledContentKeyObject);
size_t entitled_content_key_array_length =
fuzzed_entitled_content_key_array.remaining_bytes() /
entitled_content_key_object_size;
if (entitled_content_key_array_length == 0) {
return 0;
}
OEMCrypto_EntitledContentKeyObject* entitled_content_key_array =
new OEMCrypto_EntitledContentKeyObject[entitled_content_key_array_length];
for (size_t i = 0; i < entitled_content_key_array_length; i++) {
fuzzed_entitled_content_key_array.ConsumeData(
&entitled_content_key_array[i], entitled_content_key_object_size);
}
OEMCryptoLicenseAPIFuzz license_api_fuzz;
// Setting up state. Load default entitlement license to load entitlement
// keys into sessions key table.
license_api_fuzz.license_messages().set_license_type(
OEMCrypto_EntitlementLicense);
license_api_fuzz.LoadLicense();
// Call OEMCrypto_LoadEntitledContentKeys with fuzzed buffers.
Session* session = license_api_fuzz.session();
uint8_t* fuzzed_key_data = inputs[0].data();
size_t fuzzed_key_data_size = inputs[0].size();
OEMCrypto_LoadEntitledContentKeys(
session->session_id(), fuzzed_key_data, fuzzed_key_data_size,
entitled_content_key_array_length, entitled_content_key_array);
delete[] entitled_content_key_array;
return 0;
}
} // namespace wvoec

View File

@@ -0,0 +1,30 @@
// Copyright 2020 Google LLC. All Rights Reserved. This file and proprietary
// source code may only be used and distributed under the Widevine Master
// License Agreement.
#include "oemcrypto_fuzz_helper.h"
namespace wvoec {
extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
// Redirect printf and log statements from oemcrypto functions to a file to
// reduce noise
RedirectStdoutToFile();
if (size < sizeof(ODK_ParsedLicense) + sizeof(MessageData)) {
return 0;
}
OEMCryptoLicenseAPIFuzz license_api_fuzz;
license_api_fuzz.license_messages().SignAndVerifyRequest();
// Interpreting input fuzz data as unencrypted (core_response + license
// message data) from license server.
license_api_fuzz.license_messages().InjectFuzzedResponseData(data, size);
// Convert OEMCrypto_LicenseType in core_response to a valid enum value.
ConvertDataToValidEnum(
OEMCrypto_LicenstType_MaxValue,
&license_api_fuzz.license_messages().core_response().license_type);
license_api_fuzz.license_messages().EncryptAndSignResponse();
license_api_fuzz.license_messages().LoadResponse();
return 0;
}
} // namespace wvoec

View File

@@ -0,0 +1,27 @@
// Copyright 2020 Google LLC. All Rights Reserved. This file and proprietary
// source code may only be used and distributed under the Widevine Master
// License Agreement.
#include "oemcrypto_fuzz_helper.h"
namespace wvoec {
extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
// Redirect printf and log statements from oemcrypto functions to a file to
// reduce noise
RedirectStdoutToFile();
if (size < sizeof(ODK_ParsedProvisioning) + sizeof(RSAPrivateKeyMessage)) {
return 0;
}
OEMCryptoProvisioningAPIFuzz provisioning_api_fuzz;
provisioning_api_fuzz.provisioning_messages().SignAndVerifyRequest();
// Interpreting input fuzz data as unencrypted(core_response + provisioning
// message data) from provisioning server.
provisioning_api_fuzz.provisioning_messages().InjectFuzzedResponseData(data,
size);
provisioning_api_fuzz.provisioning_messages().EncryptAndSignResponse();
provisioning_api_fuzz.provisioning_messages().LoadResponse();
return 0;
}
} // namespace wvoec

View File

@@ -0,0 +1,42 @@
// Copyright 2020 Google LLC. All Rights Reserved. This file and proprietary
// source code may only be used and distributed under the Widevine Master
// License Agreement.
#include "oemcrypto_fuzz_helper.h"
#include "oemcrypto_fuzz_structs.h"
namespace wvoec {
extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
// Redirect printf and log statements from oemcrypto functions to a file to
// reduce noise
RedirectStdoutToFile();
if (size < sizeof(OEMCrypto_Renewal_Response_Fuzz)) {
return 0;
}
// Copy input data to OEMCrypto_Renewal_Response_Fuzz and rest of message
// into encrypted license_renewal_response.
OEMCrypto_Renewal_Response_Fuzz fuzzed_data;
memcpy(&fuzzed_data, data, sizeof(fuzzed_data));
const uint8_t* renewal_response =
data + sizeof(OEMCrypto_Renewal_Response_Fuzz);
const size_t renewal_response_size =
size - sizeof(OEMCrypto_Renewal_Response_Fuzz);
OEMCryptoRenewalAPIFuzz renewal_response_fuzz;
renewal_response_fuzz.license_messages().SignAndVerifyRequest();
renewal_response_fuzz.license_messages().CreateDefaultResponse();
// Inject timer limits from fuzzed input to timer_limits field from
// core license response.
renewal_response_fuzz.license_messages().InjectFuzzedTimerLimits(fuzzed_data);
renewal_response_fuzz.license_messages().EncryptAndSignResponse();
renewal_response_fuzz.license_messages().LoadResponse();
// Call renewal response API using fuzzed data.
renewal_response_fuzz.renewal_messages().SignAndVerifyRequest();
renewal_response_fuzz.renewal_messages().InjectFuzzedResponseData(
fuzzed_data, renewal_response, renewal_response_size);
renewal_response_fuzz.renewal_messages().LoadResponse();
return 0;
}
} // namespace wvoec

View File

@@ -0,0 +1,28 @@
// Copyright 2020 Google LLC. All Rights Reserved. This file and proprietary
// source code may only be used and distributed under the Widevine Master
// License Agreement.
#include "oemcrypto_fuzz_helper.h"
#include "oemcrypto_fuzz_structs.h"
namespace wvoec {
extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
// Redirect printf and log statements from oemcrypto functions to a file to
// reduce noise
RedirectStdoutToFile();
// If input size is less than fuzz data structure size, reject the input.
if (size < sizeof(OEMCrypto_Request_Fuzz)) {
return 0;
}
// Input for provisioning request API will be modified by OEMCrypto, hence it
// cannot be a const. Fuzzer complains if const identifier is removed of data,
// hence copying data into a non const pointer.
uint8_t* input = new uint8_t[size];
memcpy(input, data, size);
OEMCryptoProvisioningAPIFuzz provisioning_api_fuzz;
provisioning_api_fuzz.provisioning_messages().InjectFuzzedRequestData(input,
size);
delete[] input;
return 0;
}
} // namespace wvoec

View File

@@ -0,0 +1,27 @@
// Copyright 2020 Google LLC. All Rights Reserved. This file and proprietary
// source code may only be used and distributed under the Widevine Master
// License Agreement.
#include "oemcrypto_fuzz_helper.h"
#include "oemcrypto_fuzz_structs.h"
namespace wvoec {
extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
// Redirect printf and log statements from oemcrypto functions to a file to
// reduce noise
RedirectStdoutToFile();
// If input size is less than fuzz data structure, reject the input.
if (size < sizeof(OEMCrypto_Request_Fuzz)) {
return 0;
}
// Input for renewal request API will be modified by OEMCrypto, hence it
// cannot be a const. Fuzzer complains if const identifier is removed of data,
// hence copying data into a non const pointer.
uint8_t* input = new uint8_t[size];
memcpy(input, data, size);
OEMCryptoRenewalAPIFuzz renewal_api_fuzz;
renewal_api_fuzz.renewal_messages().InjectFuzzedRequestData(input, size);
delete[] input;
return 0;
}
} // namespace wvoec

View File

@@ -0,0 +1,10 @@
#include <stddef.h>
#include <stdint.h>
extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
if (size > 0 && data[0] == 'H')
if (size > 1 && data[1] == 'I')
if (size > 2 && data[2] == '!')
__builtin_trap();
return 0;
}

View File

@@ -7,6 +7,7 @@
#include <stdint.h>
#include <string.h>
#include "oemcrypto_corpus_generator_helper.h"
#include "oemcrypto_types.h"
#include "string_conversions.h"
@@ -55,7 +56,12 @@ OEMCryptoResult DecryptFallbackChain::Decrypt(
OEMCrypto_DecryptCENC(session_id, samples, samples_length, pattern);
// No need for a fallback. Abort early.
if (sts != OEMCrypto_ERROR_BUFFER_TOO_LARGE) return sts;
if (sts != OEMCrypto_ERROR_BUFFER_TOO_LARGE) {
if (ShouldGenerateCorpus()) {
WriteDecryptCencCorpus(cipher_mode, samples, pattern, samples_length);
}
return sts;
}
// Fall back to decrypting individual samples.
for (size_t i = 0; i < samples_length; ++i) {
@@ -75,7 +81,12 @@ OEMCryptoResult DecryptFallbackChain::DecryptSample(
OEMCryptoResult sts = OEMCrypto_DecryptCENC(session_id, &sample, 1, pattern);
// No need for a fallback. Abort early.
if (sts != OEMCrypto_ERROR_BUFFER_TOO_LARGE) return sts;
if (sts != OEMCrypto_ERROR_BUFFER_TOO_LARGE) {
if (ShouldGenerateCorpus()) {
WriteDecryptCencCorpus(cipher_mode, &sample, pattern, 1);
}
return sts;
}
// Fall back to decrypting individual subsamples.
OEMCrypto_SampleDescription fake_sample = sample;
@@ -88,7 +99,7 @@ OEMCryptoResult DecryptFallbackChain::DecryptSample(
fake_sample.subsamples = &subsample;
fake_sample.subsamples_length = 1;
sts = DecryptSubsample(session_id, fake_sample, pattern);
sts = DecryptSubsample(session_id, fake_sample, pattern, cipher_mode);
if (sts != OEMCrypto_SUCCESS) return sts;
fake_sample.buffers.input_data += length;
@@ -106,11 +117,17 @@ OEMCryptoResult DecryptFallbackChain::DecryptSample(
// OEMCrypto implementation does not accept full subsamples.
OEMCryptoResult DecryptFallbackChain::DecryptSubsample(
OEMCrypto_SESSION session_id, const OEMCrypto_SampleDescription& sample,
const OEMCrypto_CENCEncryptPatternDesc* pattern) {
const OEMCrypto_CENCEncryptPatternDesc* pattern,
OEMCryptoCipherMode cipher_mode) {
OEMCryptoResult sts = OEMCrypto_DecryptCENC(session_id, &sample, 1, pattern);
// No need for a fallback. Abort early.
if (sts != OEMCrypto_ERROR_BUFFER_TOO_LARGE) return sts;
if (sts != OEMCrypto_ERROR_BUFFER_TOO_LARGE) {
if (ShouldGenerateCorpus()) {
WriteDecryptCencCorpus(cipher_mode, &sample, pattern, 1);
}
return sts;
}
// Fall back to decrypting individual subsample halves.
const OEMCrypto_SubSampleDescription& subsample = sample.subsamples[0];
@@ -132,7 +149,7 @@ OEMCryptoResult DecryptFallbackChain::DecryptSubsample(
subsample.num_bytes_encrypted == 0)
fake_subsample.subsample_flags |= OEMCrypto_LastSubsample;
sts = DecryptSubsampleHalf(session_id, fake_sample, pattern);
sts = DecryptSubsampleHalf(session_id, fake_sample, pattern, cipher_mode);
if (sts != OEMCrypto_SUCCESS) return sts;
// Advance the buffers for the other half, in case they're needed.
@@ -154,7 +171,7 @@ OEMCryptoResult DecryptFallbackChain::DecryptSubsample(
if (subsample.subsample_flags & OEMCrypto_LastSubsample)
fake_subsample.subsample_flags |= OEMCrypto_LastSubsample;
sts = DecryptSubsampleHalf(session_id, fake_sample, pattern);
sts = DecryptSubsampleHalf(session_id, fake_sample, pattern, cipher_mode);
if (sts != OEMCrypto_SUCCESS) return sts;
}
@@ -166,7 +183,11 @@ OEMCryptoResult DecryptFallbackChain::DecryptSubsample(
// caller.
OEMCryptoResult DecryptFallbackChain::DecryptSubsampleHalf(
OEMCrypto_SESSION session_id, const OEMCrypto_SampleDescription& sample,
const OEMCrypto_CENCEncryptPatternDesc* pattern) {
const OEMCrypto_CENCEncryptPatternDesc* pattern,
OEMCryptoCipherMode cipher_mode) {
if (ShouldGenerateCorpus()) {
WriteDecryptCencCorpus(cipher_mode, &sample, pattern, 1);
}
return OEMCrypto_DecryptCENC(session_id, &sample, 1, pattern);
// In a real CDM, you would want some fallback here to handle the case where
// the buffer is too big for the OEMCrypto implementation. But in the case of
@@ -175,4 +196,40 @@ OEMCryptoResult DecryptFallbackChain::DecryptSubsampleHalf(
// here.
}
// Used for OEMCrypto Fuzzing: Corpus format is as below, let | be separator.
// cipher_mode + pattern + sample_data for all samples |
// subsample_data for all samples
void WriteDecryptCencCorpus(
OEMCryptoCipherMode cipher_mode,
const OEMCrypto_SampleDescription* samples_description,
const OEMCrypto_CENCEncryptPatternDesc* pattern, size_t samples_length) {
const std::string file_name =
GetFileName("oemcrypto_decrypt_cenc_fuzz_seed_corpus");
// Cipher mode.
AppendToFile(file_name, reinterpret_cast<const char*>(&cipher_mode),
sizeof(OEMCryptoCipherMode));
// Pattern.
AppendToFile(file_name, reinterpret_cast<const char*>(pattern),
sizeof(OEMCrypto_CENCEncryptPatternDesc));
// Sample data for all samples.
for (size_t i = 0; i < samples_length; i++) {
AppendToFile(file_name,
reinterpret_cast<const char*>(&samples_description[i]),
sizeof(OEMCrypto_SampleDescription));
}
AppendSeparator(file_name);
// Subsample data for all samples.
for (size_t i = 0; i < samples_length; i++) {
for (size_t j = 0; j < samples_description[i].subsamples_length; j++) {
AppendToFile(
file_name,
reinterpret_cast<const char*>(&samples_description[i].subsamples[j]),
sizeof(OEMCrypto_SubSampleDescription));
}
}
}
} // namespace wvoec

View File

@@ -43,17 +43,24 @@ class DecryptFallbackChain {
static OEMCryptoResult DecryptSubsample(
OEMCrypto_SESSION session_id, const OEMCrypto_SampleDescription& sample,
const OEMCrypto_CENCEncryptPatternDesc* pattern);
const OEMCrypto_CENCEncryptPatternDesc* pattern,
OEMCryptoCipherMode cipher_mode);
static OEMCryptoResult DecryptSubsampleHalf(
OEMCrypto_SESSION session_id, const OEMCrypto_SampleDescription& sample,
const OEMCrypto_CENCEncryptPatternDesc* pattern);
const OEMCrypto_CENCEncryptPatternDesc* pattern,
OEMCryptoCipherMode cipher_mode);
// There is no reason to have an instance of this class.
DecryptFallbackChain() = delete;
CORE_DISALLOW_COPY_AND_ASSIGN(DecryptFallbackChain);
};
void WriteDecryptCencCorpus(OEMCryptoCipherMode cipher_mode,
const OEMCrypto_SampleDescription* samples,
const OEMCrypto_CENCEncryptPatternDesc* pattern,
size_t samples_length);
} // namespace wvoec
#endif // CDM_OEC_DECRYPT_FALLBACK_CHAIN_H_

View File

@@ -32,6 +32,7 @@ void DeviceFeatures::Initialize() {
return;
}
uint8_t buffer[1];
uint8_t iv[16] = {};
size_t size = 0;
provisioning_method = OEMCrypto_GetProvisioningMethod();
printf("provisioning_method = %s\n",
@@ -58,12 +59,12 @@ void DeviceFeatures::Initialize() {
printf("loads_certificate = %s.\n", loads_certificate ? "true" : "false");
generic_crypto =
(OEMCrypto_ERROR_NOT_IMPLEMENTED !=
OEMCrypto_Generic_Encrypt(session, buffer, 0, buffer,
OEMCrypto_Generic_Encrypt(session, buffer, 0, iv,
OEMCrypto_AES_CBC_128_NO_PADDING, buffer));
printf("generic_crypto = %s.\n", generic_crypto ? "true" : "false");
OEMCrypto_CloseSession(session);
api_version = OEMCrypto_APIVersion();
printf("api_version = %d.\n", api_version);
printf("api_version = %u.\n", api_version);
// These unit tests only work with new usage tables. We do not test v12
// usage tables.
if (api_version > 12) usage_table = OEMCrypto_SupportsUsageTable();
@@ -80,7 +81,7 @@ void DeviceFeatures::Initialize() {
}
printf("cast_receiver = %s.\n", cast_receiver ? "true" : "false");
resource_rating = OEMCrypto_ResourceRatingTier();
printf("resource_rating = %d, security level %s.\n", resource_rating,
printf("resource_rating = %u, security level %s.\n", resource_rating,
OEMCrypto_SecurityLevel());
uint32_t decrypt_hash_type = OEMCrypto_SupportsDecryptHash();
supports_crc = (decrypt_hash_type == OEMCrypto_CRC_Clear_Buffer);
@@ -114,8 +115,7 @@ void DeviceFeatures::Initialize() {
std::string security_level = OEMCrypto_SecurityLevel();
supports_level_1 = (security_level == "L1");
printf("SecurityLevel is %s (%s)\n",
supports_level_1 ? "Level 1" : "Not Level 1",
security_level.c_str());
supports_level_1 ? "Level 1" : "Not Level 1", security_level.c_str());
CheckSecureBuffers();
OEMCrypto_Terminate();
initialized_ = true;

View File

@@ -61,6 +61,12 @@ void Encryptor::PadAndEncryptProvisioningMessage(
EXPECT_EQ(1, GetRandBytes(data->rsa_key_iv, KEY_IV_SIZE));
ASSERT_EQ(enc_key_.size(), KEY_SIZE);
*encrypted = *data;
if (data->rsa_key_length > sizeof(data->rsa_key)) {
// OEMCrypto Fuzzing: fuzzed |rsa_key_length| overflows the allocated
// buffer. Skip encryption in that case.
return;
}
size_t padding = AES_BLOCK_SIZE - (data->rsa_key_length % AES_BLOCK_SIZE);
memset(data->rsa_key + data->rsa_key_length, static_cast<uint8_t>(padding),
padding);

View File

@@ -31,8 +31,11 @@
#include "core_message_serialize.h"
#include "disallow_copy_and_assign.h"
#include "log.h"
#include "odk_attributes.h"
#include "odk_structs.h"
#include "oec_device_features.h"
#include "oec_test_data.h"
#include "oemcrypto_corpus_generator_helper.h"
#include "oemcrypto_types.h"
#include "platform.h"
#include "string_conversions.h"
@@ -162,6 +165,10 @@ void RoundTrip<CoreRequest, PrepAndSignRequest, CoreResponse,
std::max(required_message_size_, core_message_length + small_size);
data.resize(message_size);
for (size_t i = 0; i < data.size(); i++) data[i] = i & 0xFF;
if (ShouldGenerateCorpus()) {
WriteRequestApiCorpus<CoreRequest>(gen_signature_length,
core_message_length, data);
}
vector<uint8_t> gen_signature(gen_signature_length);
ASSERT_EQ(PrepAndSignRequest(session()->session_id(), data.data(),
@@ -177,6 +184,27 @@ void RoundTrip<CoreRequest, PrepAndSignRequest, CoreResponse,
VerifyRequestSignature(data, gen_signature, core_message_length);
}
template <class CoreRequest, PrepAndSignRequest_t PrepAndSignRequest,
class CoreResponse, class ResponseData>
void RoundTrip<CoreRequest, PrepAndSignRequest, CoreResponse,
ResponseData>::InjectFuzzedRequestData(uint8_t* data,
size_t size) {
OEMCrypto_Request_Fuzz fuzz_structure;
// Copy data into fuzz structure, cap signature length at 1mb as it will be
// used to initialize signature vector.
memcpy(&fuzz_structure, data, sizeof(fuzz_structure));
fuzz_structure.signature_length =
std::min(fuzz_structure.signature_length, MB);
vector<uint8_t> signature(fuzz_structure.signature_length);
// Interpret rest of data as actual message buffer to request APIs.
uint8_t* message_ptr = data + sizeof(fuzz_structure);
size_t message_size = size - sizeof(fuzz_structure);
PrepAndSignRequest(session()->session_id(), message_ptr, message_size,
&fuzz_structure.core_message_length, signature.data(),
&fuzz_structure.signature_length);
}
template <class CoreRequest, PrepAndSignRequest_t PrepAndSignRequest,
class CoreResponse, class ResponseData>
OEMCrypto_Substring RoundTrip<CoreRequest, PrepAndSignRequest, CoreResponse,
@@ -309,8 +337,35 @@ void ProvisioningRoundTrip::EncryptAndSignResponse() {
&response_signature_);
}
void ProvisioningRoundTrip::InjectFuzzedResponseData(const uint8_t* data,
size_t size UNUSED) {
// Interpreting fuzz data as unencrypted core_response + message_data
const size_t core_response_size = sizeof(ODK_ParsedProvisioning);
// Copy core_response from data and serialize.
memcpy(&core_response_, data, core_response_size);
// Copy provisioning message data into response_data.
memcpy(&response_data_, data + core_response_size, sizeof(response_data_));
// Set nonce to one from session to pass nonce checks.
response_data_.nonce = session()->nonce();
}
OEMCryptoResult ProvisioningRoundTrip::LoadResponse(Session* session) {
EXPECT_NE(session, nullptr);
// Write corpus for oemcrypto_load_provisioning_fuzz. Fuzz script expects
// unencrypted response from provisioning server as input corpus data.
// Data will be encrypted and signed again explicitly by fuzzer script after
// mutations.
if (ShouldGenerateCorpus()) {
const std::string file_name =
GetFileName("oemcrypto_load_provisioning_fuzz_seed_corpus");
// Corpus for license response fuzzer should be in the format:
// unencrypted (core_response + response_data).
AppendToFile(file_name, reinterpret_cast<const char*>(&core_response_),
sizeof(ODK_ParsedProvisioning));
AppendToFile(file_name, reinterpret_cast<const char*>(&response_data_),
sizeof(response_data_));
}
size_t wrapped_key_length = 0;
const OEMCryptoResult sts = LoadResponseNoRetry(session, &wrapped_key_length);
if (sts != OEMCrypto_ERROR_SHORT_BUFFER) return sts;
@@ -472,6 +527,69 @@ void LicenseRoundTrip::CreateDefaultResponse() {
FillCoreResponseSubstrings();
}
void LicenseRoundTrip::ConvertDataToValidBools(ODK_ParsedLicense* t) {
t->nonce_required = ConvertByteToValidBoolean(&t->nonce_required);
t->timer_limits.soft_enforce_playback_duration = ConvertByteToValidBoolean(
&t->timer_limits.soft_enforce_playback_duration);
t->timer_limits.soft_enforce_rental_duration =
ConvertByteToValidBoolean(&t->timer_limits.soft_enforce_rental_duration);
}
void LicenseRoundTrip::InjectFuzzedTimerLimits(
OEMCrypto_Renewal_Response_Fuzz& fuzzed_data) {
// Interpreting fuzz data as timer limits.
// Copy timer limits from data.
memcpy(&core_response_.timer_limits, &fuzzed_data.timer_limits,
sizeof(fuzzed_data.timer_limits));
ConvertDataToValidBools(&core_response_);
}
void LicenseRoundTrip::InjectFuzzedResponseData(const uint8_t* data,
size_t size UNUSED) {
// Interpreting fuzz data as unencrypted core_response + message_data
const size_t core_response_size = sizeof(ODK_ParsedLicense);
// Copy core_response from data.
memcpy(&core_response_, data, core_response_size);
// Maximum number of keys could be kMaxNumKeys(30). key_array_length can be
// any random value as it is read from fuzz data.
// Key data array(MessageKeyData keys[kMaxNumKeys]) will be looped over
// key_array_length number of times during LoadLicense. If key_array_length is
// more than kMaxNumKeys, setting it to max value of kMaxNumKeys as we should
// not go out of bounds of this array length. For corpus, this value is
// already hard coded to 4.
if (core_response_.key_array_length > kMaxNumKeys) {
core_response_.key_array_length = kMaxNumKeys;
}
// For corpus data, this value gets set to 4, but we need to test other
// scenarios too, hence reading key_array_length value.
set_num_keys(core_response_.key_array_length);
ConvertDataToValidBools(&core_response_);
// TODO(b/157520981): Once assertion bug is fixed, for loop can be removed.
// Workaround for the above bug: key_data.length and key_id.length are being
// used in AES decryption process and are expected to be a multiple of 16. An
// assertion in AES decryption fails if this condition is not met which will
// crash fuzzer.
for (uint32_t i = 0; i < num_keys_; ++i) {
size_t key_data_length = core_response_.key_array[i].key_data.length;
size_t key_id_length = core_response_.key_array[i].key_id.length;
if (key_data_length % 16 != 0) {
core_response_.key_array[i].key_data.length =
key_data_length - (key_data_length % 16);
}
if (key_id_length % 16 != 0) {
core_response_.key_array[i].key_id.length =
key_id_length - (key_id_length % 16);
}
}
// Copy response_data from data and set nonce to match one in request to pass
// nonce validations.
memcpy(&response_data_, data + core_response_size, sizeof(response_data_));
for (uint32_t i = 0; i < num_keys_; ++i) {
response_data_.keys[i].control.nonce = session()->nonce();
}
}
void LicenseRoundTrip::CreateResponseWithGenericCryptoKeys() {
CreateDefaultResponse();
response_data_.keys[0].control.control_bits |=
@@ -540,17 +658,28 @@ void LicenseRoundTrip::EncryptAndSignResponse() {
2 * MAC_KEY_SIZE, response_data_.mac_key_iv);
for (unsigned int i = 0; i < num_keys_; i++) {
memcpy(iv_buffer, &response_data_.keys[i].control_iv[0], KEY_IV_SIZE);
AES_KEY aes_key;
AES_set_encrypt_key(&response_data_.keys[i].key_data[0], 128, &aes_key);
AES_cbc_encrypt(
reinterpret_cast<const uint8_t*>(&response_data_.keys[i].control),
reinterpret_cast<uint8_t*>(&encrypted_response_data_.keys[i].control),
KEY_SIZE, &aes_key, iv_buffer, AES_ENCRYPT);
session_->key_deriver().CBCEncrypt(
&response_data_.keys[i].key_data[0],
&encrypted_response_data_.keys[i].key_data[0],
response_data_.keys[i].key_data_length, response_data_.keys[i].key_iv);
// OEMCrypto Fuzzing skip encryption: key_data_length can be any number when
// called from fuzzer. We want to skip encryption if that happens and let
// LoadLicense be called with unencrypted data for that key. OEMCrypto
// Fuzzing skip encryption: key_data_length being a random value will
// encrypt data which is not expected to, there by leading to inefficient
// fuzzing.
if (response_data_.keys[i].key_data_length <=
sizeof(response_data_.keys[i].key_data) &&
response_data_.keys[i].key_data_length % 16 == 0) {
memcpy(iv_buffer, &response_data_.keys[i].control_iv[0], KEY_IV_SIZE);
AES_KEY aes_key;
AES_set_encrypt_key(&response_data_.keys[i].key_data[0], 128, &aes_key);
AES_cbc_encrypt(
reinterpret_cast<const uint8_t*>(&response_data_.keys[i].control),
reinterpret_cast<uint8_t*>(&encrypted_response_data_.keys[i].control),
KEY_SIZE, &aes_key, iv_buffer, AES_ENCRYPT);
session_->key_deriver().CBCEncrypt(
&response_data_.keys[i].key_data[0],
&encrypted_response_data_.keys[i].key_data[0],
response_data_.keys[i].key_data_length,
response_data_.keys[i].key_iv);
}
}
if (api_version_ < kCoreMessagesAPI) {
serialized_core_message_.resize(0);
@@ -588,6 +717,21 @@ void LicenseRoundTrip::EncryptAndSignResponse() {
OEMCryptoResult LicenseRoundTrip::LoadResponse(Session* session) {
EXPECT_NE(session, nullptr);
// Write corpus for oemcrypto_load_license_fuzz. Fuzz script expects
// unecnrypted response from license server as input corpus data.
// Data will be encrypted and signed again explicitly by fuzzer script
// after mutations.
if (ShouldGenerateCorpus()) {
const std::string file_name =
GetFileName("oemcrypto_load_license_fuzz_seed_corpus");
// Corpus for license response fuzzer should be in the format:
// core_response + response_data.
AppendToFile(file_name, reinterpret_cast<const char*>(&core_response_),
sizeof(ODK_ParsedLicense));
AppendToFile(file_name, reinterpret_cast<const char*>(&response_data_),
sizeof(response_data_));
}
// Some tests adjust the offset to be beyond the length of the message. Here,
// we create a duplicate of the main message buffer so that these offsets do
// not point to garbage data. The goal is to make sure OEMCrypto is verifying
@@ -757,6 +901,17 @@ void EntitledMessage::LoadKeys(OEMCryptoResult expected_sts) {
key_data->encrypted_content_key_data, KEY_SIZE, &aes_key,
iv, AES_ENCRYPT);
}
if (ShouldGenerateCorpus()) {
const std::string file_name =
GetFileName("oemcrypto_load_entitled_content_keys_fuzz_seed_corpus");
// Corpus for load entitled keys fuzzer should be in the format:
// message buffer to be verified | entitled content key object array.
AppendToFile(file_name, reinterpret_cast<const char*>(entitled_key_data_),
sizeof(entitled_key_data_));
AppendSeparator(file_name);
AppendToFile(file_name, reinterpret_cast<const char*>(entitled_key_array_),
num_keys_);
}
ASSERT_EQ(expected_sts,
OEMCrypto_LoadEntitledContentKeys(
license_messages_->session()->session_id(),
@@ -906,7 +1061,46 @@ void RenewalRoundTrip::EncryptAndSignResponse() {
&response_signature_);
}
void RenewalRoundTrip::InjectFuzzedResponseData(
OEMCrypto_Renewal_Response_Fuzz& fuzzed_data,
const uint8_t* renewal_response, const size_t renewal_response_size) {
// Serializing core message.
// This call also sets nonce in core response to match with session nonce.
oemcrypto_core_message::serialize::CreateCoreRenewalResponse(
fuzzed_data.core_request, fuzzed_data.renewal_duration_seconds,
&serialized_core_message_);
// Copy serialized core message and encrypted response from data and
// calculate signature. Now we will have a valid signature for data generated
// by fuzzer.
encrypted_response_.assign(serialized_core_message_.begin(),
serialized_core_message_.end());
encrypted_response_.insert(encrypted_response_.end(), renewal_response,
renewal_response + renewal_response_size);
session()->key_deriver().ServerSignBuffer(encrypted_response_.data(),
encrypted_response_.size(),
&response_signature_);
}
OEMCryptoResult RenewalRoundTrip::LoadResponse(Session* session) {
// Write corpus for oemcrypto_load_renewal_fuzz. Fuzz script expects
// encrypted response from Renewal server as input corpus data.
// Data will be signed again explicitly by fuzzer script after mutations.
if (ShouldGenerateCorpus()) {
const std::string file_name =
GetFileName("oemcrypto_load_renewal_fuzz_seed_corpus");
// Corpus for renewal response fuzzer should be in the format:
// OEMCrypto_Renewal_Response_Fuzz + license_renewal_response.
OEMCrypto_Renewal_Response_Fuzz renewal_response_fuzz;
renewal_response_fuzz.core_request = core_request_;
renewal_response_fuzz.renewal_duration_seconds = renewal_duration_seconds_;
AppendToFile(file_name,
reinterpret_cast<const char*>(&renewal_response_fuzz),
sizeof(renewal_response_fuzz));
AppendToFile(file_name,
reinterpret_cast<const char*>(&encrypted_response_data_),
sizeof(encrypted_response_data_));
}
if (license_messages_->api_version() < kCoreMessagesAPI) {
return OEMCrypto_RefreshKeys(
session->session_id(), encrypted_response_.data(),
@@ -1129,10 +1323,9 @@ void Session::TestDecryptResult(OEMCryptoResult expected_result,
void Session::TestSelectExpired(unsigned int key_index) {
if (global_features.api_version >= 13) {
OEMCryptoResult status =
OEMCrypto_SelectKey(session_id(), license().keys[key_index].key_id,
license().keys[key_index].key_id_length,
OEMCrypto_CipherMode_CTR);
OEMCryptoResult status = OEMCrypto_SelectKey(
session_id(), license().keys[key_index].key_id,
license().keys[key_index].key_id_length, OEMCrypto_CipherMode_CTR);
// It is OK for SelectKey to succeed with an expired key, but if there is
// an error, it must be OEMCrypto_ERROR_KEY_EXIRED.
if (status != OEMCrypto_SUCCESS) {
@@ -1200,9 +1393,9 @@ void Session::LoadOEMCert(bool verify_cert) {
// TODO(fredgc): Verify cert is signed by Google.
int result = X509_verify_cert(store_ctx.get());
ASSERT_GE(0, result) << " OEM Cert not valid. " <<
X509_verify_cert_error_string(
X509_STORE_CTX_get_error(store_ctx.get()));
ASSERT_GE(0, result) << " OEM Cert not valid. "
<< X509_verify_cert_error_string(
X509_STORE_CTX_get_error(store_ctx.get()));
if (result == 0) {
printf("Cert not verified: %s.\n",
X509_verify_cert_error_string(
@@ -1258,7 +1451,7 @@ bool Session::VerifyPSSSignature(EVP_PKEY* pkey, const uint8_t* message,
}
if (EVP_PKEY_CTX_set_signature_md(pkey_ctx,
const_cast<EVP_MD *>(EVP_sha1())) != 1) {
const_cast<EVP_MD*>(EVP_sha1())) != 1) {
LOGE("EVP_PKEY_CTX_set_signature_md failed in VerifyPSSSignature");
goto err;
}
@@ -1307,18 +1500,17 @@ void Session::VerifyRSASignature(const vector<uint8_t>& message,
boringssl_ptr<EVP_PKEY, EVP_PKEY_free> pkey(EVP_PKEY_new());
ASSERT_EQ(1, EVP_PKEY_set1_RSA(pkey.get(), public_rsa_));
const bool ok = VerifyPSSSignature(
pkey.get(), message.data(), message.size(), signature,
signature_length);
const bool ok =
VerifyPSSSignature(pkey.get(), message.data(), message.size(),
signature, signature_length);
EXPECT_TRUE(ok) << "PSS signature check failed.";
} else if (padding_scheme == kSign_PKCS1_Block1) {
vector<uint8_t> padded_digest(signature_length);
int size;
// RSA_public_decrypt decrypts the signature, and then verifies that
// it was padded with RSA PKCS1 padding.
size = RSA_public_decrypt(
signature_length, signature, padded_digest.data(), public_rsa_,
RSA_PKCS1_PADDING);
size = RSA_public_decrypt(signature_length, signature, padded_digest.data(),
public_rsa_, RSA_PKCS1_PADDING);
EXPECT_GT(size, 0);
padded_digest.resize(size);
EXPECT_EQ(message, padded_digest);
@@ -1384,16 +1576,14 @@ void Session::UpdateUsageEntry(std::vector<uint8_t>* header_buffer) {
void Session::LoadUsageEntry(uint32_t index, const vector<uint8_t>& buffer) {
usage_entry_number_ = index;
encrypted_usage_entry_ = buffer;
ASSERT_EQ(
OEMCrypto_SUCCESS,
OEMCrypto_LoadUsageEntry(
session_id(), index, buffer.data(), buffer.size()));
ASSERT_EQ(OEMCrypto_SUCCESS,
OEMCrypto_LoadUsageEntry(session_id(), index, buffer.data(),
buffer.size()));
}
void Session::MoveUsageEntry(uint32_t new_index,
std::vector<uint8_t>* header_buffer,
OEMCryptoResult expect_result) {
ASSERT_NO_FATAL_FAILURE(open());
ASSERT_NO_FATAL_FAILURE(ReloadUsageEntry());
ASSERT_EQ(expect_result, OEMCrypto_MoveEntry(session_id(), new_index));
@@ -1405,8 +1595,7 @@ void Session::MoveUsageEntry(uint32_t new_index,
}
void Session::GenerateReport(const std::string& pst,
OEMCryptoResult expected_result,
Session* other) {
OEMCryptoResult expected_result, Session* other) {
ASSERT_TRUE(open_);
if (other) { // If other is specified, copy mac keys.
key_deriver_ = other->key_deriver_;
@@ -1442,14 +1631,13 @@ void Session::GenerateReport(const std::string& pst,
void Session::VerifyPST(const Test_PST_Report& expected) {
wvcdm::Unpacked_PST_Report computed = pst_report();
EXPECT_EQ(expected.status, computed.status());
char* pst_ptr = reinterpret_cast<char *>(computed.pst());
char* pst_ptr = reinterpret_cast<char*>(computed.pst());
std::string computed_pst(pst_ptr, pst_ptr + computed.pst_length());
ASSERT_EQ(expected.pst, computed_pst);
int64_t now = wvcdm::Clock().GetCurrentTime();
int64_t age = now - expected.time_created; // How old is this report.
EXPECT_NEAR(expected.seconds_since_license_received + age,
computed.seconds_since_license_received(),
kTimeTolerance);
computed.seconds_since_license_received(), kTimeTolerance);
// Decrypt times only valid on licenses that have been active.
if (expected.status == kActive || expected.status == kInactiveUsed) {
EXPECT_NEAR(expected.seconds_since_first_decrypt + age,
@@ -1461,8 +1649,8 @@ void Session::VerifyPST(const Test_PST_Report& expected) {
}
std::vector<uint8_t> signature(SHA_DIGEST_LENGTH);
key_deriver_.ClientSignPstReport(pst_report_buffer_, &signature);
EXPECT_EQ(0, memcmp(computed.signature(), signature.data(),
SHA_DIGEST_LENGTH));
EXPECT_EQ(0,
memcmp(computed.signature(), signature.data(), SHA_DIGEST_LENGTH));
}
void Session::VerifyReport(Test_PST_Report expected,
@@ -1484,4 +1672,41 @@ void Session::VerifyReport(Test_PST_Report expected,
: 0;
ASSERT_NO_FATAL_FAILURE(VerifyPST(expected));
}
bool ConvertByteToValidBoolean(const bool* in) {
const char* buf = reinterpret_cast<const char*>(in);
for (size_t i = 0; i < sizeof(bool); i++) {
if (buf[i]) {
return true;
}
}
return false;
}
template <class CoreRequest>
void WriteRequestApiCorpus(size_t signature_length, size_t core_message_length,
vector<uint8_t>& data) {
std::string file_name;
if (std::is_same<CoreRequest,
oemcrypto_core_message::ODK_LicenseRequest>::value) {
file_name = GetFileName("oemcrypto_license_request_fuzz_seed_corpus");
} else if (std::is_same<
CoreRequest,
oemcrypto_core_message::ODK_ProvisioningRequest>::value) {
file_name = GetFileName("oemcrypto_provisioning_request_fuzz_seed_corpus");
} else if (std::is_same<CoreRequest,
oemcrypto_core_message::ODK_RenewalRequest>::value) {
file_name = GetFileName("oemcrypto_renewal_request_fuzz_seed_corpus");
} else {
LOGE("Invalid CoreRequest type while writing request api corups.");
}
// Corpus for request APIs should be signature_length + core_message_length +
// data pointer.
AppendToFile(file_name, reinterpret_cast<const char*>(&signature_length),
sizeof(signature_length));
AppendToFile(file_name, reinterpret_cast<const char*>(&core_message_length),
sizeof(core_message_length));
AppendToFile(file_name, reinterpret_cast<const char*>(data.data()),
data.size());
}
} // namespace wvoec

View File

@@ -18,6 +18,7 @@
#include "odk.h"
#include "oec_device_features.h"
#include "oec_key_deriver.h"
#include "oemcrypto_fuzz_structs.h"
#include "oemcrypto_types.h"
#include "pst_report.h"
@@ -32,6 +33,8 @@ void PrintTo(const vector<uint8_t>& value, ostream* os);
} // namespace std
namespace wvoec {
// OEMCrypto Fuzzing: Set max signture length to 1mb.
const size_t MB = 1024 * 1024;
// Make sure this is larger than kMaxKeysPerSession, in oemcrypto_test.cpp
constexpr size_t kMaxNumKeys = 30;
@@ -158,6 +161,9 @@ class RoundTrip {
// Have OEMCrypto sign a request message and then verify the signature and the
// core message.
virtual void SignAndVerifyRequest();
// Used for OEMCrypto Fuzzing: Function to convert fuzzer data to valid
// License/Provisioning/Renwal request data that can be serialized.
virtual void InjectFuzzedRequestData(uint8_t* data, size_t size);
// Create a default |response_data| and |core_response|.
virtual void CreateDefaultResponse() = 0;
// Copy fields from |response_data| to |padded_response_data|, encrypting
@@ -241,6 +247,11 @@ class ProvisioningRoundTrip
void set_allowed_schemes(uint32_t allowed_schemes) {
allowed_schemes_ = allowed_schemes;
}
// Used for OEMCrypto Fuzzing: Function to convert fuzzer data to valid
// provisioning response data that can be parsed. Calculates signature for
// data generated by fuzzer, so that signature validation passes when parsing
// provisioning response.
void InjectFuzzedResponseData(const uint8_t* data, size_t size);
protected:
void VerifyRequestSignature(const vector<uint8_t>& data,
@@ -286,6 +297,18 @@ class LicenseRoundTrip
license_type_(OEMCrypto_ContentLicense),
request_hash_() {}
void CreateDefaultResponse() override;
// Used for OEMCrypto Fuzzing: Function to inject fuzzed timer limits
// into timer_limits field from core_response. We need to fuzz timer
// limits in order to efficiently fuzz load renewal response API.
void InjectFuzzedTimerLimits(OEMCrypto_Renewal_Response_Fuzz& fuzzed_data);
// Used for OEMCrypto Fuzzing: Function to convert fuzzer data to valid
// License response data that can be parsed. Calculates signature for data
// generated by fuzzer, so that signature validation passes when parsing
// license response.
void InjectFuzzedResponseData(const uint8_t* data, size_t size);
// Used for OEMCrypto Fuzzing: Convert boolean flags in parsed_license to
// valid bytes to avoid errors from msan.
void ConvertDataToValidBools(ODK_ParsedLicense* t);
// Create a license with four keys. Each key is responsible for one of generic
// encrypt (key 0), decrypt (key 1), sign (key 2) and verify (key 3). Each key
// is allowed only one type of operation.
@@ -386,6 +409,9 @@ class RenewalRoundTrip
is_release_(false) {}
void CreateDefaultResponse() override;
void EncryptAndSignResponse() override;
void InjectFuzzedResponseData(OEMCrypto_Renewal_Response_Fuzz& fuzzed_data,
const uint8_t* renewal_response,
const size_t renewal_response_size);
OEMCryptoResult LoadResponse() override { return LoadResponse(session_); }
OEMCryptoResult LoadResponse(Session* session) override;
uint64_t renewal_duration_seconds() const {
@@ -557,15 +583,14 @@ class Session {
// Verify the Usage Report. If any time is greater than 10 minutes, it is
// assumed to be an absolute time, and time_since will be computed relative to
// now.
void VerifyReport(Test_PST_Report report,
int64_t time_license_received = 0,
void VerifyReport(Test_PST_Report report, int64_t time_license_received = 0,
int64_t time_first_decrypt = 0,
int64_t time_last_decrypt = 0);
// Create an entry in the old usage table based on the given report.
void CreateOldEntry(const Test_PST_Report &report);
void CreateOldEntry(const Test_PST_Report& report);
// Create a new entry and copy the old entry into it. Then very the report
// is right.
void CopyAndVerifyOldEntry(const Test_PST_Report &report,
void CopyAndVerifyOldEntry(const Test_PST_Report& report,
std::vector<uint8_t>* header_buffer);
// The unencrypted license response or license renewal response.
@@ -599,6 +624,13 @@ class Session {
string pst_;
};
// Used for OEMCrypto Fuzzing: Convert byte to a valid boolean to avoid errors
// generated by msan.
bool ConvertByteToValidBoolean(const bool* in);
// Used for OEMCrypto Fuzzing: Generates corpus for request APIs.
template <class CoreRequest>
void WriteRequestApiCorpus(size_t signature_length, size_t core_message_length,
vector<uint8_t>& data);
} // namespace wvoec
#endif // CDM_OEC_SESSION_UTIL_H_

View File

@@ -0,0 +1,44 @@
/* Copyright 2020 Google LLC. All rights reserved. This file and proprietary */
/* source code may only be used and distributed under the Widevine Master */
/* License Agreement. */
#include "oemcrypto_corpus_generator_helper.h"
#include <fstream>
#include <iostream>
namespace wvoec {
bool g_generate_corpus;
void AppendToFile(const std::string& file_name, const char* message,
const size_t message_size) {
std::ofstream filebuf(file_name.c_str(), std::ios::app | std::ios::binary);
if (!filebuf) {
std::cout << "Cannot open file " << file_name.c_str() << std::endl;
}
filebuf.write(message, message_size);
filebuf.close();
}
void AppendSeparator(const std::string& file_name) {
std::ofstream filebuf(file_name.c_str(), std::ios::app | std::ios::binary);
if (!filebuf) {
std::cout << "Cannot open file " << file_name.c_str() << std::endl;
}
filebuf.write(reinterpret_cast<const char*>(&kFuzzDataSeparator),
sizeof(kFuzzDataSeparator));
filebuf.close();
}
std::string GetFileName(const char* directory) {
std::string file_name(PATH_TO_CORPUS);
file_name += directory;
file_name += "/";
file_name += std::to_string(rand());
return file_name;
}
void SetGenerateCorpus(bool should_generate_corpus) {
g_generate_corpus = should_generate_corpus;
}
bool ShouldGenerateCorpus() { return g_generate_corpus; }
} // namespace wvoec

View File

@@ -0,0 +1,30 @@
/* Copyright 2020 Google LLC. All rights reserved. This file and proprietary */
/* source code may only be used and distributed under the Widevine Master */
/* License Agreement. */
#ifndef CDM_OEMCRYPTO_CORPUS_GENERATOR_HELPER_H_
#define CDM_OEMCRYPTO_CORPUS_GENERATOR_HELPER_H_
#define PATH_TO_CORPUS "./oemcrypto/test/fuzz_tests/corpus/"
#include <stdio.h>
#include <stdlib.h>
#include <string>
namespace wvoec {
const uint8_t kFuzzDataSeparator[] = {'-', '_', '^', '_'};
void AppendToFile(const std::string& file_name, const char* message,
const size_t message_size);
// Function to append separator "-_^_" between contents of corpus file.
void AppendSeparator(const std::string& file_name);
std::string GetFileName(const char* directory);
void SetGenerateCorpus(bool should_generate_corpus);
// Output of this function decides if binary data needs to be written
// to corpus files or not. Controlled by --generate_corpus flag.
bool ShouldGenerateCorpus();
} // namespace wvoec
#endif // CDM_OEMCRYPTO_CORPUS_GENERATOR_HELPER_H_

View File

@@ -2,8 +2,29 @@
// source code may only be used and distributed under the Widevine Master
// License Agreement.
//
// OEMCrypto unit tests
//
/**
* @mainpage OEMCrypto Unit Tests
*
* The OEMCrypto unit tests are designed to verify that an implementation of
* OEMCrypto is correctly supporting the OEMCrypto API.
*
* @defgroup basic Basic Functionality Tests
* Basic functionality tests.
*
* @defgroup license License Request Tests
* Test for requesting and loading licenses.
*
* @defgroup renewal License Renewal Tests
* Tests for renewing licenses.
*
* @defgroup decrypt Decrypt Tests
* Tests for decrypting content.
*
* @defgroup usage_table Usage Table Tests
* Tests that use the usage table.
*/
#include <ctype.h>
#include <openssl/aes.h>
#include <openssl/err.h>
@@ -17,6 +38,7 @@
#include <gtest/gtest.h>
#include <algorithm>
#include <chrono>
#include <functional>
#include <iostream>
#include <map>
#include <string>
@@ -82,6 +104,9 @@ constexpr size_t kBufferOverrunPadding = 16;
// Resource tiers:
constexpr size_t KiB = 1024;
constexpr size_t MiB = 1024 * 1024;
// Huge input buffer length used for OEMCryptoMemory* tests.
constexpr size_t kHugeInputBufferLength = 100 * MiB;
constexpr bool kCheckStatus = true;
// With OEMCrypto v15 and above, we have different resource requirements
// depending on the resource rating reported by OEMCrypto. This function looks
// up the required value for the specified resource for the target OEMCrypto
@@ -93,6 +118,32 @@ T GetResourceValue(T (&resource_values)[N]) {
return resource_values[global_features.resource_rating - 1];
}
// Used for testing oemcrypto APIs with huge buffers.
typedef const std::function<OEMCryptoResult(size_t)> oemcrypto_function;
// Function to test APIs that expect a buffer length as input
// by passing huge buffer lengths upto end_buffer_length and test that the API
// doesn't crash.
void TestHugeLengthDoesNotCrashAPI(oemcrypto_function f,
size_t start_buffer_length,
size_t end_buffer_length,
bool check_status) {
OEMCryptoResult sts = OEMCrypto_SUCCESS;
for (size_t buffer_length = start_buffer_length;
buffer_length < end_buffer_length &&
(sts == OEMCrypto_SUCCESS || sts == OEMCrypto_ERROR_SHORT_BUFFER ||
!check_status);
buffer_length *= 2) {
sts = f(buffer_length);
}
}
// Function to test APIs that expect a buffer length as input
// by passing huge buffer lengths upto kHugeInputBufferLength and test that
// the API doesn't crash.
void TestHugeLengthDoesNotCrashAPI(oemcrypto_function f, bool check_status) {
TestHugeLengthDoesNotCrashAPI(f, 1, kHugeInputBufferLength, check_status);
}
// After API 16, we require 300 entries in the usage table. Before API 16, we
// required 200.
size_t RequiredUsageSize() {
@@ -148,10 +199,14 @@ class OEMCryptoClientTest : public ::testing::Test, public SessionUtil {
}
};
//
// General tests.
// This test is first, becuase it might give an idea why other
// tests are failing when the device has the wrong keybox installed.
/// @addtogroup basic
/// @{
/**
* Verifies initialization and logs version information.
* This test is first, because it might give an idea why other
* tests are failing when the device has the wrong keybox installed.
*/
TEST_F(OEMCryptoClientTest, VersionNumber) {
const std::string log_message =
"OEMCrypto unit tests for API 16.3. Tests last updated 2020-06-01";
@@ -186,14 +241,18 @@ TEST_F(OEMCryptoClientTest, VersionNumber) {
ASSERT_LE(version, kCurrentAPI);
}
// The resource rating is a number from 1 to 4. The first three levels were
// initially defined in API 15 and they were expanded in API 16.
/**
* The resource rating is a number from 1 to 4. The first three levels
* were initially defined in API 15 and they were expanded in API 16.
*/
TEST_F(OEMCryptoClientTest, ResourceRatingAPI15) {
ASSERT_GE(OEMCrypto_ResourceRatingTier(), 1u);
ASSERT_LE(OEMCrypto_ResourceRatingTier(), 4u);
}
// OEMCrypto must declare what type of provisioning scheme it uses.
/**
* OEMCrypto must declare what type of provisioning scheme it uses.
*/
TEST_F(OEMCryptoClientTest, ProvisioningDeclaredAPI12) {
OEMCrypto_ProvisioningMethod provisioning_method =
OEMCrypto_GetProvisioningMethod();
@@ -228,10 +287,10 @@ TEST_F(OEMCryptoClientTest, CheckHDCPCapabilityAPI09) {
OEMCrypto_HDCP_Capability current, maximum;
sts = OEMCrypto_GetHDCPCapability(&current, &maximum);
ASSERT_EQ(OEMCrypto_SUCCESS, sts);
printf(" Current HDCP Capability: 0x%02x = %s.\n", current,
HDCPCapabilityAsString(current));
printf(" Maximum HDCP Capability: 0x%02x = %s.\n", maximum,
HDCPCapabilityAsString(maximum));
printf(" Current HDCP Capability: 0x%02x = %s.\n",
static_cast<unsigned int>(current), HDCPCapabilityAsString(current));
printf(" Maximum HDCP Capability: 0x%02x = %s.\n",
static_cast<unsigned int>(maximum), HDCPCapabilityAsString(maximum));
}
TEST_F(OEMCryptoClientTest, CheckSRMCapabilityV13) {
@@ -285,6 +344,15 @@ TEST_F(OEMCryptoClientTest, NormalInitTermination) {
ASSERT_EQ(OEMCrypto_SUCCESS, OEMCrypto_Initialize());
}
// Test that set sandbox doesn't crash for a large sandbox id leangth.
TEST_F(OEMCryptoClientTest, OEMCryptoMemorySetSandboxForLargeSandboxIdLength) {
auto oemcrypto_function = [](size_t buffer_length) {
vector<uint8_t> buffer(buffer_length);
return OEMCrypto_SetSandbox(buffer.data(), buffer.size());
};
TestHugeLengthDoesNotCrashAPI(oemcrypto_function, !kCheckStatus);
}
//
// Session Tests
//
@@ -375,6 +443,17 @@ TEST_F(OEMCryptoClientTest, GetRandomLargeBuffer) {
ASSERT_LE(count, 3); // P(count > 3) = 1/256^3 = 6e-8.
}
// Verify that GetRandom doesn't crash for large input lengths.
TEST_F(OEMCryptoClientTest, OEMCryptoMemoryGetRandomForLargeBuffer) {
auto oemcrypto_function = [](size_t buffer_length) {
vector<uint8_t> buffer(buffer_length);
// TODO(ellurubharath, fredgc): Need to re-evaluate this on a real device
// as GetRandom can be slow.
return OEMCrypto_GetRandom(buffer.data(), buffer.size());
};
TestHugeLengthDoesNotCrashAPI(oemcrypto_function, !kCheckStatus);
}
TEST_F(OEMCryptoClientTest, GenerateNonce) {
Session s;
ASSERT_NO_FATAL_FAILURE(s.open());
@@ -532,6 +611,33 @@ class OEMCryptoKeyboxTest : public OEMCryptoClientTest {
}
};
// Test that OEMCrypto_InstallKeyboxOrOEMCert doesn't crash for large keybox.
TEST_F(OEMCryptoKeyboxTest, OEMCryptoMemoryInstallKeyboxForHugeKeyboxBuffer) {
auto f = [](size_t keybox_length) {
vector<uint8_t> keybox(keybox_length);
memcpy(keybox.data(), &kTestKeybox, sizeof(kTestKeybox));
return OEMCrypto_InstallKeyboxOrOEMCert(keybox.data(), keybox.size());
};
// Starting at sizeof(kTestKeybox) as we are copying valid keybox
// at beginning of generated buffers.
TestHugeLengthDoesNotCrashAPI(f, sizeof(kTestKeybox), kHugeInputBufferLength,
kCheckStatus);
}
// This test verifies that load test key box doesn't crash for large
// buffer length.
TEST_F(OEMCryptoKeyboxTest, OEMCryptoMemoryLoadTestKeyBoxForHugeKeyboxBuffer) {
auto f = [](size_t keybox_length) {
vector<uint8_t> keybox(keybox_length);
memcpy(keybox.data(), &kTestKeybox, sizeof(kTestKeybox));
return OEMCrypto_LoadTestKeybox(keybox.data(), keybox.size());
};
// Starting at sizeof(kTestKeybox) as we are copying valid keybox
// at beginning of generated buffers.
TestHugeLengthDoesNotCrashAPI(f, sizeof(kTestKeybox), kHugeInputBufferLength,
kCheckStatus);
}
// This test is used to print the device ID to stdout.
TEST_F(OEMCryptoKeyboxTest, NormalGetDeviceId) {
OEMCryptoResult sts;
@@ -543,6 +649,15 @@ TEST_F(OEMCryptoKeyboxTest, NormalGetDeviceId) {
ASSERT_EQ(OEMCrypto_SUCCESS, sts);
}
TEST_F(OEMCryptoKeyboxTest, OEMCryptoMemoryGetDeviceIdForHugeIdLength) {
auto oemcrypto_function = [](size_t input_length) {
size_t device_id_length = input_length;
vector<uint8_t> device_id(device_id_length);
return OEMCrypto_GetDeviceID(device_id.data(), &device_id_length);
};
TestHugeLengthDoesNotCrashAPI(oemcrypto_function, kCheckStatus);
}
TEST_F(OEMCryptoKeyboxTest, GetDeviceIdShortBuffer) {
OEMCryptoResult sts;
uint8_t dev_id[128];
@@ -570,11 +685,20 @@ TEST_F(OEMCryptoKeyboxTest, NormalGetKeyData) {
sts = OEMCrypto_GetKeyData(key_data, &key_data_len);
uint32_t* data = reinterpret_cast<uint32_t*>(key_data);
printf(" NormalGetKeyData: system_id = %d = 0x%04X, version=%d\n",
printf(" NormalGetKeyData: system_id = %u = 0x%04X, version=%u\n",
htonl(data[1]), htonl(data[1]), htonl(data[0]));
ASSERT_EQ(OEMCrypto_SUCCESS, sts);
}
TEST_F(OEMCryptoKeyboxTest, OEMCryptoMemoryGetKeyIdForLargeIdLength) {
auto oemcrypto_function = [](size_t input_length) {
size_t key_data_length = input_length;
vector<uint8_t> key_data(key_data_length);
return OEMCrypto_GetKeyData(key_data.data(), &key_data_length);
};
TestHugeLengthDoesNotCrashAPI(oemcrypto_function, !kCheckStatus);
}
TEST_F(OEMCryptoKeyboxTest, GetKeyDataNullPointer) {
OEMCryptoResult sts;
uint8_t key_data[256];
@@ -606,6 +730,42 @@ TEST_F(OEMCryptoKeyboxTest, GenerateDerivedKeysFromKeyboxLargeBuffer) {
enc_context.data(), enc_context.size()));
}
TEST_F(OEMCryptoKeyboxTest,
OEMCryptoMemoryGenerateDerivedKeysForLargeMacContextLength) {
Session s;
ASSERT_NO_FATAL_FAILURE(s.open());
vector<uint8_t> mac_context;
vector<uint8_t> enc_context;
s.FillDefaultContext(&mac_context, &enc_context);
auto oemcrypto_function = [&s, &mac_context,
&enc_context](size_t buffer_length) {
mac_context.resize(buffer_length);
return OEMCrypto_GenerateDerivedKeys(s.session_id(), mac_context.data(),
mac_context.size(), enc_context.data(),
enc_context.size());
};
TestHugeLengthDoesNotCrashAPI(oemcrypto_function, kCheckStatus);
}
TEST_F(OEMCryptoKeyboxTest,
OEMCryptoMemoryGenerateDerivedKeysForLargeEncContextLength) {
Session s;
ASSERT_NO_FATAL_FAILURE(s.open());
vector<uint8_t> mac_context;
vector<uint8_t> enc_context;
s.FillDefaultContext(&mac_context, &enc_context);
auto oemcrypto_function = [&s, &mac_context,
&enc_context](size_t buffer_length) {
enc_context.resize(buffer_length);
return OEMCrypto_GenerateDerivedKeys(s.session_id(), mac_context.data(),
mac_context.size(), enc_context.data(),
enc_context.size());
};
TestHugeLengthDoesNotCrashAPI(oemcrypto_function, kCheckStatus);
}
// This class is for tests that have an OEM Certificate instead of a keybox.
class OEMCryptoProv30Test : public OEMCryptoClientTest {};
@@ -644,6 +804,11 @@ TEST_F(OEMCryptoProv30Test, OEMCertValid) {
ASSERT_NO_FATAL_FAILURE(s.LoadOEMCert(kVerify)); // Load and verify.
}
/// @}
/// @addtogroup license
/// @{
// This verifies that the OEM Certificate cannot be used for other RSA padding
// schemes. Those schemes should only be used by cast receiver certificates.
TEST_F(OEMCryptoProv30Test, OEMCertForbiddenPaddingScheme) {
@@ -711,6 +876,27 @@ TEST_F(OEMCryptoProv30Test, GetCertOnlyAPI16) {
ASSERT_EQ(OEMCrypto_SUCCESS, license_messages.LoadResponse());
}
TEST_F(OEMCryptoProv30Test, OEMCryptoMemoryGetOEMPublicCertForLargeCertLength) {
if (wrapped_rsa_key_.size() == 0) {
// If we don't have a wrapped key yet, create one.
// This wrapped key will be shared by all sessions in the test.
ASSERT_NO_FATAL_FAILURE(CreateWrappedRSAKey());
}
Session s;
ASSERT_NO_FATAL_FAILURE(s.open());
// Install the DRM Cert's RSA key.
ASSERT_NO_FATAL_FAILURE(s.InstallRSASessionTestKey(wrapped_rsa_key_));
ASSERT_NO_FATAL_FAILURE(s.PreparePublicKey());
auto oemcrypto_function = [](size_t input_length) {
size_t public_cert_length = input_length;
vector<uint8_t> public_cert(public_cert_length);
return OEMCrypto_GetOEMPublicCertificate(public_cert.data(),
&public_cert_length);
};
TestHugeLengthDoesNotCrashAPI(oemcrypto_function, !kCheckStatus);
}
//
// AddKey Tests
//
@@ -836,6 +1022,7 @@ TEST_P(OEMCryptoLicenseTest, LoadKeyNoNonce) {
ASSERT_NO_FATAL_FAILURE(license_messages_.CreateDefaultResponse());
ASSERT_NO_FATAL_FAILURE(license_messages_.EncryptAndSignResponse());
ASSERT_EQ(OEMCrypto_SUCCESS, license_messages_.LoadResponse());
ASSERT_NO_FATAL_FAILURE(session_.TestDecryptCTR(true, OEMCrypto_SUCCESS));
}
// Verify that a preloaded license may be loaded without first signing the
@@ -849,6 +1036,8 @@ TEST_P(OEMCryptoLicenseTest, LoadKeyWithNoRequest) {
license_messages_.set_api_version(license_api_version_);
}
license_messages_.set_control(0);
// Notice that we do not call SignAndVerifyRequest -- we do not need a request
// in order to generate a response for a preloaded license.
// The test code uses the core request to create the core response.
license_messages_.core_request().api_major_version = ODK_MAJOR_VERSION;
license_messages_.core_request().api_minor_version = ODK_MINOR_VERSION;
@@ -861,6 +1050,34 @@ TEST_P(OEMCryptoLicenseTest, LoadKeyWithNoRequest) {
ASSERT_NO_FATAL_FAILURE(InstallTestRSAKey(&session2));
ASSERT_NO_FATAL_FAILURE(session2.GenerateDerivedKeysFromSessionKey());
ASSERT_EQ(OEMCrypto_SUCCESS, license_messages_.LoadResponse(&session2));
ASSERT_NO_FATAL_FAILURE(session2.TestDecryptCTR(true, OEMCrypto_SUCCESS));
}
// Verify that a license may be reloaded without a nonce, but with a nonzero
// rental duration. In order to start the rental clock, we sign a dummy license
// instead.
TEST_P(OEMCryptoLicenseTest, LoadKeyWithNoRequestRentalDuration) {
ASSERT_NO_FATAL_FAILURE(license_messages_.SignAndVerifyRequest());
license_messages_.set_control(0);
ASSERT_NO_FATAL_FAILURE(license_messages_.CreateDefaultResponse());
// It is not recommended for a license without a nonce to have a nonzero
// rental duration. But there are content providers that have licenses with
// this policy.
license_messages_.core_response().timer_limits.rental_duration_seconds =
kDuration;
ASSERT_NO_FATAL_FAILURE(license_messages_.EncryptAndSignResponse());
// Load license in a different session, which did not create the request.
Session session2;
ASSERT_NO_FATAL_FAILURE(session2.open());
ASSERT_NO_FATAL_FAILURE(InstallTestRSAKey(&session2));
// However, in order to start the rental clock, we have to sign something. So
// we will sign a dummy license request.
LicenseRoundTrip dummy_license(&session2);
ASSERT_NO_FATAL_FAILURE(dummy_license.SignAndVerifyRequest());
ASSERT_NO_FATAL_FAILURE(session2.GenerateDerivedKeysFromSessionKey());
ASSERT_EQ(OEMCrypto_SUCCESS, license_messages_.LoadResponse(&session2));
ASSERT_NO_FATAL_FAILURE(session2.TestDecryptCTR(true, OEMCrypto_SUCCESS));
}
// Verify that a license may be loaded with a nonce.
@@ -870,6 +1087,7 @@ TEST_P(OEMCryptoLicenseTest, LoadKeyWithNonce) {
ASSERT_NO_FATAL_FAILURE(license_messages_.CreateDefaultResponse());
ASSERT_NO_FATAL_FAILURE(license_messages_.EncryptAndSignResponse());
ASSERT_EQ(OEMCrypto_SUCCESS, license_messages_.LoadResponse());
ASSERT_NO_FATAL_FAILURE(session_.TestDecryptCTR(true, OEMCrypto_SUCCESS));
}
// Verify that a second license may not be loaded in a session.
@@ -1336,6 +1554,10 @@ TEST_F(OEMCryptoLicenseTestAPI16, BadCoreHashAPI16) {
ASSERT_NO_FATAL_FAILURE(license_messages_.EncryptAndSignResponse());
ASSERT_NE(OEMCrypto_SUCCESS, license_messages_.LoadResponse());
}
/// @}
/// @addtogroup decrypt
/// @{
// LoadKeys should fail if we try to load keys with no keys.
TEST_P(OEMCryptoLicenseTest, LoadKeyNoKeys) {
@@ -1520,6 +1742,58 @@ TEST_P(OEMCryptoLicenseTest, QueryKeyControl) {
strlen(key_id), reinterpret_cast<uint8_t*>(&block), &size));
}
// Test OEMCrypto_QueryKeyControl doesn't crash for huge key_id_length.
TEST_F(OEMCryptoLicenseTestAPI16,
OEMCryptoMemoryQueryKeyControlForHugeKeyIdLength) {
ASSERT_NO_FATAL_FAILURE(session_.GenerateNonce());
ASSERT_NO_FATAL_FAILURE(license_messages_.SignAndVerifyRequest());
ASSERT_NO_FATAL_FAILURE(license_messages_.CreateDefaultResponse());
ASSERT_NO_FATAL_FAILURE(license_messages_.EncryptAndSignResponse());
ASSERT_EQ(OEMCrypto_SUCCESS, license_messages_.LoadResponse());
OEMCrypto_SESSION session_id = session_.session_id();
vector<uint8_t> valid_key_id(
license_messages_.response_data().keys[0].key_id,
license_messages_.response_data().keys[0].key_id + kTestKeyIdMaxLength);
auto oemcrypto_function = [&session_id,
&valid_key_id](size_t additional_key_id_length) {
vector<uint8_t> key_id(valid_key_id);
key_id.resize(valid_key_id.size() + additional_key_id_length);
KeyControlBlock block;
size_t size = sizeof(block);
return OEMCrypto_QueryKeyControl(session_id, key_id.data(), key_id.size(),
reinterpret_cast<uint8_t*>(&block), &size);
};
// We do not want to stop as soon as API results in an error as it would
// return error on first iteration as key_id is invalid.
TestHugeLengthDoesNotCrashAPI(oemcrypto_function, !kCheckStatus);
}
// Test OEMCrypto_QueryKeyControl doesn't crash for huge key_control_block
// length.
TEST_F(OEMCryptoLicenseTestAPI16,
OEMCryptoMemoryQueryKeyControlForHugeKeyControlBlockLength) {
ASSERT_NO_FATAL_FAILURE(session_.GenerateNonce());
ASSERT_NO_FATAL_FAILURE(license_messages_.SignAndVerifyRequest());
ASSERT_NO_FATAL_FAILURE(license_messages_.CreateDefaultResponse());
ASSERT_NO_FATAL_FAILURE(license_messages_.EncryptAndSignResponse());
ASSERT_EQ(OEMCrypto_SUCCESS, license_messages_.LoadResponse());
OEMCrypto_SESSION session_id = session_.session_id();
uint8_t* key_id = license_messages_.response_data().keys[0].key_id;
size_t key_id_length =
license_messages_.response_data().keys[0].key_id_length;
auto oemcrypto_function = [&session_id, &key_id,
&key_id_length](size_t buffer_length) {
size_t key_control_block_length = buffer_length;
vector<uint8_t> key_control_block(key_control_block_length);
return OEMCrypto_QueryKeyControl(session_id, key_id, key_id_length,
key_control_block.data(),
&key_control_block_length);
};
TestHugeLengthDoesNotCrashAPI(oemcrypto_function, kCheckStatus);
}
// If the device says it supports anti-rollback in the hardware, then it should
// accept a key control block with the anti-rollback hardware bit set.
// Otherwise, it should reject that key control block.
@@ -1718,6 +1992,11 @@ TEST_P(OEMCryptoSessionTestDecryptWithHDCP, DecryptAPI09) {
INSTANTIATE_TEST_CASE_P(TestHDCP, OEMCryptoSessionTestDecryptWithHDCP,
Range(1, 6));
/// @}
/// @addtogroup renewal
/// @{
//
// Load, Refresh Keys Test
//
@@ -1903,6 +2182,30 @@ TEST_P(OEMCryptoLicenseTest, HashForbiddenAPI15) {
OEMCrypto_GetHashErrorCode(session_.session_id(), &frame_number));
}
// This test verifies that OEMCrypto_SetDecryptHash doesn't crash for a very
// large hash buffer.
TEST_F(OEMCryptoLicenseTestAPI16,
OEMCryptoMemoryDecryptHashForLargeHashBuffer) {
uint32_t session_id = session_.session_id();
auto f = [session_id](size_t hash_length) {
uint32_t frame_number = 1;
vector<uint8_t> hash_buffer(hash_length);
return OEMCrypto_SetDecryptHash(session_id, frame_number,
hash_buffer.data(), hash_buffer.size());
};
TestHugeLengthDoesNotCrashAPI(f, sizeof(uint32_t), kHugeInputBufferLength,
kCheckStatus);
}
// This test verifies OEMCrypto_SetDecryptHash for out of range frame number.
TEST_P(OEMCryptoLicenseTest, DecryptHashForOutOfRangeFrameNumber) {
uint32_t frame_number = 40;
uint32_t hash = 42;
ASSERT_NO_FATAL_FAILURE(OEMCrypto_SetDecryptHash(
session_.session_id(), frame_number,
reinterpret_cast<const uint8_t*>(&hash), sizeof(hash)));
}
//
// Decrypt Tests -- these test Decrypt CTR mode only.
//
@@ -2582,18 +2885,6 @@ TEST_P(OEMCryptoLicenseTest, DecryptSecureToClear) {
session_.TestDecryptCTR(true, OEMCrypto_ERROR_UNKNOWN_FAILURE));
}
// If analog is forbidden, then decrypt to a clear buffer should be forbidden.
TEST_P(OEMCryptoLicenseTest, DecryptNoAnalogToClearAPI13) {
ASSERT_NO_FATAL_FAILURE(session_.GenerateNonce());
ASSERT_NO_FATAL_FAILURE(license_messages_.SignAndVerifyRequest());
license_messages_.set_control(wvoec::kControlDisableAnalogOutput);
ASSERT_NO_FATAL_FAILURE(license_messages_.CreateDefaultResponse());
ASSERT_NO_FATAL_FAILURE(license_messages_.EncryptAndSignResponse());
ASSERT_EQ(OEMCrypto_SUCCESS, license_messages_.LoadResponse());
ASSERT_NO_FATAL_FAILURE(
session_.TestDecryptCTR(true, OEMCrypto_ERROR_ANALOG_OUTPUT));
}
// Test that key duration is honored.
TEST_P(OEMCryptoLicenseTest, KeyDuration) {
ASSERT_NO_FATAL_FAILURE(session_.GenerateNonce());
@@ -3087,6 +3378,50 @@ TEST_F(OEMCryptoUsesCertificate, GenerateDerivedKeysLargeBuffer) {
enc_context.data(), enc_context.size()));
}
TEST_F(OEMCryptoUsesCertificate,
OEMCryptoMemoryDeriveKeysFromSessionKeyForHugeMacContext) {
vector<uint8_t> session_key;
vector<uint8_t> enc_session_key;
ASSERT_NO_FATAL_FAILURE(session_.PreparePublicKey(encoded_rsa_key_.data(),
encoded_rsa_key_.size()));
ASSERT_TRUE(session_.GenerateRSASessionKey(&session_key, &enc_session_key));
vector<uint8_t> mac_context;
vector<uint8_t> enc_context;
session_.FillDefaultContext(&mac_context, &enc_context);
OEMCrypto_SESSION session_id = session_.session_id();
auto oemcrypto_function = [&session_id, &enc_context, &mac_context,
&enc_session_key](size_t buffer_length) {
mac_context.resize(buffer_length);
return OEMCrypto_DeriveKeysFromSessionKey(
session_id, enc_session_key.data(), enc_session_key.size(),
mac_context.data(), mac_context.size(), enc_context.data(),
enc_context.size());
};
TestHugeLengthDoesNotCrashAPI(oemcrypto_function, kCheckStatus);
}
TEST_F(OEMCryptoUsesCertificate,
OEMCryptoMemoryDeriveKeysFromSessionKeyForLargeEncContext) {
vector<uint8_t> session_key;
vector<uint8_t> enc_session_key;
ASSERT_NO_FATAL_FAILURE(session_.PreparePublicKey(encoded_rsa_key_.data(),
encoded_rsa_key_.size()));
ASSERT_TRUE(session_.GenerateRSASessionKey(&session_key, &enc_session_key));
vector<uint8_t> mac_context;
vector<uint8_t> enc_context;
session_.FillDefaultContext(&mac_context, &enc_context);
OEMCrypto_SESSION session_id = session_.session_id();
auto oemcrypto_function = [&session_id, &enc_context, &mac_context,
&enc_session_key](size_t buffer_length) {
enc_context.resize(buffer_length);
return OEMCrypto_DeriveKeysFromSessionKey(
session_id, enc_session_key.data(), enc_session_key.size(),
mac_context.data(), mac_context.size(), enc_context.data(),
enc_context.size());
};
TestHugeLengthDoesNotCrashAPI(oemcrypto_function, kCheckStatus);
}
// This test attempts to use alternate algorithms for loaded device certs.
class OEMCryptoLoadsCertificateAlternates : public OEMCryptoLoadsCertificate {
protected:
@@ -4369,6 +4704,17 @@ TEST_P(OEMCryptoGenericCryptoTest, GenericKeyEncryptSameBufferAPI12) {
ASSERT_EQ(expected_encrypted, buffer);
}
TEST_P(OEMCryptoGenericCryptoTest, OEMCryptoMemorySelectKeyForHugeKeyIdLength) {
EncryptAndLoadKeys();
OEMCrypto_SESSION session_id = session_.session_id();
auto oemcrypto_function = [session_id](size_t key_id_length) {
vector<uint8_t> key_id(key_id_length);
return OEMCrypto_SelectKey(session_id, key_id.data(), key_id.size(),
OEMCrypto_CipherMode_CTR);
};
TestHugeLengthDoesNotCrashAPI(oemcrypto_function, !kCheckStatus);
}
// Test Generic_Decrypt works correctly.
TEST_P(OEMCryptoGenericCryptoTest, GenericKeyDecrypt) {
EncryptAndLoadKeys();
@@ -4833,6 +5179,11 @@ INSTANTIATE_TEST_CASE_P(TestAll, OEMCryptoGenericCryptoTest,
INSTANTIATE_TEST_CASE_P(TestAll, OEMCryptoGenericCryptoKeyIdLengthTest,
Range<uint32_t>(kCurrentAPI - 1, kCurrentAPI + 1));
/// @}
/// @addtogroup usage_table
/// @{
// Test usage table functionality.
class LicenseWithUsageEntry {
public:
@@ -6186,9 +6537,7 @@ TEST_P(OEMCryptoUsageTableTest, VerifyUsageTimes) {
// on a device that allows an application to set the clock.
class OEMCryptoUsageTableTestWallClock : public OEMCryptoUsageTableTest {
public:
void SetUp() override {
OEMCryptoUsageTableTest::SetUp();
}
void SetUp() override { OEMCryptoUsageTableTest::SetUp(); }
void TearDown() override {
wvcdm::TestSleep::ResetRollback();
@@ -6332,4 +6681,5 @@ INSTANTIATE_TEST_CASE_P(TestAPI16, OEMCryptoUsageTableDefragTest,
INSTANTIATE_TEST_CASE_P(TestAPI16, OEMCryptoUsageTableTestWallClock,
Values<uint32_t>(kCurrentAPI));
/// @}
} // namespace wvoec

View File

@@ -4,6 +4,7 @@
#include "OEMCryptoCENC.h"
#include "log.h"
#include "oec_device_features.h"
#include "oemcrypto_corpus_generator_helper.h"
#include "test_sleep.h"
static void acknowledge_cast() {
@@ -23,6 +24,9 @@ int main(int argc, char** argv) {
// Skip the first element, which is the program name.
const std::vector<std::string> args(argv + 1, argv + argc);
for (const std::string& arg : args) {
if (arg == "--generate_corpus") {
wvoec::SetGenerateCorpus(true);
}
if (arg == "--verbose" || arg == "-v") {
++verbosity;
} else if (arg == "--cast") {