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:
@@ -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 \
|
||||
|
||||
|
||||
@@ -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 \
|
||||
|
||||
|
||||
Binary file not shown.
File diff suppressed because it is too large
Load Diff
@@ -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" {
|
||||
|
||||
|
||||
@@ -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_
|
||||
|
||||
@@ -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_
|
||||
|
||||
@@ -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_
|
||||
|
||||
@@ -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_
|
||||
|
||||
@@ -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_
|
||||
|
||||
@@ -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_
|
||||
|
||||
14
libwvdrmengine/oemcrypto/odk/include/odk_attributes.h
Normal file
14
libwvdrmengine/oemcrypto/odk/include/odk_attributes.h
Normal 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_
|
||||
@@ -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_
|
||||
|
||||
@@ -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_
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)) {
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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_
|
||||
|
||||
@@ -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_
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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_
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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_
|
||||
|
||||
@@ -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_
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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"
|
||||
|
||||
|
||||
@@ -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_
|
||||
|
||||
@@ -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
|
||||
*/
|
||||
|
||||
@@ -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_
|
||||
|
||||
@@ -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
|
||||
```
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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_
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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_
|
||||
|
||||
@@ -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_
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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;
|
||||
|
||||
37
libwvdrmengine/oemcrypto/odk/test/odk_core_message_test.cpp
Normal file
37
libwvdrmengine/oemcrypto/odk/test/odk_core_message_test.cpp
Normal 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
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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
|
||||
|
||||
@@ -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_
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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()) {
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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 \
|
||||
|
||||
247
libwvdrmengine/oemcrypto/test/fuzz_tests/README.md
Normal file
247
libwvdrmengine/oemcrypto/test/fuzz_tests/README.md
Normal 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.
|
||||
@@ -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
|
||||
@@ -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
|
||||
106
libwvdrmengine/oemcrypto/test/fuzz_tests/oemcrypto_fuzz_helper.h
Normal file
106
libwvdrmengine/oemcrypto/test/fuzz_tests/oemcrypto_fuzz_helper.h
Normal 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_
|
||||
@@ -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_
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
10
libwvdrmengine/oemcrypto/test/fuzz_tests/sample_test.cc
Normal file
10
libwvdrmengine/oemcrypto/test/fuzz_tests/sample_test.cc
Normal 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;
|
||||
}
|
||||
@@ -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
|
||||
|
||||
@@ -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_
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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_
|
||||
|
||||
@@ -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
|
||||
@@ -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_
|
||||
@@ -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(¤t, &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
|
||||
|
||||
@@ -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") {
|
||||
|
||||
Reference in New Issue
Block a user