diff --git a/docs/WidevineSecurityIntegrationGuideforCENCAndroidSupplement_v16.pdf b/docs/WidevineSecurityIntegrationGuideforCENCAndroidSupplement_v16.pdf new file mode 100644 index 0000000..72d4f66 Binary files /dev/null and b/docs/WidevineSecurityIntegrationGuideforCENCAndroidSupplement_v16.pdf differ diff --git a/oemcrypto/include/OEMCryptoCENC.h b/oemcrypto/include/OEMCryptoCENC.h index 6e71a89..e63c92e 100644 --- a/oemcrypto/include/OEMCryptoCENC.h +++ b/oemcrypto/include/OEMCryptoCENC.h @@ -2,19 +2,133 @@ // source code may only be used and distributed under the Widevine Master // License Agreement. -/********************************************************************* - * OEMCryptoCENC.h +/** + * @mainpage OEMCrypto API * - * Reference APIs needed to support Widevine's crypto algorithms. + * OEMCrypto is the low level library implemented by the OEM to provide key and + * content protection, usually in a separate secure memory or process space. The + * term *OEMCrypto* refers to both the API described here and the library + * implementing the API. * - * See the document "WV Modular DRM Security Integration Guide for Common - * Encryption (CENC) -- version 16.3" for a description of this API. You - * can find this document in the widevine repository as - * docs/WidevineModularDRMSecurityIntegrationGuideforCENC_v16.pdf - * Changes between different versions of this API are documented in the files - * docs/Widevine_Modular_DRM_Version_*_Delta.pdf + * For an overview of OEMCrypto functionality, please see + * [Widevine Modular DRM Security Integration Guide for Common + * Encryption](../oemcrypto) * - *********************************************************************/ + * The OEMCrypto API is divided into several sections. + * + * @defgroup initcontrol Initialization and Control API + * Initialization and set up OEMCrypto. + * + * @defgroup keyladder Crypto Key Ladder API + * The crypto key ladder is a mechanism for staging crypto keys for use by the + * hardware crypto engine. + * + * Keys are always encrypted for transmission. Before + * a key can be used, it must be decrypted (typically using the top key in the + * key ladder) and then added to the key ladder for upcoming decryption + * operations. The Crypto Key Ladder API requires the device to provide + * hardware support for AES-128 CTR and CBC modes and prevent clear keys from + * being exposed to the insecure OS. + * + * @defgroup decryption Decryption API + * Devices that implement the Key Ladder API must also support a secure decode + * or secure decode and rendering implementation. + * + * This can be done by either + * decrypting into buffers secured by hardware protections and providing these + * secured buffers to the decoder/renderer or by implementing decrypt operations + * in the decoder/renderer. + * + * In a Security Level 2 implementation where the video path is not protected, + * the audio and video streams are decrypted using OEMCrypto_DecryptCENC() and + * buffers are returned to the media player in the clear. + * + * Generic Modular DRM allows an application to encrypt, decrypt, sign and + * verify arbitrary user data using a content key. This content key is + * securely delivered from the server to the client device using the same + * factory installed root of trust as a media content keys. + * + * @defgroup factory_provision Factory Provisioning API + * Functions that are used to install the root of trust. This could be either a + * keybox or an OEM Certificate. + * + * Widevine keyboxes are used to establish a root of trust to secure content on + * a device that uses Provisioning 2.0. OEM Certificates are used to establish + * a root of trust to secure content on a device that uses Provisioning + * 3.0. Factory Provisioning a device is related to manufacturing methods. This + * section describes the API that installs the Widevine Keybox and the + * recommended methods for the OEM’s factory provisioning procedure. + * + * Starting with API version 10, devices should have two keyboxes. One is the + * production keybox which may be installed in the factory, or using + * OEMCrypto_WrapKeyboxOrOEMCert and OEMCrypto_InstallKeyboxOrOEMCert as + * described below. The second keybox is a test keybox. The test keybox is the + * same for all devices and is used for a suite of unit tests. The test keybox + * will only be used temporarily while the unit tests are running, and will not + * be used by the general public. After the unit tests have been run, and + * OEMCrypto_Terminate has been called, the production keybox should be active + * again. + * + * API functions marked as optional may be used by the OEM’s factory + * provisioning procedure and implemented in the library, but are not called + * from the Widevine DRM Plugin during normal operation. + * + * @defgroup keybox Keybox and Provisioning 2.0 API + * Functions that are needed to for a device with a keybox. + * + * The OEMCrypto API allows for a device to be initially provisioned with a + * keybox or with an OEM certificate. See the section Provisioning above. In a + * Level 1 or Level 2 implementation, only the security processor may access the + * keys in the keybox. The following functions are for devices that are + * provisioned with a keybox, i.e. Provisioning 2.0. + * + * @defgroup oem_cert OEM Certificate and Provisioning 3.0 API + * Functions that are needed to for a device with an OEM Certificate. + * + * The OEMCrypto API allows for a device to be initially provisioned with a + * keybox or with an OEM certificate. See the Provisioning above. The + * functions in this section are for devices that are provisioned with an OEM + * Certificate, i.e. Provisioning 3.0. + * + * API functions marked as optional may be used by the OEM’s factory + * provisioning procedure and implemented in the library, but are not called + * from the Widevine DRM Plugin during normal operation. + * + * @defgroup validation Validation and Feature Support API + * The OEMCrypto API is flexible enough to allow different devices to support + * different features. This section has functions that specify the level of + * support for various features. These values are reported to either the + * application or the license server. + * + * @defgroup drm_cert DRM Certificate Provisioning API + * This section of functions are used to provision the device with an DRM + * certificate. This certificate is obtained by a device in the field from a + * Google/Widevine provisioning server, or from a third party server running the + * Google/Widevine provisioning server SDK. Since the DRM certificate may be + * origin or application specific, a device may have several DRM certificates + * installed at a time. The DRM certificate is used to authenticate the device + * to a license server. In order to obtain a DRM certificate from a + * provisioning server, the device may authenticate itself using a keybox or + * using an OEM certificate. + * + * @defgroup usage_table Usage Table API + * The usage table is used to store license usage and allows a persistent + * license to be reloaded. + * + * @defgroup test_verify Test and Verification API + * Functions that are designed to help test OEMCrypto and the device. They are + * not used during normal operation. Some functions, like OEMCrypto_RemoveSRM + * should only be implemented on test devices. Other functions, like those that + * test the full decrypt data path may be supported on a production device with + * no added risk of security loss. + * + * The following functions are used just for testing and verification of + * OEMCrypto and the CDM code. + * + * @defgroup common_types Common Types + * Enumerations and structures that are used by several OEMCrypto and ODK + * functions. + */ #ifndef OEMCRYPTO_CENC_H_ #define OEMCRYPTO_CENC_H_ @@ -29,9 +143,17 @@ extern "C" { #endif +/// @addtogroup keyladder +/// @{ + +/// This is the internal session identifier. typedef uint32_t OEMCrypto_SESSION; -/* +/// @} + +/// @addtogroup decryption +/// @{ +/** * The memory referenced by OEMCrypto_SharedMemory* is safe to be placed in * shared memory. The only data that should be placed into shared * memory is the contents of input/output buffers, i.e. data that will @@ -40,70 +162,67 @@ typedef uint32_t OEMCrypto_SESSION; */ typedef uint8_t OEMCrypto_SharedMemory; -/* - * OEMCrypto_DestBufferDesc Structure - * - * Description: - * This structure is used as parameters in the OEMCrypto_DecryptCENC and - * OEMCrypto_CopyBuffer functions. This describes the type and access - * information for the memory to receive decrypted data. - * - * The OEMCrypto API supports a range of client device architectures. - * Different architectures have different methods for acquiring and securing - * buffers that will hold portions of the audio or video stream after - * decryption. Three basic strategies are recognized for handling decrypted - * stream data: - * - * 1. Return the decrypted data in the clear into normal user memory - * (ClearBuffer). The caller uses normal memory allocation methods to - * acquire a buffer, and supplies the memory address of the buffer in - * the descriptor. - * 2. Place the decrypted data into protected memory (SecureBuffer). The - * caller uses a platform-specific method to acquire the protected - * buffer and a user-memory handle that references it. The handle is - * supplied to the decrypt call in the descriptor. If the buffer is - * filled with several OEMCrypto calls, the same handle will be used, - * and the offset will be incremented to indicate where the next write - * should take place. - * 3. Place the decrypted data directly into the audio or video decoder - * fifo (Direct). The caller will use platform-specific methods to - * initialize the fifo and the decoders. The decrypted stream data is - * not accessible to the caller. This is used on some platforms only. - * - * Fields: - * [in] type: A tag that indicates which variant of the union is valid for - * this instance of the structure. - * [variant] clear: This variant is valid when the type is - * OEMCrypto_BufferType_Clear. This OEMCrypto_DestBufferDesc indicates output - * should be written to a clear buffer. - * [in] address: A pointer to the address in memory to begin writing output. - * [in] address_length: The length of the buffer that is available to contain - * output. - * [variant] secure: This variant is valid when the type is - * OEMCrypto_BufferType_Secure. This OEMCrypto_DestBufferDesc indicates - * output should be written to a secure buffer. The decrypted output must - * never leave the secure area until it is output from the device. - * [in] handle: An opaque handle to a secure buffer. The meaning of this - * handle is platform-specific. - * [in] handle_length: The length of the data contained in the secure buffer. - * [in] offset: An offset indicating where in the secure buffer to start - * writing data. - * [variant] direct: This variant is valid when the type is - * OEMCrypto_BufferType_Direct. This OEMCrypto_DestBufferDesc indicates - * output should be written directly to the decoder. - * [in] is_video: A flag indicating if the data is video and should be sent - * to the video decoder. If this is false, the data can be assumed to be - * audio and sent to the audio decoder. - * - * Version: - * This struct changed in API version 16. +/** Specifies destination buffer type. */ typedef enum OEMCryptoBufferType { OEMCrypto_BufferType_Clear, OEMCrypto_BufferType_Secure, - OEMCrypto_BufferType_Direct + OEMCrypto_BufferType_Direct, + OEMCrypto_BufferType_MaxValue = OEMCrypto_BufferType_Direct, } OEMCryptoBufferType; +/** + * This structure is used as parameters in the OEMCrypto_DecryptCENC and + * OEMCrypto_CopyBuffer functions. This describes the type and access + * information for the memory to receive decrypted data. + * + * The OEMCrypto API supports a range of client device architectures. Different + * architectures have different methods for acquiring and securing buffers that + * will hold portions of the audio or video stream after decryption. Three basic + * strategies are recognized for handling decrypted stream data: + * + * 1. Return the decrypted data in the clear into normal user memory + * (ClearBuffer). The caller uses normal memory allocation methods to + * acquire a buffer, and supplies the memory address of the buffer in + * the descriptor. + * 2. Place the decrypted data into protected memory (SecureBuffer). The + * caller uses a platform-specific method to acquire the protected + * buffer and a user-memory handle that references it. The handle is + * supplied to the decrypt call in the descriptor. If the buffer is + * filled with several OEMCrypto calls, the same handle will be used, + * and the offset will be incremented to indicate where the next write + * should take place. + * 3. Place the decrypted data directly into the audio or video decoder + * fifo (Direct). The caller will use platform-specific methods to + * initialize the fifo and the decoders. The decrypted stream data is + * not accessible to the caller. This is used on some platforms only. + * + * @param[in] type: A tag that indicates which variant of the union is valid for + * this instance of the structure. [variant] clear: This variant is valid + * when the type is OEMCrypto_BufferType_Clear. This OEMCrypto_DestBufferDesc + * indicates output should be written to a clear buffer. + * @param[in] address: A pointer to the address in memory to begin writing + * output. + * @param[in] address_length: The length of the buffer that is available to + * contain output. [variant] secure: This variant is valid when the type is + * OEMCrypto_BufferType_Secure. This OEMCrypto_DestBufferDesc indicates + * output should be written to a secure buffer. The decrypted output must + * never leave the secure area until it is output from the device. + * @param[in] handle: An opaque handle to a secure buffer. The meaning of this + * handle is platform-specific. + * @param[in] handle_length: The length of the data contained in the secure + * buffer. + * @param[in] offset: An offset indicating where in the secure buffer to start + * writing data. [variant] direct: This variant is valid when the type is + * OEMCrypto_BufferType_Direct. This OEMCrypto_DestBufferDesc indicates + * output should be written directly to the decoder. + * @param[in] is_video: A flag indicating if the data is video and should be + * sent to the video decoder. If this is false, the data can be assumed to be + * audio and sent to the audio decoder. + * + * @version + * This struct changed in API version 16. + */ typedef struct { OEMCryptoBufferType type; union { @@ -122,20 +241,17 @@ typedef struct { } buffer; } OEMCrypto_DestBufferDesc; -/* - * OEMCrypto_InputOutputPair Structure +/** + * This structure is used as parameters in the OEMCrypto_DecryptCENC function. * - * Description: - * This structure is used as parameters in the OEMCrypto_DecryptCENC function. + * @param[in] input_data: An unaligned pointer to this sample from the stream. + * @param[in] input_data_length: The length of this sample in the stream, in + * bytes. + * @param[in] output_descriptor: A caller-owned descriptor that specifies the + * handling of the decrypted byte stream. See OEMCrypto_DestbufferDesc for + * details. * - * Fields: - * [in] input_data: An unaligned pointer to this sample from the stream. - * [in] input_data_length: The length of this sample in the stream, in bytes. - * [in] output_descriptor: A caller-owned descriptor that specifies the - * handling of the decrypted byte stream. See OEMCrypto_DestbufferDesc for - * details. - * - * Version: + * @version * This struct changed in API version 16. */ typedef struct { @@ -144,39 +260,35 @@ typedef struct { OEMCrypto_DestBufferDesc output_descriptor; // destination for clear data. } OEMCrypto_InputOutputPair; -/* - * OEMCrypto_SubSampleDescription Structure +/** + * This structure is used as parameters in the OEMCrypto_DecryptCENC + * function. In the DASH specification, a sample is composed of multiple + * samples, and each subsample is composed of two regions. The first region is + * clear unprotected data. We also call this clear data or unencrypted + * data. Immediately following the clear region is the protected region. The + * protected region is encrypted or encrypted with a pattern. The pattern and + * number of bytes that are encrypted in the protected region is discussed in + * this document when we talk about the function OEMCryptoDecryptCENC. For + * historic reasons, this document also calls the protected region the encrypted + * region. * - * Description: - * This structure is used as parameters in the OEMCrypto_DecryptCENC - * function. In the DASH specification, a sample is composed of multiple - * samples, and each subsample is composed of two regions. The first region - * is clear unprotected data. We also call this clear data or unencrypted - * data. Immediately following the clear region is the protected region. The - * protected region is encrypted or encrypted with a pattern. The pattern and - * number of bytes that are encrypted in the protected region is discussed in - * this document when we talk about the function OEMCryptoDecryptCENC. For - * historic reasons, this document also calls the protected region the - * encrypted region. + * @param[in] num_bytes_clear: The number of unprotected bytes in this + * subsample. The clear bytes come before the encrypted bytes. + * @param[in] num_bytes_encrypted: The number of protected bytes in this + * subsample. The protected bytes come after the clear bytes. + * @param[in] subsample_flags: bitwise flags indicating if this is the first, + * middle, or last subsample in a sample. 1 = first subsample, 2 = last + * subsample, 3 = both first and last subsample, 0 = neither first nor last + * subsample. + * @param[in] block_offset: This will only be non-zero for the 'cenc' scheme. + * If it is non-zero, the decryption block boundary is different from the + * start of the data. block_offset should be subtracted from data to compute + * the starting address of the first decrypted block. The bytes between the + * decryption block start address and data are discarded after decryption. It + * does not adjust the beginning of the source or destination data. This + * parameter satisfies 0 <= block_offset < 16. * - * Fields: - * [in] num_bytes_clear: The number of unprotected bytes in this subsample. - * The clear bytes come before the encrypted bytes. - * [in] num_bytes_encrypted: The number of protected bytes in this subsample. - * The protected bytes come after the clear bytes. - * [in] subsample_flags: bitwise flags indicating if this is the first, - * middle, or last subsample in a sample. 1 = first subsample, 2 = last - * subsample, 3 = both first and last subsample, 0 = neither first nor last - * subsample. - * [in] block_offset: This will only be non-zero for the 'cenc' scheme. If it - * is non-zero, the decryption block boundary is different from the start of - * the data. block_offset should be subtracted from data to compute the - * starting address of the first decrypted block. The bytes between the - * decryption block start address and data are discarded after decryption. It - * does not adjust the beginning of the source or destination data. This - * parameter satisfies 0 <= block_offset < 16. - * - * Version: + * @version * This struct changed in API version 16. */ typedef struct { @@ -189,23 +301,20 @@ typedef struct { #define OEMCrypto_FirstSubsample 1 #define OEMCrypto_LastSubsample 2 -/* - * OEMCrypto_SampleDescription Structure +/** + * This structure is used as parameters in the OEMCrypto_DecryptCENC function. * - * Description: - * This structure is used as parameters in the OEMCrypto_DecryptCENC function. + * @param[in] buffers: A structure containing information about the input and + * output buffers. + * @param[in] iv: A 16-byte array containing the IV for the initial subsample of + * the sample. + * @param[in] subsamples: A caller-owned array of OEMCrypto_SubSampleDescription + * structures. Each entry in this array describes one subsample in the + * sample. + * @param[in] subsamples_length: The length of the array pointed to by the + * subsamples parameter. * - * Fields: - * [in] buffers: A structure containing information about the input and - * output buffers. - * [in] iv: A 16-byte array containing the IV for the initial subsample of - * the sample. - * [in] subsamples: A caller-owned array of OEMCrypto_SubSampleDescription - * structures. Each entry in this array describes one subsample in the sample. - * [in] subsamples_length: The length of the array pointed to by the - * subsamples parameter. - * - * Version: + * @version * This struct changed in API version 16. */ typedef struct { @@ -215,17 +324,14 @@ typedef struct { size_t subsamples_length; // the number of subsamples in the sample. } OEMCrypto_SampleDescription; -/* - * OEMCrypto_CENCEncryptPatternDesc Structure - * - * Description: - * This structure is used as parameters in the OEMCrypto_DecryptCENC function. +/** + * This structure is used as parameters in the OEMCrypto_DecryptCENC function. * * Fields: - * [in] encrypt: The number of 16-byte crypto blocks to encrypt. - * [in] skip: The number of 16-byte crypto blocks to leave in the clear. + * @param[in] encrypt: The number of 16-byte crypto blocks to encrypt. + * @param[in] skip: The number of 16-byte crypto blocks to leave in the clear. * - * Version: + * @version * This struct changed in API version 16. */ typedef struct { @@ -233,26 +339,31 @@ typedef struct { size_t skip; // number of 16 byte blocks to leave in clear. } OEMCrypto_CENCEncryptPatternDesc; -/** OEMCryptoCipherMode is used in SelectKey to prepare a key for either CTR +/** + * OEMCryptoCipherMode is used in SelectKey to prepare a key for either CTR * decryption or CBC decryption. */ typedef enum OEMCryptoCipherMode { OEMCrypto_CipherMode_CTR, OEMCrypto_CipherMode_CBC, + OEMCrypto_CipherMode_MaxValue = OEMCrypto_CipherMode_CBC, } OEMCryptoCipherMode; -/* - * OEMCrypto_EntitledContentKeyObject +/** * Contains encrypted content key data for loading into the sessions keytable. * The content key data is encrypted using AES-256-CBC encryption, with PKCS#7 * padding. - * entitlement_key_id - entitlement key id to be matched to key table. - * entitlement_key_id_length - length of entitlment_key_id in bytes (1 to 16). - * content_key_id - content key id to be loaded into key table. - * content_key_id_length - length of content key id in bytes (1 to 16). - * key_data_iv - the IV for performing AES-256-CBC decryption of the key data. - * key_data - encrypted content key data. - * key_data_length - length of key_data - 16 or 32 depending on intended use. + + * @param entitlement_key_id: entitlement key id to be matched to key table. + * @param entitlement_key_id_length: length of entitlment_key_id in bytes (1 to + * 16). + * @param content_key_id: content key id to be loaded into key table. + * @param content_key_id_length: length of content key id in bytes (1 to 16). + * @param key_data_iv: the IV for performing AES-256-CBC decryption of the key + * data. + * @param key_data: encrypted content key data. + * @param key_data_length: length of key_data: 16 or 32 depending on intended us + e. */ typedef struct { OEMCrypto_Substring entitlement_key_id; @@ -261,18 +372,31 @@ typedef struct { OEMCrypto_Substring content_key_data; } OEMCrypto_EntitledContentKeyObject; -/* - * OEMCrypto_KeyRefreshObject - * This structure is being deprecated. It is only used for legacy licenses. - * Points to the relevant fields for renewing a content key. The fields are - * extracted from the License Renewal Response message offered to - * OEMCrypto_RefreshKeys(). Each field points to one of the components of - * the key. - * key_id - the unique id of this key. - * key_control_iv - the IV for performing AES-128-CBC decryption of the - * key_control field. 16 bytes. - * key_control - the key control block. It is encrypted (AES-128-CBC) with - * the content key from the key_data field. 16 bytes. +/** + * This is a list of valid algorithms for OEMCrypto_Generic_* functions. + * Some are valid for encryption/decryption, and some for signing/verifying. + */ +typedef enum OEMCrypto_Algorithm { + OEMCrypto_AES_CBC_128_NO_PADDING = 0, + OEMCrypto_HMAC_SHA256 = 1, +} OEMCrypto_Algorithm; + +/// @} + +/// @addtogroup keyladder +/// @{ +/** + * This structure is being deprecated. It is only used for legacy licenses. + * Points to the relevant fields for renewing a content key. The fields are + * extracted from the License Renewal Response message offered to + * OEMCrypto_RefreshKeys(). Each field points to one of the components of + * the key. + + * @param key_id: the unique id of this key. + * @param key_control_iv: the IV for performing AES-128-CBC decryption of the + * key_control field. 16 bytes. + * @param key_control: the key control block. It is encrypted (AES-128-CBC) with + * the content key from the key_data field. 16 bytes. * * The key_data is unchanged from the original OEMCrypto_LoadKeys() call. Some * Key Control Block fields, especially those related to key lifetime, may @@ -287,17 +411,13 @@ typedef struct { OEMCrypto_Substring key_control; } OEMCrypto_KeyRefreshObject; -/* - * OEMCrypto_Algorithm - * This is a list of valid algorithms for OEMCrypto_Generic_* functions. - * Some are valid for encryption/decryption, and some for signing/verifying. - */ -typedef enum OEMCrypto_Algorithm { - OEMCrypto_AES_CBC_128_NO_PADDING = 0, - OEMCrypto_HMAC_SHA256 = 1, -} OEMCrypto_Algorithm; +/// @} -/* +/// @addtogroup usage_table +/// @{ + +#if 0 // If your compiler supports __attribute__((packed)). +/** * OEMCrypto_PST_Report is used to report an entry from the Usage Table. * * Platforms that have compilers that support packed structures, may use the @@ -306,7 +426,6 @@ typedef enum OEMCrypto_Algorithm { * * All fields are in network byte order. */ -#if 0 // If your compiler supports __attribute__((packed)). typedef struct { uint8_t signature[20]; // -- HMAC SHA1 of the rest of the report. uint8_t status; // current status of entry. (OEMCrypto_Usage_Entry_Status) @@ -320,9 +439,8 @@ typedef struct { } __attribute__((packed)) OEMCrypto_PST_Report; #endif -/* - * OEMCrypto_Clock_Security_Level. - * Valid values for clock_security_level in OEMCrypto_PST_Report. +/** + * Valid values for clock_security_level in OEMCrypto_PST_Report. */ typedef enum OEMCrypto_Clock_Security_Level { kInsecureClock = 0, @@ -337,21 +455,27 @@ typedef uint8_t RSA_Padding_Scheme; // PKCS1 with block type 1 padding (only). #define kSign_PKCS1_Block1 ((RSA_Padding_Scheme)0x2) -/* +/// @} + +/// @addtogroup validation +/// @{ +/** * OEMCrypto_HDCP_Capability is used in the key control block to enforce HDCP * level, and in GetHDCPCapability for reporting. */ typedef enum OEMCrypto_HDCP_Capability { - HDCP_NONE = 0, // No HDCP supported, no secure data path. - HDCP_V1 = 1, // HDCP version 1.0 - HDCP_V2 = 2, // HDCP version 2.0 Type 1. - HDCP_V2_1 = 3, // HDCP version 2.1 Type 1. - HDCP_V2_2 = 4, // HDCP version 2.2 Type 1. - HDCP_V2_3 = 5, // HDCP version 2.3 Type 1. - HDCP_NO_DIGITAL_OUTPUT = 0xff // No digital output. + HDCP_NONE = 0, // No HDCP supported, no secure data path. + HDCP_V1 = 1, // HDCP version 1.x + HDCP_V2 = 2, // HDCP version 2.0 Type 1. + HDCP_V2_1 = 3, // HDCP version 2.1 Type 1. + HDCP_V2_2 = 4, // HDCP version 2.2 Type 1. + HDCP_V2_3 = 5, // HDCP version 2.3 Type 1. + HDCP_NO_DIGITAL_OUTPUT = 0xff // No digital output. } OEMCrypto_HDCP_Capability; -/* Return value for OEMCrypto_GetProvisioningMethod(). */ +/** + Return value for OEMCrypto_GetProvisioningMethod(). + */ typedef enum OEMCrypto_ProvisioningMethod { OEMCrypto_ProvisioningError = 0, // Device cannot be provisioned. OEMCrypto_DrmCertificate = 1, // Device has baked in DRM certificate @@ -360,7 +484,7 @@ typedef enum OEMCrypto_ProvisioningMethod { OEMCrypto_OEMCertificate = 3 // Device has factory installed OEM certificate. } OEMCrypto_ProvisioningMethod; -/* +/** * Flags indicating public/private key types supported. */ #define OEMCrypto_Supports_RSA_2048bit 0x1 @@ -370,14 +494,14 @@ typedef enum OEMCrypto_ProvisioningMethod { #define OEMCrypto_Supports_ECC_secp384r1 0x200 #define OEMCrypto_Supports_ECC_secp521r1 0x400 -/* +/** * Flags indicating full decrypt path hash supported. */ #define OEMCrypto_Hash_Not_Supported 0 #define OEMCrypto_CRC_Clear_Buffer 1 #define OEMCrypto_Partner_Defined_Hash 2 -/* +/** * Return values from OEMCrypto_GetAnalogOutputFlags. */ #define OEMCrypto_No_Analog_Output 0x0 @@ -387,7 +511,9 @@ typedef enum OEMCrypto_ProvisioningMethod { // Unknown_Analog_Output is used only for backwards compatibility. #define OEMCrypto_Unknown_Analog_Output (1<<31) -/* +/// @} + +/** * Obfuscation Renames. */ // clang-format off @@ -498,187 +624,164 @@ typedef enum OEMCrypto_ProvisioningMethod { #define OEMCrypto_MinorAPIVersion _oecc108 // clang-format on -/* - * OEMCrypto_SetSandbox +/// @addtogroup initcontrol +/// @{ + +/** + * This tells OEMCrypto which sandbox the current process belongs to. Any + * persistent memory used to store the generation number should be associated + * with this sandbox id. OEMCrypto can assume that this sandbox will be tied + * to the current process or VM until OEMCrypto_Terminate is called. See the + * section "VM and Sandbox Support" above for more details. * - * Description: - * This tells OEMCrypto which sandbox the current process belongs to. Any - * persistent memory used to store the generation number should be associated - * with this sandbox id. OEMCrypto can assume that this sandbox will be tied - * to the current process or VM until OEMCrypto_Terminate is called. See the - * section "VM and Sandbox Support" above for more details. + * If OEMCrypto does not support sandboxes, it will return + * OEMCrypto_ERROR_NOT_IMPLEMENTED. On most platforms, this function will + * just return OEMCrypto_ERROR_NOT_IMPLEMENTED. If OEMCrypto supports + * sandboxes, this function returns OEMCrypto_SUCCESS on success, and + * OEMCrypto_ERROR_UNKNOWN_FAILURE on failure. * - * If OEMCrypto does not support sandboxes, it will return - * OEMCrypto_ERROR_NOT_IMPLEMENTED. On most platforms, this function will - * just return OEMCrypto_ERROR_NOT_IMPLEMENTED. If OEMCrypto supports - * sandboxes, this function returns OEMCrypto_SUCCESS on success, and - * OEMCrypto_ERROR_UNKNOWN_FAILURE on failure. + * The CDM layer will call OEMCrypto_SetSandbox once before + * OEMCrypto_Initialize. After this function is called and returns success, + * it will be OEMCrypto's responsibility to keep calls to usage table + * functions separate, and to accept a call to OEMCrypto_Terminate for each + * sandbox. * - * The CDM layer will call OEMCrypto_SetSandbox once before - * OEMCrypto_Initialize. After this function is called and returns success, - * it will be OEMCrypto's responsibility to keep calls to usage table - * functions separate, and to accept a call to OEMCrypto_Terminate for each - * sandbox. + * @param[in] sandbox_id: a short string unique to the current sandbox. + * @param[in] sandbox_id_length: length of sandbox_id. * - * Parameters: - * [in] sandbox_id: a short string unique to the current sandbox. - * [in] sandbox_id_length: length of sandbox_id. + * @retval OEMCrypto_SUCCESS success + * @retval OEMCrypto_ERROR_INIT_FAILED failed to initialize crypto hardware + * @retval OEMCrypto_ERROR_NOT_IMPLEMENTED sandbox functionality not supported * - * Returns: - * OEMCrypto_SUCCESS success - * OEMCrypto_ERROR_INIT_FAILED failed to initialize crypto hardware - * OEMCrypto_ERROR_NOT_IMPLEMENTED - sandbox functionality not supported - * - * Threading: + * @threading * This is an "Initialization and Termination Function" and will not be * called simultaneously with any other function, as if the CDM holds a write * lock on the OEMCrypto system. It is called once before * OEMCrypto_Initialize. * - * Version: + * @version * This method is new in version 15 of the API. */ OEMCryptoResult OEMCrypto_SetSandbox(const uint8_t* sandbox_id, size_t sandbox_id_length); -/* - * OEMCrypto_Initialize +/** + * Initialize the crypto firmware/hardware. * - * Description: - * Initialize the crypto firmware/hardware. + * @retval OEMCrypto_SUCCESS success + * @retval OEMCrypto_ERROR_INIT_FAILED failed to initialize crypto hardware * - * Parameters: - * None - * - * Returns: - * OEMCrypto_SUCCESS success - * OEMCrypto_ERROR_INIT_FAILED failed to initialize crypto hardware - * - * Threading: + * @threading * This is an "Initialization and Termination Function" and will not be * called simultaneously with any other function, as if the CDM holds a write * lock on the OEMCrypto system. * - * Version: + * @version * This method is supported by all API versions. */ OEMCryptoResult OEMCrypto_Initialize(void); -/* - * OEMCrypto_Terminate +/** + * Closes the crypto operation and releases all related resources. * - * Description: - * Closes the crypto operation and releases all related resources. + * @retval OEMCrypto_SUCCESS success + * @retval OEMCrypto_ERROR_TERMINATE_FAILED failed to de-initialize crypto + * hardware * - * Parameters: - * None - * - * Returns: - * OEMCrypto_SUCCESS success - * OEMCrypto_ERROR_TERMINATE_FAILED failed to de-initialize crypto hardware - * - * Threading: + * @threading * This is an "Initialization and Termination Function" and will not be * called simultaneously with any other function, as if the CDM holds a write * lock on the OEMCrypto system. No other functions will be called before the * system is re-initialized. * - * Version: + * @version * This method is supported by all API versions. */ OEMCryptoResult OEMCrypto_Terminate(void); -/* - * OEMCrypto_OpenSession +/// @} + +/// @addtogroup keyladder +/// @{ +/** + * Open a new crypto security engine context. The security engine hardware + * and firmware shall acquire resources that are needed to support the + * session, and return a session handle that identifies that session in + * future calls. * - * Description: - * Open a new crypto security engine context. The security engine hardware - * and firmware shall acquire resources that are needed to support the - * session, and return a session handle that identifies that session in - * future calls. + * This function shall call ODK_InitializeSessionValues to initialize the + * session's clock values, timer values, and nonce values. + * ODK_InitializeSessionValues is described in the document "License Duration + * and Renewal", to initialize the session's clock values. * - * This function shall call ODK_InitializeSessionValues to initialize the - * session's clock values, timer values, and nonce values. - * ODK_InitializeSessionValues is described in the document "License Duration - * and Renewal", to initialize the session's clock values. + * @param[out] session: an opaque handle that the crypto firmware uses to + * identify the session. * - * Parameters: - * [out] session: an opaque handle that the crypto firmware uses to identify - * the session. - * - * Returns: - * OEMCrypto_SUCCESS success - * OEMCrypto_ERROR_TOO_MANY_SESSIONS failed because too many sessions are open - * OEMCrypto_ERROR_OPEN_SESSION_FAILED there is a resource issue or the + * @retval OEMCrypto_SUCCESS success + * @retval OEMCrypto_ERROR_TOO_MANY_SESSIONS failed because too many sessions + * are open + * @retval OEMCrypto_ERROR_OPEN_SESSION_FAILED there is a resource issue or the * security engine is not properly initialized. - * OEMCrypto_ERROR_SYSTEM_INVALIDATED + * @retval OEMCrypto_ERROR_SYSTEM_INVALIDATED * - * Threading: + * @threading * This is a "Session Initialization Function" and will not be called * simultaneously with any other function, as if the CDM holds a write lock * on the OEMCrypto system. * - * Version: + * @version * This method changed in API version 16. */ OEMCryptoResult OEMCrypto_OpenSession(OEMCrypto_SESSION* session); -/* - * OEMCrypto_CloseSession +/** + * Closes the crypto security engine session and frees any associated + * resources. If this session is associated with a Usage Entry, all resident + * memory associated with it will be freed. It is the CDM layer's + * responsibility to call OEMCrypto_UpdateUsageEntry before closing the + * session. * - * Description: - * Closes the crypto security engine session and frees any associated - * resources. If this session is associated with a Usage Entry, all resident - * memory associated with it will be freed. It is the CDM layer's - * responsibility to call OEMCrypto_UpdateUsageEntry before closing the - * session. + * @param[in] session: handle for the session to be closed. * - * Parameters: - * [in] session: handle for the session to be closed. + * @retval OEMCrypto_SUCCESS success + * @retval OEMCrypto_ERROR_INVALID_SESSION no open session with that id. + * @retval OEMCrypto_ERROR_CLOSE_SESSION_FAILED illegal/unrecognized handle or + * the security engine is not properly initialized. + * @retval OEMCrypto_ERROR_SYSTEM_INVALIDATED * - * Returns: - * OEMCrypto_SUCCESS success - * OEMCrypto_ERROR_INVALID_SESSION no open session with that id. - * OEMCrypto_ERROR_CLOSE_SESSION_FAILED illegal/unrecognized handle or the - * security engine is not properly initialized. - * OEMCrypto_ERROR_SYSTEM_INVALIDATED - * - * Threading: + * @threading * This is a "Session Initialization Function" and will not be called * simultaneously with any other function, as if the CDM holds a write lock * on the OEMCrypto system. * - * Version: + * @version * This method changed in API version 13. */ OEMCryptoResult OEMCrypto_CloseSession(OEMCrypto_SESSION session); -/* - * OEMCrypto_GenerateDerivedKeys +/** + * Generates three secondary keys, mac_key[server], mac_key[client], and + * encrypt_key, for handling signing and content key decryption under the + * license server protocol for CENC. * - * Description: - * Generates three secondary keys, mac_key[server], mac_key[client], and - * encrypt_key, for handling signing and content key decryption under the - * license server protocol for CENC. + * Refer to the Key Derivation section above for more details. This function + * computes the AES-128-CMAC of the enc_key_context and stores it in secure + * memory as the encrypt_key. It then computes four cycles of AES-128-CMAC of + * the mac_key_context and stores it in the mac_keys -- the first two cycles + * generate the mac_key[server] and the second two cycles generate the + * mac_key[client]. These two keys will be stored until the next call to + * OEMCrypto_LoadKeys(). The device key from the keybox is used as the key + * for the AES-128-CMAC. * - * Refer to the Key Derivation section above for more details. This function - * computes the AES-128-CMAC of the enc_key_context and stores it in secure - * memory as the encrypt_key. It then computes four cycles of AES-128-CMAC of - * the mac_key_context and stores it in the mac_keys -- the first two cycles - * generate the mac_key[server] and the second two cycles generate the - * mac_key[client]. These two keys will be stored until the next call to - * OEMCrypto_LoadKeys(). The device key from the keybox is used as the key - * for the AES-128-CMAC. - * - * Parameters: - * [in] session: handle for the session to be used. - * [in] mac_key_context: pointer to memory containing context data for - * computing the HMAC generation key. - * [in] mac_key_context_length: length of the HMAC key context data, in bytes. - * [in] enc_key_context: pointer to memory containing context data for - * computing the encryption key. - * [in] enc_key_context_length: length of the encryption key context data, in - * bytes. + * @param[in] session: handle for the session to be used. + * @param[in] mac_key_context: pointer to memory containing context data for + * computing the HMAC generation key. + * @param[in] mac_key_context_length: length of the HMAC key context data, in + * bytes. + * @param[in] enc_key_context: pointer to memory containing context data for + * computing the encryption key. + * @param[in] enc_key_context_length: length of the encryption key context data, + * in bytes. * * Results: * mac_key[server]: the 256 bit mac key is generated and stored in secure @@ -688,31 +791,30 @@ OEMCryptoResult OEMCrypto_CloseSession(OEMCrypto_SESSION session); * enc_key: the 128 bit encryption key is generated and stored in secure * memory. * - * Returns: - * OEMCrypto_SUCCESS success - * OEMCrypto_ERROR_NO_DEVICE_KEY - * OEMCrypto_ERROR_INVALID_SESSION - * OEMCrypto_ERROR_INVALID_CONTEXT - * OEMCrypto_ERROR_INSUFFICIENT_RESOURCES - * OEMCrypto_ERROR_UNKNOWN_FAILURE - * OEMCrypto_ERROR_BUFFER_TOO_LARGE - * OEMCrypto_ERROR_SESSION_LOST_STATE - * OEMCrypto_ERROR_SYSTEM_INVALIDATED + * @retval OEMCrypto_SUCCESS success + * @retval OEMCrypto_ERROR_NO_DEVICE_KEY + * @retval OEMCrypto_ERROR_INVALID_SESSION + * @retval OEMCrypto_ERROR_INVALID_CONTEXT + * @retval OEMCrypto_ERROR_INSUFFICIENT_RESOURCES + * @retval OEMCrypto_ERROR_UNKNOWN_FAILURE + * @retval OEMCrypto_ERROR_BUFFER_TOO_LARGE + * @retval OEMCrypto_ERROR_SESSION_LOST_STATE + * @retval OEMCrypto_ERROR_SYSTEM_INVALIDATED * - * Buffer Sizes: + * @buffer_size * OEMCrypto shall support mac_key_context and enc_key_context sizes as * described in the section OEMCrypto_ResourceRatingTier. * OEMCrypto shall return OEMCrypto_ERROR_BUFFER_TOO_LARGE if the buffers are * too large. * - * Threading: + * @threading * This is a "Session Function" and may be called simultaneously with session * functions for other sessions but not simultaneously with other functions * for this session. It will not be called simultaneously with initialization * or usage table functions. It is as if the CDM holds a write lock for this * session, and a read lock on the OEMCrypto system. * - * Version: + * @version * This method changed in API version 12. */ OEMCryptoResult OEMCrypto_GenerateDerivedKeys( @@ -721,50 +823,47 @@ OEMCryptoResult OEMCrypto_GenerateDerivedKeys( const OEMCrypto_SharedMemory* enc_key_context, size_t enc_key_context_length); -/* - * OEMCrypto_DeriveKeysFromSessionKey +/** + * Generates three secondary keys, mac_key[server], mac_key[client] and + * encrypt_key, for handling signing and content key decryption under the + * license server protocol for CENC. * - * Description: - * Generates three secondary keys, mac_key[server], mac_key[client] and - * encrypt_key, for handling signing and content key decryption under the - * license server protocol for CENC. + * This function is similar to OEMCrypto_GenerateDerivedKeys, except that it + * uses a session key to generate the secondary keys instead of the Widevine + * Keybox device key. These three keys will be stored in secure memory until + * the next call to LoadLicense or LoadProvisioning. * - * This function is similar to OEMCrypto_GenerateDerivedKeys, except that it - * uses a session key to generate the secondary keys instead of the Widevine - * Keybox device key. These three keys will be stored in secure memory until - * the next call to LoadLicense or LoadProvisioning. + * If the session's private key is an RSA key, then the session key is passed + * in encrypted by the device RSA public key as the derivation_key, and must + * be decrypted with the RSA private key before use. * - * If the session's private key is an RSA key, then the session key is passed - * in encrypted by the device RSA public key as the derivation_key, and must - * be decrypted with the RSA private key before use. + * If the sesion's private key is an ECC key, then the session key is the + * SHA256 of the shared secret key calculated by ECDH between the device's + * ECC private key and the derivation_key. See the document "OEMCrypto + * Elliptic Curve Support" for details. * - * If the sesion's private key is an ECC key, then the session key is the - * SHA256 of the shared secret key calculated by ECDH between the device's - * ECC private key and the derivation_key. See the document "OEMCrypto - * Elliptic Curve Support" for details. + * Once the enc_key and mac_keys have been generated, all calls to LoadKeys + * or LoadLicense proceed in the same manner for license requests using RSA + * or using a Widevine keybox token. * - * Once the enc_key and mac_keys have been generated, all calls to LoadKeys - * or LoadLicense proceed in the same manner for license requests using RSA - * or using a Widevine keybox token. - * - * Verification: + * @verification * If the RSA key's allowed_schemes is not kSign_RSASSA_PSS, then no keys are * derived and the error OEMCrypto_ERROR_INVALID_RSA_KEY is returned. An RSA * key cannot be used for both deriving session keys and also for PKCS1 * signatures. * - * Parameters: - * [in] session: handle for the session to be used. - * [in] derivation_key: session key, encrypted with the public RSA key (from - * the DRM certifcate) using RSA-OAEP. - * [in] derivation_key_length: length of derivation_key, in bytes. - * [in] mac_key_context: pointer to memory containing context data for - * computing the HMAC generation key. - * [in] mac_key_context_length: length of the HMAC key context data, in bytes. - * [in] enc_key_context: pointer to memory containing context data for - * computing the encryption key. - * [in] enc_key_context_length: length of the encryption key context data, in - * bytes. + * @param[in] session: handle for the session to be used. + * @param[in] derivation_key: session key, encrypted with the public RSA key + * (from the DRM certifcate) using RSA-OAEP. + * @param[in] derivation_key_length: length of derivation_key, in bytes. + * @param[in] mac_key_context: pointer to memory containing context data for + * computing the HMAC generation key. + * @param[in] mac_key_context_length: length of the HMAC key context data, in + * bytes. + * @param[in] enc_key_context: pointer to memory containing context data for + * computing the encryption key. + * @param[in] enc_key_context_length: length of the encryption key context data, + * in bytes. * * Results: * mac_key[server]: the 256 bit mac key is generated and stored in secure @@ -774,31 +873,30 @@ OEMCryptoResult OEMCrypto_GenerateDerivedKeys( * enc_key: the 128 bit encryption key is generated and stored in secure * memory. * - * Returns: - * OEMCrypto_SUCCESS success - * OEMCrypto_ERROR_DEVICE_NOT_RSA_PROVISIONED - * OEMCrypto_ERROR_INVALID_SESSION - * OEMCrypto_ERROR_INVALID_CONTEXT - * OEMCrypto_ERROR_INSUFFICIENT_RESOURCES - * OEMCrypto_ERROR_UNKNOWN_FAILURE - * OEMCrypto_ERROR_BUFFER_TOO_LARGE - * OEMCrypto_ERROR_SESSION_LOST_STATE - * OEMCrypto_ERROR_SYSTEM_INVALIDATED + * @retval OEMCrypto_SUCCESS success + * @retval OEMCrypto_ERROR_DEVICE_NOT_RSA_PROVISIONED + * @retval OEMCrypto_ERROR_INVALID_SESSION + * @retval OEMCrypto_ERROR_INVALID_CONTEXT + * @retval OEMCrypto_ERROR_INSUFFICIENT_RESOURCES + * @retval OEMCrypto_ERROR_UNKNOWN_FAILURE + * @retval OEMCrypto_ERROR_BUFFER_TOO_LARGE + * @retval OEMCrypto_ERROR_SESSION_LOST_STATE + * @retval OEMCrypto_ERROR_SYSTEM_INVALIDATED * - * Buffer Sizes: + * @buffer_size * OEMCrypto shall support mac_key_context and enc_key_context sizes as * described in the section OEMCrypto_ResourceRatingTier. * OEMCrypto shall return OEMCrypto_ERROR_BUFFER_TOO_LARGE if the buffers are * too large. * - * Threading: + * @threading * This is a "Session Function" and may be called simultaneously with session * functions for other sessions but not simultaneously with other functions * for this session. It will not be called simultaneously with initialization * or usage table functions. It is as if the CDM holds a write lock for this * session, and a read lock on the OEMCrypto system. * - * Version: + * @version * This method changed in API version 16. */ OEMCryptoResult OEMCrypto_DeriveKeysFromSessionKey( @@ -808,575 +906,544 @@ OEMCryptoResult OEMCrypto_DeriveKeysFromSessionKey( const OEMCrypto_SharedMemory* enc_key_context, size_t enc_key_context_length); -/* - * OEMCrypto_GenerateNonce +/** + * Generates a 32-bit nonce to detect possible replay attack on the key + * control block. The nonce is stored in secure memory and will be used in + * the license or provisioning request. * - * Description: - * Generates a 32-bit nonce to detect possible replay attack on the key - * control block. The nonce is stored in secure memory and will be used in - * the license or provisioning request. + * Because the nonce will be used to prevent replay attacks, it is desirable + * that a rogue application cannot rapidly call this function until a + * repeated nonce is created randomly. This is called a nonce flood. With + * this in mind, if more than 200 nonces are requested within one second, + * OEMCrypto will return an error after the 200th and not generate any more + * nonces for the rest of the second. After an error, if the application + * waits at least one second before requesting more nonces, then OEMCrypto + * will reset the error condition and generate valid nonces again. * - * Because the nonce will be used to prevent replay attacks, it is desirable - * that a rogue application cannot rapidly call this function until a - * repeated nonce is created randomly. This is called a nonce flood. With - * this in mind, if more than 200 nonces are requested within one second, - * OEMCrypto will return an error after the 200th and not generate any more - * nonces for the rest of the second. After an error, if the application - * waits at least one second before requesting more nonces, then OEMCrypto - * will reset the error condition and generate valid nonces again. + * The nonce should be stored in the session's ODK_NonceValue field by + * calling the function ODK_SetNonceValue(&nonce_values, nonce). The ODK + * functions are documented in "Widevine Core Message Serialization". * - * The nonce should be stored in the session's ODK_NonceValue field by - * calling the function ODK_SetNonceValue(&nonce_values, nonce). The ODK - * functions are documented in "Widevine Core Message Serialization". + * This function shall only be called at most once per open session. It shall + * only be called before signing either a provisioning request or a license + * request. If an attempt is made to generate a nonce while in the wrong + * state, an error of OEMCrypto_ERROR_INVALID_CONTEXT is returned. * - * This function shall only be called at most once per open session. It shall - * only be called before signing either a provisioning request or a license - * request. If an attempt is made to generate a nonce while in the wrong - * state, an error of OEMCrypto_ERROR_INVALID_CONTEXT is returned. - * - * Parameters: - * [in] session: handle for the session to be used. - * [out] nonce: pointer to memory to receive the computed nonce. + * @param[in] session: handle for the session to be used. + * @param[out] nonce: pointer to memory to receive the computed nonce. * * Results: - * nonce: the nonce is also stored in secure memory. Each session should - * store 4 nonces. + * nonce: the nonce is also stored in secure memory. * - * Returns: - * OEMCrypto_SUCCESS success - * OEMCrypto_ERROR_INVALID_SESSION - * OEMCrypto_ERROR_INSUFFICIENT_RESOURCES - * OEMCrypto_ERROR_UNKNOWN_FAILURE - * OEMCrypto_ERROR_SESSION_LOST_STATE - * OEMCrypto_ERROR_SYSTEM_INVALIDATED + * @retval OEMCrypto_SUCCESS success + * @retval OEMCrypto_ERROR_INVALID_SESSION + * @retval OEMCrypto_ERROR_INSUFFICIENT_RESOURCES + * @retval OEMCrypto_ERROR_UNKNOWN_FAILURE + * @retval OEMCrypto_ERROR_SESSION_LOST_STATE + * @retval OEMCrypto_ERROR_SYSTEM_INVALIDATED * - * Threading: + * @threading * This is a "Session Initialization Function" and will not be called * simultaneously with any other function, as if the CDM holds a write lock * on the OEMCrypto system. * - * Version: + * @version * This method changed in API version 16. */ OEMCryptoResult OEMCrypto_GenerateNonce(OEMCrypto_SESSION session, uint32_t* nonce); -/* - * OEMCrypto_PrepAndSignLicenseRequest +/** + * OEMCrypto will use ODK_PrepareCoreLicenseRequest to prepare the core + * message. If it returns OEMCrypto_SUCCESS, then OEMCrypto shall sign the + * the message body using the DRM certificate's private key. If it returns an + * error, the error should be returned by OEMCrypto to the CDM layer. + * ODK_PrepareCoreLicenseRequest is described in the document "Widevine Core + * Message Serialization". * - * Description: - * OEMCrypto will use ODK_PrepareCoreLicenseRequest to prepare the core - * message. If it returns OEMCrypto_SUCCESS, then OEMCrypto shall sign the - * the message body using the DRM certificate's private key. If it returns an - * error, the error should be returned by OEMCrypto to the CDM layer. - * ODK_PrepareCoreLicenseRequest is described in the document "Widevine Core - * Message Serialization". + * The message body is the buffer starting at message + core_message_size, + * and with length message_length - core_message_size. The reason OEMCrypto + * only signs the message body and not the entire message is to allow a v16 + * device to request a license from a v15 license server. * - * The message body is the buffer starting at message + core_message_size, - * and with length message_length - core_message_size. The reason OEMCrypto - * only signs the message body and not the entire message is to allow a v16 - * device to request a license from a v15 license server. + * If the session's private RSA key has an "allowed_schemes" bit field, then + * it must be 0x1 (RSASSA-PSS with SHA1). If not, then an error of + * OEMCrypto_ERROR_SIGNATURE_FAILURE shall be returned. * - * If the session's private RSA key has an "allowed_schemes" bit field, then - * it must be 0x1 (RSASSA-PSS with SHA1). If not, then an error of - * OEMCrypto_ERROR_SIGNATURE_FAILURE shall be returned. + * OEMCrypto shall compute a hash of the core license request. The core + * license request is the buffer starting at message and with length + * core_message_size. The hash will be saved with the session and verified + * that it matches a hash in the license response. * - * OEMCrypto shall compute a hash of the core license request. The core - * license request is the buffer starting at message and with length - * core_message_size. The hash will be saved with the session and verified - * that it matches a hash in the license response. + * OEMCrypto shall also call the function ODK_InitializeClockValues, + * described in the document "License Duration and Renewal", to initialize + * the session's clock values. * - * OEMCrypto shall also call the function ODK_InitializeClockValues, - * described in the document "License Duration and Renewal", to initialize - * the session's clock values. + * Refer to the Signing Messages Sent to a Server section above for more + * details about the signature algorithm. * - * Refer to the Signing Messages Sent to a Server section above for more - * details about the signature algorithm. + * NOTE: if signature pointer is null and/or input signature_length is zero, + * this function returns OEMCrypto_ERROR_SHORT_BUFFER and sets output + * signature_length to the size needed to receive the output signature. * - * NOTE: if signature pointer is null and/or input signature_length is zero, - * this function returns OEMCrypto_ERROR_SHORT_BUFFER and sets output - * signature_length to the size needed to receive the output signature. + * @param[in] session: handle for the session to be used. + * @param[in,out] message: Pointer to memory for the entire message. Modified by + * OEMCrypto via 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[out] signature: pointer to memory to receive the computed signature. + * @param[in,out] signature_length: (in) length of the signature buffer, in + * bytes. (out) actual length of the signature, in bytes. * - * Parameters: - * [in] session: handle for the session to be used. - * [in/out] message: Pointer to memory for the entire message. Modified by - * OEMCrypto via 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. - * [out] signature: pointer to memory to receive the computed signature. - * [in/out] signature_length: (in) length of the signature buffer, in bytes. - * (out) actual length of the signature, in bytes. + * @retval OEMCrypto_SUCCESS success + * @retval OEMCrypto_ERROR_INVALID_SESSION + * @retval OEMCrypto_ERROR_SHORT_BUFFER if signature buffer is not large enough + * to hold the signature. + * @retval OEMCrypto_ERROR_INSUFFICIENT_RESOURCES + * @retval OEMCrypto_ERROR_UNKNOWN_FAILURE + * @retval OEMCrypto_ERROR_SIGNATURE_FAILURE + * @retval OEMCrypto_ERROR_BUFFER_TOO_LARGE + * @retval OEMCrypto_ERROR_SESSION_LOST_STATE + * @retval OEMCrypto_ERROR_SYSTEM_INVALIDATED * - * Returns: - * OEMCrypto_SUCCESS success - * OEMCrypto_ERROR_INVALID_SESSION - * OEMCrypto_ERROR_SHORT_BUFFER if signature buffer is not large enough to - * hold the signature. - * OEMCrypto_ERROR_INSUFFICIENT_RESOURCES - * OEMCrypto_ERROR_UNKNOWN_FAILURE - * OEMCrypto_ERROR_SIGNATURE_FAILURE - * OEMCrypto_ERROR_BUFFER_TOO_LARGE - * OEMCrypto_ERROR_SESSION_LOST_STATE - * OEMCrypto_ERROR_SYSTEM_INVALIDATED - * - * Buffer Sizes: + * @buffer_size * OEMCrypto shall support message sizes as described in the section * OEMCrypto_ResourceRatingTier. * OEMCrypto shall return OEMCrypto_ERROR_BUFFER_TOO_LARGE if the buffer is * larger than the supported size. * - * Threading: + * @threading * This is a "Session Function" and may be called simultaneously with session * functions for other sessions but not simultaneously with other functions * for this session. It will not be called simultaneously with initialization * or usage table functions. It is as if the CDM holds a write lock for this * session, and a read lock on the OEMCrypto system. * - * Version: + * @version * This method changed in API version 16. */ OEMCryptoResult OEMCrypto_PrepAndSignLicenseRequest( OEMCrypto_SESSION session, uint8_t* message, size_t message_length, size_t* core_message_size, uint8_t* signature, size_t* signature_length); -/* - * OEMCrypto_PrepAndSignRenewalRequest +/** + * OEMCrypto will use ODK_PrepareCoreRenewalRequest, as described in the + * document "Widevine Core Message Serialization", to prepare the core + * message. * - * Description: - * OEMCrypto will use ODK_PrepareCoreRenewalRequest, as described in the - * document "Widevine Core Message Serialization", to prepare the core - * message. + * If it returns an error, the error should be returned by OEMCrypto to the + * CDM layer. If it returns OEMCrypto_SUCCESS, then OEMCrypto computes the + * signature using the renewal mac key which was delivered in the license via + * LoadLicense. * - * If it returns an error, the error should be returned by OEMCrypto to the - * CDM layer. If it returns OEMCrypto_SUCCESS, then OEMCrypto computes the - * signature using the renewal mac key which was delivered in the license via - * LoadLicense. + * If nonce_values.api_level is 16, then OEMCrypto shall compute the + * signature of the entire message using the session's client renewal mac + * key. The entire message is the buffer starting at message with length + * message_length. * - * If nonce_values.api_level is 16, then OEMCrypto shall compute the - * signature of the entire message using the session's client renewal mac - * key. The entire message is the buffer starting at message with length - * message_length. + * If nonce_values.api_major_version is 15, then OEMCrypto shall compute the + * signature of the message body using the session's client renewal mac key. + * The message body is the buffer starting at message+core_message_size with + * length message_length - core_message_size. If the session has not had a + * license loaded, it will use the usage entries client mac key to sign the + * message body. * - * If nonce_values.api_major_version is 15, then OEMCrypto shall compute the - * signature of the message body using the session's client renewal mac key. - * The message body is the buffer starting at message+core_message_size with - * length message_length - core_message_size. If the session has not had a - * license loaded, it will use the usage entries client mac key to sign the - * message body. + * This function generates a HMAC-SHA256 signature using the mac_key[client] + * for license request signing under the license server protocol for CENC. * - * This function generates a HMAC-SHA256 signature using the mac_key[client] - * for license request signing under the license server protocol for CENC. + * The key used for signing should be the mac_key[client] that was generated + * for this session or loaded for this session by OEMCrypto_LoadKeys, + * OEMCrypto_LoadLicense, or OEMCrypto_LoadUsageEntry. * - * The key used for signing should be the mac_key[client] that was generated - * for this session or loaded for this session by OEMCrypto_LoadKeys, - * OEMCrypto_LoadLicense, or OEMCrypto_LoadUsageEntry. + * Refer to the Signing Messages Sent to a Server section above for more + * details. * - * Refer to the Signing Messages Sent to a Server section above for more - * details. + * If a usage entry has been loaded, but keys have not been loaded through + * OEMCrypto_LoadKeys, then the derived mac keys and the keys in the usage + * entry may be different. In this case, the mac keys specified in the usage + * entry should be used. * - * If a usage entry has been loaded, but keys have not been loaded through - * OEMCrypto_LoadKeys, then the derived mac keys and the keys in the usage - * entry may be different. In this case, the mac keys specified in the usage - * entry should be used. + * NOTE: if signature pointer is null and/or input signature_length is zero, + * this function returns OEMCrypto_ERROR_SHORT_BUFFER and sets output + * signature_length to the size needed to receive the output signature. * - * NOTE: if signature pointer is null and/or input signature_length is zero, - * this function returns OEMCrypto_ERROR_SHORT_BUFFER and sets output - * signature_length to the size needed to receive the output signature. + * @param[in] session: handle for the session to be used. + * @param[in,out] message: Pointer to memory for the entire message. Modified by + * OEMCrypto via 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[out] signature: pointer to memory to receive the computed signature. + * @param[in,out] signature_length: (in) length of the signature buffer, in + * bytes. (out) actual length of the signature, in bytes. * - * Parameters: - * [in] session: handle for the session to be used. - * [in/out] message: Pointer to memory for the entire message. Modified by - * OEMCrypto via 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. - * [out] signature: pointer to memory to receive the computed signature. - * [in/out] signature_length: (in) length of the signature buffer, in bytes. - * (out) actual length of the signature, in bytes. + * @retval OEMCrypto_SUCCESS success + * @retval OEMCrypto_ERROR_INVALID_SESSION + * @retval OEMCrypto_ERROR_SHORT_BUFFER if signature buffer is not large enough + * to hold the signature. + * @retval OEMCrypto_ERROR_INSUFFICIENT_RESOURCES + * @retval OEMCrypto_ERROR_UNKNOWN_FAILURE + * @retval OEMCrypto_ERROR_BUFFER_TOO_LARGE + * @retval OEMCrypto_ERROR_SESSION_LOST_STATE + * @retval OEMCrypto_ERROR_SYSTEM_INVALIDATED * - * Returns: - * OEMCrypto_SUCCESS success - * OEMCrypto_ERROR_INVALID_SESSION - * OEMCrypto_ERROR_SHORT_BUFFER if signature buffer is not large enough to - * hold the signature. - * OEMCrypto_ERROR_INSUFFICIENT_RESOURCES - * OEMCrypto_ERROR_UNKNOWN_FAILURE - * OEMCrypto_ERROR_BUFFER_TOO_LARGE - * OEMCrypto_ERROR_SESSION_LOST_STATE - * OEMCrypto_ERROR_SYSTEM_INVALIDATED - * - * Buffer Sizes: + * @buffer_size * OEMCrypto shall support message sizes as described in the section * OEMCrypto_ResourceRatingTier. * OEMCrypto shall return OEMCrypto_ERROR_BUFFER_TOO_LARGE if the buffer is * larger than the supported size. * - * Threading: + * @threading * This is a "Session Function" and may be called simultaneously with session * functions for other sessions but not simultaneously with other functions * for this session. It will not be called simultaneously with initialization * or usage table functions. It is as if the CDM holds a write lock for this * session, and a read lock on the OEMCrypto system. * - * Version: + * @version * This method changed in API version 16. */ OEMCryptoResult OEMCrypto_PrepAndSignRenewalRequest( OEMCrypto_SESSION session, uint8_t* message, size_t message_length, size_t* core_message_size, uint8_t* signature, size_t* signature_length); -/* - * OEMCrypto_PrepAndSignProvisioningRequest +/** + * OEMCrypto will use OEMCrypto_PrepAndSignProvisioningRequest, as described + * in the document "Widevine Core Message Serialization", to prepare the core + * message. If it returns an error, the error should be returned by OEMCrypto + * to the CDM layer. If it returns OEMCrypto_SUCCESS, then OEMCrypto shall + * compute the signature of the entire message. The entire message is the + * buffer starting at message with length message_length. * - * Description: - * OEMCrypto will use OEMCrypto_PrepAndSignProvisioningRequest, as described - * in the document "Widevine Core Message Serialization", to prepare the core - * message. If it returns an error, the error should be returned by OEMCrypto - * to the CDM layer. If it returns OEMCrypto_SUCCESS, then OEMCrypto shall - * compute the signature of the entire message. The entire message is the - * buffer starting at message with length message_length. + * For a device that has a keybox, i.e. Provisioning 2.0, OEMCrypto will sign + * the request with the session's derived client mac key from the previous + * call to OEMCrypto_GenerateDerivedKeys. * - * For a device that has a keybox, i.e. Provisioning 2.0, OEMCrypto will sign - * the request with the session's derived client mac key from the previous - * call to OEMCrypto_GenerateDerivedKeys. + * For a device that has an OEM Certificate, i.e. Provisioning 3.0, OEMCrypto + * will sign the request with the private key associated with the OEM + * Certificate. The key shall have been loaded by a previous call to + * OEMCrypto_LoadDRMPrivateKey. * - * For a device that has an OEM Certificate, i.e. Provisioning 3.0, OEMCrypto - * will sign the request with the private key associated with the OEM - * Certificate. The key shall have been loaded by a previous call to - * OEMCrypto_LoadDRMPrivateKey. + * Refer to the Signing Messages Sent to a Server section above for more + * details. * - * Refer to the Signing Messages Sent to a Server section above for more - * details. + * NOTE: if signature pointer is null and/or input signature_length is zero, + * this function returns OEMCrypto_ERROR_SHORT_BUFFER and sets output + * signature_length to the size needed to receive the output signature. * - * NOTE: if signature pointer is null and/or input signature_length is zero, - * this function returns OEMCrypto_ERROR_SHORT_BUFFER and sets output - * signature_length to the size needed to receive the output signature. + * @param[in] session: handle for the session to be used. + * @param[in,out] message: Pointer to memory for the entire message. Modified by + * OEMCrypto via 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[out] signature: pointer to memory to receive the computed signature. + * @param[in,out] signature_length: (in) length of the signature buffer, in + * bytes. (out) actual length of the signature, in bytes. * - * Parameters: - * [in] session: handle for the session to be used. - * [in/out] message: Pointer to memory for the entire message. Modified by - * OEMCrypto via 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. - * [out] signature: pointer to memory to receive the computed signature. - * [in/out] signature_length: (in) length of the signature buffer, in bytes. - * (out) actual length of the signature, in bytes. + * @retval OEMCrypto_SUCCESS success + * @retval OEMCrypto_ERROR_INVALID_SESSION + * @retval OEMCrypto_ERROR_SHORT_BUFFER if signature buffer is not large enough + * to hold the signature. + * @retval OEMCrypto_ERROR_INSUFFICIENT_RESOURCES + * @retval OEMCrypto_ERROR_UNKNOWN_FAILURE + * @retval OEMCrypto_ERROR_BUFFER_TOO_LARGE + * @retval OEMCrypto_ERROR_SESSION_LOST_STATE + * @retval OEMCrypto_ERROR_SYSTEM_INVALIDATED * - * Returns: - * OEMCrypto_SUCCESS success - * OEMCrypto_ERROR_INVALID_SESSION - * OEMCrypto_ERROR_SHORT_BUFFER if signature buffer is not large enough to - * hold the signature. - * OEMCrypto_ERROR_INSUFFICIENT_RESOURCES - * OEMCrypto_ERROR_UNKNOWN_FAILURE - * OEMCrypto_ERROR_BUFFER_TOO_LARGE - * OEMCrypto_ERROR_SESSION_LOST_STATE - * OEMCrypto_ERROR_SYSTEM_INVALIDATED - * - * Buffer Sizes: + * @buffer_size * OEMCrypto shall support message sizes as described in the section * OEMCrypto_ResourceRatingTier. * OEMCrypto shall return OEMCrypto_ERROR_BUFFER_TOO_LARGE if the buffer is * larger than the supported size. * - * Threading: + * @threading * This is a "Session Function" and may be called simultaneously with session * functions for other sessions but not simultaneously with other functions * for this session. It will not be called simultaneously with initialization * or usage table functions. It is as if the CDM holds a write lock for this * session, and a read lock on the OEMCrypto system. * - * Version: + * @version * This method changed in API version 16. */ OEMCryptoResult OEMCrypto_PrepAndSignProvisioningRequest( OEMCrypto_SESSION session, uint8_t* message, size_t message_length, size_t* core_message_size, uint8_t* signature, size_t* signature_length); -/* - * OEMCrypto_LoadSRM +/** + * Verify and install a new SRM file. The device shall install the new file + * only if verification passes. If verification fails, the existing SRM will + * be left in place. Verification is defined by DCP, and includes + * verification of the SRM's signature and verification that the SRM version + * number will not be decreased. See the section HDCP SRM Update above for + * more details about the SRM. This function is for devices that support HDCP + * v2.2 or higher and wish to receive 4k content. * - * Description: - * Verify and install a new SRM file. The device shall install the new file - * only if verification passes. If verification fails, the existing SRM will - * be left in place. Verification is defined by DCP, and includes - * verification of the SRM's signature and verification that the SRM version - * number will not be decreased. See the section HDCP SRM Update above for - * more details about the SRM. This function is for devices that support HDCP - * v2.2 or higher and wish to receive 4k content. + * @param[in] bufer: buffer containing the SRM + * @param[in] buffer_length: length of the SRM, in bytes. * - * Parameters: - * [in] bufer: buffer containing the SRM - * [in] buffer_length: length of the SRM, in bytes. - * - * Returns: - * OEMCrypto_SUCCESS - if the file was valid and was installed. - * OEMCrypto_ERROR_INVALID_CONTEXT - if the SRM version is too low, or the - * file is corrupted. - * OEMCrypto_ERROR_SIGNATURE_FAILURE - If the signature is invalid. - * OEMCrypto_ERROR_BUFFER_TOO_LARGE - if the buffer is too large for the + * @retval OEMCrypto_SUCCESS if the file was valid and was installed. + * @retval OEMCrypto_ERROR_INVALID_CONTEXT if the SRM version is too low, or + * the file is corrupted. + * @retval OEMCrypto_ERROR_SIGNATURE_FAILURE If the signature is invalid. + * @retval OEMCrypto_ERROR_BUFFER_TOO_LARGE if the buffer is too large for the * device. - * OEMCrypto_ERROR_NOT_IMPLEMENTED - * OEMCrypto_ERROR_SYSTEM_INVALIDATED + * @retval OEMCrypto_ERROR_NOT_IMPLEMENTED + * @retval OEMCrypto_ERROR_SYSTEM_INVALIDATED * - * Buffer Sizes: + * @buffer_size * The size of the buffer is determined by the HDCP specification. * - * Threading: + * @threading * This is an "Initialization and Termination Function" and will not be * called simultaneously with any other function, as if the CDM holds a write * lock on the OEMCrypto system. * - * Version: + * @version * This method changed in API version 13. */ OEMCryptoResult OEMCrypto_LoadSRM(const uint8_t* buffer, size_t buffer_length); -/* - * OEMCrypto_LoadKeys +/** + * Install a set of keys for performing decryption in the current session. + * This function will be deprecated and will only be used for legacy license + * from a license server that does not yet support the v16 interface. * - * Description: - * Install a set of keys for performing decryption in the current session. - * This function will be deprecated and will only be used for legacy license - * from a license server that does not yet support the v16 interface. + * The relevant fields have been extracted from the License Response protocol + * message, but the entire message and associated signature are provided so + * the message can be verified (using HMAC-SHA256 with the derived + * mac_key[server]). If the signature verification fails, ignore all other + * arguments and return OEMCrypto_ERROR_SIGNATURE_FAILURE. Otherwise, add the + * keys to the session context. * - * The relevant fields have been extracted from the License Response protocol - * message, but the entire message and associated signature are provided so - * the message can be verified (using HMAC-SHA256 with the derived - * mac_key[server]). If the signature verification fails, ignore all other - * arguments and return OEMCrypto_ERROR_SIGNATURE_FAILURE. Otherwise, add the - * keys to the session context. + * The keys will be decrypted using the current encrypt_key (AES-128-CBC) and + * the IV given in the KeyObject. Each key control block will be decrypted + * using the first 128 bits of the corresponding content key (AES-128-CBC) + * and the IV given in the KeyObject. * - * The keys will be decrypted using the current encrypt_key (AES-128-CBC) and - * the IV given in the KeyObject. Each key control block will be decrypted - * using the first 128 bits of the corresponding content key (AES-128-CBC) - * and the IV given in the KeyObject. + * If its length is not zero, enc_mac_keys will be used to create new + * mac_keys. After all keys have been decrypted and validated, the new + * mac_keys are decrypted with the current encrypt_key and the offered IV. + * The new mac_keys replaces the current mac_keys for future calls to + * OEMCrypto_RefreshKeys(). The first 256 bits of the mac_keys become the + * mac_key[server] and the following 256 bits of the mac_keys become the + * mac_key[client]. * - * If its length is not zero, enc_mac_keys will be used to create new - * mac_keys. After all keys have been decrypted and validated, the new - * mac_keys are decrypted with the current encrypt_key and the offered IV. - * The new mac_keys replaces the current mac_keys for future calls to - * OEMCrypto_RefreshKeys(). The first 256 bits of the mac_keys become the - * mac_key[server] and the following 256 bits of the mac_keys become the - * mac_key[client]. + * The mac_key and encrypt_key were generated and stored by the previous call + * to OEMCrypto_GenerateDerivedKeys() or + * OEMCrypto_DeriveKeysFromSessionKey(). The nonce was generated and stored + * in the session's nonce_values by the previous call to + * OEMCrypto_GenerateNonce(). * - * The mac_key and encrypt_key were generated and stored by the previous call - * to OEMCrypto_GenerateDerivedKeys() or - * OEMCrypto_DeriveKeysFromSessionKey(). The nonce was generated and stored - * in the session's nonce_values by the previous call to - * OEMCrypto_GenerateNonce(). + * This session's elapsed time clock is started at 0. The clock will be used + * in OEMCrypto_DecryptCENC(). * - * This session's elapsed time clock is started at 0. The clock will be used - * in OEMCrypto_DecryptCENC(). + * NOTE: The calling software must have previously established the mac_keys + * and encrypt_key with a call to OEMCrypto_DeriveKeysFromSessionKey(). * - * NOTE: The calling software must have previously established the mac_keys - * and encrypt_key with a call to OEMCrypto_DeriveKeysFromSessionKey(). + * Refer to the Verification of Messages from a Server section above for more + * details. * - * Refer to the Verification of Messages from a Server section above for more - * details. + * If the parameter license_type is OEMCrypto_ContentLicense, then the fields + * key_id and key_data in an OEMCrypto_KeyObject are loaded in to the + * content_key_id and content_key_data fields of the key table entry. In this + * case, entitlement key ids and entitlement key data is left blank. * - * If the parameter license_type is OEMCrypto_ContentLicense, then the fields - * key_id and key_data in an OEMCrypto_KeyObject are loaded in to the - * content_key_id and content_key_data fields of the key table entry. In this - * case, entitlement key ids and entitlement key data is left blank. + * If the parameter license_type is OEMCrypto_EntitlementLicense, then the + * fields key_id and key_data in an OEMCrypto_KeyObject are loaded in to the + * entitlement_key_id and entitlement_key_data fields of the key table entry. + * In this case, content key ids and content key data will be loaded later + * with a call to OEMCrypto_LoadEntitledContentKeys(). * - * If the parameter license_type is OEMCrypto_EntitlementLicense, then the - * fields key_id and key_data in an OEMCrypto_KeyObject are loaded in to the - * entitlement_key_id and entitlement_key_data fields of the key table entry. - * In this case, content key ids and content key data will be loaded later - * with a call to OEMCrypto_LoadEntitledContentKeys(). + * OEMCrypto may assume that the key_id_length is at most 16. However, + * OEMCrypto shall correctly handle key id lengths from 1 to 16 bytes. * - * OEMCrypto may assume that the key_id_length is at most 16. However, - * OEMCrypto shall correctly handle key id lengths from 1 to 16 bytes. + * OEMCrypto shall handle at least 20 keys per session. This allows a single + * license to contain separate keys for 3 key rotations (previous interval, + * current interval, next interval) times 4 content keys (audio, SD, HD, UHD) + * plus up to 8 keys for watermarks. * - * OEMCrypto shall handle at least 20 keys per session. This allows a single - * license to contain separate keys for 3 key rotations (previous interval, - * current interval, next interval) times 4 content keys (audio, SD, HD, UHD) - * plus up to 8 keys for watermarks. + * After a call to OEMCrypto_LoadKeys, oemcrypto should clear the encrypt_key + * for the session. * - * After a call to OEMCrypto_LoadKeys, oemcrypto should clear the encrypt_key - * for the session. + * @verification + * The following checks should be performed. If any check fails, an error is + * returned, and none of the keys are loaded. + * 1. The signature of the message shall be computed, and the API shall + * verify the computed signature matches the signature passed in. If + * not, return OEMCrypto_ERROR_SIGNATURE_FAILURE. The signature + * verification shall use a constant-time algorithm (a signature + * mismatch will always take the same time as a successful comparison). + * 2. If there already is a license loaded into this session, return + * OEMCrypto_ERROR_LICENSE_RELOAD. + * 3. The enc_mac_keys substring must either have zero length, or satisfy + * the range check. I.e. (offset < message_length) && (offset + length + * <= message_length) && (offset <= offset + length), and offset + length + * does not cause an integer overflow. If it does not have zero length, + * then enc_mac_keys_iv must not have zero length, and must also satisfy + * the range check. If not, return OEMCrypto_ERROR_INVALID_CONTEXT. If + * the length is zero, then OEMCrypto may assume that the offset is also + * zero. + * 4. The API shall verify that each substring in each KeyObject points to + * a location in the message. I.e. (offset < message_length) && + * (offset + length <= message_length) && (offset <= offset + length), + * and offset + length does not cause an integer overflow, for each of + * key_id, key_data_iv, key_data, key_control_iv, key_control. If not, + * return OEMCrypto_ERROR_INVALID_CONTEXT. + * 5. Each key's control block, after decryption, shall have a valid + * verification field. If not, return OEMCrypto_ERROR_INVALID_CONTEXT. + * 6. If any key control block has the Nonce_Enabled bit set, that key's + * Nonce field shall match a nonce in the cache. If not, return + * OEMCrypto_ERROR_INVALID_NONCE. If there is a match, remove that + * nonce from the cache. Note that all the key control blocks in a + * particular call shall have the same nonce value. + * 7. If any key control block has the Require_AntiRollback_Hardware bit + * set, and the device does not protect the usage table from rollback, + * then do not load the keys and return OEMCrypto_ERROR_UNKNOWN_FAILURE. + * 8. If the key control block has a nonzero Replay_Control, then the + * verification described below is also performed. + * 9. If the key control block has the bit SRMVersionRequired is set, then + * the verification described below is also performed. If the SRM + * requirement is not met, then the key control block's HDCP_Version + * will be changed to 0xF - local display only. + * 10. If key_array_length == 0, then return + * OEMCrypto_ERROR_INVALID_CONTEXT. + * 11. If this session is associated with a usage table entry, and that + * entry is marked as "inactive" (either kInactiveUsed or + * kInactiveUnused), then the keys are not loaded, and the error + * OEMCrypto_ERROR_LICENSE_INACTIVE is returned. + * 12. The data in enc_mac_keys_iv is not identical to the 16 bytes before + * enc_mac_keys. If it is, return OEMCrypto_ERROR_INVALID_CONTEXT. + * Usage Table and Provider Session Token (pst) + * If a key control block has a nonzero value for Replay_Control, then all + * keys in this license will have the same value for Replay_Control. In this + * case, the following additional checks are performed. + * - The substring pst must have nonzero length and must satisfy the range + * check described above. If not, return + * OEMCrypto_ERROR_INVALID_CONTEXT. + * - The session must be associated with a usage table entry, either + * created via OEMCrypto_CreateNewUsageEntry or loaded via + * OEMCrypto_LoadUsageEntry. + * - If Replay_Control is 1 = Nonce_Required, then OEMCrypto will perform a + * nonce check as described above. OEMCrypto will verify that the + * usage entry is newly created with OEMCrypto_CreateNewUsageEntry. If + * an existing entry was reloaded, an error + * OEMCrypto_ERROR_INVALID_CONTEXT is returned and no keys are loaded. + * OEMCrypto will then copy the pst and the mac keys to the usage entry, + * and set the status to Unused. This Replay_Control prevents the + * license from being loaded more than once, and will be used for online + * streaming. + * - If Replay_Control is 2 = "Require existing Session Usage table entry + * or Nonce", then OEMCrypto will behave slightly differently on the + * first call to LoadKeys for this license. + * * If the usage entry was created with OEMCrypto_CreateNewUsageEntry + * for this session, then OEMCrypto will verify the nonce for each + * key. OEMCrypto will copy the pst and mac keys to the usage + * entry. The license received time of the entry will be updated + * to the current time, and the status will be set to Unused. + * * If the usage entry was loaded with OEMCrypto_LoadUsageEntry for + * this session, then OEMCrypto will NOT verify the nonce for each + * key. Instead, it will verify that the pst passed in matches + * that in the entry. Also, the entry's mac keys will be verified + * against the current session's mac keys. This allows an offline + * license to be reloaded but maintain continuity of the playback + * times from one session to the next. + * * If the nonce is not valid and a usage entry was not loaded, the + * return error is OEMCrypto_ERROR_INVALID_NONCE. + * * If the loaded usage entry has a pst that does not match, + * OEMCrypto returns the error OEMCrypto_ERROR_WRONG_PST. + * * If the loaded usage entry has mac keys that do not match the + * license, OEMCrypto returns the error OEMCrypto_ERROR_WRONG_KEYS. + * Note: If LoadKeys updates the mac keys, then the new updated mac keys will + * be used with the Usage Entry -- i.e. the new keys are stored in the + * usage table when creating a new entry, or the new keys are verified + * against those in the usage table if there is an existing entry. If + * LoadKeys does not update the mac keys, the existing session mac keys are + * used. * - * Verification: - * The following checks should be performed. If any check fails, an error is - * returned, and none of the keys are loaded. - * 1. The signature of the message shall be computed, and the API shall - * verify the computed signature matches the signature passed in. If - * not, return OEMCrypto_ERROR_SIGNATURE_FAILURE. The signature - * verification shall use a constant-time algorithm (a signature - * mismatch will always take the same time as a successful comparison). - * 2. If there already is a license loaded into this session, return - * OEMCrypto_ERROR_LICENSE_RELOAD. - * 3. The enc_mac_keys substring must either have zero length, or satisfy - * the range check. I.e. (offset < message_length) && (offset + length - * < message_length) && (offset < offset + length),and offset + length - * does not cause an integer overflow. If it does not have zero length, - * then enc_mac_keys_iv must not have zero length, and must also satisfy - * the range check. If not, return OEMCrypto_ERROR_INVALID_CONTEXT. If - * the length is zero, then OEMCrypto may assume that the offset is also - * zero. - * 4. The API shall verify that each substring in each KeyObject points to - * a location in the message. I.e. (offset < message_length) && - * (offset + length < message_length) && (offset < offset + length) and - * offset + length does not cause an integer overflow, for each of - * key_id, key_data_iv, key_data, key_control_iv, key_control. If not, - * return OEMCrypto_ERROR_INVALID_CONTEXT. - * 5. Each key's control block, after decryption, shall have a valid - * verification field. If not, return OEMCrypto_ERROR_INVALID_CONTEXT. - * 6. If any key control block has the Nonce_Enabled bit set, that key's - * Nonce field shall match a nonce in the cache. If not, return - * OEMCrypto_ERROR_INVALID_NONCE. If there is a match, remove that - * nonce from the cache. Note that all the key control blocks in a - * particular call shall have the same nonce value. - * 7. If any key control block has the Require_AntiRollback_Hardware bit - * set, and the device does not protect the usage table from rollback, - * then do not load the keys and return OEMCrypto_ERROR_UNKNOWN_FAILURE. - * 8. If the key control block has a nonzero Replay_Control, then the - * verification described below is also performed. - * 9. If the key control block has the bit SRMVersionRequired is set, then - * the verification described below is also performed. If the SRM - * requirement is not met, then the key control block's HDCP_Version - * will be changed to 0xF - local display only. - * 10. If key_array_length == 0, then return - * OEMCrypto_ERROR_INVALID_CONTEXT. - * 11. If this session is associated with a usage table entry, and that - * entry is marked as "inactive" (either kInactiveUsed or - * kInactiveUnused), then the keys are not loaded, and the error - * OEMCrypto_ERROR_LICENSE_INACTIVE is returned. - * 12. The data in enc_mac_keys_iv is not identical to the 16 bytes before - * enc_mac_keys. If it is, return OEMCrypto_ERROR_INVALID_CONTEXT. - * Usage Table and Provider Session Token (pst) - * If a key control block has a nonzero value for Replay_Control, then all - * keys in this license will have the same value for Replay_Control. In this - * case, the following additional checks are performed. - * - The substring pst must have nonzero length and must satisfy the range - * check described above. If not, return - * OEMCrypto_ERROR_INVALID_CONTEXT. - * - The session must be associated with a usage table entry, either - * created via OEMCrypto_CreateNewUsageEntry or loaded via - * OEMCrypto_LoadUsageEntry. - * - If Replay_Control is 1 = Nonce_Required, then OEMCrypto will perform a - * nonce check as described above. OEMCrypto will verify that the - * usage entry is newly created with OEMCrypto_CreateNewUsageEntry. If - * an existing entry was reloaded, an error - * OEMCrypto_ERROR_INVALID_CONTEXT is returned and no keys are loaded. - * OEMCrypto will then copy the pst and the mac keys to the usage entry, - * and set the status to Unused. This Replay_Control prevents the - * license from being loaded more than once, and will be used for online - * streaming. - * - If Replay_Control is 2 = "Require existing Session Usage table entry - * or Nonce", then OEMCrypto will behave slightly differently on the - * first call to LoadKeys for this license. - * * If the usage entry was created with OEMCrypto_CreateNewUsageEntry - * for this session, then OEMCrypto will verify the nonce for each - * key. OEMCrypto will copy the pst and mac keys to the usage - * entry. The license received time of the entry will be updated - * to the current time, and the status will be set to Unused. - * * If the usage entry was loaded with OEMCrypto_LoadUsageEntry for - * this session, then OEMCrypto will NOT verify the nonce for each - * key. Instead, it will verify that the pst passed in matches - * that in the entry. Also, the entry's mac keys will be verified - * against the current session's mac keys. This allows an offline - * license to be reloaded but maintain continuity of the playback - * times from one session to the next. - * * If the nonce is not valid and a usage entry was not loaded, the - * return error is OEMCrypto_ERROR_INVALID_NONCE. - * * If the loaded usage entry has a pst that does not match, - * OEMCrypto returns the error OEMCrypto_ERROR_WRONG_PST. - * * If the loaded usage entry has mac keys that do not match the - * license, OEMCrypto returns the error OEMCrypto_ERROR_WRONG_KEYS. - * Note: If LoadKeys updates the mac keys, then the new updated mac keys will - * be used with the Usage Entry -- i.e. the new keys are stored in the - * usage table when creating a new entry, or the new keys are verified - * against those in the usage table if there is an existing entry. If - * LoadKeys does not update the mac keys, the existing session mac keys are - * used. + * Sessions that are associated with an entry will need to be able to update + * and verify the status of the entry, and the time stamps in the entry. * - * Sessions that are associated with an entry will need to be able to update - * and verify the status of the entry, and the time stamps in the entry. + * Devices that do not support the Usage Table will return + * OEMCrypto_ERROR_INVALID_CONTEXT if the Replay_Control is nonzero. * - * Devices that do not support the Usage Table will return - * OEMCrypto_ERROR_INVALID_CONTEXT if the Replay_Control is nonzero. + * Timer Update + * After verification, the session's clock and timer values are updated by + * calling the function ODK_InitializeV15Values as described in the document + * "Widevine Core Message Serialization". * - * Timer Update - * After verification, the session's clock and timer values are updated by - * calling the function ODK_InitializeV15Values as described in the document - * "Widevine Core Message Serialization". + * SRM Restriction Data * - * SRM Restriction Data + * If any key control block has the flag SRMVersionRequired set, then the + * following verification is also performed. * - * If any key control block has the flag SRMVersionRequired set, then the - * following verification is also performed. + * 1. The substring srm_restriction_data must have nonzero length and must + * satisfy the range check described above. If not, return + * OEMCrypto_ERROR_INVALID_CONTEXT. + * 2. The first 8 bytes of srm_restriction_data must match the string + * "HDCPDATA". If not, return OEMCrypto_ERROR_INVALID_CONTEXT. + * 3. The next 4 bytes of srm_restriction_data will be converted from + * network byte order. If the current SRM installed on the device has a + * version number less than this, then the SRM requirement is not met. + * If the device does not support SRM files, or OEMCrypto cannot + * determine the current SRM version number, then the SRM requirement is + * not met. + * Note: if the current SRM version requirement is not met, LoadKeys will + * still succeed and the keys will be loaded. However, those keys with the + * SRMVersionRequired bit set will have their HDCP_Version increased to 0xF - + * local display only. Any future call to SelectKey for these keys while + * there is an external display will return OEMCrypto_ERROR_INSUFFICIENT_HDCP + * at that time. * - * 1. The substring srm_restriction_data must have nonzero length and must - * satisfy the range check described above. If not, return - * OEMCrypto_ERROR_INVALID_CONTEXT. - * 2. The first 8 bytes of srm_restriction_data must match the string - * "HDCPDATA". If not, return OEMCrypto_ERROR_INVALID_CONTEXT. - * 3. The next 4 bytes of srm_restriction_data will be converted from - * network byte order. If the current SRM installed on the device has a - * version number less than this, then the SRM requirement is not met. - * If the device does not support SRM files, or OEMCrypto cannot - * determine the current SRM version number, then the SRM requirement is - * not met. - * Note: if the current SRM version requirement is not met, LoadKeys will - * still succeed and the keys will be loaded. However, those keys with the - * SRMVersionRequired bit set will have their HDCP_Version increased to 0xF - - * local display only. Any future call to SelectKey for these keys while - * there is an external display will return OEMCrypto_ERROR_INSUFFICIENT_HDCP - * at that time. + * @param[in] session: crypto session identifier. + * @param[in] message: pointer to memory containing message to be verified. + * @param[in] message_length: length of the message, in bytes. + * @param[in] signature: pointer to memory containing the signature. + * @param[in] signature_length: length of the signature, in bytes. + * @param[in] enc_mac_keys_iv: IV for decrypting new mac_key. Size is 128 bits. + * @param[in] enc_mac_keys: encrypted mac_keys for generating new mac_keys. + * Size is 512 bits. + * @param[in] key_array_length: number of keys present. + * @param[in] key_array: set of keys to be installed. + * @param[in] pst: the Provider Session Token. + * @param[in] srm_restriction_data: optional data specifying the minimum SRM + * version. + * @param[in] license_type: specifies if the license contains content keys or + * entitlement keys. * - * Parameters: - * [in] session: crypto session identifier. - * [in] message: pointer to memory containing message to be verified. - * [in] message_length: length of the message, in bytes. - * [in] signature: pointer to memory containing the signature. - * [in] signature_length: length of the signature, in bytes. - * [in] enc_mac_keys_iv: IV for decrypting new mac_key. Size is 128 bits. - * [in] enc_mac_keys: encrypted mac_keys for generating new mac_keys. Size is - * 512 bits. - * [in] key_array_length: number of keys present. - * [in] key_array: set of keys to be installed. - * [in] pst: the Provider Session Token. - * [in] srm_restriction_data: optional data specifying the minimum SRM - * version. - * [in] license_type: specifies if the license contains content keys or - * entitlement keys. + * @retval OEMCrypto_SUCCESS success + * @retval OEMCrypto_ERROR_NO_DEVICE_KEY + * @retval OEMCrypto_ERROR_INVALID_SESSION + * @retval OEMCrypto_ERROR_UNKNOWN_FAILURE + * @retval OEMCrypto_ERROR_INVALID_CONTEXT + * @retval OEMCrypto_ERROR_SIGNATURE_FAILURE + * @retval OEMCrypto_ERROR_INVALID_NONCE + * @retval OEMCrypto_ERROR_TOO_MANY_KEYS + * @retval OEMCrypto_ERROR_NOT_IMPLEMENTED + * @retval OEMCrypto_ERROR_BUFFER_TOO_LARGE + * @retval OEMCrypto_ERROR_SESSION_LOST_STATE + * @retval OEMCrypto_ERROR_SYSTEM_INVALIDATED + * @retval OEMCrypto_ERROR_LICENSE_RELOAD * - * Returns: - * OEMCrypto_SUCCESS success - * OEMCrypto_ERROR_NO_DEVICE_KEY - * OEMCrypto_ERROR_INVALID_SESSION - * OEMCrypto_ERROR_UNKNOWN_FAILURE - * OEMCrypto_ERROR_INVALID_CONTEXT - * OEMCrypto_ERROR_SIGNATURE_FAILURE - * OEMCrypto_ERROR_INVALID_NONCE - * OEMCrypto_ERROR_TOO_MANY_KEYS - * OEMCrypto_ERROR_NOT_IMPLEMENTED - * OEMCrypto_ERROR_BUFFER_TOO_LARGE - * OEMCrypto_ERROR_SESSION_LOST_STATE - * OEMCrypto_ERROR_SYSTEM_INVALIDATED - * OEMCrypto_ERROR_LICENSE_RELOAD - * - * Buffer Sizes: + * @buffer_size * OEMCrypto shall support message sizes as described in the section * OEMCrypto_ResourceRatingTier. * OEMCrypto shall return OEMCrypto_ERROR_BUFFER_TOO_LARGE if the buffer is * larger than the supported size. * - * Threading: + * @threading * This is a "Session Function" and may be called simultaneously with session * functions for other sessions but not simultaneously with other functions * for this session. It will not be called simultaneously with initialization * or usage table functions. It is as if the CDM holds a write lock for this * session, and a read lock on the OEMCrypto system. * - * Version: + * @version * This method changed in API version 16. */ OEMCryptoResult OEMCrypto_LoadKeys( @@ -1387,251 +1454,248 @@ OEMCryptoResult OEMCrypto_LoadKeys( OEMCrypto_Substring pst, OEMCrypto_Substring srm_restriction_data, OEMCrypto_LicenseType license_type); -/* - * OEMCrypto_LoadLicense +/** + * Install a set of keys for performing decryption in the current session. * - * Description: - * Install a set of keys for performing decryption in the current session. + * First, OEMCrypto shall verify the signature of the message using + * HMAC-SHA256 with the derived mac_key[server]. The signature verification + * shall use a constant-time algorithm (a signature mismatch will always take + * the same time as a successful comparison). The signature is over the + * entire message buffer starting at message with length message_length. If + * the signature verification fails, ignore all other arguments and return + * OEMCrypto_ERROR_SIGNATURE_FAILURE. Otherwise, add the keys to the session + * context. * - * First, OEMCrypto shall verify the signature of the message using - * HMAC-SHA256 with the derived mac_key[server]. The signature verification - * shall use a constant-time algorithm (a signature mismatch will always take - * the same time as a successful comparison). The signature is over the - * entire message buffer starting at message with length message_length. If - * the signature verification fails, ignore all other arguments and return - * OEMCrypto_ERROR_SIGNATURE_FAILURE. Otherwise, add the keys to the session - * context. + * NOTE: The calling software must have previously established the mac_keys + * and encrypt_key with a call to OEMCrypto_DeriveKeysFromSessionKey(). * - * NOTE: The calling software must have previously established the mac_keys - * and encrypt_key with a call to OEMCrypto_DeriveKeysFromSessionKey(). + * Refer to the Verification of Messages from a Server section above for more + * details. * - * Refer to the Verification of Messages from a Server section above for more - * details. + * The function ODK_ParseLicense is called to parse the message. If it + * returns an error, OEMCrypto shall return that error to the CDM layer. The + * function ODK_ParseLicense is described in the document "Widevine Core + * Message Serialization". * - * The function ODK_ParseLicense is called to parse the message. If it - * returns an error, OEMCrypto shall return that error to the CDM layer. The - * function ODK_ParseLicense is described in the document "Widevine Core - * Message Serialization". + * Below, all fields are found in the struct ODK_ParsedLicense parsed_license + * returned by ODK_ParseLicense. * - * Below, all fields are found in the struct ODK_ParsedLicense parsed_license - * returned by ODK_ParseLicense. + * The keys will be decrypted using the current encrypt_key (AES-128-CBC) and + * the IV given in the KeyObject. Each key control block will be decrypted + * using the first 128 bits of the corresponding content key (AES-128-CBC) + * and the IV given in the KeyObject. * - * The keys will be decrypted using the current encrypt_key (AES-128-CBC) and - * the IV given in the KeyObject. Each key control block will be decrypted - * using the first 128 bits of the corresponding content key (AES-128-CBC) - * and the IV given in the KeyObject. + * If its length is not zero, enc_mac_keys will be used to create new + * mac_keys. After all keys have been decrypted and validated, the new + * mac_keys are decrypted with the current encrypt_key and the offered IV. + * The new mac_keys replaces the current mac_keys for future signing renewal + * requests and loading renewal responses. The first 256 bits of the mac_keys + * become the mac_key[server] and the following 256 bits of the mac_keys + * become the mac_key[client]. If enc_mac_keys is null, then there will not + * be a call to OEMCrypto_LoadRenewal for this session and the current + * mac_keys may be deleted. * - * If its length is not zero, enc_mac_keys will be used to create new - * mac_keys. After all keys have been decrypted and validated, the new - * mac_keys are decrypted with the current encrypt_key and the offered IV. - * The new mac_keys replaces the current mac_keys for future signing renewal - * requests and loading renewal responses. The first 256 bits of the mac_keys - * become the mac_key[server] and the following 256 bits of the mac_keys - * become the mac_key[client]. If enc_mac_keys is null, then there will not - * be a call to OEMCrypto_LoadRenewal for this session and the current - * mac_keys may be deleted. + * If the field license_type is OEMCrypto_ContentLicense, then the fields + * key_id and key_data in an OEMCrypto_KeyObject are loaded in to the + * content_key_id and content_key_data fields of the key table entry. In this + * case, entitlement key ids and entitlement key data is left blank. * - * If the field license_type is OEMCrypto_ContentLicense, then the fields - * key_id and key_data in an OEMCrypto_KeyObject are loaded in to the - * content_key_id and content_key_data fields of the key table entry. In this - * case, entitlement key ids and entitlement key data is left blank. + * If the field license_type is OEMCrypto_EntitlementLicense, then the + * fields key_id and key_data in an OEMCrypto_KeyObject are loaded in to the + * entitlement_key_id and entitlement_key_data fields of the key table entry. + * In this case, content key ids and content key data will be loaded later + * with a call to OEMCrypto_LoadEntitledContentKeys(). * - * If the field license_type is OEMCrypto_EntitlementLicense, then the - * fields key_id and key_data in an OEMCrypto_KeyObject are loaded in to the - * entitlement_key_id and entitlement_key_data fields of the key table entry. - * In this case, content key ids and content key data will be loaded later - * with a call to OEMCrypto_LoadEntitledContentKeys(). + * OEMCrypto may assume that the key_id_length is at most 16. However, + * OEMCrypto shall correctly handle key id lengths from 1 to 16 bytes. * - * OEMCrypto may assume that the key_id_length is at most 16. However, - * OEMCrypto shall correctly handle key id lengths from 1 to 16 bytes. + * OEMCrypto shall handle multiple keys, as described in the section on + * Resource Rating Tiers in this document. * - * OEMCrypto shall handle multiple keys, as described in the section on - * Resource Rating Tiers in this document. + * After a call to OEMCrypto_LoadLicense, oemcrypto should clear the + * encrypt_key for the session. * - * After a call to OEMCrypto_LoadLicense, oemcrypto should clear the - * encrypt_key for the session. + * @verification + * The following checks should be performed. If any check fails, an error is + * returned, and none of the keys are loaded. + * 13. The signature of the message shall be computed, and the API shall + * verify the computed signature matches the signature passed in. If + * not, return OEMCrypto_ERROR_SIGNATURE_FAILURE. The signature + * verification shall use a constant-time algorithm (a signature + * mismatch will always take the same time as a successful comparison). + * 14. If there already is a license loaded into this session, return + * OEMCrypto_ERROR_LICENSE_RELOAD. + * 15. The enc_mac_keys substring must either have zero length, or satisfy + * the range check. I.e. (offset < message_length) && (offset + length + * <= message_length) && (offset <= offset + length), and offset + length + * does not cause an integer overflow. If it does not have zero length, + * then enc_mac_keys_iv must not have zero length, and must also satisfy + * the range check. If not, return OEMCrypto_ERROR_INVALID_CONTEXT. If + * the length is zero, then OEMCrypto may assume that the offset is also + * zero. + * 16. The API shall verify that each substring in each KeyObject points to + * a location in the message. I.e. (offset < message_length) && + * (offset + length <= message_length) && (offset <= offset + length), + * and offset + length does not cause an integer overflow, for each of + * key_id, key_data_iv, key_data, key_control_iv, key_control. If not, + * return OEMCrypto_ERROR_INVALID_CONTEXT. + * 17. Each key's control block, after decryption, shall have a valid + * verification field. If not, return OEMCrypto_ERROR_INVALID_CONTEXT. + * 18. If any key control block has the Nonce_Enabled bit set, that key's + * Nonce field shall match a nonce in the cache. If not, return + * OEMCrypto_ERROR_INVALID_NONCE. If there is a match, remove that + * nonce from the cache. Note that all the key control blocks in a + * particular call shall have the same nonce value. + * 19. If any key control block has the Require_AntiRollback_Hardware bit + * set, and the device does not protect the usage table from rollback, + * then do not load the keys and return OEMCrypto_ERROR_UNKNOWN_FAILURE. + * 20. If the key control block has a nonzero Replay_Control, then the + * verification described below is also performed. + * 21. If the key control block has the bit SRMVersionRequired is set, then + * the verification described below is also performed. If the SRM + * requirement is not met, then the key control block's HDCP_Version + * will be changed to 0xF - local display only. + * 22. If key_array_length == 0, then return + * OEMCrypto_ERROR_INVALID_CONTEXT. + * 23. If this session is associated with a usage table entry, and that + * entry is marked as "inactive" (either kInactiveUsed or + * kInactiveUnused), then the keys are not loaded, and the error + * OEMCrypto_ERROR_LICENSE_INACTIVE is returned. + * 24. The data in enc_mac_keys_iv is not identical to the 16 bytes before + * enc_mac_keys. If it is, return OEMCrypto_ERROR_INVALID_CONTEXT. * - * Verification: - * The following checks should be performed. If any check fails, an error is - * returned, and none of the keys are loaded. - * 13. The signature of the message shall be computed, and the API shall - * verify the computed signature matches the signature passed in. If - * not, return OEMCrypto_ERROR_SIGNATURE_FAILURE. The signature - * verification shall use a constant-time algorithm (a signature - * mismatch will always take the same time as a successful comparison). - * 14. If there already is a license loaded into this session, return - * OEMCrypto_ERROR_LICENSE_RELOAD. - * 15. The enc_mac_keys substring must either have zero length, or satisfy - * the range check. I.e. (offset < message_length) && (offset + length - * < message_length) && (offset < offset + length),and offset + length - * does not cause an integer overflow. If it does not have zero length, - * then enc_mac_keys_iv must not have zero length, and must also satisfy - * the range check. If not, return OEMCrypto_ERROR_INVALID_CONTEXT. If - * the length is zero, then OEMCrypto may assume that the offset is also - * zero. - * 16. The API shall verify that each substring in each KeyObject points to - * a location in the message. I.e. (offset < message_length) && - * (offset + length < message_length) && (offset < offset + length) and - * offset + length does not cause an integer overflow, for each of - * key_id, key_data_iv, key_data, key_control_iv, key_control. If not, - * return OEMCrypto_ERROR_INVALID_CONTEXT. - * 17. Each key's control block, after decryption, shall have a valid - * verification field. If not, return OEMCrypto_ERROR_INVALID_CONTEXT. - * 18. If any key control block has the Nonce_Enabled bit set, that key's - * Nonce field shall match a nonce in the cache. If not, return - * OEMCrypto_ERROR_INVALID_NONCE. If there is a match, remove that - * nonce from the cache. Note that all the key control blocks in a - * particular call shall have the same nonce value. - * 19. If any key control block has the Require_AntiRollback_Hardware bit - * set, and the device does not protect the usage table from rollback, - * then do not load the keys and return OEMCrypto_ERROR_UNKNOWN_FAILURE. - * 20. If the key control block has a nonzero Replay_Control, then the - * verification described below is also performed. - * 21. If the key control block has the bit SRMVersionRequired is set, then - * the verification described below is also performed. If the SRM - * requirement is not met, then the key control block's HDCP_Version - * will be changed to 0xF - local display only. - * 22. If key_array_length == 0, then return - * OEMCrypto_ERROR_INVALID_CONTEXT. - * 23. If this session is associated with a usage table entry, and that - * entry is marked as "inactive" (either kInactiveUsed or - * kInactiveUnused), then the keys are not loaded, and the error - * OEMCrypto_ERROR_LICENSE_INACTIVE is returned. - * 24. The data in enc_mac_keys_iv is not identical to the 16 bytes before - * enc_mac_keys. If it is, return OEMCrypto_ERROR_INVALID_CONTEXT. + * Usage Table and Provider Session Token (pst) + * The function ODK_ParseLicense takes several parameters that may need more + * explanation. + * The parameter usage_entry_present shall be set to true if a usage entry + * was created or loaded for this session. This parameter is used by + * ODK_ParseLicense for usage entry verification. + * The parameter initial_license_load shall be false if the usage entry was + * loaded. If there is no usage entry or if the usage entry was created with + * OEMCrypto_CreateNewUsageEntry, then initial_license_load shall be true. + * If a usage entry is present, then it shall be verified after the call to + * ODK_ParseLicense. + * If initial_license_load is true: + * 1. OEMCrypto shall copy the PST from the parsed license to the usage + * entry. + * 2. OEMCrypto shall verify that the server and client mac keys were + * updated by the license. The server and client mac keys shall be + * copied to the usage entry. + * If initial_license_load is false: + * 1. OEMCrypto shall verify the PST from the parsed license matches that + * in the usage entry. If not, then an error OEMCrypto_ERROR_WRONG_PST + * is returned. + * 2. OEMCrypto shall verify that the server and client mac keys were + * updated by the license. OEMCrypto shall verify that the server and + * client mac keys match those in the usage entry. If not the error + * OEMCrypto_ERROR_WRONG_KEYS is returned. + * If a key control block has a nonzero value for Replay_Control, then all + * keys in this license will have the same value for Replay_Control. In this + * case, the following additional checks are performed. + * - The substring pst must have nonzero length and must satisfy the range + * check described above. If not, return + * OEMCrypto_ERROR_INVALID_CONTEXT. + * - The session must be associated with a usage table entry, either + * created via OEMCrypto_CreateNewUsageEntry or loaded via + * OEMCrypto_LoadUsageEntry. + * - If Replay_Control is 1 = Nonce_Required, then OEMCrypto will perform a + * nonce check as described above. OEMCrypto will verify that the + * usage entry is newly created with OEMCrypto_CreateNewUsageEntry. If + * an existing entry was reloaded, an error + * OEMCrypto_ERROR_INVALID_CONTEXT is returned and no keys are loaded. + * OEMCrypto will then copy the pst and the mac keys to the usage entry, + * and set the status to Unused. The license received time of the entry + * will be updated to the current time, and the status will be set to + * Unused. This Replay_Control prevents the license from being loaded + * more than once, and will be used for online streaming. + * - If Replay_Control is 2 = "Require existing Session Usage table entry + * or Nonce", then OEMCrypto will behave slightly differently on the + * first call to LoadKeys for this license. + * * If the usage entry was created with OEMCrypto_CreateNewUsageEntry + * for this session, then OEMCrypto will verify the nonce for each + * key. OEMCrypto will copy the pst and mac keys to the usage + * entry. The license received time of the entry will be updated + * to the current time, and the status will be set to Unused. + * * If the usage entry was loaded with OEMCrypto_LoadUsageEntry for + * this session, then OEMCrypto will NOT verify the nonce for each + * key. Instead, it will verify that the pst passed in matches + * that in the entry. Also, the entry's mac keys will be verified + * against the current session's mac keys. This allows an offline + * license to be reloaded but maintain continuity of the playback + * times from one session to the next. + * * If the nonce is not valid and a usage entry was not loaded, the + * return error is OEMCrypto_ERROR_INVALID_NONCE. + * * If the loaded usage entry has a pst that does not match, + * OEMCrypto returns the error OEMCrypto_ERROR_WRONG_PST. + * * If the loaded usage entry has mac keys that do not match the + * license, OEMCrypto returns the error OEMCrypto_ERROR_WRONG_KEYS. + * Note: If LoadKeys updates the mac keys, then the new updated mac keys will + * be used with the Usage Entry -- i.e. the new keys are stored in the + * usage table when creating a new entry, or the new keys are verified + * against those in the usage table if there is an existing entry. If + * LoadKeys does not update the mac keys, the existing session mac keys are + * used. + * Sessions that are associated with an entry will need to be able to update + * and verify the status of the entry, and the time stamps in the entry. + * Devices that do not support the Usage Table will return + * OEMCrypto_ERROR_INVALID_CONTEXT if the Replay_Control is nonzero. + * SRM Restriction Data + * If any key control block has the flag SRMVersionRequired set, then the + * following verification is also performed. + * 4. The substring srm_restriction_data must have nonzero length and must + * satisfy the range check described above. If not, return + * OEMCrypto_ERROR_INVALID_CONTEXT. + * 5. The first 8 bytes of srm_restriction_data must match the string + * "HDCPDATA". If not, return OEMCrypto_ERROR_INVALID_CONTEXT. + * 6. The next 4 bytes of srm_restriction_data will be converted from + * network byte order. If the current SRM installed on the device has a + * version number less than this, then the SRM requirement is not met. + * If the device does not support SRM files, or OEMCrypto cannot + * determine the current SRM version number, then the SRM requirement is + * not met. + * Note: if the current SRM version requirement is not met, LoadKeys will + * still succeed and the keys will be loaded. However, those keys with the + * SRMVersionRequired bit set will have their HDCP_Version increased to 0xF - + * local display only. Any future call to SelectKey for these keys while + * there is an external display will return OEMCrypto_ERROR_INSUFFICIENT_HDCP + * at that time. * - * Usage Table and Provider Session Token (pst) - * The function ODK_ParseLicense takes several parameters that may need more - * explanation. - * The parameter usage_entry_present shall be set to true if a usage entry - * was created or loaded for this session. This parameter is used by - * ODK_ParseLicense for usage entry verification. - * The parameter initial_license_load shall be false if the usage entry was - * loaded. If there is no usage entry or if the usage entry was created with - * OEMCrypto_CreateNewUsageEntry, then initial_license_load shall be true. - * If a usage entry is present, then it shall be verified after the call to - * ODK_ParseLicense. - * If initial_license_load is true: - * 1. OEMCrypto shall copy the PST from the parsed license to the usage - * entry. - * 2. OEMCrypto shall verify that the server and client mac keys were - * updated by the license. The server and client mac keys shall be - * copied to the usage entry. - * If initial_license_load is false: - * 1. OEMCrypto shall verify the PST from the parsed license matches that - * in the usage entry. If not, then an error OEMCrypto_ERROR_WRONG_PST - * is returned. - * 2. OEMCrypto shall verify that the server and client mac keys were - * updated by the license. OEMCrypto shall verify that the server and - * client mac keys match those in the usage entry. If not the error - * OEMCrypto_ERROR_WRONG_KEYS is returned. - * If a key control block has a nonzero value for Replay_Control, then all - * keys in this license will have the same value for Replay_Control. In this - * case, the following additional checks are performed. - * - The substring pst must have nonzero length and must satisfy the range - * check described above. If not, return - * OEMCrypto_ERROR_INVALID_CONTEXT. - * - The session must be associated with a usage table entry, either - * created via OEMCrypto_CreateNewUsageEntry or loaded via - * OEMCrypto_LoadUsageEntry. - * - If Replay_Control is 1 = Nonce_Required, then OEMCrypto will perform a - * nonce check as described above. OEMCrypto will verify that the - * usage entry is newly created with OEMCrypto_CreateNewUsageEntry. If - * an existing entry was reloaded, an error - * OEMCrypto_ERROR_INVALID_CONTEXT is returned and no keys are loaded. - * OEMCrypto will then copy the pst and the mac keys to the usage entry, - * and set the status to Unused. This Replay_Control prevents the - * license from being loaded more than once, and will be used for online - * streaming. - * - If Replay_Control is 2 = "Require existing Session Usage table entry - * or Nonce", then OEMCrypto will behave slightly differently on the - * first call to LoadKeys for this license. - * * If the usage entry was created with OEMCrypto_CreateNewUsageEntry - * for this session, then OEMCrypto will verify the nonce for each - * key. OEMCrypto will copy the pst and mac keys to the usage - * entry. The license received time of the entry will be updated - * to the current time, and the status will be set to Unused. - * * If the usage entry was loaded with OEMCrypto_LoadUsageEntry for - * this session, then OEMCrypto will NOT verify the nonce for each - * key. Instead, it will verify that the pst passed in matches - * that in the entry. Also, the entry's mac keys will be verified - * against the current session's mac keys. This allows an offline - * license to be reloaded but maintain continuity of the playback - * times from one session to the next. - * * If the nonce is not valid and a usage entry was not loaded, the - * return error is OEMCrypto_ERROR_INVALID_NONCE. - * * If the loaded usage entry has a pst that does not match, - * OEMCrypto returns the error OEMCrypto_ERROR_WRONG_PST. - * * If the loaded usage entry has mac keys that do not match the - * license, OEMCrypto returns the error OEMCrypto_ERROR_WRONG_KEYS. - * Note: If LoadKeys updates the mac keys, then the new updated mac keys will - * be used with the Usage Entry -- i.e. the new keys are stored in the - * usage table when creating a new entry, or the new keys are verified - * against those in the usage table if there is an existing entry. If - * LoadKeys does not update the mac keys, the existing session mac keys are - * used. - * Sessions that are associated with an entry will need to be able to update - * and verify the status of the entry, and the time stamps in the entry. - * Devices that do not support the Usage Table will return - * OEMCrypto_ERROR_INVALID_CONTEXT if the Replay_Control is nonzero. - * SRM Restriction Data - * If any key control block has the flag SRMVersionRequired set, then the - * following verification is also performed. - * 4. The substring srm_restriction_data must have nonzero length and must - * satisfy the range check described above. If not, return - * OEMCrypto_ERROR_INVALID_CONTEXT. - * 5. The first 8 bytes of srm_restriction_data must match the string - * "HDCPDATA". If not, return OEMCrypto_ERROR_INVALID_CONTEXT. - * 6. The next 4 bytes of srm_restriction_data will be converted from - * network byte order. If the current SRM installed on the device has a - * version number less than this, then the SRM requirement is not met. - * If the device does not support SRM files, or OEMCrypto cannot - * determine the current SRM version number, then the SRM requirement is - * not met. - * Note: if the current SRM version requirement is not met, LoadKeys will - * still succeed and the keys will be loaded. However, those keys with the - * SRMVersionRequired bit set will have their HDCP_Version increased to 0xF - - * local display only. Any future call to SelectKey for these keys while - * there is an external display will return OEMCrypto_ERROR_INSUFFICIENT_HDCP - * at that time. + * @param[in] session: crypto session identifier. + * @param[in] message: pointer to memory containing data. + * @param[in] message_length: length of the message, in bytes. + * @param[in] core_message_length: length of the core submessage, in bytes. + * @param[in] signature: pointer to memory containing the signature. + * @param[in] signature_length: length of the signature, in bytes. * - * Parameters: - * [in] session: crypto session identifier. - * [in] message: pointer to memory containing data. - * [in] message_length: length of the message, in bytes. - * [in] core_message_length: length of the core submessage, in bytes. - * [in] signature: pointer to memory containing the signature. - * [in] signature_length: length of the signature, in bytes. + * @retval OEMCrypto_SUCCESS success OEMCrypto_ERROR_NO_DEVICE_KEY + * @retval OEMCrypto_SUCCESS success + * @retval OEMCrypto_ERROR_NO_DEVICE_KEY + * @retval OEMCrypto_ERROR_INVALID_SESSION + * @retval OEMCrypto_ERROR_UNKNOWN_FAILURE + * @retval OEMCrypto_ERROR_INVALID_CONTEXT + * @retval OEMCrypto_ERROR_SIGNATURE_FAILURE + * @retval OEMCrypto_ERROR_INVALID_NONCE + * @retval OEMCrypto_ERROR_TOO_MANY_KEYS + * @retval OEMCrypto_ERROR_NOT_IMPLEMENTED + * @retval OEMCrypto_ERROR_BUFFER_TOO_LARGE + * @retval OEMCrypto_ERROR_SESSION_LOST_STATE + * @retval OEMCrypto_ERROR_SYSTEM_INVALIDATED + * @retval OEMCrypto_ERROR_LICENSE_RELOAD * - * Returns: - * OEMCrypto_SUCCESS success - * OEMCrypto_ERROR_NO_DEVICE_KEY - * OEMCrypto_ERROR_INVALID_SESSION - * OEMCrypto_ERROR_UNKNOWN_FAILURE - * OEMCrypto_ERROR_INVALID_CONTEXT - * OEMCrypto_ERROR_SIGNATURE_FAILURE - * OEMCrypto_ERROR_INVALID_NONCE - * OEMCrypto_ERROR_TOO_MANY_KEYS - * OEMCrypto_ERROR_NOT_IMPLEMENTED - * OEMCrypto_ERROR_BUFFER_TOO_LARGE - * OEMCrypto_ERROR_SESSION_LOST_STATE - * OEMCrypto_ERROR_SYSTEM_INVALIDATED - * OEMCrypto_ERROR_LICENSE_RELOAD - * - * Buffer Sizes: + * @buffer_size * OEMCrypto shall support message sizes as described in the section * OEMCrypto_ResourceRatingTier. * OEMCrypto shall return OEMCrypto_ERROR_BUFFER_TOO_LARGE if the buffer is * larger than the supported size. * - * Threading: + * @threading * This is a "Session Function" and may be called simultaneously with session * functions for other sessions but not simultaneously with other functions * for this session. It will not be called simultaneously with initialization * or usage table functions. It is as if the CDM holds a write lock for this * session, and a read lock on the OEMCrypto system. * - * Version: + * @version * This method changed in API version 16. */ OEMCryptoResult OEMCrypto_LoadLicense(OEMCrypto_SESSION session, @@ -1641,65 +1705,60 @@ OEMCryptoResult OEMCrypto_LoadLicense(OEMCrypto_SESSION session, const uint8_t* signature, size_t signature_length); -/* - * OEMCrypto_LoadEntitledContentKeys +/** + * Load content keys into a session which already has entitlement keys + * loaded. This function will only be called for a session after a call to + * OEMCrypto_LoadKeys with the parameter type license_type equal to + * OEMCrypto_EntitlementLicense. This function may be called multiple times + * for the same session. * - * Description: - * Load content keys into a session which already has entitlement keys - * loaded. This function will only be called for a session after a call to - * OEMCrypto_LoadKeys with the parameter type license_type equal to - * OEMCrypto_EntitlementLicense. This function may be called multiple times - * for the same session. + * If the session does not have license_type equal to + * OEMCrypto_EntitlementLicense, return OEMCrypto_ERROR_INVALID_CONTEXT and + * perform no work. * - * If the session does not have license_type equal to - * OEMCrypto_EntitlementLicense, return OEMCrypto_ERROR_INVALID_CONTEXT and - * perform no work. + * For each key object in key_array, OEMCrypto shall look up the entry in the + * key table with the corresponding entitlement_key_id. * - * For each key object in key_array, OEMCrypto shall look up the entry in the - * key table with the corresponding entitlement_key_id. + * 1. If no entry is found, return OEMCrypto_KEY_NOT_ENTITLED. + * 2. If the entry already has a content_key_id and content_key_data, that + * id and data are erased. + * 3. The content_key_id from the key_array is copied to the entry's + * content_key_id. + * 4. The content_key_data decrypted using the entitlement_key_data as a + * key for AES-256-CBC with an IV of content_key_data_iv. Wrapped + * content is padded using PKCS#7 padding. Notice that the entitlement + * key will be an AES 256 bit key. The clear content key data will be + * stored in the entry's content_key_data. + * Entries in the key table that do not correspond to anything in the + * key_array are not modified or removed. * - * 1. If no entry is found, return OEMCrypto_KEY_NOT_ENTITLED. - * 2. If the entry already has a content_key_id and content_key_data, that - * id and data are erased. - * 3. The content_key_id from the key_array is copied to the entry's - * content_key_id. - * 4. The content_key_data decrypted using the entitlement_key_data as a - * key for AES-256-CBC with an IV of content_key_data_iv. Wrapped - * content is padded using PKCS#7 padding. Notice that the entitlement - * key will be an AES 256 bit key. The clear content key data will be - * stored in the entry's content_key_data. - * Entries in the key table that do not correspond to anything in the - * key_array are not modified or removed. + * For devices that use a hardware key ladder, it may be more convenient to + * store the encrypted content key data in the key table, and decrypt it when + * the function SelectKey is called. * - * For devices that use a hardware key ladder, it may be more convenient to - * store the encrypted content key data in the key table, and decrypt it when - * the function SelectKey is called. + * @param[in] session: handle for the session to be used. + * @param[in] message: pointer to memory containing message to be verified. + * @param[in] message_length: length of the message, in bytes. + * @param[in] key_array_length: number of keys present. + * @param[in] key_array: set of key updates. * - * Parameters: - * [in] session: handle for the session to be used. - * [in] message: pointer to memory containing message to be verified. - * [in] message_length: length of the message, in bytes. - * [in] key_array_length: number of keys present. - * [in] key_array: set of key updates. + * @retval OEMCrypto_SUCCESS success + * @retval OEMCrypto_ERROR_INVALID_SESSION + * @retval OEMCrypto_ERROR_INVALID_CONTEXT + * @retval OEMCrypto_ERROR_INSUFFICIENT_RESOURCES + * @retval OEMCrypto_ERROR_UNKNOWN_FAILURE + * @retval OEMCrypto_KEY_NOT_ENTITLED + * @retval OEMCrypto_ERROR_SESSION_LOST_STATE + * @retval OEMCrypto_ERROR_SYSTEM_INVALIDATED * - * Returns: - * OEMCrypto_SUCCESS success - * OEMCrypto_ERROR_INVALID_SESSION - * OEMCrypto_ERROR_INVALID_CONTEXT - * OEMCrypto_ERROR_INSUFFICIENT_RESOURCES - * OEMCrypto_ERROR_UNKNOWN_FAILURE - * OEMCrypto_KEY_NOT_ENTITLED - * OEMCrypto_ERROR_SESSION_LOST_STATE - * OEMCrypto_ERROR_SYSTEM_INVALIDATED - * - * Threading: + * @threading * This is a "Session Function" and may be called simultaneously with session * functions for other sessions but not simultaneously with other functions * for this session. It will not be called simultaneously with initialization * or usage table functions. It is as if the CDM holds a write lock for this * session, and a read lock on the OEMCrypto system. * - * Version: + * @version * This method is new in API version 14. */ OEMCryptoResult OEMCrypto_LoadEntitledContentKeys( @@ -1707,100 +1766,95 @@ OEMCryptoResult OEMCrypto_LoadEntitledContentKeys( size_t key_array_length, const OEMCrypto_EntitledContentKeyObject* key_array); -/* - * OEMCrypto_RefreshKeys +/** + * Updates the license clock values to allow playback to continue. This + * function is being deprecated and is only used for version v15 licenses -- + * i.e. offline license saved before an update or licenses from a server that + * has not update to the v16 license server SDK. * - * Description: - * Updates the license clock values to allow playback to continue. This - * function is being deprecated and is only used for version v15 licenses -- - * i.e. offline license saved before an update or licenses from a server that - * has not update to the v16 license server SDK. + * OEMCrypto shall compute the signature of the message using + * mac_key[server], and shall verify the computed signature matches the + * signature passed in. If not, return OEMCrypto_ERROR_SIGNATURE_FAILURE. The + * signature verification shall use a constant-time algorithm (a signature + * mismatch will always take the same time as a successful comparison). * - * OEMCrypto shall compute the signature of the message using - * mac_key[server], and shall verify the computed signature matches the - * signature passed in. If not, return OEMCrypto_ERROR_SIGNATURE_FAILURE. The - * signature verification shall use a constant-time algorithm (a signature - * mismatch will always take the same time as a successful comparison). + * The key control from the first OEMCrypto_KeyRefreshObject in the key_array + * shall be extracted. If it is encrypted, as described below, it shall be + * decrypted. The duration from the key control shall be extracted and + * converted to host byte order. This duration shall be passed to the + * function ODK_RefreshV15Values as the parameter new_key_duration. * - * The key control from the first OEMCrypto_KeyRefreshObject in the key_array - * shall be extracted. If it is encrypted, as described below, it shall be - * decrypted. The duration from the key control shall be extracted and - * converted to host byte order. This duration shall be passed to the - * function ODK_RefreshV15Values as the parameter new_key_duration. + * If the KeyRefreshObject's key_control_iv has zero length, then the + * key_control is not encrypted. If the key_control_iv is specified, then + * key_control is encrypted with the first 128 bits of the corresponding + * content key. * - * If the KeyRefreshObject's key_control_iv has zero length, then the - * key_control is not encrypted. If the key_control_iv is specified, then - * key_control is encrypted with the first 128 bits of the corresponding - * content key. + * If the KeyRefreshObject's key_id has zero length, then it is an error for + * the key_control_iv to have nonzero length. OEMCrypto shall return an error + * of OEMCrypto_ERROR_INVALID_CONTEXT. * - * If the KeyRefreshObject's key_id has zero length, then it is an error for - * the key_control_iv to have nonzero length. OEMCrypto shall return an error - * of OEMCrypto_ERROR_INVALID_CONTEXT. + * If the session's license_type is OEMCrypto_ContentLicense, and the + * KeyRefreshObject's key_id is not null, then the entry in the keytable with + * the matching content_key_id is used. * - * If the session's license_type is OEMCrypto_ContentLicense, and the - * KeyRefreshObject's key_id is not null, then the entry in the keytable with - * the matching content_key_id is used. + * If the session's license_type is OEMCrypto_EntitlementLicense, and the + * KeyRefreshObject's key_id is not null, then the entry in the keytable with + * the matching entitlment_key_id is used. * - * If the session's license_type is OEMCrypto_EntitlementLicense, and the - * KeyRefreshObject's key_id is not null, then the entry in the keytable with - * the matching entitlment_key_id is used. + * The function ODK_RefreshV15Values shall be called to update the clock + * values. See the document "Widevine Core Message Serialization" for the + * documentation of the ODK library functions. * - * The function ODK_RefreshV15Values shall be called to update the clock - * values. See the document "Widevine Core Message Serialization" for the - * documentation of the ODK library functions. + * If ODK_RefreshV15Values returns * - * If ODK_RefreshV15Values returns + * - ODK_SET_TIMER: Success. The timer should be reset to the specified + * timer value. + * - ODK_DISABLE_TIMER: Success, but disable timer. Unlimited playback is + * allowed. + * - ODK_TIMER_EXPIRED: Set timer as disabled. Playback is not allowed. + * - ODK_STALE_RENEWAL: This renewal is not the most recently signed. It is + * rejected. Return this error + * - Any other error - OEMCrypto shall pass any other error up to the + * caller of OEMCrypto_RefreshKeys. * - * - ODK_SET_TIMER: Success. The timer should be reset to the specified - * timer value. - * - ODK_DISABLE_TIMER: Success, but disable timer. Unlimited playback is - * allowed. - * - ODK_TIMER_EXPIRED: Set timer as disabled. Playback is not allowed. - * - ODK_STALE_RENEWAL: This renewal is not the most recently signed. It is - * rejected. Return this error - * - Any other error - OEMCrypto shall pass any other error up to the - * caller of OEMCrypto_RefreshKeys. + * NOTE: OEMCrypto_LoadKeys() must be called first to load the keys into the + * session. * - * NOTE: OEMCrypto_LoadKeys() must be called first to load the keys into the - * session. + * @param[in] session: handle for the session to be used. + * @param[in] message: pointer to memory containing message to be verified. + * @param[in] message_length: length of the message, in bytes. + * @param[in] signature: pointer to memory containing the signature. + * @param[in] signature_length: length of the signature, in bytes. + * @param[in] num_keys: number of keys present. + * @param[in] key_array: set of key updates. * - * Parameters: - * [in] session: handle for the session to be used. - * [in] message: pointer to memory containing message to be verified. - * [in] message_length: length of the message, in bytes. - * [in] signature: pointer to memory containing the signature. - * [in] signature_length: length of the signature, in bytes. - * [in] num_keys: number of keys present. - * [in] key_array: set of key updates. + * @retval OEMCrypto_SUCCESS success + * @retval OEMCrypto_ERROR_NO_DEVICE_KEY + * @retval OEMCrypto_ERROR_INVALID_SESSION + * @retval OEMCrypto_ERROR_INVALID_CONTEXT + * @retval OEMCrypto_ERROR_SIGNATURE_FAILURE + * @retval OEMCrypto_ERROR_INVALID_NONCE + * @retval OEMCrypto_ERROR_INSUFFICIENT_RESOURCES + * @retval OEMCrypto_ERROR_UNKNOWN_FAILURE + * @retval OEMCrypto_ERROR_BUFFER_TOO_LARGE + * @retval OEMCrypto_ERROR_NO_CONTENT_KEY + * @retval OEMCrypto_ERROR_SESSION_LOST_STATE + * @retval OEMCrypto_ERROR_SYSTEM_INVALIDATED * - * Returns: - * OEMCrypto_SUCCESS success - * OEMCrypto_ERROR_NO_DEVICE_KEY - * OEMCrypto_ERROR_INVALID_SESSION - * OEMCrypto_ERROR_INVALID_CONTEXT - * OEMCrypto_ERROR_SIGNATURE_FAILURE - * OEMCrypto_ERROR_INVALID_NONCE - * OEMCrypto_ERROR_INSUFFICIENT_RESOURCES - * OEMCrypto_ERROR_UNKNOWN_FAILURE - * OEMCrypto_ERROR_BUFFER_TOO_LARGE - * OEMCrypto_ERROR_NO_CONTENT_KEY - * OEMCrypto_ERROR_SESSION_LOST_STATE - * OEMCrypto_ERROR_SYSTEM_INVALIDATED - * - * Buffer Sizes: + * @buffer_size * OEMCrypto shall support message sizes as described in the section * OEMCrypto_ResourceRatingTier. * OEMCrypto shall return OEMCrypto_ERROR_BUFFER_TOO_LARGE if the buffer is * larger than the supported size. * - * Threading: + * @threading * This is a "Session Function" and may be called simultaneously with session * functions for other sessions but not simultaneously with other functions * for this session. It will not be called simultaneously with initialization * or usage table functions. It is as if the CDM holds a write lock for this * session, and a read lock on the OEMCrypto system. * - * Version: + * @version * This method changed in API version 16. */ OEMCryptoResult OEMCrypto_RefreshKeys( @@ -1808,78 +1862,73 @@ OEMCryptoResult OEMCrypto_RefreshKeys( const uint8_t* signature, size_t signature_length, size_t num_keys, const OEMCrypto_KeyRefreshObject* key_array); -/* - * OEMCrypto_LoadRenewal +/** + * Updates the clock values and resets the renewal timer for the current + * session. * - * Description: - * Updates the clock values and resets the renewal timer for the current - * session. + * OEMCrypto shall verify the signature of the entire message using the + * session's renewal mac key for the server. The entire message is the buffer + * starting at message with length message_length. If the signature does not + * match, OEMCrypto returns OEMCrypto_ERROR_SIGNATURE_FAILURE. * - * OEMCrypto shall verify the signature of the entire message using the - * session's renewal mac key for the server. The entire message is the buffer - * starting at message with length message_length. If the signature does not - * match, OEMCrypto returns OEMCrypto_ERROR_SIGNATURE_FAILURE. + * OEMCrypto shall verify that nonce_values.api_major_version is 16. If not, + * return the error OEMCrypto_ERROR_INVALID_CONTEXT. Legacy licenses will use + * the function OEMCrypto_RefreshKeys instead of OEMCrypto_LoadRenewal. * - * OEMCrypto shall verify that nonce_values.api_major_version is 16. If not, - * return the error OEMCrypto_ERROR_INVALID_CONTEXT. Legacy licenses will use - * the function OEMCrypto_RefreshKeys instead of OEMCrypto_LoadRenewal. + * If the signature passes, OEMCrypto shall use the function + * ODK_ParseRenewal, as described in the document "Widevine Core Message + * Serialization" to parse and verify the message. If ODK_ParseRenewal + * returns an error OEMCrypto returns the error to the CDM layer. * - * If the signature passes, OEMCrypto shall use the function - * ODK_ParseRenewal, as described in the document "Widevine Core Message - * Serialization" to parse and verify the message. If ODK_ParseRenewal - * returns an error OEMCrypto returns the error to the CDM layer. + * The function ODK_ParseRenewal updates the clock values for the session, + * and may return ODK_SET_TIMER, ODK_DISABLE_TIMER or ODK_TIMER_EXPIRED on + * success. These values shall be handled by OEMCrypto, as discussed in the + * document "License Duration and Renewal". * - * The function ODK_ParseRenewal updates the clock values for the session, - * and may return ODK_SET_TIMER, ODK_DISABLE_TIMER or ODK_TIMER_EXPIRED on - * success. These values shall be handled by OEMCrypto, as discussed in the - * document "License Duration and Renewal". + * NOTE: OEMCrypto_LoadLicense() must be called first to load the keys into + * the session. * - * NOTE: OEMCrypto_LoadLicense() must be called first to load the keys into - * the session. - * - * Verification: + * @verification * The signature of the message shall be computed using mac_key[server], and * the API shall verify the computed signature matches the signature passed * in. If not, return OEMCrypto_ERROR_SIGNATURE_FAILURE. The signature * verification shall use a constant-time algorithm (a signature mismatch * will always take the same time as a successful comparison). * - * Parameters: - * [in] session: handle for the session to be used. - * [in] message: pointer to memory containing message to be verified. - * [in] message_length: length of the message, in bytes. - * [in] core_message_length: length of the core submessage, in bytes. - * [in] signature: pointer to memory containing the signature. - * [in] signature_length: length of the signature, in bytes. + * @param[in] session: handle for the session to be used. + * @param[in] message: pointer to memory containing message to be verified. + * @param[in] message_length: length of the message, in bytes. + * @param[in] core_message_length: length of the core submessage, in bytes. + * @param[in] signature: pointer to memory containing the signature. + * @param[in] signature_length: length of the signature, in bytes. * - * Returns: - * OEMCrypto_SUCCESS success - * OEMCrypto_ERROR_NO_DEVICE_KEY - * OEMCrypto_ERROR_INVALID_SESSION - * OEMCrypto_ERROR_INVALID_CONTEXT - * OEMCrypto_ERROR_SIGNATURE_FAILURE - * OEMCrypto_ERROR_INVALID_NONCE - * OEMCrypto_ERROR_INSUFFICIENT_RESOURCES - * OEMCrypto_ERROR_UNKNOWN_FAILURE - * OEMCrypto_ERROR_BUFFER_TOO_LARGE - * OEMCrypto_ERROR_SESSION_LOST_STATE - * OEMCrypto_ERROR_SYSTEM_INVALIDATED - * ODK_STALE_RENEWAL + * @retval OEMCrypto_SUCCESS success + * @retval OEMCrypto_ERROR_NO_DEVICE_KEY + * @retval OEMCrypto_ERROR_INVALID_SESSION + * @retval OEMCrypto_ERROR_INVALID_CONTEXT + * @retval OEMCrypto_ERROR_SIGNATURE_FAILURE + * @retval OEMCrypto_ERROR_INVALID_NONCE + * @retval OEMCrypto_ERROR_INSUFFICIENT_RESOURCES + * @retval OEMCrypto_ERROR_UNKNOWN_FAILURE + * @retval OEMCrypto_ERROR_BUFFER_TOO_LARGE + * @retval OEMCrypto_ERROR_SESSION_LOST_STATE + * @retval OEMCrypto_ERROR_SYSTEM_INVALIDATED + * @retval ODK_STALE_RENEWAL * - * Buffer Sizes: + * @buffer_size * OEMCrypto shall support message sizes as described in the section * OEMCrypto_ResourceRatingTier. * OEMCrypto shall return OEMCrypto_ERROR_BUFFER_TOO_LARGE if the buffer is * larger than the supported size. * - * Threading: + * @threading * This is a "Session Function" and may be called simultaneously with session * functions for other sessions but not simultaneously with other functions * for this session. It will not be called simultaneously with initialization * or usage table functions. It is as if the CDM holds a write lock for this * session, and a read lock on the OEMCrypto system. * - * Version: + * @version * This method changed in API version 12. */ OEMCryptoResult OEMCrypto_LoadRenewal(OEMCrypto_SESSION session, @@ -1889,23 +1938,20 @@ OEMCryptoResult OEMCrypto_LoadRenewal(OEMCrypto_SESSION session, const uint8_t* signature, size_t signature_length); -/* - * OEMCrypto_QueryKeyControl +/** + * Returns the decrypted key control block for the given content_key_id. This + * function is for application developers to debug license server and key + * timelines. It only returns a key control block if LoadKeys was successful, + * otherwise it returns OEMCrypto_ERROR_NO_CONTENT_KEY. The developer of the + * OEMCrypto library must be careful that the keys themselves are not + * accidentally revealed. * - * Description: - * Returns the decrypted key control block for the given content_key_id. This - * function is for application developers to debug license server and key - * timelines. It only returns a key control block if LoadKeys was successful, - * otherwise it returns OEMCrypto_ERROR_NO_CONTENT_KEY. The developer of the - * OEMCrypto library must be careful that the keys themselves are not - * accidentally revealed. + * Note: returns control block in original, network byte order. If OEMCrypto + * converts fields to host byte order internally for storage, it should + * convert them back. Since OEMCrypto might not store the nonce or validation + * fields, values of 0 may be used instead. * - * Note: returns control block in original, network byte order. If OEMCrypto - * converts fields to host byte order internally for storage, it should - * convert them back. Since OEMCrypto might not store the nonce or validation - * fields, values of 0 may be used instead. - * - * Verification: + * @verification * The following checks should be performed. * 1. If key_id is null, return OEMCrypto_ERROR_INVALID_CONTEXT. * 2. If key_control_block_length is null, return @@ -1917,30 +1963,29 @@ OEMCryptoResult OEMCrypto_LoadRenewal(OEMCrypto_SESSION session, * 5. If the specified key has not been loaded, return * OEMCrypto_ERROR_NO_CONTENT_KEY. * - * Parameters: - * [in] session: handle for the session to be used. - * [in] content_key_id: The unique id of the key of interest. - * [in] content_key_id_length: The length of key_id, in bytes. From 1 to 16, - * inclusive. - * [out] key_control_block: A caller-owned buffer. - * [in/out] key_control_block_length. The length of key_control_block buffer. + * @param[in] session: handle for the session to be used. + * @param[in] content_key_id: The unique id of the key of interest. + * @param[in] content_key_id_length: The length of key_id, in bytes. From 1 to + * 16, inclusive. + * @param[out] key_control_block: A caller-owned buffer. + * @param[in,out] key_control_block_length. The length of key_control_block + * buffer. * - * Returns: - * OEMCrypto_SUCCESS - * OEMCrypto_ERROR_INVALID_CONTEXT - * OEMCrypto_ERROR_INSUFFICIENT_RESOURCES - * OEMCrypto_ERROR_UNKNOWN_FAILURE - * OEMCrypto_ERROR_SESSION_LOST_STATE - * OEMCrypto_ERROR_SYSTEM_INVALIDATED + * @retval OEMCrypto_SUCCESS + * @retval OEMCrypto_ERROR_INVALID_CONTEXT + * @retval OEMCrypto_ERROR_INSUFFICIENT_RESOURCES + * @retval OEMCrypto_ERROR_UNKNOWN_FAILURE + * @retval OEMCrypto_ERROR_SESSION_LOST_STATE + * @retval OEMCrypto_ERROR_SYSTEM_INVALIDATED * - * Threading: + * @threading * This is a "Session Function" and may be called simultaneously with session * functions for other sessions but not simultaneously with other functions * for this session. It will not be called simultaneously with initialization * or usage table functions. It is as if the CDM holds a write lock for this * session, and a read lock on the OEMCrypto system. * - * Version: + * @version * This method is new in API version 10. */ OEMCryptoResult OEMCrypto_QueryKeyControl(OEMCrypto_SESSION session, @@ -1949,88 +1994,86 @@ OEMCryptoResult OEMCrypto_QueryKeyControl(OEMCrypto_SESSION session, uint8_t* key_control_block, size_t* key_control_block_length); -/* - * OEMCrypto_SelectKey +/// @} + +/// @addtogroup decryption +/// @{ + +/** + * Select a content key and install it in the hardware key ladder for + * subsequent decryption operations (OEMCrypto_DecryptCENC()) for this + * session. The specified key must have been previously "installed" via + * OEMCrypto_LoadKeys(), OEMCrypto_LoadLicense, or + * OEMCrypto_LoadEntitledContentKeys(). * - * Description: - * Select a content key and install it in the hardware key ladder for - * subsequent decryption operations (OEMCrypto_DecryptCENC()) for this - * session. The specified key must have been previously "installed" via - * OEMCrypto_LoadKeys(), OEMCrypto_LoadLicense, or - * OEMCrypto_LoadEntitledContentKeys(). + * A key control block is associated with the key and the session, and is + * used to configure the session context. The Key Control data is documented + * in "Key Control Block Definition". * - * A key control block is associated with the key and the session, and is - * used to configure the session context. The Key Control data is documented - * in "Key Control Block Definition". + * Step 1: Lookup the content key data via the offered key_id. The key data + * includes the key value, and the key control block. * - * Step 1: Lookup the content key data via the offered key_id. The key data - * includes the key value, and the key control block. + * Step 2: Latch the content key into the hardware key ladder. Set permission + * flags based on the key's control block. * - * Step 2: Latch the content key into the hardware key ladder. Set permission - * flags based on the key's control block. + * Step 3: use the latched content key to decrypt (AES-128-CTR or + * AES-128-CBC) buffers passed in via OEMCrypto_DecryptCENC(). If the key is + * 256 bits it will be used for OEMCrypto_Generic_Sign or + * OEMCrypto_Generic_Verify as specified in the key control block. If the key + * will be used for OEMCrypto_Generic_Encrypt or OEMCrypto_Generic_Decrypt + * then the cipher mode will always be OEMCrypto_CipherMode_CBC. Continue to + * use this key for this session until OEMCrypto_SelectKey() is called again, + * or until OEMCrypto_CloseSession() is called. * - * Step 3: use the latched content key to decrypt (AES-128-CTR or - * AES-128-CBC) buffers passed in via OEMCrypto_DecryptCENC(). If the key is - * 256 bits it will be used for OEMCrypto_Generic_Sign or - * OEMCrypto_Generic_Verify as specified in the key control block. If the key - * will be used for OEMCrypto_Generic_Encrypt or OEMCrypto_Generic_Decrypt - * then the cipher mode will always be OEMCrypto_CipherMode_CBC. Continue to - * use this key for this session until OEMCrypto_SelectKey() is called again, - * or until OEMCrypto_CloseSession() is called. + * @verification + * 1. If the key id is not found in the keytable for this session, then the + * key state is not changed and OEMCrypto shall return + * OEMCrypto_ERROR_NO_CONTENT_KEY. + * 2. If the key control block has the bit Disable_Analog_Output set, then + * the device should disable analog video output. If the device has + * analog video output that cannot be disabled, then the key is not + * selected, and OEMCrypto_ERROR_ANALOG_OUTPUT is returned. This step is + * optional -- SelectKey may return OEMCrypto_SUCCESS and delay the + * error until a call to OEMCrypto_DecryptCENC. + * 3. If the key control block has HDCP required, and the device cannot + * enforce HDCP, then the key is not selected, and + * OEMCrypto_ERROR_INSUFFICIENT_HDCP is returned. This step is optional + * -- SelectKey may return OEMCrypto_SUCCESS and delay the error until a + * call to OEMCrypto_DecryptCENC. + * 4. If the key control block has a nonzero value for HDCP_Version, and + * the device cannot enforce at least that version of HDCP, then the key + * is not selected, and OEMCrypto_ERROR_INSUFFICIENT_HDCP is returned. * - * Verification: - * 1. If the key id is not found in the keytable for this session, then the - * key state is not changed and OEMCrypto shall return - * OEMCrypto_ERROR_NO_CONTENT_KEY. - * 2. If the key control block has the bit Disable_Analog_Output set, then - * the device should disable analog video output. If the device has - * analog video output that cannot be disabled, then the key is not - * selected, and OEMCrypto_ERROR_ANALOG_OUTPUT is returned. This step is - * optional -- SelectKey may return OEMCrypto_SUCCESS and delay the - * error until a call to OEMCrypto_DecryptCENC. - * 3. If the key control block has HDCP required, and the device cannot - * enforce HDCP, then the key is not selected, and - * OEMCrypto_ERROR_INSUFFICIENT_HDCP is returned. This step is optional - * -- SelectKey may return OEMCrypto_SUCCESS and delay the error until a - * call to OEMCrypto_DecryptCENC. - * 4. If the key control block has a nonzero value for HDCP_Version, and - * the device cannot enforce at least that version of HDCP, then the key - * is not selected, and OEMCrypto_ERROR_INSUFFICIENT_HDCP is returned. + * @param[in] session: crypto session identifier. + * @param[in] content_key_id: pointer to the content Key ID. + * @param[in] content_key_id_length: length of the content Key ID, in bytes. + * From 1 to 16, inclusive. + * @param[in] cipher_mode: whether the key should be prepared for CTR mode or + * CBC mode when used in later calls to DecryptCENC. This should be ignored + * when the key is used for Generic Crypto calls. * - * Parameters: - * [in] session: crypto session identifier. - * [in] content_key_id: pointer to the content Key ID. - * [in] content_key_id_length: length of the content Key ID, in bytes. From - * 1 to 16, inclusive. - * [in] cipher_mode: whether the key should be prepared for CTR mode or CBC - * mode when used in later calls to DecryptCENC. This should be ignored - * when the key is used for Generic Crypto calls. + * @retval OEMCrypto_SUCCESS success + * @retval OEMCrypto_ERROR_KEY_EXPIRED if the session's timer has expired + * @retval OEMCrypto_ERROR_INVALID_SESSION crypto session ID invalid or not open + * @retval OEMCrypto_ERROR_NO_DEVICE_KEY failed to decrypt device key + * @retval OEMCrypto_ERROR_NO_CONTENT_KEY failed to decrypt content key + * @retval OEMCrypto_ERROR_CONTROL_INVALID invalid or unsupported control input + * @retval OEMCrypto_ERROR_KEYBOX_INVALID cannot decrypt and read from Keybox + * @retval OEMCrypto_ERROR_INSUFFICIENT_RESOURCES + * @retval OEMCrypto_ERROR_UNKNOWN_FAILURE + * @retval OEMCrypto_ERROR_ANALOG_OUTPUT + * @retval OEMCrypto_ERROR_INSUFFICIENT_HDCP + * @retval OEMCrypto_ERROR_SESSION_LOST_STATE + * @retval OEMCrypto_ERROR_SYSTEM_INVALIDATED * - * Returns: - * OEMCrypto_SUCCESS success - * OEMCrypto_ERROR_KEY_EXPIRED - if the session's timer has expired - * OEMCrypto_ERROR_INVALID_SESSION crypto session ID invalid or not open - * OEMCrypto_ERROR_NO_DEVICE_KEY failed to decrypt device key - * OEMCrypto_ERROR_NO_CONTENT_KEY failed to decrypt content key - * OEMCrypto_ERROR_CONTROL_INVALID invalid or unsupported control input - * OEMCrypto_ERROR_KEYBOX_INVALID cannot decrypt and read from Keybox - * OEMCrypto_ERROR_INSUFFICIENT_RESOURCES - * OEMCrypto_ERROR_UNKNOWN_FAILURE - * OEMCrypto_ERROR_KEY_EXPIRED - * OEMCrypto_ERROR_ANALOG_OUTPUT - * OEMCrypto_ERROR_INSUFFICIENT_HDCP - * OEMCrypto_ERROR_NO_CONTENT_KEY - * OEMCrypto_ERROR_SESSION_LOST_STATE - * OEMCrypto_ERROR_SYSTEM_INVALIDATED - * - * Threading: + * @threading * This is a "Session Function" and may be called simultaneously with session * functions for other sessions but not simultaneously with other functions * for this session. It will not be called simultaneously with initialization * or usage table functions. It is as if the CDM holds a write lock for this * session, and a read lock on the OEMCrypto system. * - * Version: + * @version * This method changed in API version 16. */ OEMCryptoResult OEMCrypto_SelectKey(OEMCrypto_SESSION session, @@ -2038,365 +2081,359 @@ OEMCryptoResult OEMCrypto_SelectKey(OEMCrypto_SESSION session, size_t content_key_id_length, OEMCryptoCipherMode cipher_mode); -/* - * OEMCrypto_DecryptCENC +/** + * Decrypts or copies a series of input payloads into output buffers using + * the session context indicated by the session parameter. The input payload + * is delivered in the form of samples. The samples are subdivided into + * subsamples. "Samples" and "subsamples" are defined as in the ISO Common + * Encryption standard (ISO/IEC 23001-7:2016). The samples parameter contains + * a list of samples, each of which has its own input and output buffers. + * Each sample contains a buffers field that contains the input and output + * buffers in its input_data and output fields, respectively. * - * Description: - * Decrypts or copies a series of input payloads into output buffers using - * the session context indicated by the session parameter. The input payload - * is delivered in the form of samples. The samples are subdivided into - * subsamples. "Samples" and "subsamples" are defined as in the ISO Common - * Encryption standard (ISO/IEC 23001-7:2016). The samples parameter contains - * a list of samples, each of which has its own input and output buffers. - * Each sample contains a buffers field that contains the input and output - * buffers in its input_data and output fields, respectively. + * Each sample contains an array of subsample descriptions in its subsamples + * field. Each subsample is defined as a number of clear bytes followed by a + * number of encrypted bytes. Subsamples are consecutive inside the sample; + * the clear bytes of the second subsample begin immediately after the + * encrypted bytes of the first subsample. This follows the definition in the + * ISO-CENC standard. * - * Each sample contains an array of subsample descriptions in its subsamples - * field. Each subsample is defined as a number of clear bytes followed by a - * number of encrypted bytes. Subsamples are consecutive inside the sample; - * the clear bytes of the second subsample begin immediately after the - * encrypted bytes of the first subsample. This follows the definition in the - * ISO-CENC standard. + * Decryption mode is AES-128-CTR or AES-128-CBC depending on the value of + * cipher_mode previously passed in to OEMCrypto_SelectKey. For the encrypted + * portion of subsamples, the content key associated with the session is + * latched in the active hardware key ladder and is used for the decryption + * operation. For the clear portion of subsamples, the data is simply copied. * - * Decryption mode is AES-128-CTR or AES-128-CBC depending on the value of - * cipher_mode previously passed in to OEMCrypto_SelectKey. For the encrypted - * portion of subsamples, the content key associated with the session is - * latched in the active hardware key ladder and is used for the decryption - * operation. For the clear portion of subsamples, the data is simply copied. + * After decryption, all the input_data bytes are copied to the location + * described by the output field. The output field is an + * OEMCrypto_DestBufferDesc, which could be one of: * - * After decryption, all the input_data bytes are copied to the location - * described by the output field. The output field is an - * OEMCrypto_DestBufferDesc, which could be one of: + * 1. The structure OEMCrypto_DestBufferDesc contains a pointer to a clear + * text buffer. The OEMCrypto library shall verify that key control + * allows data to be returned in clear text. If it is not authorized, + * this method should return an error. + * 2. The structure OEMCrypto_DestBufferDesc contains a handle to a secure + * buffer. + * 3. The structure OEMCrypto_DestBufferDesc indicates that the data should + * be sent directly to the decoder and renderer. + * Depending on your platform's needs, you may not need to support all three + * of these options. * - * 1. The structure OEMCrypto_DestBufferDesc contains a pointer to a clear - * text buffer. The OEMCrypto library shall verify that key control - * allows data to be returned in clear text. If it is not authorized, - * this method should return an error. - * 2. The structure OEMCrypto_DestBufferDesc contains a handle to a secure - * buffer. - * 3. The structure OEMCrypto_DestBufferDesc indicates that the data should - * be sent directly to the decoder and renderer. - * Depending on your platform's needs, you may not need to support all three - * of these options. + * SINGLE-SAMPLE DECRYPTION AND SINGLE-SUBSAMPLE DECRYPTION: * - * SINGLE-SAMPLE DECRYPTION AND SINGLE-SUBSAMPLE DECRYPTION: + * If the OEMCrypto implementation is not able to handle the amount of + * samples and subsamples passed into it, it should return + * OEMCrypto_ERROR_BUFFER_TOO_LARGE, in which case the CDM can respond by + * breaking the samples up into smaller pieces and trying to decrypt each of + * them individually. It is possible that the CDM will break the samples + * array up into pieces that are still too large, in which case OEMCrypto may + * return OEMCrypto_ERROR_BUFFER_TOO_LARGE again. * - * If the OEMCrypto implementation is not able to handle the amount of - * samples and subsamples passed into it, it should return - * OEMCrypto_ERROR_BUFFER_TOO_LARGE, in which case the CDM can respond by - * breaking the samples up into smaller pieces and trying to decrypt each of - * them individually. It is possible that the CDM will break the samples - * array up into pieces that are still too large, in which case OEMCrypto may - * return OEMCrypto_ERROR_BUFFER_TOO_LARGE again. + * If the OEMCrypto implementation cannot handle multiple samples at once, it + * may return OEMCrypto_ERROR_BUFFER_TOO_LARGE any time it receives more than + * one sample in a single call to OEMCrypto_DecryptCENC. * - * If the OEMCrypto implementation cannot handle multiple samples at once, it - * may return OEMCrypto_ERROR_BUFFER_TOO_LARGE any time it receives more than - * one sample in a single call to OEMCrypto_DecryptCENC. + * Similarly, if the OEMCrypto implementation cannot handle multiple + * subsamples at once, it may return OEMCrypto_ERROR_BUFFER_TOO_LARGE any + * time it receives more than one subsample in a single call to + * OEMCrypto_DecryptCENC. * - * Similarly, if the OEMCrypto implementation cannot handle multiple - * subsamples at once, it may return OEMCrypto_ERROR_BUFFER_TOO_LARGE any - * time it receives more than one subsample in a single call to - * OEMCrypto_DecryptCENC. + * The exact way that the CDM code breaks up the samples array is not + * guaranteed by this specification. The CDM may break down the array of + * samples into many arrays each containing one sample. The CDM may break + * down samples into subsamples and pass individual subsamples into + * OEMCrypto, just like in OEMCrypto v15. The CDM may break down individual + * subsamples into smaller subsamples, just like in OEMCrypto v15. * - * The exact way that the CDM code breaks up the samples array is not - * guaranteed by this specification. The CDM may break down the array of - * samples into many arrays each containing one sample. The CDM may break - * down samples into subsamples and pass individual subsamples into - * OEMCrypto, just like in OEMCrypto v15. The CDM may break down individual - * subsamples into smaller subsamples, just like in OEMCrypto v15. + * If OEMCrypto requests that the CDM break samples into subsamples, the + * "samples" passed into OEMCrypto_DecryptCENC will no longer be full + * samples. When a full sample is passed into OEMCrypto_DecryptCENC, the + * first subsample in the subsample array will have the + * OEMCrypto_FirstSubsample flag set in its subsample_flags field and the + * last subsample array will have the OEMCrypto_LastSubsample flag set in its + * subsample_flags field. If this is not the case, OEMCrypto will need to + * accumulate more subsamples from successive calls to OEMCrypto_DecryptCENC + * to receive the full sample. * - * If OEMCrypto requests that the CDM break samples into subsamples, the - * "samples" passed into OEMCrypto_DecryptCENC will no longer be full - * samples. When a full sample is passed into OEMCrypto_DecryptCENC, the - * first subsample in the subsample array will have the - * OEMCrypto_FirstSubsample flag set in its subsample_flags field and the - * last subsample array will have the OEMCrypto_LastSubsample flag set in its - * subsample_flags field. If this is not the case, OEMCrypto will need to - * accumulate more subsamples from successive calls to OEMCrypto_DecryptCENC - * to receive the full sample. + * The first subsample in the sample will always have + * OEMCrypto_FirstSubsample set and the last subsample will always have the + * OEMCrypto_LastSubsample flag set, even if those subsamples are passed in + * separate calls to OEMCrypto_DecryptCENC. This is the same as in OEMCrypto + * v15. The decrypted data will not be used until after the subsample with + * the flag OEMCrypto_LastSubsample has been sent to OEMCrypto. This can be + * relied on by OEMCrypto for optimization by not doing decrypt until the + * last subsample has been received. However, a device that can do decrypt of + * more than one subsample at a time will always have better performance if + * it can receive those subsamples in one OEMCrypto_Decrypt call rather than + * as individual subsamples. * - * The first subsample in the sample will always have - * OEMCrypto_FirstSubsample set and the last subsample will always have the - * OEMCrypto_LastSubsample flag set, even if those subsamples are passed in - * separate calls to OEMCrypto_DecryptCENC. This is the same as in OEMCrypto - * v15. The decrypted data will not be used until after the subsample with - * the flag OEMCrypto_LastSubsample has been sent to OEMCrypto. This can be - * relied on by OEMCrypto for optimization by not doing decrypt until the - * last subsample has been received. However, a device that can do decrypt of - * more than one subsample at a time will always have better performance if - * it can receive those subsamples in one OEMCrypto_Decrypt call rather than - * as individual subsamples. + * Although the exact way that the CDM code breaks up the samples array when + * it receives OEMCrypto_ERROR_BUFFER_TOO_LARGE is not guaranteed by this + * specification, here is a sample way it might work: * - * Although the exact way that the CDM code breaks up the samples array when - * it receives OEMCrypto_ERROR_BUFFER_TOO_LARGE is not guaranteed by this - * specification, here is a sample way it might work: + * 1. It tries to pass the array of samples to OEMCrypto_DecryptCENC. + * 2. If OEMCrypto returns OEMCrypto_ERROR_BUFFER_TOO_LARGE, it tries to + * pass each sample individually into OEMCrypto_DecryptCENC. + * 3. If OEMCrypto returns OEMCrypto_ERROR_BUFFER_TOO_LARGE, it tries to + * pass the clear and encrypted parts of each subsample individually + * into OEMCrypto_DecryptCENC. At this point, (and in the subsequent + * steps) it is replicating the behavior of OEMCrypto v15 and lower. + * 4. If OEMCrypto returns OEMCrypto_ERROR_BUFFER_TOO_LARGE, it breaks each + * piece of a subsample into smaller pieces, down to the minimum + * subsample size required by the device's resource rating tier. It + * passes these pieces into OEMCrypto_DecryptCENC. + * 5. If OEMCrypto returns OEMCrypto_ERROR_BUFFER_TOO_LARGE, the device has + * failed to meet its resource rating tier requirements. It returns an + * error. + * Because this process requires a lot of back-and-forth between the CDM and + * OEMCrypto, partners are strongly recommended to support decrypting full + * samples or even multiple samples in their OEMCrypto implementation. * - * 1. It tries to pass the array of samples to OEMCrypto_DecryptCENC. - * 2. If OEMCrypto returns OEMCrypto_ERROR_BUFFER_TOO_LARGE, it tries to - * pass each sample individually into OEMCrypto_DecryptCENC. - * 3. If OEMCrypto returns OEMCrypto_ERROR_BUFFER_TOO_LARGE, it tries to - * pass the clear and encrypted parts of each subsample individually - * into OEMCrypto_DecryptCENC. At this point, (and in the subsequent - * steps) it is replicating the behavior of OEMCrypto v15 and lower. - * 4. If OEMCrypto returns OEMCrypto_ERROR_BUFFER_TOO_LARGE, it breaks each - * piece of a subsample into smaller pieces, down to the minimum - * subsample size required by the device's resource rating tier. It - * passes these pieces into OEMCrypto_DecryptCENC. - * 5. If OEMCrypto returns OEMCrypto_ERROR_BUFFER_TOO_LARGE, the device has - * failed to meet its resource rating tier requirements. It returns an - * error. - * Because this process requires a lot of back-and-forth between the CDM and - * OEMCrypto, partners are strongly recommended to support decrypting full - * samples or even multiple samples in their OEMCrypto implementation. + * ISO-CENC SCHEMES: * - * ISO-CENC SCHEMES: + * The ISO Common Encryption standard (ISO/IEC 23001-7:2016) defines four + * "schemes" that may be used to encrypt content: 'cenc', 'cens', 'cbc1', and + * 'cbcs'. Starting with v16, OEMCrypto only supports 'cenc' and 'cbcs'. The + * schemes 'cens' and 'cbc1' are not supported. * - * The ISO Common Encryption standard (ISO/IEC 23001-7:2016) defines four - * "schemes" that may be used to encrypt content: 'cenc', 'cens', 'cbc1', and - * 'cbcs'. Starting with v16, OEMCrypto only supports 'cenc' and 'cbcs'. The - * schemes 'cens' and 'cbc1' are not supported. + * The decryption mode, either OEMCrypto_CipherMode_CTR or + * OEMCrypto_CipherMode_CBC, was already specified in the call to + * OEMCrypto_SelectKey. The encryption pattern is specified by the fields in + * the parameter pattern. A description of partial encryption patterns for + * 'cbcs' can be found in the ISO-CENC standard, section 10.4. * - * The decryption mode, either OEMCrypto_CipherMode_CTR or - * OEMCrypto_CipherMode_CBC, was already specified in the call to - * OEMCrypto_SelectKey. The encryption pattern is specified by the fields in - * the parameter pattern. A description of partial encryption patterns for - * 'cbcs' can be found in the ISO-CENC standard, section 10.4. + * 'cenc' SCHEME: * - * 'cenc' SCHEME: + * The 'cenc' scheme is OEMCrypto_CipherMode_CTR without an encryption + * pattern. All the bytes in the encrypted portion of each subsample are + * encrypted. In the pattern parameter, both the encrypt and skip fields will + * be zero. * - * The 'cenc' scheme is OEMCrypto_CipherMode_CTR without an encryption - * pattern. All the bytes in the encrypted portion of each subsample are - * encrypted. In the pattern parameter, both the encrypt and skip fields will - * be zero. + * The length of a crypto block in AES-128 is 16 bytes. In the 'cenc' scheme, + * if an encrypted subsample has a length that is not a multiple of 16 bytes, + * then all the bytes of the encrypted subsample must be decrypted, but the + * next encrypted subsample will begin by completing the incomplete crypto + * block from the previous encrypted subsample. The following diagram + * provides an example: * - * The length of a crypto block in AES-128 is 16 bytes. In the 'cenc' scheme, - * if an encrypted subsample has a length that is not a multiple of 16 bytes, - * then all the bytes of the encrypted subsample must be decrypted, but the - * next encrypted subsample will begin by completing the incomplete crypto - * block from the previous encrypted subsample. The following diagram - * provides an example: + * (See drawing in "Widevine Modular DRM Security Integration Guide") * - * (See drawing in "Widevine Modular DRM Security Integration Guide") + * To help with this, the block_offset field of each subsample will contain + * the number of bytes the initial crypto block of that subsample should be + * offset by. In the example above, the block_offset for the first subsample + * would be 0 and the block_offset for the second subsample would be 12. + * 'cenc' is the only mode that allows for a nonzero block_offset. This field + * satisfies 0 <= block_offset < 16. * - * To help with this, the block_offset field of each subsample will contain - * the number of bytes the initial crypto block of that subsample should be - * offset by. In the example above, the block_offset for the first subsample - * would be 0 and the block_offset for the second subsample would be 12. - * 'cenc' is the only mode that allows for a nonzero block_offset. This field - * satisfies 0 <= block_offset < 16. + * 'cbcs' SCHEME: * - * 'cbcs' SCHEME: + * The 'cbcs' scheme is OEMCrypto_CipherMode_CBC with an encryption pattern. + * Only some of the bytes in the encrypted portion of each subsample are + * encrypted. In the pattern parameter, the encrypt and skip fields will + * usually be non-zero. This mode allows devices to decrypt FMP4 HLS content, + * SAMPLE-AES HLS content, as well as content using the DASH 'cbcs' scheme. * - * The 'cbcs' scheme is OEMCrypto_CipherMode_CBC with an encryption pattern. - * Only some of the bytes in the encrypted portion of each subsample are - * encrypted. In the pattern parameter, the encrypt and skip fields will - * usually be non-zero. This mode allows devices to decrypt FMP4 HLS content, - * SAMPLE-AES HLS content, as well as content using the DASH 'cbcs' scheme. + * The skip field of OEMCrypto_CENCEncryptPatternDesc may also be zero. If + * the skip field is zero, then patterns are not in use and all crypto blocks + * in the encrypted part of the subsample are encrypted. It is not valid for + * the encrypt field to be zero. * - * The skip field of OEMCrypto_CENCEncryptPatternDesc may also be zero. If - * the skip field is zero, then patterns are not in use and all crypto blocks - * in the encrypted part of the subsample are encrypted. It is not valid for - * the encrypt field to be zero. + * The length of a crypto block in AES-128 is 16 bytes. In the 'cbcs' scheme, + * if the encrypted part of a subsample has a length that is not a multiple + * of 16 bytes, then the final bytes that do not make up a full crypto block + * are clear and should never be decrypted. The following diagram provides an + * example: * - * The length of a crypto block in AES-128 is 16 bytes. In the 'cbcs' scheme, - * if the encrypted part of a subsample has a length that is not a multiple - * of 16 bytes, then the final bytes that do not make up a full crypto block - * are clear and should never be decrypted. The following diagram provides an - * example: + * (See drawing in "Widevine Modular DRM Security Integration Guide") * - * (See drawing in "Widevine Modular DRM Security Integration Guide") + * Whether any given protected block is actually encrypted also depends on + * the pattern. But the bytes at the end that do not make up a full crypto + * block will never be encrypted, regardless of what the pattern is. Even if + * the pattern says to decrypt every protected block, these bytes are clear + * and should not be decrypted. * - * Whether any given protected block is actually encrypted also depends on - * the pattern. But the bytes at the end that do not make up a full crypto - * block will never be encrypted, regardless of what the pattern is. Even if - * the pattern says to decrypt every protected block, these bytes are clear - * and should not be decrypted. + * Of course, if the encrypted subsample has a length that is a multiple of + * 16 bytes, all the bytes in it are protected, and they may need to be + * decrypted following the pattern. The following diagram provides an example: * - * Of course, if the encrypted subsample has a length that is a multiple of - * 16 bytes, all the bytes in it are protected, and they may need to be - * decrypted following the pattern. The following diagram provides an example: + * (See drawing in "Widevine Modular DRM Security Integration Guide") * - * (See drawing in "Widevine Modular DRM Security Integration Guide") + * INITIALIZATION VECTOR BETWEEN SUBSAMPLES: * - * INITIALIZATION VECTOR BETWEEN SUBSAMPLES: + * The IV is specified for the initial subsample in a sample in the iv field + * of the OEMCrypto_SampleDescription. OEMCrypto is responsible for correctly + * updating the IV for subsequent subsamples according to the ISO Common + * Encryption standard (ISO/IEC 23001-7:2016). Section 9.5.2.3 covers 'cenc' + * and section 9.5.2.5 covers 'cbcs'. A summary of the ISO-CENC behavior + * follows: * - * The IV is specified for the initial subsample in a sample in the iv field - * of the OEMCrypto_SampleDescription. OEMCrypto is responsible for correctly - * updating the IV for subsequent subsamples according to the ISO Common - * Encryption standard (ISO/IEC 23001-7:2016). Section 9.5.2.3 covers 'cenc' - * and section 9.5.2.5 covers 'cbcs'. A summary of the ISO-CENC behavior - * follows: + * For 'cenc', the IV at the end of each subsample carries forward to the + * next subsample and becomes the IV at the beginning of the next subsample. + * If the subsample ends on a crypto block boundary, then the IV should be + * incremented as normal at the end of the crypto block. If the subsample + * ends in the middle of a crypto block, the same IV should continue to be + * used until the crypto block is completed in the next subsample. Only + * increment the IV after the partial crypto block is completed. * - * For 'cenc', the IV at the end of each subsample carries forward to the - * next subsample and becomes the IV at the beginning of the next subsample. - * If the subsample ends on a crypto block boundary, then the IV should be - * incremented as normal at the end of the crypto block. If the subsample - * ends in the middle of a crypto block, the same IV should continue to be - * used until the crypto block is completed in the next subsample. Only - * increment the IV after the partial crypto block is completed. + * For 'cbcs', the IV is reset at the beginning of each subsample. Each + * subsample should start with the IV that was passed into + * OEMCrypto_DecryptCENC. * - * For 'cbcs', the IV is reset at the beginning of each subsample. Each - * subsample should start with the IV that was passed into - * OEMCrypto_DecryptCENC. + * To phrase it another way: In 'cenc', the encrypted portions of the + * subsamples can be concatenated to form one continuous ciphertext. In + * 'cbcs', each encrypted portion of a subsample is a separate ciphertext. + * Each separate ciphertext begins with the IV specified in the iv field of + * the OEMCrypto_SampleDescription. * - * To phrase it another way: In 'cenc', the encrypted portions of the - * subsamples can be concatenated to form one continuous ciphertext. In - * 'cbcs', each encrypted portion of a subsample is a separate ciphertext. - * Each separate ciphertext begins with the IV specified in the iv field of - * the OEMCrypto_SampleDescription. + * INITIALIZATION VECTOR WITHIN SUBSAMPLES: * - * INITIALIZATION VECTOR WITHIN SUBSAMPLES: + * Once it has the IV for each subsample, OEMCrypto is responsible for + * correctly updating the IV for each crypto block of each encrypted + * subsample portion, as outlined in the ISO Common Encryption standard + * (ISO/IEC 23001-7:2016). Section 9.5.1 includes general information about + * IVs in subsample decryption. A summary of the ISO-CENC behavior follows: * - * Once it has the IV for each subsample, OEMCrypto is responsible for - * correctly updating the IV for each crypto block of each encrypted - * subsample portion, as outlined in the ISO Common Encryption standard - * (ISO/IEC 23001-7:2016). Section 9.5.1 includes general information about - * IVs in subsample decryption. A summary of the ISO-CENC behavior follows: + * For 'cenc', the subsample's IV is the counter value to be used for the + * initial encrypted block of the subsample. The IV length is the AES block + * size. For subsequent encrypted AES blocks, OEMCrypto must calculate the IV + * by incrementing the lower 64 bits (byte 8-15) of the IV value used for the + * previous block. The counter rolls over to zero when it reaches its maximum + * value (0xFFFFFFFFFFFFFFFF). The upper 64 bits (byte 0-7) of the IV do not + * change. * - * For 'cenc', the subsample's IV is the counter value to be used for the - * initial encrypted block of the subsample. The IV length is the AES block - * size. For subsequent encrypted AES blocks, OEMCrypto must calculate the IV - * by incrementing the lower 64 bits (byte 8-15) of the IV value used for the - * previous block. The counter rolls over to zero when it reaches its maximum - * value (0xFFFFFFFFFFFFFFFF). The upper 64 bits (byte 0-7) of the IV do not - * change. + * For 'cbcs', the subsample's IV is the initialization vector for the + * initial encrypted block of the subsample. Within each subsample, each + * crypto block is used as the IV for the next crypto block, as prescribed by + * AES-CBC. * - * For 'cbcs', the subsample's IV is the initialization vector for the - * initial encrypted block of the subsample. Within each subsample, each - * crypto block is used as the IV for the next crypto block, as prescribed by - * AES-CBC. + * NOTES: * - * NOTES: + * If the destination buffer is secure, an offset may be specified. + * OEMCrypto_DecryptCENC begins storing data buffers.output.secure.offset + * bytes after the beginning of the secure buffer. * - * If the destination buffer is secure, an offset may be specified. - * OEMCrypto_DecryptCENC begins storing data buffers.output.secure.offset - * bytes after the beginning of the secure buffer. + * If the session has an entry in the Usage Table, then OEMCrypto must update + * the time_of_last_decrypt. If the status of the entry is "unused", then + * change the status to "active" and set the time_of_first_decrypt. * - * If the session has an entry in the Usage Table, then OEMCrypto must update - * the time_of_last_decrypt. If the status of the entry is "unused", then - * change the status to "active" and set the time_of_first_decrypt. + * OEMCrypto cannot assume that the buffers of consecutive samples are + * consecutive in memory. * - * OEMCrypto cannot assume that the buffers of consecutive samples are - * consecutive in memory. + * A subsample may consist entirely of encrypted bytes or clear bytes. In + * this case, the clear or the encrypted part of the subsample will be zero, + * indicating that no bytes of that kind appear in the subsample. * - * A subsample may consist entirely of encrypted bytes or clear bytes. In - * this case, the clear or the encrypted part of the subsample will be zero, - * indicating that no bytes of that kind appear in the subsample. + * The ISO-CENC spec implicitly limits both the skip and encrypt values to be + * 4 bits, so they are at most 15. * - * The ISO-CENC spec implicitly limits both the skip and encrypt values to be - * 4 bits, so they are at most 15. + * (See drawing in "Widevine Modular DRM Security Integration Guide") * - * (See drawing in "Widevine Modular DRM Security Integration Guide") + * If OEMCrypto assembles all of the encrypted subsample portions into a + * single buffer and then decrypts it in one pass, it can assume that the + * block offset is 0. * - * If OEMCrypto assembles all of the encrypted subsample portions into a - * single buffer and then decrypts it in one pass, it can assume that the - * block offset is 0. + * (See drawing in "Widevine Modular DRM Security Integration Guide") * - * (See drawing in "Widevine Modular DRM Security Integration Guide") - * - * Verification: - * The total size of all the subsamples cannot exceed the total size of the - * input buffer. OEMCrypto integrations should validate this and return - * OEMCrypto_ERROR_UNKNOWN_FAILURE if the subsamples are larger than the - * input buffer. No decryption should be performed in this case. - * If the subsamples all contain only clear bytes, then no further - * verification is performed. This call shall copy clear data even when there - * are no keys loaded, or there is no selected key. - * If this is the first use of a key for this session, then OEMCrypto shall - * call ODK_AttemptFirstPlayback to update the session's clock values and - * verify playback is allowed. If this is not the first use of a key for this - * session, then OEMCrypto shall call ODK_UpdateLastPlaybackTime. See the - * document "License Duration and Renewal" for handling the return value of - * these ODK functions. - * The following checks should be performed if any subsamples contain any - * encrypted bytes. If any check fails, an error is returned, and no - * decryption is performed. - * 1. If the current key's control block has the Data_Path_Type bit set, - * then the API shall verify that the output buffer is secure or direct. - * If not, return OEMCrypto_ERROR_DECRYPT_FAILED. - * 2. If the current key control block has the bit Disable_Analog_Output - * set, then the device should disable analog video output. If the - * device has analog video output that cannot be disabled, then - * OEMCrypto_ERROR_ANALOG_OUTPUT is returned. (See note on delayed - * error conditions below) - * 3. If the current key's control block has the HDCP bit set, then the API - * shall verify that the buffer will be displayed locally, or output - * externally using HDCP only. If not, return - * OEMCrypto_ERROR_INSUFFICIENT_HDCP. (See note on delayed error - * conditions below) - * 4. If the current key's control block has a nonzero value for - * HDCP_Version, then the current version of HDCP for the device and the - * display combined will be compared against the version specified in - * the control block. If the current version is not at least as high as - * that in the control block, and the device is not able to restrict - * displays with HDCP levels lower than what's in the control block, - * return OEMCrypto_ERROR_INSUFFICIENT_HDCP. If the device is able to - * restrict those displays, return - * OEMCrypto_WARNING_MIXED_OUTPUT_PROTECTION. (See note on delayed - * error conditions below) - * 5. If the current session has an entry in the Usage Table, and the - * status of that entry is either kInactiveUsed or kInactiveUnused, then - * return the error OEMCrypto_ERROR_LICENSE_INACTIVE. - * 6. If a Decrypt Hash has been initialized via OEMCrypto_SetDecryptHash, - * and the current key's control block does not have the - * Allow_Hash_Verification bit set, then do not compute a hash and - * return OEMCrypto_ERROR_UNKNOWN_FAILURE. + * @verification + * The total size of all the subsamples cannot exceed the total size of the + * input buffer. OEMCrypto integrations should validate this and return + * OEMCrypto_ERROR_UNKNOWN_FAILURE if the subsamples are larger than the + * input buffer. No decryption should be performed in this case. + * If the subsamples all contain only clear bytes, then no further + * verification is performed. This call shall copy clear data even when there + * are no keys loaded, or there is no selected key. + * If this is the first use of a key for this session, then OEMCrypto shall + * call ODK_AttemptFirstPlayback to update the session's clock values and + * verify playback is allowed. If this is not the first use of a key for this + * session, then OEMCrypto shall call ODK_UpdateLastPlaybackTime. See the + * document "License Duration and Renewal" for handling the return value of + * these ODK functions. + * The following checks should be performed if any subsamples contain any + * encrypted bytes. If any check fails, an error is returned, and no + * decryption is performed. + * 1. If the current key's control block has the Data_Path_Type bit set, + * then the API shall verify that the output buffer is secure or direct. + * If not, return OEMCrypto_ERROR_DECRYPT_FAILED. + * 2. If the current key control block has the bit Disable_Analog_Output + * set, then the device should disable analog video output. If the + * device has analog video output that cannot be disabled, then + * OEMCrypto_ERROR_ANALOG_OUTPUT is returned. (See note on delayed + * error conditions below) + * 3. If the current key's control block has the HDCP bit set, then the API + * shall verify that the buffer will be displayed locally, or output + * externally using HDCP only. If not, return + * OEMCrypto_ERROR_INSUFFICIENT_HDCP. (See note on delayed error + * conditions below) + * 4. If the current key's control block has a nonzero value for + * HDCP_Version, then the current version of HDCP for the device and the + * display combined will be compared against the version specified in + * the control block. If the current version is not at least as high as + * that in the control block, and the device is not able to restrict + * displays with HDCP levels lower than what's in the control block, + * return OEMCrypto_ERROR_INSUFFICIENT_HDCP. If the device is able to + * restrict those displays, return + * OEMCrypto_WARNING_MIXED_OUTPUT_PROTECTION. (See note on delayed + * error conditions below) + * 5. If the current session has an entry in the Usage Table, and the + * status of that entry is either kInactiveUsed or kInactiveUnused, then + * return the error OEMCrypto_ERROR_LICENSE_INACTIVE. + * 6. If a Decrypt Hash has been initialized via OEMCrypto_SetDecryptHash, + * and the current key's control block does not have the + * Allow_Hash_Verification bit set, then do not compute a hash and + * return OEMCrypto_ERROR_UNKNOWN_FAILURE. * * Delayed Error Conditions * - * On some devices, the HDCP subsystem is not directly connected to the - * OEMCrypto TA. This means that returning the error - * OEMCrypto_ERROR_INSUFFICIENT_HDCP at the time of the decrypt call is a - * performance hit. However, some devices have the ability to tag output - * buffers with security requirements, such as the required HDCP level. - * For those devices, when a call to OEMCrypto_DecryptCENC is made using a - * key that requires HDCP output, and if the HDCP level on the output does - * not meet the required level. - * - OEMCrypto may tag the output buffer as requiring HDCP at the required - * level and return OEMCrypto_SUCCESS. - * - Output shall not be sent to the display. - * - On the second or third call to OEMCrypto_DecryptCENC with the same - * key, OEMCrypto shall return OEMCrypto_ERROR_INSUFFICIENT_HDCP. - * For those devices, when a call to OEMCrypto_DecryptCENC is made using a - * key that requires HDCP output, and if the HDCP level on some of the - * displays does not meet the required level. - * - OEMCrypto may tag the output buffer as requiring HDCP at the required - * level and return OEMCrypto_SUCCESS. - * - Output shall only be sent to the display with sufficient output - * control, e.g. the local display. - * - On the second or third call to OEMCrypto_DecryptCENC with the same - * key, OEMCrypto shall return OEMCrypto_WARNING_MIXED_OUTPUT_PROTECTION. - * In either case, a call to OEMCrypto_GetHDCPCapability shall return the - * current HDCP level. + * On some devices, the HDCP subsystem is not directly connected to the + * OEMCrypto TA. This means that returning the error + * OEMCrypto_ERROR_INSUFFICIENT_HDCP at the time of the decrypt call is a + * performance hit. However, some devices have the ability to tag output + * buffers with security requirements, such as the required HDCP level. + * For those devices, when a call to OEMCrypto_DecryptCENC is made using a + * key that requires HDCP output, and if the HDCP level on the output does + * not meet the required level. + * - OEMCrypto may tag the output buffer as requiring HDCP at the required + * level and return OEMCrypto_SUCCESS. + * - Output shall not be sent to the display. + * - On the second or third call to OEMCrypto_DecryptCENC with the same + * key, OEMCrypto shall return OEMCrypto_ERROR_INSUFFICIENT_HDCP. + * For those devices, when a call to OEMCrypto_DecryptCENC is made using a + * key that requires HDCP output, and if the HDCP level on some of the + * displays does not meet the required level. + * - OEMCrypto may tag the output buffer as requiring HDCP at the required + * level and return OEMCrypto_SUCCESS. + * - Output shall only be sent to the display with sufficient output + * control, e.g. the local display. + * - On the second or third call to OEMCrypto_DecryptCENC with the same + * key, OEMCrypto shall return OEMCrypto_WARNING_MIXED_OUTPUT_PROTECTION. + * In either case, a call to OEMCrypto_GetHDCPCapability shall return the + * current HDCP level. * - * Parameters: - * [in] session: Crypto session identifier. The crypto session in which - * decrypt is to be performed. - * [in] samples: A caller-owned array of OEMCrypto_SampleDescription - * structures. Each entry in this array contains one sample of the - * content. - * [in] samples_length: The length of the array pointed to by the samples - * parameter. - * [in] pattern: A caller-owned structure indicating the encrypt/skip pattern - * as specified in the ISO-CENC standard. + * @param[in] session: Crypto session identifier. The crypto session in which + * decrypt is to be performed. + * @param[in] samples: A caller-owned array of OEMCrypto_SampleDescription + * structures. Each entry in this array contains one sample of the content. + * @param[in] samples_length: The length of the array pointed to by the samples + * parameter. + * @param[in] pattern: A caller-owned structure indicating the encrypt/skip + * pattern as specified in the ISO-CENC standard. * - * Returns: - * OEMCrypto_SUCCESS - * OEMCrypto_ERROR_NO_DEVICE_KEY - * OEMCrypto_ERROR_INVALID_SESSION - * OEMCrypto_ERROR_INVALID_CONTEXT - * OEMCrypto_ERROR_DECRYPT_FAILED - * OEMCrypto_ERROR_KEY_EXPIRED - * OEMCrypto_ERROR_INSUFFICIENT_HDCP - * OEMCrypto_ERROR_ANALOG_OUTPUT - * OEMCrypto_ERROR_INSUFFICIENT_RESOURCES - * OEMCrypto_ERROR_UNKNOWN_FAILURE - * OEMCrypto_ERROR_BUFFER_TOO_LARGE - * OEMCrypto_ERROR_OUTPUT_TOO_LARGE - * OEMCrypto_ERROR_SESSION_LOST_STATE - * OEMCrypto_ERROR_SYSTEM_INVALIDATED + * @retval OEMCrypto_SUCCESS + * @retval OEMCrypto_ERROR_NO_DEVICE_KEY + * @retval OEMCrypto_ERROR_INVALID_SESSION + * @retval OEMCrypto_ERROR_INVALID_CONTEXT + * @retval OEMCrypto_ERROR_DECRYPT_FAILED + * @retval OEMCrypto_ERROR_KEY_EXPIRED + * @retval OEMCrypto_ERROR_INSUFFICIENT_HDCP + * @retval OEMCrypto_ERROR_ANALOG_OUTPUT + * @retval OEMCrypto_ERROR_INSUFFICIENT_RESOURCES + * @retval OEMCrypto_ERROR_UNKNOWN_FAILURE + * @retval OEMCrypto_ERROR_BUFFER_TOO_LARGE + * @retval OEMCrypto_ERROR_OUTPUT_TOO_LARGE + * @retval OEMCrypto_ERROR_SESSION_LOST_STATE + * @retval OEMCrypto_ERROR_SYSTEM_INVALIDATED * - * Buffer Sizes: + * @buffer_size * OEMCrypto shall support subsample sizes and total input buffer sizes as * specified by its resource rating tier. * OEMCrypto shall return OEMCrypto_ERROR_BUFFER_TOO_LARGE if the buffer is @@ -2411,14 +2448,14 @@ OEMCryptoResult OEMCrypto_SelectKey(OEMCrypto_SESSION session, * application, which can decide to skip the current frame of video or to * switch to a lower resolution. * - * Threading: + * @threading * This is a "Session Function" and may be called simultaneously with session * functions for other sessions but not simultaneously with other functions * for this session. It will not be called simultaneously with initialization * or usage table functions. It is as if the CDM holds a write lock for this * session, and a read lock on the OEMCrypto system. * - * Version: + * @version * This method changed in API version 16. This method changed its name in API * version 11. */ @@ -2428,65 +2465,57 @@ OEMCryptoResult OEMCrypto_DecryptCENC( size_t samples_length, // the number of samples. const OEMCrypto_CENCEncryptPatternDesc* pattern); -/* - * OEMCrypto_CopyBuffer +/** + * Copies the payload in the buffer referenced by the *data parameter into + * the buffer referenced by the out_buffer parameter. The data is simply + * copied. The definition of OEMCrypto_DestBufferDesc and subsample_flags are + * the same as in OEMCrypto_DecryptCENC, above. * - * Description: - * Copies the payload in the buffer referenced by the *data parameter into - * the buffer referenced by the out_buffer parameter. The data is simply - * copied. The definition of OEMCrypto_DestBufferDesc and subsample_flags are - * the same as in OEMCrypto_DecryptCENC, above. + * The main difference between this and DecryptCENC is that this function may be + * used before a license is loaded into a session. In particular, an application + * will use this to copy the clear leader of a video to a secure buffer while + * the license request is being generated, sent to the server, and the response + * is being processed. This functionality is needed because an application may + * not have read or write access to a secure destination buffer. * - * The main difference between this and DecryptCENC is that this function - * does not need an open session, and it may be called concurrently with - * other functions on a multithreaded system. In particular, an application - * will use this to copy the clear leader of a video to a secure buffer while - * the license request is being generated, sent to the server, and the - * response is being processed. This functionality is needed because an - * application may not have read or write access to a secure destination - * buffer. + * NOTES: * - * NOTES: + * This method may be called several times before the data is used. The first + * buffer in a chunk of data will have the OEMCrypto_FirstSubsample bit set + * in subsample_flags. The last buffer in a chunk of data will have the + * OEMCrypto_LastSubsample bit set in subsample_flags. The data will not be + * used until after OEMCrypto_LastSubsample has been set. If an + * implementation copies data immediately, it may ignore subsample_flags. * - * This method may be called several times before the data is used. The first - * buffer in a chunk of data will have the OEMCrypto_FirstSubsample bit set - * in subsample_flags. The last buffer in a chunk of data will have the - * OEMCrypto_LastSubsample bit set in subsample_flags. The data will not be - * used until after OEMCrypto_LastSubsample has been set. If an - * implementation copies data immediately, it may ignore subsample_flags. + * If the destination buffer is secure, an offset may be specified. + * CopyBuffer begins storing data out_buffer->secure.offset bytes after the + * beginning of the secure buffer. * - * If the destination buffer is secure, an offset may be specified. - * CopyBuffer begins storing data out_buffer->secure.offset bytes after the - * beginning of the secure buffer. + * @verification + * The following checks should be performed. + * 1. If either data or out_buffer is null, return + * OEMCrypto_ERROR_INVALID_CONTEXT. * - * Verification: - * The following checks should be performed. - * 1. If either data or out_buffer is null, return - * OEMCrypto_ERROR_INVALID_CONTEXT. + * @param[in] session: crypto session identifier. + * @param[in] data_addr: An unaligned pointer to the buffer to be copied. + * @param[in] data_addr_length: The length of the buffer, in bytes. + * @param[in] out_buffer_descriptor: A caller-owned descriptor that specifies + * the handling of the byte stream. See OEMCrypto_DestbufferDesc for details. + * @param[in] subsample_flags: bitwise flags indicating if this is the first, + * middle, or last subsample in a chunk of data. 1 = first subsample, 2 = + * last subsample, 3 = both first and last subsample, 0 = neither first nor + * last subsample. * - * Parameters: - * [in] session: crypto session identifier. - * [in] data_addr: An unaligned pointer to the buffer to be copied. - * [in] data_addr_length: The length of the buffer, in bytes. - * [in] out_buffer_descriptor: A caller-owned descriptor that specifies the - * handling of the byte stream. See OEMCrypto_DestbufferDesc for - * details. - * [in] subsample_flags: bitwise flags indicating if this is the first, - * middle, or last subsample in a chunk of data. 1 = first subsample, 2 - * = last subsample, 3 = both first and last subsample, 0 = neither - * first nor last subsample. + * @retval OEMCrypto_SUCCESS + * @retval OEMCrypto_ERROR_INVALID_CONTEXT + * @retval OEMCrypto_ERROR_INSUFFICIENT_RESOURCES + * @retval OEMCrypto_ERROR_UNKNOWN_FAILURE + * @retval OEMCrypto_ERROR_BUFFER_TOO_LARGE + * @retval OEMCrypto_ERROR_OUTPUT_TOO_LARGE + * @retval OEMCrypto_ERROR_SESSION_LOST_STATE + * @retval OEMCrypto_ERROR_SYSTEM_INVALIDATED * - * Returns: - * OEMCrypto_SUCCESS - * OEMCrypto_ERROR_INVALID_CONTEXT - * OEMCrypto_ERROR_INSUFFICIENT_RESOURCES - * OEMCrypto_ERROR_UNKNOWN_FAILURE - * OEMCrypto_ERROR_BUFFER_TOO_LARGE - * OEMCrypto_ERROR_OUTPUT_TOO_LARGE - * OEMCrypto_ERROR_SESSION_LOST_STATE - * OEMCrypto_ERROR_SYSTEM_INVALIDATED - * - * Buffer Sizes: + * @buffer_size * OEMCrypto shall support subsample sizes and total input buffer sizes as * specified by its resource rating tier. * OEMCrypto shall return OEMCrypto_ERROR_BUFFER_TOO_LARGE if the buffer is @@ -2501,14 +2530,14 @@ OEMCryptoResult OEMCrypto_DecryptCENC( * application, which can decide to skip the current frame of video or to * switch to a lower resolution. * - * Threading: + * @threading * This is a "Session Function" and may be called simultaneously with session * functions for other sessions but not simultaneously with other functions * for this session. It will not be called simultaneously with initialization * or usage table functions. It is as if the CDM holds a write lock for this * session, and a read lock on the OEMCrypto system. * - * Version: + * @version * This method is changed in API version 15. */ OEMCryptoResult OEMCrypto_CopyBuffer( @@ -2517,19 +2546,16 @@ OEMCryptoResult OEMCrypto_CopyBuffer( const OEMCrypto_DestBufferDesc* out_buffer_descriptor, uint8_t subsample_flags); -/* - * OEMCrypto_Generic_Encrypt +/** + * This function encrypts a generic buffer of data using the current key. * - * Description: - * This function encrypts a generic buffer of data using the current key. + * If the session has an entry in the Usage Table, then OEMCrypto will update + * the time_of_last_decrypt. If the status of the entry is "unused", then + * change the status to "active" and set the time_of_first_decrypt. * - * If the session has an entry in the Usage Table, then OEMCrypto will update - * the time_of_last_decrypt. If the status of the entry is "unused", then - * change the status to "active" and set the time_of_first_decrypt. + * OEMCrypto shall be able to handle buffers at least 100 KiB long. * - * OEMCrypto shall be able to handle buffers at least 100 KiB long. - * - * Verification: + * @verification * The following checks should be performed. If any check fails, an error is * returned, and the data is not encrypted. * 1. The control bit for the current key shall have the Allow_Encrypt set. @@ -2544,43 +2570,41 @@ OEMCryptoResult OEMCrypto_CopyBuffer( * status of that entry is either kInactiveUsed or kInactiveUnused, then * return the error OEMCrypto_ERROR_LICENSE_INACTIVE. * - * Parameters: - * [in] session: crypto session identifier. - * [in] in_buffer: pointer to memory containing data to be encrypted. - * [in] in_buffer_length: length of the buffer, in bytes. The algorithm may - * restrict in_buffer_length to be a multiple of block size. - * [in] iv: IV for encrypting data. Size is 128 bits. - * [in] algorithm: Specifies which encryption algorithm to use. Currently, - * only CBC 128 mode is allowed for encryption. - * [out] out_buffer: pointer to buffer in which encrypted data should be - * stored. + * @param[in] session: crypto session identifier. + * @param[in] in_buffer: pointer to memory containing data to be encrypted. + * @param[in] in_buffer_length: length of the buffer, in bytes. The algorithm + * may restrict in_buffer_length to be a multiple of block size. + * @param[in] iv: IV for encrypting data. Size is 128 bits. + * @param[in] algorithm: Specifies which encryption algorithm to use. + * Currently, only CBC 128 mode is allowed for encryption. + * @param[out] out_buffer: pointer to buffer in which encrypted data should be + * stored. * - * Returns: - * OEMCrypto_SUCCESS success - * OEMCrypto_ERROR_KEY_EXPIRED - * OEMCrypto_ERROR_NO_DEVICE_KEY - * OEMCrypto_ERROR_INVALID_SESSION - * OEMCrypto_ERROR_INSUFFICIENT_RESOURCES - * OEMCrypto_ERROR_UNKNOWN_FAILURE - * OEMCrypto_ERROR_BUFFER_TOO_LARGE - * OEMCrypto_ERROR_SESSION_LOST_STATE - * OEMCrypto_ERROR_SYSTEM_INVALIDATED - * OEMCrypto_ERROR_NOT_IMPLEMENTED + * @retval OEMCrypto_SUCCESS success + * @retval OEMCrypto_ERROR_KEY_EXPIRED + * @retval OEMCrypto_ERROR_NO_DEVICE_KEY + * @retval OEMCrypto_ERROR_INVALID_SESSION + * @retval OEMCrypto_ERROR_INSUFFICIENT_RESOURCES + * @retval OEMCrypto_ERROR_UNKNOWN_FAILURE + * @retval OEMCrypto_ERROR_BUFFER_TOO_LARGE + * @retval OEMCrypto_ERROR_SESSION_LOST_STATE + * @retval OEMCrypto_ERROR_SYSTEM_INVALIDATED + * @retval OEMCrypto_ERROR_NOT_IMPLEMENTED * - * Buffer Sizes: + * @buffer_size * OEMCrypto shall support buffers sizes of at least 100 KiB for generic * crypto operations. * OEMCrypto shall return OEMCrypto_ERROR_BUFFER_TOO_LARGE if the buffer is * larger than the supported size. * - * Threading: + * @threading * This is a "Session Function" and may be called simultaneously with session * functions for other sessions but not simultaneously with other functions * for this session. It will not be called simultaneously with initialization * or usage table functions. It is as if the CDM holds a write lock for this * session, and a read lock on the OEMCrypto system. * - * Version: + * @version * This method changed in API version 16. */ OEMCryptoResult OEMCrypto_Generic_Encrypt( @@ -2588,19 +2612,16 @@ OEMCryptoResult OEMCrypto_Generic_Encrypt( size_t in_buffer_length, const uint8_t* iv, OEMCrypto_Algorithm algorithm, OEMCrypto_SharedMemory* out_buffer); -/* - * OEMCrypto_Generic_Decrypt +/** + * This function decrypts a generic buffer of data using the current key. * - * Description: - * This function decrypts a generic buffer of data using the current key. + * If the session has an entry in the Usage Table, then OEMCrypto will update + * the time_of_last_decrypt. If the status of the entry is "unused", then + * change the status to "active" and set the time_of_first_decrypt. * - * If the session has an entry in the Usage Table, then OEMCrypto will update - * the time_of_last_decrypt. If the status of the entry is "unused", then - * change the status to "active" and set the time_of_first_decrypt. + * OEMCrypto should be able to handle buffers at least 100 KiB long. * - * OEMCrypto should be able to handle buffers at least 100 KiB long. - * - * Verification: + * @verification * The following checks should be performed. If any check fails, an error is * returned, and the data is not decrypted. * 1. The control bit for the current key shall have the Allow_Decrypt set. @@ -2617,44 +2638,42 @@ OEMCryptoResult OEMCrypto_Generic_Encrypt( * status of that entry is either kInactiveUsed or kInactiveUnused, then * return the error OEMCrypto_ERROR_LICENSE_INACTIVE. * - * Parameters: - * [in] session: crypto session identifier. - * [in] in_buffer: pointer to memory containing data to be encrypted. - * [in] in_buffer_length: length of the buffer, in bytes. The algorithm may - * restrict in_buffer_length to be a multiple of block size. - * [in] iv: IV for encrypting data. Size is 128 bits. - * [in] algorithm: Specifies which encryption algorithm to use. Currently, - * only CBC 128 mode is allowed for decryption. - * [out] out_buffer: pointer to buffer in which decrypted data should be - * stored. + * @param[in] session: crypto session identifier. + * @param[in] in_buffer: pointer to memory containing data to be encrypted. + * @param[in] in_buffer_length: length of the buffer, in bytes. The algorithm + * may restrict in_buffer_length to be a multiple of block size. + * @param[in] iv: IV for encrypting data. Size is 128 bits. + * @param[in] algorithm: Specifies which encryption algorithm to use. + * Currently, only CBC 128 mode is allowed for decryption. + * @param[out] out_buffer: pointer to buffer in which decrypted data should be + * stored. * - * Returns: - * OEMCrypto_SUCCESS success - * OEMCrypto_ERROR_KEY_EXPIRED - * OEMCrypto_ERROR_DECRYPT_FAILED - * OEMCrypto_ERROR_NO_DEVICE_KEY - * OEMCrypto_ERROR_INVALID_SESSION - * OEMCrypto_ERROR_INSUFFICIENT_RESOURCES - * OEMCrypto_ERROR_UNKNOWN_FAILURE - * OEMCrypto_ERROR_BUFFER_TOO_LARGE - * OEMCrypto_ERROR_SESSION_LOST_STATE - * OEMCrypto_ERROR_SYSTEM_INVALIDATED - * OEMCrypto_ERROR_NOT_IMPLEMENTED + * @retval OEMCrypto_SUCCESS success + * @retval OEMCrypto_ERROR_KEY_EXPIRED + * @retval OEMCrypto_ERROR_DECRYPT_FAILED + * @retval OEMCrypto_ERROR_NO_DEVICE_KEY + * @retval OEMCrypto_ERROR_INVALID_SESSION + * @retval OEMCrypto_ERROR_INSUFFICIENT_RESOURCES + * @retval OEMCrypto_ERROR_UNKNOWN_FAILURE + * @retval OEMCrypto_ERROR_BUFFER_TOO_LARGE + * @retval OEMCrypto_ERROR_SESSION_LOST_STATE + * @retval OEMCrypto_ERROR_SYSTEM_INVALIDATED + * @retval OEMCrypto_ERROR_NOT_IMPLEMENTED * - * Buffer Sizes: + * @buffer_size * OEMCrypto shall support buffers sizes of at least 100 KiB for generic * crypto operations. * OEMCrypto shall return OEMCrypto_ERROR_BUFFER_TOO_LARGE if the buffer is * larger than the supported size. * - * Threading: + * @threading * This is a "Session Function" and may be called simultaneously with session * functions for other sessions but not simultaneously with other functions * for this session. It will not be called simultaneously with initialization * or usage table functions. It is as if the CDM holds a write lock for this * session, and a read lock on the OEMCrypto system. * - * Version: + * @version * This method changed in API version 16. */ OEMCryptoResult OEMCrypto_Generic_Decrypt( @@ -2662,17 +2681,14 @@ OEMCryptoResult OEMCrypto_Generic_Decrypt( size_t in_buffer_length, const uint8_t* iv, OEMCrypto_Algorithm algorithm, OEMCrypto_SharedMemory* out_buffer); -/* - * OEMCrypto_Generic_Sign +/** + * This function signs a generic buffer of data using the current key. * - * Description: - * This function signs a generic buffer of data using the current key. + * If the session has an entry in the Usage Table, then OEMCrypto will update + * the time_of_last_decrypt. If the status of the entry is "unused", then + * change the status to "active" and set the time_of_first_decrypt. * - * If the session has an entry in the Usage Table, then OEMCrypto will update - * the time_of_last_decrypt. If the status of the entry is "unused", then - * change the status to "active" and set the time_of_first_decrypt. - * - * Verification: + * @verification * The following checks should be performed. If any check fails, an error is * returned, and the data is not signed. * 1. The control bit for the current key shall have the Allow_Sign set. @@ -2686,44 +2702,43 @@ OEMCryptoResult OEMCrypto_Generic_Decrypt( * status of that entry is either kInactiveUsed or kInactiveUnused, then * return the error OEMCrypto_ERROR_LICENSE_INACTIVE. * - * Parameters: - * [in] session: crypto session identifier. - * [in] buffer: pointer to memory containing data to be encrypted. - * [in] buffer_length: length of the buffer, in bytes. - * [in] algorithm: Specifies which algorithm to use. - * [out] signature: pointer to buffer in which signature should be stored. - * May be null on the first call in order to find required buffer size. - * [in/out] signature_length: (in) length of the signature buffer, in bytes. - * (out) actual length of the signature + * @param[in] session: crypto session identifier. + * @param[in] buffer: pointer to memory containing data to be encrypted. + * @param[in] buffer_length: length of the buffer, in bytes. + * @param[in] algorithm: Specifies which algorithm to use. + * @param[out] signature: pointer to buffer in which signature should be + * stored. May be null on the first call in order to find required buffer + * size. + * @param[in,out] signature_length: (in) length of the signature buffer, in + * bytes. (out) actual length of the signature * - * Returns: - * OEMCrypto_SUCCESS success - * OEMCrypto_ERROR_KEY_EXPIRED - * OEMCrypto_ERROR_SHORT_BUFFER if signature buffer is not large enough to - * hold the output signature. - * OEMCrypto_ERROR_NO_DEVICE_KEY - * OEMCrypto_ERROR_INVALID_SESSION - * OEMCrypto_ERROR_INSUFFICIENT_RESOURCES - * OEMCrypto_ERROR_UNKNOWN_FAILURE - * OEMCrypto_ERROR_BUFFER_TOO_LARGE - * OEMCrypto_ERROR_SESSION_LOST_STATE - * OEMCrypto_ERROR_SYSTEM_INVALIDATED - * OEMCrypto_ERROR_NOT_IMPLEMENTED + * @retval OEMCrypto_SUCCESS success + * @retval OEMCrypto_ERROR_KEY_EXPIRED + * @retval OEMCrypto_ERROR_SHORT_BUFFER if signature buffer is not large enough + * to hold the output signature. + * @retval OEMCrypto_ERROR_NO_DEVICE_KEY + * @retval OEMCrypto_ERROR_INVALID_SESSION + * @retval OEMCrypto_ERROR_INSUFFICIENT_RESOURCES + * @retval OEMCrypto_ERROR_UNKNOWN_FAILURE + * @retval OEMCrypto_ERROR_BUFFER_TOO_LARGE + * @retval OEMCrypto_ERROR_SESSION_LOST_STATE + * @retval OEMCrypto_ERROR_SYSTEM_INVALIDATED + * @retval OEMCrypto_ERROR_NOT_IMPLEMENTED * - * Buffer Sizes: + * @buffer_size * OEMCrypto shall support buffers sizes of at least 100 KiB for generic * crypto operations. * OEMCrypto shall return OEMCrypto_ERROR_BUFFER_TOO_LARGE if the buffer is * larger than the supported size. * - * Threading: + * @threading * This is a "Session Function" and may be called simultaneously with session * functions for other sessions but not simultaneously with other functions * for this session. It will not be called simultaneously with initialization * or usage table functions. It is as if the CDM holds a write lock for this * session, and a read lock on the OEMCrypto system. * - * Version: + * @version * This method changed in API version 16. */ OEMCryptoResult OEMCrypto_Generic_Sign(OEMCrypto_SESSION session, @@ -2733,18 +2748,15 @@ OEMCryptoResult OEMCrypto_Generic_Sign(OEMCrypto_SESSION session, OEMCrypto_SharedMemory* signature, size_t* signature_length); -/* - * OEMCrypto_Generic_Verify +/** + * This function verifies the signature of a generic buffer of data using the + * current key. * - * Description: - * This function verifies the signature of a generic buffer of data using the - * current key. + * If the session has an entry in the Usage Table, then OEMCrypto will update + * the time_of_last_decrypt. If the status of the entry is "unused", then + * change the status to "active" and set the time_of_first_decrypt. * - * If the session has an entry in the Usage Table, then OEMCrypto will update - * the time_of_last_decrypt. If the status of the entry is "unused", then - * change the status to "active" and set the time_of_first_decrypt. - * - * Verification: + * @verification * The following checks should be performed. If any check fails, an error is * returned. * 1. The control bit for the current key shall have the Allow_Verify set. @@ -2764,41 +2776,39 @@ OEMCryptoResult OEMCrypto_Generic_Sign(OEMCrypto_SESSION session, * status of that entry is either kInactiveUsed or kInactiveUnused, then * return the error OEMCrypto_ERROR_LICENSE_INACTIVE. * - * Parameters: - * [in] session: crypto session identifier. - * [in] buffer: pointer to memory containing data to be encrypted. - * [in] buffer_length: length of the buffer, in bytes. - * [in] algorithm: Specifies which algorithm to use. - * [in] signature: pointer to buffer in which signature resides. - * [in] signature_length: length of the signature buffer, in bytes. + * @param[in] session: crypto session identifier. + * @param[in] buffer: pointer to memory containing data to be encrypted. + * @param[in] buffer_length: length of the buffer, in bytes. + * @param[in] algorithm: Specifies which algorithm to use. + * @param[in] signature: pointer to buffer in which signature resides. + * @param[in] signature_length: length of the signature buffer, in bytes. * - * Returns: - * OEMCrypto_SUCCESS success - * OEMCrypto_ERROR_KEY_EXPIRED - * OEMCrypto_ERROR_SIGNATURE_FAILURE - * OEMCrypto_ERROR_NO_DEVICE_KEY - * OEMCrypto_ERROR_INVALID_SESSION - * OEMCrypto_ERROR_INSUFFICIENT_RESOURCES - * OEMCrypto_ERROR_UNKNOWN_FAILURE - * OEMCrypto_ERROR_BUFFER_TOO_LARGE - * OEMCrypto_ERROR_SESSION_LOST_STATE - * OEMCrypto_ERROR_SYSTEM_INVALIDATED - * OEMCrypto_ERROR_NOT_IMPLEMENTED + * @retval OEMCrypto_SUCCESS success + * @retval OEMCrypto_ERROR_KEY_EXPIRED + * @retval OEMCrypto_ERROR_SIGNATURE_FAILURE + * @retval OEMCrypto_ERROR_NO_DEVICE_KEY + * @retval OEMCrypto_ERROR_INVALID_SESSION + * @retval OEMCrypto_ERROR_INSUFFICIENT_RESOURCES + * @retval OEMCrypto_ERROR_UNKNOWN_FAILURE + * @retval OEMCrypto_ERROR_BUFFER_TOO_LARGE + * @retval OEMCrypto_ERROR_SESSION_LOST_STATE + * @retval OEMCrypto_ERROR_SYSTEM_INVALIDATED + * @retval OEMCrypto_ERROR_NOT_IMPLEMENTED * - * Buffer Sizes: + * @buffer_size * OEMCrypto shall support buffers sizes of at least 100 KiB for generic * crypto operations. * OEMCrypto shall return OEMCrypto_ERROR_BUFFER_TOO_LARGE if the buffer is * larger than the supported size. * - * Threading: + * @threading * This is a "Session Function" and may be called simultaneously with session * functions for other sessions but not simultaneously with other functions * for this session. It will not be called simultaneously with initialization * or usage table functions. It is as if the CDM holds a write lock for this * session, and a read lock on the OEMCrypto system. * - * Version: + * @version * This method changed in API version 16. */ OEMCryptoResult OEMCrypto_Generic_Verify( @@ -2806,62 +2816,62 @@ OEMCryptoResult OEMCrypto_Generic_Verify( size_t buffer_length, OEMCrypto_Algorithm algorithm, const OEMCrypto_SharedMemory* signature, size_t signature_length); -/* - * OEMCrypto_WrapKeyboxOrOEMCert +/// @} + +/// @addtogroup factory_provision +/// @{ + +/** + * A device should be provisioned at the factory with either an OEM + * Certificate or a keybox. We will call this data the root of trust. During + * manufacturing, the root of trust should be encrypted with the OEM root key + * and stored on the file system in a region that will not be erased during + * factory reset. This function may be used by legacy systems that use the + * two-step WrapKeyboxOrOEMCert/InstallKeyboxOrOEMCert approach. When the + * Widevine DRM plugin initializes, it will look for a wrapped root of trust + * in the file /factory/wv.keys and install it into the security processor by + * calling OEMCrypto_InstallKeyboxOrOEMCert(). * - * Description: - * A device should be provisioned at the factory with either an OEM - * Certificate or a keybox. We will call this data the root of trust. During - * manufacturing, the root of trust should be encrypted with the OEM root key - * and stored on the file system in a region that will not be erased during - * factory reset. This function may be used by legacy systems that use the - * two-step WrapKeyboxOrOEMCert/InstallKeyboxOrOEMCert approach. When the - * Widevine DRM plugin initializes, it will look for a wrapped root of trust - * in the file /factory/wv.keys and install it into the security processor by - * calling OEMCrypto_InstallKeyboxOrOEMCert(). + * Figure 10. OEMCrypto_WrapKeyboxOrOEMCert Operation * - * Figure 10. OEMCrypto_WrapKeyboxOrOEMCert Operation + * OEMCrypto_WrapKeyboxOrOEMCert() is used to generate an OEM-encrypted root + * of trust that may be passed to OEMCrypto_InstallKeyboxOrOEMCert() for + * provisioning. The root of trust may be either passed in the clear or + * previously encrypted with a transport key. If a transport key is supplied, + * the keybox is first decrypted with the transport key before being wrapped + * with the OEM root key. This function is only needed if the root of trust + * provisioning method involves saving the keybox or OEM Certificate to the + * file system. * - * OEMCrypto_WrapKeyboxOrOEMCert() is used to generate an OEM-encrypted root - * of trust that may be passed to OEMCrypto_InstallKeyboxOrOEMCert() for - * provisioning. The root of trust may be either passed in the clear or - * previously encrypted with a transport key. If a transport key is supplied, - * the keybox is first decrypted with the transport key before being wrapped - * with the OEM root key. This function is only needed if the root of trust - * provisioning method involves saving the keybox or OEM Certificate to the - * file system. + * @param[in] keybox_or_cert: pointer to root of trust data to encrypt -- this + * is either a keybox or an OEM Certificate private key. May be NULL on the + * first call to test the size of the wrapped keybox. The keybox may either + * be clear or previously encrypted. + * @param[in] keybox_or_cert_length: length the keybox or cert data in bytes + * @param[out] wrapped_keybox_or_cert: Pointer to wrapped keybox or cert + * @param[in,out] wrapped_keybox_or_cert_length: Pointer to the length of the + * wrapped keybox or certificate key in bytes + * @param[in] transport_key: Optional. AES transport key. If provided, the + * keybox_or_cert parameter was previously encrypted with this key. The + * keybox will be decrypted with the transport key using AES-CBC and a null + * IV. + * @param[in] transport_key_length: Optional. Number of bytes in the + * transport_key, if used. * - * Parameters: - * [in] keybox_or_cert - pointer to root of trust data to encrypt -- this is - * either a keybox or an OEM Certificate private key. May be NULL on - * the first call to test size of wrapped keybox. The keybox may either - * be clear or previously encrypted. - * [in] keybox_or_cert_length - length the keybox or cert data in bytes - * [out] wrapped_keybox_or_cert – Pointer to wrapped keybox or cert - * [out] wrapped_keybox_or_cert_length – Pointer to the length of the wrapped - * keybox or certificate key in bytes - * [in] transport_key – Optional. AES transport key. If provided, the - * keybox_or_cert parameter was previously encrypted with this key. The - * keybox will be decrypted with the transport key using AES-CBC and a - * null IV. - * [in] transport_key_length – Optional. Number of bytes in the - * transport_key, if used. + * @retval OEMCrypto_SUCCESS success + * @retval OEMCrypto_ERROR_WRITE_KEYBOX failed to encrypt the keybox + * @retval OEMCrypto_ERROR_SHORT_BUFFER if keybox is provided as NULL, to + * determine the size of the wrapped keybox + * @retval OEMCrypto_ERROR_INSUFFICIENT_RESOURCES + * @retval OEMCrypto_ERROR_NOT_IMPLEMENTED + * @retval OEMCrypto_ERROR_SYSTEM_INVALIDATED * - * Returns: - * OEMCrypto_SUCCESS success - * OEMCrypto_ERROR_WRITE_KEYBOX failed to encrypt the keybox - * OEMCrypto_ERROR_SHORT_BUFFER if keybox is provided as NULL, to determine - * the size of the wrapped keybox - * OEMCrypto_ERROR_INSUFFICIENT_RESOURCES - * OEMCrypto_ERROR_NOT_IMPLEMENTED - * OEMCrypto_ERROR_SYSTEM_INVALIDATED - * - * Threading: + * @threading * This is an "Initialization and Termination Function" and will not be * called simultaneously with any other function, as if the CDM holds a write * lock on the OEMCrypto system. * - * Version: + * @version * This method is supported in all API versions. */ OEMCryptoResult OEMCrypto_WrapKeyboxOrOEMCert( @@ -2869,714 +2879,606 @@ OEMCryptoResult OEMCrypto_WrapKeyboxOrOEMCert( uint8_t* wrapped_keybox_or_cert, size_t* wrapped_keybox_or_cert_length, const uint8_t* transport_key, size_t transport_key_length); -/* - * OEMCrypto_InstallKeyboxOrOEMCert +/** + * Decrypts a wrapped root of trust and installs it in the security + * processor. The root of trust is unwrapped then encrypted with the OEM root + * key. This function is called from the Widevine DRM plugin at + * initialization time if there is no valid root of trust installed. It looks + * for wrapped data in the file /factory/wv.keys and if it is present, will + * read the file and call OEMCrypto_InstallKeyboxOrOEMCert() with the + * contents of the file. This function is only needed if the factory + * provisioning method involves saving the keybox or OEM Certificate to the + * file system. * - * Description: - * Decrypts a wrapped root of trust and installs it in the security - * processor. The root of trust is unwrapped then encrypted with the OEM root - * key. This function is called from the Widevine DRM plugin at - * initialization time if there is no valid root of trust installed. It looks - * for wrapped data in the file /factory/wv.keys and if it is present, will - * read the file and call OEMCrypto_InstallKeyboxOrOEMCert() with the - * contents of the file. This function is only needed if the factory - * provisioning method involves saving the keybox or OEM Certificate to the - * file system. + * @param[in] keybox_or_cert: pointer to encrypted data as input + * @param[in] keybox_or_cert_length: length of the data in bytes * - * Parameters: - * [in] keybox_or_cert - pointer to encrypted data as input - * [in] keybox_or_cert_length - length of the data in bytes + * @retval OEMCrypto_SUCCESS success + * @retval OEMCrypto_ERROR_BAD_MAGIC + * @retval OEMCrypto_ERROR_BAD_CRC + * @retval OEMCrypto_ERROR_INSUFFICIENT_RESOURCES + * @retval OEMCrypto_ERROR_NOT_IMPLEMENTED + * @retval OEMCrypto_ERROR_SYSTEM_INVALIDATED * - * Returns: - * OEMCrypto_SUCCESS success - * OEMCrypto_ERROR_BAD_MAGIC - * OEMCrypto_ERROR_BAD_CRC - * OEMCrypto_ERROR_INSUFFICIENT_RESOURCES - * OEMCrypto_ERROR_NOT_IMPLEMENTED - * OEMCrypto_ERROR_SYSTEM_INVALIDATED - * - * Threading: + * @threading * This is an "Initialization and Termination Function" and will not be * called simultaneously with any other function, as if the CDM holds a write * lock on the OEMCrypto system. * - * Version: + * @version * This method is supported in all API versions. */ OEMCryptoResult OEMCrypto_InstallKeyboxOrOEMCert(const uint8_t* keybox_or_cert, size_t keybox_or_cert_length); -/* - * OEMCrypto_GetProvisioningMethod +/** + * This function is for OEMCrypto to tell the layer above what provisioning + * method it uses: keybox or OEM certificate. * - * Description: - * This function is for OEMCrypto to tell the layer above what provisioning - * method it uses: keybox or OEM certificate. + * @retval OEMCrypto_DrmCertificate means the device has a DRM certificate built + * into the system. This cannot be used by level 1 devices. This + * provisioning method is deprecated and should not be used on new + * devices. OEMCertificate provisioning should be used instead. + * @retval OEMCrypto_Keybox means the device has a unique keybox. For level 1 + * devices this keybox must be securely installed by the device + * manufacturer. + * @retval OEMCrypto_OEMCertificate means the device has a factory installed OEM + * certificate. This is also called Provisioning 3.0. + * @retval OEMCrypto_ProvisioningError indicates a serious problem with the + * OEMCrypto library. * - * Parameters: - * none - * - * Returns: - * - DrmCertificate means the device has a DRM certificate built into the - * system. This cannot be used by level 1 devices. This provisioning - * method is deprecated and should not be used on new devices. - * OEMCertificate provisioning should be used instead. - * - Keybox means the device has a unique keybox. For level 1 devices this - * keybox must be securely installed by the device manufacturer. - * - OEMCertificate means the device has a factory installed OEM - * certificate. This is also called Provisioning 3.0. - * - ProvisioningError indicates a serious problem with the OEMCrypto - * library. - * - * Threading: + * @threading * This is a "Property Function" and may be called simultaneously with any * other property function or session function, but not any initialization or * usage table function, as if the CDM holds a read lock on the OEMCrypto * system. * - * Version: + * @version * This method is new API version 12. */ OEMCrypto_ProvisioningMethod OEMCrypto_GetProvisioningMethod(void); -/* - * OEMCrypto_IsKeyboxOrOEMCertValid +/** + * If the device has a keybox, this validates the Widevine Keybox loaded into + * the security processor device. This method verifies two fields in the + * keybox: * - * Description: - * If the device has a keybox, this validates the Widevine Keybox loaded into - * the security processor device. This method verifies two fields in the - * keybox: + * - Verify the MAGIC field contains a valid signature (such as, + * 'k''b''o''x'). + * - Compute the CRC using CRC-32-POSIX-1003.2 standard and compare the + * checksum to the CRC stored in the Keybox. + * The CRC is computed over the entire Keybox excluding the 4 bytes of the + * CRC (for example, Keybox[0..123]). For a description of the fields stored + * in the keybox, see Keybox Definition. * - * - Verify the MAGIC field contains a valid signature (such as, - * 'k''b''o''x'). - * - Compute the CRC using CRC-32-POSIX-1003.2 standard and compare the - * checksum to the CRC stored in the Keybox. - * The CRC is computed over the entire Keybox excluding the 4 bytes of the - * CRC (for example, Keybox[0..123]). For a description of the fields stored - * in the keybox, see Keybox Definition. + * If the device has an OEM Certificate, this validates the certificate + * private key. * - * If the device has an OEM Certificate, this validates the certificate - * private key. + * @retval OEMCrypto_SUCCESS + * @retval OEMCrypto_ERROR_BAD_MAGIC + * @retval OEMCrypto_ERROR_BAD_CRC + * @retval OEMCrypto_ERROR_KEYBOX_INVALID + * @retval OEMCrypto_ERROR_INVALID_RSA_KEY + * @retval OEMCrypto_ERROR_SYSTEM_INVALIDATED * - * Parameters: - * none - * - * Returns: - * OEMCrypto_SUCCESS - * OEMCrypto_ERROR_BAD_MAGIC - * OEMCrypto_ERROR_BAD_CRC - * OEMCrypto_ERROR_KEYBOX_INVALID - * OEMCrypto_ERROR_INVALID_RSA_KEY - * OEMCrypto_ERROR_SYSTEM_INVALIDATED - * - * Threading: + * @threading * This is a "Property Function" and may be called simultaneously with any * other property function or session function, but not any initialization or * usage table function, as if the CDM holds a read lock on the OEMCrypto * system. * - * Version: + * @version * This method is supported in all API versions. */ OEMCryptoResult OEMCrypto_IsKeyboxOrOEMCertValid(void); -/* - * OEMCrypto_GetDeviceID +/** + * Return a device unique id. For devices with a keybox, retrieve the + * DeviceID from the Keybox. For devices that have an OEM Certificate instead + * of a keybox, it should set the device ID to a device-unique string, such + * as the device serial number. The ID should be device-unique and it should + * be stable -- i.e. it should not change across a device reboot or a system + * upgrade. This shall match the device id found in the core provisioning + * request message. The maximum length of the device id is 64 bytes. The + * device ID field in a keybox is 32 bytes. * - * Description: - * Return a device unique id. For devices with a keybox, retrieve the - * DeviceID from the Keybox. For devices that have an OEM Certificate instead - * of a keybox, it should set the device ID to a device-unique string, such - * as the device serial number. The ID should be device-unique and it should - * be stable -- i.e. it should not change across a device reboot or a system - * upgrade. This shall match the device id found in the core provisioning - * request message. The maximum length of the device id is 64 bytes. The - * device ID field in a keybox is 32 bytes. + * @param[out] device_id: pointer to the buffer that receives the Device ID. + * @param[in,out] device_id_length - on input, size of the caller's device ID + * buffer. On output, the number of bytes written into the buffer. * - * Parameters: - * [out] device_id - pointer to the buffer that receives the Device ID. - * [in/out] device_id_length – on input, size of the caller's device ID - * buffer. On output, the number of bytes written into the buffer. + * @retval OEMCrypto_SUCCESS success + * @retval OEMCrypto_ERROR_SHORT_BUFFER if the buffer is too small to return + * device ID + * @retval OEMCrypto_ERROR_NO_DEVICEID failed to return Device Id + * @retval OEMCrypto_ERROR_SYSTEM_INVALIDATED * - * Returns: - * OEMCrypto_SUCCESS success - * OEMCrypto_ERROR_SHORT_BUFFER if the buffer is too small to return device ID - * OEMCrypto_ERROR_NO_DEVICEID failed to return Device Id - * OEMCrypto_ERROR_SYSTEM_INVALIDATED - * - * Threading: + * @threading * This is a "Property Function" and may be called simultaneously with any * other property function or session function, but not any initialization or * usage table function, as if the CDM holds a read lock on the OEMCrypto * system. * - * Version: + * @version * This method is supported in all API versions. */ OEMCryptoResult OEMCrypto_GetDeviceID(uint8_t* device_id, size_t* device_id_length); -/* - * OEMCrypto_GetKeyData +/// @} + +/// @addtogroup keybox +/// @{ + +/** + * Return the Key Data field from the Keybox. * - * Description: - * Return the Key Data field from the Keybox. + * @param[out] key_data: pointer to the buffer to hold the Key Data field from + * the Keybox + * @param[in,out] key_data_length: on input, the allocated buffer size. On + * output, the number of bytes in Key Data * - * Parameters: - * [out] keyData - pointer to the buffer to hold the Key Data field from the - * Keybox - * [in/out] keyDataLength – on input, the allocated buffer size. On output, - * the number of bytes in Key Data + * @retval OEMCrypto_SUCCESS success + * @retval OEMCrypto_ERROR_SHORT_BUFFER if the buffer is too small to return + * KeyData + * @retval OEMCrypto_ERROR_NO_KEYDATA + * @retval OEMCrypto_ERROR_NOT_IMPLEMENTED: this function is for + * Provisioning 2.0 only. + * @retval OEMCrypto_ERROR_SYSTEM_INVALIDATED * - * Returns: - * OEMCrypto_SUCCESS success - * OEMCrypto_ERROR_SHORT_BUFFER if the buffer is too small to return KeyData - * OEMCrypto_ERROR_NO_KEYDATA - * OEMCrypto_ERROR_NOT_IMPLEMENTED - this function is for Provisioning 2.0 - * only. - * OEMCrypto_ERROR_SYSTEM_INVALIDATED - * - * Threading: + * @threading * This is a "Property Function" and may be called simultaneously with any * other property function or session function, but not any initialization or * usage table function, as if the CDM holds a read lock on the OEMCrypto * system. * - * Version: + * @version * This method is supported in all API versions. */ OEMCryptoResult OEMCrypto_GetKeyData(uint8_t* key_data, size_t* key_data_length); -/* - * OEMCrypto_LoadTestKeybox +/** + * Temporarily use the specified test keybox until the next call to + * OEMCrypto_Terminate. This allows a standard suite of unit tests to be run + * on a production device without permanently changing the keybox. Using the + * test keybox is not persistent. OEMCrypto cannot assume that this keybox is + * the same as previous keyboxes used for testing. * - * Description: - * Temporarily use the specified test keybox until the next call to - * OEMCrypto_Terminate. This allows a standard suite of unit tests to be run - * on a production device without permanently changing the keybox. Using the - * test keybox is not persistent. OEMCrypto cannot assume that this keybox is - * the same as previous keyboxes used for testing. + * Devices that use an OEM Certificate instead of a keybox (i.e. Provisioning + * 3.0) do not need to support this functionality, and may return + * OEMCrypto_ERROR_NOT_IMPLEMENTED. * - * Devices that use an OEM Certificate instead of a keybox (i.e. Provisioning - * 3.0) do not need to support this functionality, and may return - * OEMCrypto_ERROR_NOT_IMPLEMENTED. + * @param[in] buffer: pointer to memory containing test keybox, in binary form. + * @param[in] buffer_length: length of the buffer, in bytes. * - * Parameters: - * [in] buffer: pointer to memory containing test keybox, in binary form. - * [in] buffer_length: length of the buffer, in bytes. + * @retval OEMCrypto_SUCCESS success + * @retval OEMCrypto_ERROR_INSUFFICIENT_RESOURCES + * @retval OEMCrypto_ERROR_NOT_IMPLEMENTED this function is for + * Provisioning 2.0 only. + * @retval OEMCrypto_ERROR_SYSTEM_INVALIDATED * - * Returns: - * OEMCrypto_SUCCESS success - * OEMCrypto_ERROR_INSUFFICIENT_RESOURCES - * OEMCrypto_ERROR_NOT_IMPLEMENTED - this function is for Provisioning 2.0 - * only. - * OEMCrypto_ERROR_SYSTEM_INVALIDATED - * - * Threading: + * @threading * This is an "Initialization and Termination Function" and will not be * called simultaneously with any other function, as if the CDM holds a write * lock on the OEMCrypto system. It is called after OEMCrypto_Initialize and * after OEMCrypto_GetProvisioningMethod and only if the provisoining method * is OEMCrypto_Keybox, * - * Version: + * @version * This method changed in API version 14. */ OEMCryptoResult OEMCrypto_LoadTestKeybox(const uint8_t* buffer, size_t buffer_length); -/* - * OEMCrypto_LoadOEMPrivateKey +/// @} + +/// @addtogroup oem_cert +/// @{ +/** + * After a call to this function, all session functions using an RSA key + * should use the OEM certificate's private RSA key. See the section above + * discussing Provisioning 3.0. * - * Description: - * After a call to this function, all session functions using an RSA key - * should use the OEM certificate's private RSA key. See the section above - * discussing Provisioning 3.0. + * @param[in] session: this function affects the specified session only. * - * Parameters: - * - [in] session: this function affects the specified session only. + * @retval OEMCrypto_SUCCESS + * @retval OEMCrypto_ERROR_NOT_IMPLEMENTED this function is for + * Provisioning 3.0 only. + * @retval OEMCrypto_ERROR_SYSTEM_INVALIDATED * - * Returns: - * OEMCrypto_SUCCESS - * OEMCrypto_ERROR_NOT_IMPLEMENTED - this function is for Provisioning 3.0 - * only. - * OEMCrypto_ERROR_SYSTEM_INVALIDATED - * - * Threading: + * @threading * This is a "Session Function" and may be called simultaneously with session * functions for other sessions but not simultaneously with other functions * for this session. It will not be called simultaneously with initialization * or usage table functions. It is as if the CDM holds a write lock for this * session, and a read lock on the OEMCrypto system. * - * Version: + * @version * This method is new API version 16. */ OEMCryptoResult OEMCrypto_LoadOEMPrivateKey(OEMCrypto_SESSION session); -/* - * OEMCrypto_GetOEMPublicCertificate +/** + * This function should place the OEM public certificate in the buffer + * public_cert. See the section above discussing Provisioning 3.0. * - * Description: - * This function should place the OEM public certificate in the buffer - * public_cert. See the section above discussing Provisioning 3.0. + * If the buffer is not large enough, OEMCrypto should update + * public_cert_length and return OEMCrypto_ERROR_SHORT_BUFFER. * - * If the buffer is not large enough, OEMCrypto should update - * public_cert_length and return OEMCrypto_ERROR_SHORT_BUFFER. + * @param[out] public_cert: the buffer where the public certificate is stored. + * @param[in,out] public_cert_length: on input, this is the available size of + * the buffer. On output, this is the number of bytes needed for the + * certificate. * - * Parameters: - * - [out] public_cert: the buffer where the public certificate is stored. - * - [in/out] public_cert_length: on input, this is the available size of - * the buffer. On output, this is the number of bytes needed for the - * certificate. + * @retval OEMCrypto_SUCCESS + * @retval OEMCrypto_ERROR_NOT_IMPLEMENTED this function is for + * Provisioning 3.0 only. + * @retval OEMCrypto_ERROR_SHORT_BUFFER + * @retval OEMCrypto_ERROR_SYSTEM_INVALIDATED * - * Returns: - * OEMCrypto_SUCCESS - * OEMCrypto_ERROR_NOT_IMPLEMENTED - this function is for Provisioning 3.0 - * only. - * OEMCrypto_ERROR_SHORT_BUFFER - * OEMCrypto_ERROR_SYSTEM_INVALIDATED - * - * Threading: + * @threading * This is a "Property Function" and may be called simultaneously with any * other property function or session function, but not any initialization or * usage table function, as if the CDM holds a read lock on the OEMCrypto * system. * - * Version: + * @version * This method is new API version 16. */ OEMCryptoResult OEMCrypto_GetOEMPublicCertificate(uint8_t* public_cert, size_t* public_cert_length); -/* - * OEMCrypto_GetRandom +/// @} + +/// @addtogroup validation +/// @{ + +/** + * Returns a buffer filled with hardware-generated random bytes, if supported + * by the hardware. If the hardware feature does not exist, return + * OEMCrypto_ERROR_RNG_NOT_SUPPORTED. * - * Description: - * Returns a buffer filled with hardware-generated random bytes, if supported - * by the hardware. If the hardware feature does not exist, return - * OEMCrypto_ERROR_RNG_NOT_SUPPORTED. + * @param[out] random_data: pointer to the buffer that receives random data + * @param[in] random_data_length: length of the random data buffer in bytes * - * Parameters: - * [out] random_data- pointer to the buffer that receives random data - * [in] random_data_length- length of the random data buffer in bytes + * @retval OEMCrypto_SUCCESS success + * @retval OEMCrypto_ERROR_RNG_FAILED failed to generate random number + * @retval OEMCrypto_ERROR_RNG_NOT_SUPPORTED function not supported + * @retval OEMCrypto_ERROR_BUFFER_TOO_LARGE + * @retval OEMCrypto_ERROR_SYSTEM_INVALIDATED * - * Returns: - * OEMCrypto_SUCCESS success - * OEMCrypto_ERROR_RNG_FAILED failed to generate random number - * OEMCrypto_ERROR_RNG_NOT_SUPPORTED function not supported - * OEMCrypto_ERROR_BUFFER_TOO_LARGE - * OEMCrypto_ERROR_SYSTEM_INVALIDATED - * - * Buffer Sizes: - * OEMCrypto shall support random_data_length- sizes of at least 32 bytes + * @buffer_size + * OEMCrypto shall support random_data_length sizes of at least 32 bytes * for random number generation. * OEMCrypto shall return OEMCrypto_ERROR_BUFFER_TOO_LARGE if the buffer is * larger than the supported size. * - * Threading: + * @threading * This is a "Property Function" and may be called simultaneously with any * other property function or session function, but not any initialization or * usage table function, as if the CDM holds a read lock on the OEMCrypto * system. * - * Version: + * @version * This method is supported in all API versions. */ OEMCryptoResult OEMCrypto_GetRandom(uint8_t* random_data, size_t random_data_length); -/* - * OEMCrypto_APIVersion +/** + * This function returns the current API version number. The version number + * allows the calling application to avoid version mis-match errors, because + * this API is part of a shared library. * - * Description: - * This function returns the current API version number. The version number - * allows the calling application to avoid version mis-match errors, because - * this API is part of a shared library. + * There is a possibility that some API methods will be backwards compatible, + * or backwards compatible at a reduced security level. * - * There is a possibility that some API methods will be backwards compatible, - * or backwards compatible at a reduced security level. + * There is no plan to introduce forward-compatibility. Applications will + * reject a library with a newer version of the API. * - * There is no plan to introduce forward-compatibility. Applications will - * reject a library with a newer version of the API. + * The version specified in this document is 16. Any OEM that returns this + * version number guarantees it passes all unit tests associated with this + * version. * - * The version specified in this document is 16. Any OEM that returns this - * version number guarantees it passes all unit tests associated with this - * version. + * @retval The supported API, as specified in the header file OEMCryptoCENC.h. * - * Parameters: - * none - * - * Returns: - * The supported API, as specified in the header file OEMCryptoCENC.h. - * - * Threading: + * @threading * This is a "Property Function" and may be called simultaneously with any * other property function or session function, but not any initialization or * usage table function, as if the CDM holds a read lock on the OEMCrypto * system. * - * Version: + * @version * This method changed in each API version. */ uint32_t OEMCrypto_APIVersion(void); -/* - * OEMCrypto_MinorAPIVersion +/** + * This function returns the current API minor version number. The version + * number allows the calling application to avoid version mis-match errors, + * because this API is part of a shared library. * - * Description: - * This function returns the current API minor version number. The version - * number allows the calling application to avoid version mis-match errors, - * because this API is part of a shared library. + * The minor version specified in this document is 2. Any OEM that returns + * this version number guarantees it passes all unit tests associated with + * this version. * - * The minor version specified in this document is 2. Any OEM that returns - * this version number guarantees it passes all unit tests associated with - * this version. + * @retval The supported API, as specified in the header file OEMCryptoCENC.h. * - * Parameters: - * none - * - * Returns: - * The supported API, as specified in the header file OEMCryptoCENC.h. - * - * Threading: + * @threading * This is a "Property Function" and may be called simultaneously with any * other property function or session function, but not any initialization or * usage table function, as if the CDM holds a read lock on the OEMCrypto * system. * - * Version: + * @version * This method changed in each API version. */ uint32_t OEMCrypto_MinorAPIVersion(void); -/* - * OEMCrypto_BuildInformation +/** + * Report the build information of the OEMCrypto library as a short null + * terminated C string. The string should be at most 128 characters long. + * This string should be updated with each release or OEMCrypto build. * - * Description: - * Report the build information of the OEMCrypto library as a short null - * terminated C string. The string should be at most 128 characters long. - * This string should be updated with each release or OEMCrypto build. + * Some SOC vendors deliver a binary OEMCrypto library to a device + * manufacturer. This means the OEMCrypto version may not be exactly in sync + * with the system's versions. This string can be used to help track which + * version is installed on a device. * - * Some SOC vendors deliver a binary OEMCrypto library to a device - * manufacturer. This means the OEMCrypto version may not be exactly in sync - * with the system's versions. This string can be used to help track which - * version is installed on a device. + * It may be used for logging or bug tracking and may be bubbled up to the + * app so that it may track metrics on errors. * - * It may be used for logging or bug tracking and may be bubbled up to the - * app so that it may track metrics on errors. + * Since the OEMCrypto API also changes its minor version number when there + * are minor corrections, it would be useful to include the API version + * number in this string, e.g. "15.1" or "15.2" if those minor versions are + * released. * - * Since the OEMCrypto API also changes its minor version number when there - * are minor corrections, it would be useful to include the API version - * number in this string, e.g. "15.1" or "15.2" if those minor versions are - * released. + * @retval A printable null terminated C string, suitable for a single line in a + * log. * - * Parameters: - * none - * - * Returns: - * A printable null terminated C string, suitable for a single line in a log. - * - * Threading: + * @threading * This is a "Property Function" and may be called simultaneously with any * other property function or session function, but not any initialization or * usage table function, as if the CDM holds a read lock on the OEMCrypto * system. * - * Version: + * @version * This method changed in each API version. */ const char* OEMCrypto_BuildInformation(void); -/* - * OEMCrypto_Security_Patch_Level +/** + * This function returns the current patch level of the software running in + * the trusted environment. The patch level is defined by the OEM, and is + * only incremented when a security update has been added. * - * Description: - * This function returns the current patch level of the software running in - * the trusted environment. The patch level is defined by the OEM, and is - * only incremented when a security update has been added. + * See the section Security Patch Level above for more details. * - * See the section Security Patch Level above for more details. + * @retval The OEM defined version number. * - * Parameters: - * none - * - * Returns: - * The OEM defined version number. - * - * Threading: + * @threading * This is a "Property Function" and may be called simultaneously with any * other property function or session function, but not any initialization or * usage table function, as if the CDM holds a read lock on the OEMCrypto * system. * - * Version: + * @version * This method was introduced in API version 11. */ uint8_t OEMCrypto_Security_Patch_Level(void); -/* - * OEMCrypto_SecurityLevel +/** + * Returns a string specifying the security level of the library. * - * Description: - * Returns a string specifying the security level of the library. + * Since this function is spoofable, it is not relied on for security + * purposes. It is for information only. * - * Since this function is spoofable, it is not relied on for security - * purposes. It is for information only. + * @retval A null terminated string. Useful value are "L1", "L2" and "L3". * - * Parameters: - * none - * - * Returns: - * A null terminated string. Useful value are "L1", "L2" and "L3". - * - * Threading: + * @threading * This is a "Property Function" and may be called simultaneously with any * other property function or session function, but not any initialization or * usage table function, as if the CDM holds a read lock on the OEMCrypto * system. * - * Version: + * @version * This method changed in API version 6. */ const char* OEMCrypto_SecurityLevel(void); -/* - * OEMCrypto_GetHDCPCapability +/** + * Returns the maximum HDCP version supported by the device, and the HDCP + * version supported by the device and any connected display. * - * Description: - * Returns the maximum HDCP version supported by the device, and the HDCP - * version supported by the device and any connected display. + * Valid values for HDCP_Capability are: * - * Valid values for HDCP_Capability are: + * The value 0xFF means the device is using a local, secure, data path + * instead of HDMI output. Notice that HDCP must use flag Type 1: all + * downstream devices will also use the same version or higher. * - * The value 0xFF means the device is using a local, secure, data path - * instead of HDMI output. Notice that HDCP must use flag Type 1: all - * downstream devices will also use the same version or higher. + * The maximum HDCP level should be the maximum value that the device can + * enforce. For example, if the device has an HDCP 1.0 port and an HDCP 2.0 + * port, and the first port can be disabled, then the maximum is HDCP 2.0. If + * the first port cannot be disabled, then the maximum is HDCP 1.0. The + * maximum value can be used by the application or server to decide if a + * license may be used in the future. For example, a device may be connected + * to an external display while an offline license is downloaded, but the + * user intends to view the content on a local display. The user will want to + * download the higher quality content. * - * The maximum HDCP level should be the maximum value that the device can - * enforce. For example, if the device has an HDCP 1.0 port and an HDCP 2.0 - * port, and the first port can be disabled, then the maximum is HDCP 2.0. If - * the first port cannot be disabled, then the maximum is HDCP 1.0. The - * maximum value can be used by the application or server to decide if a - * license may be used in the future. For example, a device may be connected - * to an external display while an offline license is downloaded, but the - * user intends to view the content on a local display. The user will want to - * download the higher quality content. + * The current HDCP level should be the level of HDCP currently negotiated + * with any connected receivers or repeaters either through HDMI or a + * supported wireless format. If multiple ports are connected, the current + * level should be the minimum HDCP level of all ports. If the key control + * block requires an HDCP level equal to or lower than the current HDCP + * level, the key is expected to be usable. If the key control block requires + * a higher HDCP level, the key is expected to be forbidden. * - * The current HDCP level should be the level of HDCP currently negotiated - * with any connected receivers or repeaters either through HDMI or a - * supported wireless format. If multiple ports are connected, the current - * level should be the minimum HDCP level of all ports. If the key control - * block requires an HDCP level equal to or lower than the current HDCP - * level, the key is expected to be usable. If the key control block requires - * a higher HDCP level, the key is expected to be forbidden. + * When a key has version HDCP_V2_3 required in the key control block, the + * transmitter must have HDCP version 2.3 and have negotiated a connection + * with a version 2.2 or 2.3 receiver or repeater. The transmitter must + * configure the content stream to be Type 1. Since the transmitter cannot + * distinguish between 2.2 and 2.3 downstream receivers when connected to a + * repeater, it may transmit to both 2.2 and 2.3 receivers, but not 2.1 + * receivers. * - * When a key has version HDCP_V2_3 required in the key control block, the - * transmitter must have HDCP version 2.3 and have negotiated a connection - * with a version 2.2 or 2.3 receiver or repeater. The transmitter must - * configure the content stream to be Type 1. Since the transmitter cannot - * distinguish between 2.2 and 2.3 downstream receivers when connected to a - * repeater, it may transmit to both 2.2 and 2.3 receivers, but not 2.1 - * receivers. + * For example, if the transmitter is 2.3, and is connected to a receiver + * that supports 2.3 then the current level is HDCP_V2_3. If the transmitter + * is 2.3 and is connected to a 2.3 repeater, the current level is HDCP_V2_3 + * even though the repeater can negotiate a connection with a 2.2 downstream + * receiver for a Type 1 Content Stream. * - * For example, if the transmitter is 2.3, and is connected to a receiver - * that supports 2.3 then the current level is HDCP_V2_3. If the transmitter - * is 2.3 and is connected to a 2.3 repeater, the current level is HDCP_V2_3 - * even though the repeater can negotiate a connection with a 2.2 downstream - * receiver for a Type 1 Content Stream. + * As another example, if the transmitter can support 2.3, but a receiver + * supports 2.0, then the current level is HDCP_V2. * - * As another example, if the transmitter can support 2.3, but a receiver - * supports 2.0, then the current level is HDCP_V2. + * When a license requires HDCP, a device may use a wireless protocol to + * connect to a display only if that protocol supports the version of HDCP as + * required by the license. Both WirelessHD (formerly WiFi Display) and + * Miracast support HDCP. * - * When a license requires HDCP, a device may use a wireless protocol to - * connect to a display only if that protocol supports the version of HDCP as - * required by the license. Both WirelessHD (formerly WiFi Display) and - * Miracast support HDCP. + * @param[out] current: this is the current HDCP version, based on the device + * itself, and the display to which it is connected. + * @param[out] maximum: this is the maximum supported HDCP version for the + * device, ignoring any attached device. * - * Parameters: - * [out] current - this is the current HDCP version, based on the device - * itself, and the display to which it is connected. - * [out] maximum - this is the maximum supported HDCP version for the device, - * ignoring any attached device. + * @retval OEMCrypto_SUCCESS + * @retval OEMCrypto_ERROR_UNKNOWN_FAILURE + * @retval OEMCrypto_ERROR_SYSTEM_INVALIDATED * - * Returns: - * OEMCrypto_SUCCESS - * OEMCrypto_ERROR_UNKNOWN_FAILURE - * OEMCrypto_ERROR_SYSTEM_INVALIDATED - * - * Threading: + * @threading * This is a "Property Function" and may be called simultaneously with any * other property function or session function, but not any initialization or * usage table function, as if the CDM holds a read lock on the OEMCrypto * system. * - * Version: + * @version * This method changed in API version 10. */ OEMCryptoResult OEMCrypto_GetHDCPCapability(OEMCrypto_HDCP_Capability* current, OEMCrypto_HDCP_Capability* maximum); -/* - * OEMCrypto_SupportsUsageTable +/** + * This is used to determine if the device can support a usage table. Since + * this function is spoofable, it is not relied on for security purposes. It + * is for information only. The usage table is described in the section above. * - * Description: - * This is used to determine if the device can support a usage table. Since - * this function is spoofable, it is not relied on for security purposes. It - * is for information only. The usage table is described in the section above. - * - * Parameters: - * none - * - * Returns: - * Returns true if the device can maintain a usage table. Returns false + * @retval Returns true if the device can maintain a usage table. Returns false * otherwise. * - * Threading: + * @threading * This is a "Property Function" and may be called simultaneously with any * other property function or session function, but not any initialization or * usage table function, as if the CDM holds a read lock on the OEMCrypto * system. * - * Version: + * @version * This method changed in API version 9. */ bool OEMCrypto_SupportsUsageTable(void); -/* - * OEMCrypto_MaximumUsageTableHeaderSize +/** + * Estimates the maximum usage table size. If the device does not have a + * fixed size, this returns an estimate. A maximum size of 0 means the header + * is constrained only by dynamic memory allocation. * - * Description: - * Estimates the maximum usage table size. If the device does not have a - * fixed size, this returns an estimate. A maximum size of 0 means the header - * is constrained only by dynamic memory allocation. + * Widevine requires the size to be at least 300 entries. * - * Widevine requires the size to be at least 300 entries. + * @retval Returns an estimate for the maximum size of the usage table header. * - * Parameters: - * none - * - * Returns: - * Returns an estimate for the maximum size of the usage table header. - * - * Threading: + * @threading * This is a "Property Function" and may be called simultaneously with any * other property function or session function, but not any initialization or * usage table function, as if the CDM holds a read lock on the OEMCrypto * system. * - * Version: + * @version * This method changed in API version 16. */ size_t OEMCrypto_MaximumUsageTableHeaderSize(void); -/* - * OEMCrypto_IsAntiRollbackHwPresent +/** + * Indicate whether there is hardware protection to detect and/or prevent the + * rollback of the usage table. For example, if the usage table contents is + * stored entirely on a secure file system that the user cannot read or write + * to. Another example is if the usage table has a generation number and the + * generation number is stored in secure memory that is not user accessible. * - * Description: - * Indicate whether there is hardware protection to detect and/or prevent the - * rollback of the usage table. For example, if the usage table contents is - * stored entirely on a secure file system that the user cannot read or write - * to. Another example is if the usage table has a generation number and the - * generation number is stored in secure memory that is not user accessible. - * - * Parameters: - * none - * - * Returns: - * Returns true if oemcrypto uses anti-rollback hardware. Returns false + * @retval Returns true if oemcrypto uses anti-rollback hardware. Returns false * otherwise. * - * Threading: + * @threading * This is a "Property Function" and may be called simultaneously with any * other property function or session function, but not any initialization or * usage table function, as if the CDM holds a read lock on the OEMCrypto * system. * - * Version: + * @version * This method is new in API version 10. */ bool OEMCrypto_IsAntiRollbackHwPresent(void); -/* - * OEMCrypto_GetNumberOfOpenSessions +/** + * Returns the current number of open sessions. The CDM and OEMCrypto + * consumers can query this value so they can use resources more effectively. * - * Description: - * Returns the current number of open sessions. The CDM and OEMCrypto - * consumers can query this value so they can use resources more effectively. + * @param[out] count: this is the current number of opened sessions. * - * Parameters: - * [out] count - this is the current number of opened sessions. + * @retval OEMCrypto_SUCCESS + * @retval OEMCrypto_ERROR_UNKNOWN_FAILURE + * @retval OEMCrypto_ERROR_SYSTEM_INVALIDATED * - * Returns: - * OEMCrypto_SUCCESS - * OEMCrypto_ERROR_UNKNOWN_FAILURE - * OEMCrypto_ERROR_SYSTEM_INVALIDATED - * - * Threading: + * @threading * This is a "Property Function" and may be called simultaneously with any * other property function or session function, but not any initialization or * usage table function, as if the CDM holds a read lock on the OEMCrypto * system. * - * Version: + * @version * This method is new in API version 10. */ OEMCryptoResult OEMCrypto_GetNumberOfOpenSessions(size_t* count); -/* - * OEMCrypto_GetMaxNumberOfSessions +/** + * Returns the maximum number of concurrent OEMCrypto sessions supported by + * the device. The CDM and OEMCrypto consumers can query this value so they + * can use resources more effectively. If the maximum number of sessions + * depends on a dynamically allocated shared resource, the returned value + * should be a best estimate of the maximum number of sessions. * - * Description: - * Returns the maximum number of concurrent OEMCrypto sessions supported by - * the device. The CDM and OEMCrypto consumers can query this value so they - * can use resources more effectively. If the maximum number of sessions - * depends on a dynamically allocated shared resource, the returned value - * should be a best estimate of the maximum number of sessions. + * OEMCrypto shall support a minimum of 10 sessions. Some applications use + * multiple sessions to pre-fetch licenses, so high end devices should + * support more sessions -- we recommend a minimum of 50 sessions. * - * OEMCrypto shall support a minimum of 10 sessions. Some applications use - * multiple sessions to pre-fetch licenses, so high end devices should - * support more sessions -- we recommend a minimum of 50 sessions. + * @param[out] max: this is the max number of supported sessions. * - * Parameters: - * [out] max - this is the max number of supported sessions. + * @retval OEMCrypto_SUCCESS + * @retval OEMCrypto_ERROR_UNKNOWN_FAILURE + * @retval OEMCrypto_ERROR_SYSTEM_INVALIDATED * - * Returns: - * OEMCrypto_SUCCESS - * OEMCrypto_ERROR_UNKNOWN_FAILURE - * OEMCrypto_ERROR_SYSTEM_INVALIDATED - * - * Threading: + * @threading * This is a "Property Function" and may be called simultaneously with any * other property function or session function, but not any initialization or * usage table function, as if the CDM holds a read lock on the OEMCrypto * system. * - * Version: + * @version * This method changed in API version 12. */ OEMCryptoResult OEMCrypto_GetMaxNumberOfSessions(size_t* max); -/* - * OEMCrypto_SupportedCertificates +/** + * Returns the type of certificates keys that this device supports. With very + * few exceptions, all devices should support at least 2048 bit RSA keys. + * High end devices should also support 3072 bit RSA keys. Devices that are + * cast receivers should also support RSA cast receiver certificates. * - * Description: - * Returns the type of certificates keys that this device supports. With very - * few exceptions, all devices should support at least 2048 bit RSA keys. - * High end devices should also support 3072 bit RSA keys. Devices that are - * cast receivers should also support RSA cast receiver certificates. + * Beginning with OEMCrypto v14, the provisioning server may deliver to the + * device an RSA key that uses the Carmichael totient. This does not change + * the RSA algorithm -- however the product of the private and public keys is + * not necessarily the Euler number \phi (n). OEMCrypto should not reject + * such keys. * - * Beginning with OEMCrypto v14, the provisioning server may deliver to the - * device an RSA key that uses the Carmichael totient. This does not change - * the RSA algorithm -- however the product of the private and public keys is - * not necessarily the Euler number \phi (n). OEMCrypto should not reject - * such keys. - * - * Parameters: - * none - * - * Returns: + * @return * Returns the bitwise or of the following flags. It is likely that high end * devices will support both 2048 and 3072 bit keys while the widevine * servers transition to new key sizes. @@ -3592,367 +3494,343 @@ OEMCryptoResult OEMCrypto_GetMaxNumberOfSessions(size_t* max); * - 0x200 = OEMCrypto_Supports_ECC_secp384r1 - Elliptic Curve secp384r1 * - 0x400 = OEMCrypto_Supports_ECC_secp521r1 - Elliptic Curve secp521r1 * - * Threading: + * @threading * This is a "Property Function" and may be called simultaneously with any * other property function or session function, but not any initialization or * usage table function, as if the CDM holds a read lock on the OEMCrypto * system. * - * Version: + * @version * This method changed in API version 16. */ uint32_t OEMCrypto_SupportedCertificates(void); -/* - * OEMCrypto_IsSRMUpdateSupported +/** + * Returns true if the device supports SRM files and the file can be updated + * via the function OEMCrypto_LoadSRM. This also returns false for devices + * that do not support an SRM file, devices that do not support HDCP, and + * devices that have no external display support. * - * Description: - * Returns true if the device supports SRM files and the file can be updated - * via the function OEMCrypto_LoadSRM. This also returns false for devices - * that do not support an SRM file, devices that do not support HDCP, and - * devices that have no external display support. + * @retval true if LoadSRM is supported. + * @retval false otherwise. * - * Parameters: - * none - * - * Returns: - * true - if LoadSRM is supported. - * false - otherwise. - * - * Threading: + * @threading * This is a "Property Function" and may be called simultaneously with any * other property function or session function, but not any initialization or * usage table function, as if the CDM holds a read lock on the OEMCrypto * system. * - * Version: + * @version * This method changed in API version 13. */ bool OEMCrypto_IsSRMUpdateSupported(void); -/* - * OEMCrypto_GetCurrentSRMVersion +/** + * Returns the version number of the current SRM file. If the device does not + * support SRM files, this will return OEMCrypto_ERROR_NOT_IMPLEMENTED. If + * the device only supports local displays, it would return + * OEMCrypto_LOCAL_DISPLAY_ONLY. If the device has an SRM, but cannot use + * OEMCrypto to update the SRM, then this function would set version to be + * the current version number, and return OEMCrypto_SUCCESS, but it would + * return false from OEMCrypto_IsSRMUpdateSupported. * - * Description: - * Returns the version number of the current SRM file. If the device does not - * support SRM files, this will return OEMCrypto_ERROR_NOT_IMPLEMENTED. If - * the device only supports local displays, it would return - * OEMCrypto_LOCAL_DISPLAY_ONLY. If the device has an SRM, but cannot use - * OEMCrypto to update the SRM, then this function would set version to be - * the current version number, and return OEMCrypto_SUCCESS, but it would - * return false from OEMCrypto_IsSRMUpdateSupported. + * @param[out] version: current SRM version number. * - * Parameters: - * [out] version: current SRM version number. + * @retval OEMCrypto_ERROR_NOT_IMPLEMENTED + * @retval OEMCrypto_SUCCESS + * @retval OEMCrypto_LOCAL_DISPLAY_ONLY to indicate version was not set, and + * is not needed. + * @retval OEMCrypto_ERROR_SYSTEM_INVALIDATED * - * Returns: - * OEMCrypto_ERROR_NOT_IMPLEMENTED - * OEMCrypto_SUCCESS - * OEMCrypto_LOCAL_DISPLAY_ONLY - to indicate version was not set, and is not - * needed. - * OEMCrypto_ERROR_SYSTEM_INVALIDATED - * - * Threading: + * @threading * This is a "Property Function" and may be called simultaneously with any * other property function or session function, but not any initialization or * usage table function, as if the CDM holds a read lock on the OEMCrypto * system. * - * Version: + * @version * This method changed in API version 13. */ OEMCryptoResult OEMCrypto_GetCurrentSRMVersion(uint16_t* version); -/* - * OEMCrypto_GetAnalogOutputFlags +/** + * Returns whether the device supports analog output or not. This information + * will be sent to the license server, and may be used to determine the type + * of license allowed. This function is for reporting only. It is paired with + * the key control block flags Disable_Analog_Output and CGMS. * - * Description: - * Returns whether the device supports analog output or not. This information - * will be sent to the license server, and may be used to determine the type - * of license allowed. This function is for reporting only. It is paired with - * the key control block flags Disable_Analog_Output and CGMS. - * - * Parameters: - * none. - * - * Returns: - * Returns a bitwise OR of the following flags. - * - 0x0 = OEMCrypto_No_Analog_Output -- the device has no analog output. - * - 0x1 = OEMCrypto_Supports_Analog_Output - the device does have analog + * @return + * Returns a bitwise OR of all possible return values. + * * 0x0 = OEMCrypto_No_Analog_Output: the device has no analog output. + * * 0x1 = OEMCrypto_Supports_Analog_Output: the device does have analog * output. - * - 0x2 = OEMCrypto_Can_Disable_Analog_Ouptput - the device does have + * * 0x2 = OEMCrypto_Can_Disable_Analog_Ouptput: the device does have * analog output, but it will disable analog output if required by the * key control block. - * - 0x4 = OEMCrypto_Supports_CGMS_A - the device supports signaling 2-bit + * * 0x4 = OEMCrypto_Supports_CGMS_A: the device supports signaling 2-bit * CGMS-A, if required by the key control block * - * Threading: + * @threading * This is a "Property Function" and may be called simultaneously with any * other property function or session function, but not any initialization or * usage table function, as if the CDM holds a read lock on the OEMCrypto * system. * - * Version: + * @version * This method is new in API version 14. */ uint32_t OEMCrypto_GetAnalogOutputFlags(void); -/* - * OEMCrypto_ResourceRatingTier +/** + * This function returns a positive number indicating which resource rating + * it supports. This value will bubble up to the application level as a + * property. This will allow applications to estimate what resolution and + * bandwidth the device is expected to support. * - * Description: - * This function returns a positive number indicating which resource rating - * it supports. This value will bubble up to the application level as a - * property. This will allow applications to estimate what resolution and - * bandwidth the device is expected to support. + * OEMCrypto unit tests and Android GTS tests will verify that devices do + * support the resource values specified in the table below at the tier + * claimed by the device. If a device claims to be a low end device, the + * OEMCrypto unit tests will only verify the low end performance values. * - * OEMCrypto unit tests and Android GTS tests will verify that devices do - * support the resource values specified in the table below at the tier - * claimed by the device. If a device claims to be a low end device, the - * OEMCrypto unit tests will only verify the low end performance values. + * OEMCrypto implementers should consider the numbers below to be minimum + * values. * - * OEMCrypto implementers should consider the numbers below to be minimum - * values. + * These performance parameters are for OEMCrypto only. In particular, + * bandwidth and codec resolution are determined by the platform. * - * These performance parameters are for OEMCrypto only. In particular, - * bandwidth and codec resolution are determined by the platform. + * Some parameters need more explanation. The Sample size is typically the + * size of one encoded frame, but might be several frames for AV1. Converting + * this to resolution depends on the Codec, which is not specified by + * OEMCrypto. Some content has the sample broken into several subsamples. The + * "number of subsamples" restriction requires that any content can be broken + * into at least that many subsamples. However, this number may be larger if + * DecryptCENC returns OEMCrypto_ERROR_BUFFER_TOO_LARGE. In that case, the + * layer above OEMCrypto will break the sample into subsamples of size + * "Decrypt Buffer Size" as specified in the table below. The "Decrypt Buffer + * Size" means the size of one subsample that may be passed into DecryptCENC + * or CopyBuffer without returning error OEMCrypto_ERROR_BUFFER_TOO_LARGE. * - * Some parameters need more explanation. The Sample size is typically the - * size of one encoded frame, but might be several frames for AV1. Converting - * this to resolution depends on the Codec, which is not specified by - * OEMCrypto. Some content has the sample broken into several subsamples. The - * "number of subsamples" restriction requires that any content can be broken - * into at least that many subsamples. However, this number may be larger if - * DecryptCENC returns OEMCrypto_ERROR_BUFFER_TOO_LARGE. In that case, the - * layer above OEMCrypto will break the sample into subsamples of size - * "Decrypt Buffer Size" as specified in the table below. The "Decrypt Buffer - * Size" means the size of one subsample that may be passed into DecryptCENC - * or CopyBuffer without returning error OEMCrypto_ERROR_BUFFER_TOO_LARGE. + * The minimum subsample buffer size is the smallest buffer that the CDM + * layer above OEMCrypto will use when breaking a sample into subsamples. As + * mentioned above, the CDM layer will only break a sample into smaller + * subsamples if OEMCrypto returns OEMCrypto_ERROR_BUFFER_TOO_LARGE. Because + * this might be a performance problem, OEMCrypto implementers are encouraged + * to process larger subsamples and to process multiple subsamples in a + * single call to DecryptCENC. * - * The minimum subsample buffer size is the smallest buffer that the CDM - * layer above OEMCrypto will use when breaking a sample into subsamples. As - * mentioned above, the CDM layer will only break a sample into smaller - * subsamples if OEMCrypto returns OEMCrypto_ERROR_BUFFER_TOO_LARGE. Because - * this might be a performance problem, OEMCrypto implementers are encouraged - * to process larger subsamples and to process multiple subsamples in a - * single call to DecryptCENC. + * The number of keys per session is an indication of how many different + * track types there can be for a piece of content. Typically, content will + * have several keys corresponding to audio and video at different + * resolutions. If the content uses key rotation, there could be three keys + * -- previous interval, current interval, and next interval -- for each + * resolution. * - * The number of keys per session is an indication of how many different - * track types there can be for a piece of content. Typically, content will - * have several keys corresponding to audio and video at different - * resolutions. If the content uses key rotation, there could be three keys - * -- previous interval, current interval, and next interval -- for each - * resolution. + * Concurrent playback sessions versus concurrent sessions: some applications + * will preload multiple licenses before the user picks which content to + * play. Each of these licenses corresponds to an open session. Once playback + * starts, some platforms support picture-in-picture or multiple displays. + * Each of these pictures would correspond to a separate playback session + * with active decryption. * - * Concurrent playback sessions versus concurrent sessions: some applications - * will preload multiple licenses before the user picks which content to - * play. Each of these licenses corresponds to an open session. Once playback - * starts, some platforms support picture-in-picture or multiple displays. - * Each of these pictures would correspond to a separate playback session - * with active decryption. + * The total number of keys for all sessions indicates that the device may + * share key memory over multiple sessions. For example, on a Tier 3 device, + * the device must support four sessions with 20 keys each (80 total), or 20 + * sessions with 4 keys each (80 total), but it does not need to support 20 + * sessions with 20 keys each. * - * The total number of keys for all sessions indicates that the device may - * share key memory over multiple sessions. For example, on a Tier 3 device, - * the device must support four sessions with 20 keys each (80 total), or 20 - * sessions with 4 keys each (80 total), but it does not need to support 20 - * sessions with 20 keys each. + * The message size that is needed for a license with a large number of keys + * is larger than in previous versions. The message size limit applies to all + * functions that sign or verify messages. It also applies to the size of + * context buffers in the derive key functions. * - * The message size that is needed for a license with a large number of keys - * is larger than in previous versions. The message size limit applies to all - * functions that sign or verify messages. It also applies to the size of - * context buffers in the derive key functions. + * Decrypted frames per second -- strictly speaking, OEMCrypto only controls + * the decryption part of playback and cannot control the decoding and + * display part. However, devices that support the higher resource tiers + * should also support a higher frame rate. Platforms may enforce these + * values. For example Android will enforce a frame rate via a GTS test. * - * Decrypted frames per second -- strictly speaking, OEMCrypto only controls - * the decryption part of playback and cannot control the decoding and - * display part. However, devices that support the higher resource tiers - * should also support a higher frame rate. Platforms may enforce these - * values. For example Android will enforce a frame rate via a GTS test. + * Note on units: We will use KiB to mean 1024 bytes and MiB to mean 1024 + * KiB, as described at https://en.wikipedia.org/wiki/Kibibyte. * - * Note on units: We will use KiB to mean 1024 bytes and MiB to mean 1024 - * KiB, as described at https://en.wikipedia.org/wiki/Kibibyte. + *
+ * +--------------------------------+---------+----------+---------+---------+
+ * |Resource Rating Tier            |1 - Low  |2 - Medium|3 - High |4 - Very |
+ * |                                |         |          |         |    High |
+ * +--------------------------------+---------+----------+---------+---------+
+ * |Minimum Sample size             |1 MiB    |2 MiB     |4 MiB    |16 MiB   |
+ * +--------------------------------+---------+----------+---------+---------+
+ * |Minimum Number of Subsamples    |10       |16        |32       |64       |
+ * | (H264 or HEVC)                 |         |          |         |         |
+ * +--------------------------------+---------+----------+---------+---------+
+ * |Minimum Number of Subsamples    |9        |9         |9        |9        |
+ * |(VP9)                           |         |          |         |         |
+ * +--------------------------------+---------+----------+---------+---------+
+ * |Minimum Number of Subsamples    |72       |144       |288      |576      |
+ * |(AV1)                           |         |          |         |         |
+ * +--------------------------------+---------+----------+---------+---------+
+ * |Minimum subsample buffer size   |100 KiB  |500 KiB   |1 MiB    |4 MiB    |
+ * +--------------------------------+---------+----------+---------+---------+
+ * |Minimum Generic crypto buffer   |10 KiB   |100 KiB   |500 KiB  |1 MiB    |
+ * |size                            |         |          |         |         |
+ * +--------------------------------+---------+----------+---------+---------+
+ * |Minimum number of concurrent    |10       |20        |30       |40       |
+ * |sessions                        |         |          |         |         |
+ * +--------------------------------+---------+----------+---------+---------+
+ * |Minimum number of keys per      |4        |20        |20       |30       |
+ * |session                         |         |          |         |         |
+ * +--------------------------------+---------+----------+---------+---------+
+ * |Minimum total number of keys    |16       |40        |80       |90       |
+ * | (all sessions)                 |         |          |         |         |
+ * +--------------------------------+---------+----------+---------+---------+
+ * |Minimum Message Size            |8 KiB    |8 KiB     |16 KiB   |32 KiB   |
+ * +--------------------------------+---------+----------+---------+---------+
+ * |Decrypted Frames per Second     |30 fps SD|30 fps HD |60 fps HD|60 fps 8k|
+ * +--------------------------------+---------+----------+---------+---------+
+ * 
* - * +--------------------------------+---------+----------+---------+---------+ - * |Resource Rating Tier |1 - Low |2 - Medium|3 - High |4 - Very | - * | | | | | High | - * +--------------------------------+---------+----------+---------+---------+ - * |Minimum Sample size |1 MiB |2 MiB |4 MiB |16 MiB | - * +--------------------------------+---------+----------+---------+---------+ - * |Minimum Number of Subsamples |10 |16 |32 |64 | - * | (H264 or HEVC) | | | | | - * +--------------------------------+---------+----------+---------+---------+ - * |Minimum Number of Subsamples |9 |9 |9 |9 | - * |(VP9) | | | | | - * +--------------------------------+---------+----------+---------+---------+ - * |Minimum Number of Subsamples |72 |144 |288 |576 | - * |(AV1) | | | | | - * +--------------------------------+---------+----------+---------+---------+ - * |Minimum subsample buffer size |100 KiB |500 KiB |1 MiB |4 MiB | - * +--------------------------------+---------+----------+---------+---------+ - * |Minimum Generic crypto buffer |10 KiB |100 KiB |500 KiB |1 MiB | - * |size | | | | | - * +--------------------------------+---------+----------+---------+---------+ - * |Minimum number of concurrent |10 |20 |30 |40 | - * |sessions | | | | | - * +--------------------------------+---------+----------+---------+---------+ - * |Minimum number of keys per |4 |20 |20 |30 | - * |session | | | | | - * +--------------------------------+---------+----------+---------+---------+ - * |Minimum total number of keys |16 |40 |80 |90 | - * | (all sessions) | | | | | - * +--------------------------------+---------+----------+---------+---------+ - * |Minimum Message Size |8 KiB |8 KiB |16 KiB |32 KiB | - * +--------------------------------+---------+----------+---------+---------+ - * |Decrypted Frames per Second |30 fps SD|30 fps HD |60 fps HD|60 fps 8k| - * +--------------------------------+---------+----------+---------+---------+ + * @return + * Returns an integer indicating which resource tier the device supports. * - * Parameters: - * none. - * - * Returns: - * Returns an integer indicating which resource tier the device supports. - * - * Threading: + * @threading * This is a "Property Function" and may be called simultaneously with any * other property function or session function, but not any initialization or * usage table function, as if the CDM holds a read lock on the OEMCrypto * system. * - * Version: + * @version * This method is new in API version 15. */ uint32_t OEMCrypto_ResourceRatingTier(void); -/* - * OEMCrypto_LoadProvisioning +/// @} + +/// @addtogroup drm_cert +/// @{ + +/** + * Load and parse a provisioning response, and then rewrap the private key + * for storage on the filesystem. We recommend that the OEM use an encryption + * key and signing key generated using an algorithm at least as strong as + * that in GenerateDerivedKeys. * - * Description: - * Load and parse a provisioning response, and then rewrap the private key - * for storage on the filesystem. We recommend that the OEM use an encryption - * key and signing key generated using an algorithm at least as strong as - * that in GenerateDerivedKeys. + * First, OEMCrypto shall verify the signature of the message using + * HMAC-SHA256 with the derived mac_key[server]. The signature verification + * shall use a constant-time algorithm (a signature mismatch will always take + * the same time as a successful comparison). The signature is over the + * entire message buffer starting at message with length message_length. If + * the signature verification fails, ignore all other arguments and return + * OEMCrypto_ERROR_SIGNATURE_FAILURE. * - * First, OEMCrypto shall verify the signature of the message using - * HMAC-SHA256 with the derived mac_key[server]. The signature verification - * shall use a constant-time algorithm (a signature mismatch will always take - * the same time as a successful comparison). The signature is over the - * entire message buffer starting at message with length message_length. If - * the signature verification fails, ignore all other arguments and return - * OEMCrypto_ERROR_SIGNATURE_FAILURE. + * NOTE: The calling software must have previously established the mac_keys + * and encrypt_key with a call to OEMCrypto_DeriveKeysFromSessionKey or + * OEMCrypto_GenerateDerivedKeys. * - * NOTE: The calling software must have previously established the mac_keys - * and encrypt_key with a call to OEMCrypto_DeriveKeysFromSessionKey or - * OEMCrypto_GenerateDerivedKeys. + * The function ODK_ParseProvisioning is called to parse the message. If it + * returns an error, OEMCrypto shall return that error to the CDM layer. The + * function ODK_ParseProvisioning is described in the document "Widevine Core + * Message Serialization". * - * The function ODK_ParseProvisioning is called to parse the message. If it - * returns an error, OEMCrypto shall return that error to the CDM layer. The - * function ODK_ParseProvisioning is described in the document "Widevine Core - * Message Serialization". + * Below, all fields are found in the struct ODK_ParsedLicense parsed_license + * returned by ODK_ParsedProvisioning. * - * Below, all fields are found in the struct ODK_ParsedLicense parsed_license - * returned by ODK_ParsedProvisioning. + * After decrypting `parsed_response->enc_private_key`, If the first four bytes + * of the buffer are the string "SIGN", then the actual RSA key begins on the + * 9th byte of the buffer. The second four bytes of the buffer is the 32 bit + * field "allowed_schemes" of type RSA_Padding_Scheme, which is used in + * OEMCrypto_GenerateRSASignature. The value of allowed_schemes must also be + * wrapped with RSA key. We recommend storing the magic string "SIGN" with + * the key to distinguish keys that have a value for allowed_schemes from + * those that should use the default allowed_schemes. Devices that do not + * support the alternative signing algorithms may refuse to load these keys + * and return an error of OEMCrypto_ERROR_NOT_IMPLEMENTED. The main use case + * for these alternative signing algorithms is to support devices that use + * X509 certificates for authentication when acting as a ChromeCast receiver. + * This is not needed for devices that wish to send data to a ChromeCast. * - * After decrypting enc_rsa_key, If the first four bytes of the buffer are - * the string "SIGN", then the actual RSA key begins on the 9th byte of the - * buffer. The second four bytes of the buffer is the 32 bit field - * "allowed_schemes"of type RSA_Padding_Scheme, which is used in - * OEMCrypto_GenerateRSASignature. The value of allowed_schemes must also be - * wrapped with RSA key. We recommend storing the magic string "SIGN" with - * the key to distinguish keys that have a value for allowed_schemes from - * those that should use the default allowed_schemes. Devices that do not - * support the alternative signing algorithms may refuse to load these keys - * and return an error of OEMCrypto_ERROR_NOT_IMPLEMENTED. The main use case - * for these alternative signing algorithms is to support devices that use - * X509 certificates for authentication when acting as a ChromeCast receiver. - * This is not needed for devices that wish to send data to a ChromeCast. - * - * If the first four bytes of the buffer enc_rsa_key are not the string - * "SIGN", then the default value of allowed_schemes = 1 (kSign_RSASSA_PSS) - * will be used. + * If the first four bytes of the buffer `enc_private_key` are not the string + * "SIGN", then this key may not be used with OEMCrypto_GenerateRSASignature. * * Verification and Algorithm: - * The following checks should be performed. If any check fails, an error is - * returned, and the key is not loaded. - * 1. Check that all the pointer values passed into it are within the - * buffer specified by message and message_length. - * 2. Verify that in_wrapped_rsa_key_length is large enough to hold the - * rewrapped key, returning OEMCrypto_ERROR_SHORT_BUFFER otherwise. - * 3. Verify the message signature, using the derived signing key - * (mac_key[server]) from a previous call to - * OEMCrypto_GenerateDerivedKeys or OEMCrypto_DeriveKeysFromSessionKey. - * 4. The function ODK_ParseProvisioning is called to parse the message. - * 5. Decrypt enc_rsa_key in the buffer rsa_key using the session's derived - * encryption key (enc_key). Use enc_rsa_key_iv as the initial vector - * for AES_128-CBC mode, with PKCS#5 padding. The rsa_key should be kept - * in secure memory and protected from the user. - * 6. If the first four bytes of the buffer rsa_key are the string "SIGN", - * then the actual RSA key begins on the 9th byte of the buffer. The - * second four bytes of the buffer is the 32 bit field - * "allowed_schemes", of type RSA_Padding_Scheme, which is used in - * OEMCrypto_GenerateRSASignature. The value of allowed_schemes must - * also be wrapped with RSA key. We recommend storing the magic string - * "SIGN" with the key to distinguish keys that have a value for - * allowed_schemes from those that should use the default - * allowed_schemes. Devices that do not support the alternative signing - * algorithms may refuse to load these keys and return an error of - * OEMCrypto_ERROR_NOT_IMPLEMENTED. The main use case for these - * alternative signing algorithms is to support devices that use X.509 - * certificates for authentication when acting as a ChromeCast receiver. - * This is not needed for devices that wish to send data to a ChromeCast. - * 7. If the first four bytes of the buffer rsa_key are not the string - * "SIGN", then the default value of allowed_schemes = 1 - * (kSign_RSASSA_PSS) will be used. - * 8. After possibly skipping past the first 8 bytes signifying the allowed - * signing algorithm, the rest of the buffer rsa_key contains an RSA - * device key in PKCS#8 binary DER encoded format. The OEMCrypto library - * shall verify that this RSA key is valid. - * 9. Re-encrypt the device RSA key with an internal key (such as the OEM - * key or Widevine Keybox key) and the generated IV using AES-128-CBC - * with PKCS#5 padding. - * 10. Copy the rewrapped key to the buffer specified by wrapped_rsa_key - * and the size of the wrapped key to wrapped_rsa_key_length. + * The following checks should be performed. If any check fails, an error is + * returned, and the key is not loaded. + * 1. Check that all the pointer values passed into it are within the + * buffer specified by message and message_length. + * 2. Verify that (in) wrapped_private_key_length is large enough to hold + * the rewrapped key, returning OEMCrypto_ERROR_SHORT_BUFFER otherwise. + * 3. Verify the message signature, using the derived signing key + * (mac_key[server]) from a previous call to + * OEMCrypto_GenerateDerivedKeys or OEMCrypto_DeriveKeysFromSessionKey. + * 4. The function ODK_ParseProvisioning is called to parse the message. + * 5. Decrypt enc_private_key in the buffer private_key using the session's + * derived encryption key (enc_key). Use enc_private_key_iv as the initial + * vector for AES_128-CBC mode, with PKCS#5 padding. The private_key should + * be kept in secure memory and protected from the user. + * 6. If the first four bytes of the buffer private_key are the string "SIGN", + * then the actual RSA key begins on the 9th byte of the buffer. The + * second four bytes of the buffer is the 32 bit field + * "allowed_schemes", of type RSA_Padding_Scheme, which is used in + * OEMCrypto_GenerateRSASignature. The value of allowed_schemes must + * also be wrapped with RSA key. We recommend storing the magic string + * "SIGN" with the key to distinguish keys that have a value for + * allowed_schemes from those that should use the default + * allowed_schemes. Devices that do not support the alternative signing + * algorithms may refuse to load these keys and return an error of + * OEMCrypto_ERROR_NOT_IMPLEMENTED. The main use case for these + * alternative signing algorithms is to support devices that use X.509 + * certificates for authentication when acting as a ChromeCast receiver. + * This is not needed for devices that wish to send data to a ChromeCast. + * 7. If the first four bytes of the buffer private_key are not the string + * "SIGN", this key may not be used with OEMCrypto_GenerateRSASignature. + * 8. After possibly skipping past the first 8 bytes signifying the allowed + * signing algorithm, the rest of the buffer private_key contains an ECC + * private key or an RSA private key in PKCS#8 binary DER encoded + * format. The OEMCrypto library shall verify that this private key is + * valid. + * 9. Re-encrypt the device private key with an internal key (such as the OEM + * key or Widevine Keybox key) and the generated IV using AES-128-CBC + * with PKCS#5 padding. + * 10. Copy the rewrapped key to the buffer specified by wrapped_private_key + * and the size of the wrapped key to wrapped_private_key_length. * - * Parameters: - * [in] session: crypto session identifier. - * [in] message: pointer to memory containing data. - * [in] message_length: length of the message, in bytes. - * [in] core_message_length: length of the core submessage, in bytes. - * [in] signature: pointer to memory containing the signature. - * [in] signature_length: length of the signature, in bytes. - * [out] wrapped_private_key: pointer to buffer in which encrypted RSA or ECC - * private key should be stored. May be null on the first call in order - * to find required buffer size. - * [in/out] wrapped_private_key_length: (in) length of the encrypted private - * key, in bytes. - * (out) actual length of the encrypted private key + * @param[in] session: crypto session identifier. + * @param[in] message: pointer to memory containing data. + * @param[in] message_length: length of the message, in bytes. + * @param[in] core_message_length: length of the core submessage, in bytes. + * @param[in] signature: pointer to memory containing the signature. + * @param[in] signature_length: length of the signature, in bytes. + * @param[out] wrapped_private_key: pointer to buffer in which encrypted RSA or + * ECC private key should be stored. May be null on the first call in order + * to find required buffer size. + * @param[in,out] wrapped_private_key_length: (in) length of the encrypted + * private key, in bytes. (out) actual length of the encrypted private key * - * Returns: - * OEMCrypto_SUCCESS success - * OEMCrypto_ERROR_NO_DEVICE_KEY - * OEMCrypto_ERROR_INVALID_SESSION - * OEMCrypto_ERROR_INVALID_RSA_KEY - * OEMCrypto_ERROR_SIGNATURE_FAILURE - * OEMCrypto_ERROR_INVALID_NONCE - * OEMCrypto_ERROR_SHORT_BUFFER - * OEMCrypto_ERROR_INSUFFICIENT_RESOURCES - * OEMCrypto_ERROR_UNKNOWN_FAILURE - * OEMCrypto_ERROR_BUFFER_TOO_LARGE - * OEMCrypto_ERROR_SESSION_LOST_STATE - * OEMCrypto_ERROR_SYSTEM_INVALIDATED + * @retval OEMCrypto_SUCCESS success + * @retval OEMCrypto_ERROR_NO_DEVICE_KEY + * @retval OEMCrypto_ERROR_INVALID_SESSION + * @retval OEMCrypto_ERROR_INVALID_RSA_KEY + * @retval OEMCrypto_ERROR_SIGNATURE_FAILURE + * @retval OEMCrypto_ERROR_INVALID_NONCE + * @retval OEMCrypto_ERROR_SHORT_BUFFER + * @retval OEMCrypto_ERROR_INSUFFICIENT_RESOURCES + * @retval OEMCrypto_ERROR_UNKNOWN_FAILURE + * @retval OEMCrypto_ERROR_BUFFER_TOO_LARGE + * @retval OEMCrypto_ERROR_SESSION_LOST_STATE + * @retval OEMCrypto_ERROR_SYSTEM_INVALIDATED * - * Buffer Sizes: + * @buffer_size * OEMCrypto shall support message sizes as described in the section * OEMCrypto_ResourceRatingTier. * OEMCrypto shall return OEMCrypto_ERROR_BUFFER_TOO_LARGE if the buffer is * larger than the supported size. * - * Threading: + * @threading * This is a "Session Function" and may be called simultaneously with session * functions for other sessions but not simultaneously with other functions * for this session. It will not be called simultaneously with initialization * or usage table functions. It is as if the CDM holds a write lock for this * session, and a read lock on the OEMCrypto system. * - * Version: + * @version * This method changed in API version 16. */ OEMCryptoResult OEMCrypto_LoadProvisioning( @@ -3961,23 +3839,20 @@ OEMCryptoResult OEMCrypto_LoadProvisioning( size_t signature_length, uint8_t* wrapped_private_key, size_t* wrapped_private_key_length); -/* - * OEMCrypto_LoadDRMPrivateKey +/** + * Loads a wrapped RSA or ECC private key to secure memory for use by this + * session in future calls to OEMCrypto_PrepAndSignLicenseRequest or + * OEMCrypto_DeriveKeysFromSessionKey. The wrapped private key will be the + * one verified and wrapped by OEMCrypto_LoadProvisioning. The private key + * should be stored in secure memory. * - * Description: - * Loads a wrapped RSA or ECC private key to secure memory for use by this - * session in future calls to OEMCrypto_PrepAndSignLicenseRequest or - * OEMCrypto_DeriveKeysFromSessionKey. The wrapped private key will be the - * one verified and wrapped by OEMCrypto_LoadProvisioning. The private key - * should be stored in secure memory. + * If the bit field "allowed_schemes" was wrapped with this RSA key, its + * value will be loaded and stored with the RSA key, and the key may be used + * with calls to OEMCrypto_GenerateRSASignature. If there was not a bit field + * wrapped with the RSA key, the key will be used for + * OEMCrypto_PrepAndSignLicenseRequest or OEMCrypto_DeriveKeysFromSessionKey * - * If the bit field "allowed_schemes" was wrapped with this RSA key, its - * value will be loaded and stored with the RSA key, and the key may be used - * with calls to OEMCrypto_GenerateRSASignature. If there was not a bit field - * wrapped with the RSA key, the key will be used for - * OEMCrypto_PrepAndSignLicenseRequest or OEMCrypto_DeriveKeysFromSessionKey - * - * Verification: + * @verification * The following checks should be performed. If any check fails, an error is * returned, and the RSA key is not loaded. * 1. The wrapped key has a valid signature, as described in @@ -3986,151 +3861,136 @@ OEMCryptoResult OEMCrypto_LoadProvisioning( * 3. If a value for allowed_schemes is included with the key, it is a * valid value. * - * Parameters: - * [in] session: crypto session identifier. - * [in] key_type: indicates either an RSA or ECC key for devices that support - * both. - * [in] wrapped_rsa_key: wrapped device RSA key stored on the device. Format - * is PKCS#8, binary DER encoded, and encrypted with a key internal to - * the OEMCrypto instance, using AES-128-CBC with PKCS#5 padding. This - * is the wrapped key generated by OEMCrypto_RewrapDeviceRSAKey. - * [in] wrapped_rsa_key_length: length of the wrapped key buffer, in bytes. + * @param[in] session: crypto session identifier. + * @param[in] key_type: indicates either an RSA or ECC key for devices that + * support both. + * @param[in] wrapped_private_key: wrapped device private key (RSA or ECC). + * This is the wrapped key generated by OEMCrypto_LoadProvisioning. + * @param[in] wrapped_private_key_length: length of the wrapped key buffer, in + * bytes. * - * Returns: - * OEMCrypto_SUCCESS success - * OEMCrypto_ERROR_NO_DEVICE_KEY - * OEMCrypto_ERROR_INVALID_SESSION - * OEMCrypto_ERROR_INVALID_RSA_KEY - * OEMCrypto_ERROR_INSUFFICIENT_RESOURCES - * OEMCrypto_ERROR_UNKNOWN_FAILURE - * OEMCrypto_ERROR_SESSION_LOST_STATE - * OEMCrypto_ERROR_SYSTEM_INVALIDATED + * @retval OEMCrypto_SUCCESS success + * @retval OEMCrypto_ERROR_NO_DEVICE_KEY + * @retval OEMCrypto_ERROR_INVALID_SESSION + * @retval OEMCrypto_ERROR_INVALID_RSA_KEY + * @retval OEMCrypto_ERROR_INSUFFICIENT_RESOURCES + * @retval OEMCrypto_ERROR_UNKNOWN_FAILURE + * @retval OEMCrypto_ERROR_SESSION_LOST_STATE + * @retval OEMCrypto_ERROR_SYSTEM_INVALIDATED * - * Threading: + * @threading * This is a "Session Function" and may be called simultaneously with session * functions for other sessions but not simultaneously with other functions * for this session. It will not be called simultaneously with initialization * or usage table functions. It is as if the CDM holds a write lock for this * session, and a read lock on the OEMCrypto system. * - * Version: + * @version * This method changed in API version 16. */ OEMCryptoResult OEMCrypto_LoadDRMPrivateKey(OEMCrypto_SESSION session, OEMCrypto_PrivateKeyType key_type, - const uint8_t* wrapped_rsa_key, - size_t wrapped_rsa_key_length); + const uint8_t* wrapped_private_key, + size_t wrapped_private_key_length); -/* - * OEMCrypto_LoadTestRSAKey +/** + * Some platforms do not support keyboxes or OEM Certificates. On those + * platforms, there is a DRM certificate baked into the OEMCrypto library. + * This is unusual, and is only available for L3 devices. In order to debug + * and test those devices, they should be able to switch to the test DRM + * certificate. * - * Description: - * Some platforms do not support keyboxes or OEM Certificates. On those - * platforms, there is a DRM certificate baked into the OEMCrypto library. - * This is unusual, and is only available for L3 devices. In order to debug - * and test those devices, they should be able to switch to the test DRM - * certificate. + * Temporarily use the standard test RSA key until the next call to + * OEMCrypto_Terminate. This allows a standard suite of unit tests to be run + * on a production device without permanently changing the key. Using the + * test key is not persistent. * - * Temporarily use the standard test RSA key until the next call to - * OEMCrypto_Terminate. This allows a standard suite of unit tests to be run - * on a production device without permanently changing the key. Using the - * test key is not persistent. + * The test key can be found in the unit test code, oemcrypto_test.cpp, in + * PKCS8 form as the constant kTestRSAPKCS8PrivateKeyInfo2_2048. * - * The test key can be found in the unit test code, oemcrypto_test.cpp, in - * PKCS8 form as the constant kTestRSAPKCS8PrivateKeyInfo2_2048. + * @retval OEMCrypto_SUCCESS success + * @retval OEMCrypto_ERROR_INSUFFICIENT_RESOURCES + * @retval OEMCrypto_ERROR_NOT_IMPLEMENTED devices that use a keybox should + * not implement this function + * @retval OEMCrypto_ERROR_SYSTEM_INVALIDATED * - * Parameters: - * none - * - * Returns: - * OEMCrypto_SUCCESS success - * OEMCrypto_ERROR_INSUFFICIENT_RESOURCES - * OEMCrypto_ERROR_NOT_IMPLEMENTED - devices that use a keybox should not - * implement this function - * OEMCrypto_ERROR_SYSTEM_INVALIDATED - * - * Threading: + * @threading * This is an "Initialization and Termination Function" and will not be * called simultaneously with any other function, as if the CDM holds a write * lock on the OEMCrypto system. * - * Version: + * @version * This method is new in API version 10. */ OEMCryptoResult OEMCrypto_LoadTestRSAKey(void); -/* - * OEMCrypto_GenerateRSASignature +/** + * The OEMCrypto_GenerateRSASignature method is only used for devices that + * are CAST receivers. This function is called after + * OEMCrypto_LoadDRMPrivateKey for the same session. * - * Description: - * The OEMCrypto_GenerateRSASignature method is only used for devices that - * are CAST receivers. This function is called after - * OEMCrypto_LoadDRMPrivateKey for the same session. + * The parameter padding_scheme has two possible legacy values: * - * The parameter padding_scheme has two possible legacy values: + * 0x1 - RSASSA-PSS with SHA1. * - * 0x1 - RSASSA-PSS with SHA1. + * 0x2 - PKCS1 with block type 1 padding (only). * - * 0x2 - PKCS1 with block type 1 padding (only). + * The only supported padding scheme is 0x2 since version 16 of this API. In + * this second case, the "message" is already a digest, so no further hashing + * is applied, and the message_length can be no longer than 83 bytes. If the + * message_length is greater than 83 bytes OEMCrypto_ERROR_SIGNATURE_FAILURE + * shall be returned. * - * The only supported padding scheme is 0x2 since version 16 of this API. In - * this second case, the "message" is already a digest, so no further hashing - * is applied, and the message_length can be no longer than 83 bytes. If the - * message_length is greater than 83 bytes OEMCrypto_ERROR_SIGNATURE_FAILURE - * shall be returned. + * The second padding scheme is for devices that use X509 certificates for + * authentication. The main example is devices that work as a Cast receiver, + * like a ChromeCast, not for devices that wish to send to the Cast device, + * such as almost all Android devices. OEMs that do not support X509 + * certificate authentication need not implement this function and can return + * OEMCrypto_ERROR_NOT_IMPLEMENTED. * - * The second padding scheme is for devices that use X509 certificates for - * authentication. The main example is devices that work as a Cast receiver, - * like a ChromeCast, not for devices that wish to send to the Cast device, - * such as almost all Android devices. OEMs that do not support X509 - * certificate authentication need not implement this function and can return - * OEMCrypto_ERROR_NOT_IMPLEMENTED. - * - * Verification: + * @verification * Both the padding_scheme and the RSA key's allowed_schemes must be 0x2. If * not, then the signature is not computed and the error * OEMCrypto_ERROR_INVALID_RSA_KEY is returned. * - * Parameters: - * [in] session: crypto session identifier. - * [in] message: pointer to memory containing message to be signed. - * [in] message_length: length of the message, in bytes. - * [out] signature: buffer to hold the message signature. On return, it will - * contain the message signature generated with the device private RSA - * key using RSASSA-PSS. Will be null on the first call in order to - * find required buffer size. - * [in/out] signature_length: (in) length of the signature buffer, in bytes. - * (out) actual length of the signature - * [in] padding_scheme: specify which scheme to use for the signature. + * @param[in] session: crypto session identifier. + * @param[in] message: pointer to memory containing message to be signed. + * @param[in] message_length: length of the message, in bytes. + * @param[out] signature: buffer to hold the message signature. On return, it + * will contain the message signature generated with the device private RSA + * key using RSASSA-PSS. Will be null on the first call in order to find + * required buffer size. + * @param[in,out] signature_length: (in) length of the signature buffer, in + * bytes. (out) actual length of the signature + * @param[in] padding_scheme: specify which scheme to use for the signature. * - * Returns: - * OEMCrypto_SUCCESS success - * OEMCrypto_ERROR_SHORT_BUFFER if the signature buffer is too small. - * OEMCrypto_ERROR_INVALID_SESSION - * OEMCrypto_ERROR_INVALID_CONTEXT - * OEMCrypto_ERROR_INVALID_RSA_KEY - * OEMCrypto_ERROR_INSUFFICIENT_RESOURCES - * OEMCrypto_ERROR_UNKNOWN_FAILURE - * OEMCrypto_ERROR_NOT_IMPLEMENTED - if algorithm > 0, and the device does - * not support that algorithm. - * OEMCrypto_ERROR_BUFFER_TOO_LARGE - * OEMCrypto_ERROR_SESSION_LOST_STATE - * OEMCrypto_ERROR_SYSTEM_INVALIDATED + * @retval OEMCrypto_SUCCESS success + * @retval OEMCrypto_ERROR_SHORT_BUFFER if the signature buffer is too small. + * @retval OEMCrypto_ERROR_INVALID_SESSION + * @retval OEMCrypto_ERROR_INVALID_CONTEXT + * @retval OEMCrypto_ERROR_INVALID_RSA_KEY + * @retval OEMCrypto_ERROR_INSUFFICIENT_RESOURCES + * @retval OEMCrypto_ERROR_UNKNOWN_FAILURE + * @retval OEMCrypto_ERROR_NOT_IMPLEMENTED if algorithm > 0, and the device + * does not support that algorithm. + * @retval OEMCrypto_ERROR_BUFFER_TOO_LARGE + * @retval OEMCrypto_ERROR_SESSION_LOST_STATE + * @retval OEMCrypto_ERROR_SYSTEM_INVALIDATED * - * Buffer Sizes: + * @buffer_size * OEMCrypto shall support message sizes as described in the section * OEMCrypto_ResourceRatingTier. * OEMCrypto shall return OEMCrypto_ERROR_BUFFER_TOO_LARGE if the buffer is * larger than the supported size. * - * Threading: + * @threading * This is a "Session Function" and may be called simultaneously with session * functions for other sessions but not simultaneously with other functions * for this session. It will not be called simultaneously with initialization * or usage table functions. It is as if the CDM holds a write lock for this * session, and a read lock on the OEMCrypto system. * - * Version: + * @version * This method changed in API version 16. */ OEMCryptoResult OEMCrypto_GenerateRSASignature( @@ -4138,196 +3998,181 @@ OEMCryptoResult OEMCrypto_GenerateRSASignature( uint8_t* signature, size_t* signature_length, RSA_Padding_Scheme padding_scheme); -/* - * OEMCrypto_CreateUsageTableHeader +/// @} + +/// @addtogroup usage_table +/// @{ + +/** + * This creates a new Usage Table Header with no entries. If there is already + * a generation number stored in secure storage, it will be incremented by 1 + * and used as the new Master Generation Number. This will only be called if + * the CDM layer finds no existing usage table on the file system. OEMCrypto + * will encrypt and sign the new, empty, header and return it in the provided + * buffer. * - * Description: - * This creates a new Usage Table Header with no entries. If there is already - * a generation number stored in secure storage, it will be incremented by 1 - * and used as the new Master Generation Number. This will only be called if - * the CDM layer finds no existing usage table on the file system. OEMCrypto - * will encrypt and sign the new, empty, header and return it in the provided - * buffer. + * The new entry should be created with a status of kUnused and all times + * times should be set to 0. * - * The new entry should be created with a status of kUnused and all times - * times should be set to 0. + * Devices that do not implement a Session Usage Table may return + * OEMCrypto_ERROR_NOT_IMPLEMENTED. * - * Devices that do not implement a Session Usage Table may return - * OEMCrypto_ERROR_NOT_IMPLEMENTED. + * @param[out] header_buffer: pointer to memory where encrypted usage table + * header is written. + * @param[in,out] header_buffer_length: (in) length of the header_buffer, in + * bytes. (out) actual length of the header_buffer * - * Parameters: - * [out] header_buffer: pointer to memory where encrypted usage table header - * is written. - * [in/out] header_buffer_length: (in) length of the header_buffer, in bytes. - * (out) actual length of the header_buffer + * @retval OEMCrypto_SUCCESS success + * @retval OEMCrypto_ERROR_SHORT_BUFFER if header_buffer_length is too small. + * @retval OEMCrypto_ERROR_NOT_IMPLEMENTED + * @retval OEMCrypto_ERROR_UNKNOWN_FAILURE + * @retval OEMCrypto_ERROR_SYSTEM_INVALIDATED * - * Returns: - * OEMCrypto_SUCCESS success - * OEMCrypto_ERROR_SHORT_BUFFER - if header_buffer_length is too small. - * OEMCrypto_ERROR_NOT_IMPLEMENTED - * OEMCrypto_ERROR_UNKNOWN_FAILURE - * OEMCrypto_ERROR_SYSTEM_INVALIDATED - * - * Threading: + * @threading * This is a "Usage Table Function" and will not be called simultaneously * with any other function, as if the CDM holds a write lock on the OEMCrypto * system. * - * Version: + * @version * This method changed in API version 13. */ OEMCryptoResult OEMCrypto_CreateUsageTableHeader(uint8_t* header_buffer, size_t* header_buffer_length); -/* - * OEMCrypto_LoadUsageTableHeader +/** + * This loads the Usage Table Header. The buffer's signature is verified and + * the buffer is decrypted. OEMCrypto will verify the verification string. If + * the Master Generation Number is more than 1 off, the table is considered + * bad, the headers are NOT loaded, and the error + * OEMCrypto_ERROR_GENERATION_SKEW is returned. If the generation number is + * off by 1, the warning OEMCrypto_WARNING_GENERATION_SKEW is returned but + * the header is still loaded. This warning may be logged by the CDM layer. * - * Description: - * This loads the Usage Table Header. The buffer's signature is verified and - * the buffer is decrypted. OEMCrypto will verify the verification string. If - * the Master Generation Number is more than 1 off, the table is considered - * bad, the headers are NOT loaded, and the error - * OEMCrypto_ERROR_GENERATION_SKEW is returned. If the generation number is - * off by 1, the warning OEMCrypto_WARNING_GENERATION_SKEW is returned but - * the header is still loaded. This warning may be logged by the CDM layer. + * @param[in] buffer: pointer to memory containing encrypted usage table header. + * @param[in] buffer_length: length of the buffer, in bytes. * - * Parameters: - * [in] buffer: pointer to memory containing encrypted usage table header. - * [in] buffer_length: length of the buffer, in bytes. - * - * Returns: - * OEMCrypto_SUCCESS success - * OEMCrypto_ERROR_SHORT_BUFFER - * OEMCrypto_ERROR_NOT_IMPLEMENTED - some devices do not implement usage + * @retval OEMCrypto_SUCCESS success + * @retval OEMCrypto_ERROR_SHORT_BUFFER + * @retval OEMCrypto_ERROR_NOT_IMPLEMENTED some devices do not implement usage * tables. - * OEMCrypto_ERROR_UNKNOWN_FAILURE - * OEMCrypto_WARNING_GENERATION_SKEW - if the generation number is off by - * exactly 1. - * OEMCrypto_ERROR_GENERATION_SKEW - if the generation number is off by more - * than 1. - * OEMCrypto_ERROR_SIGNATURE_FAILURE - if the signature failed. - * OEMCrypto_ERROR_BAD_MAGIC - verification string does not match. - * OEMCrypto_ERROR_SYSTEM_INVALIDATED + * @retval OEMCrypto_ERROR_UNKNOWN_FAILURE + * @retval OEMCrypto_WARNING_GENERATION_SKEW if the generation number is off + * by exactly 1. + * @retval OEMCrypto_ERROR_GENERATION_SKEW if the generation number is off by + * more than 1. + * @retval OEMCrypto_ERROR_SIGNATURE_FAILURE if the signature failed. + * @retval OEMCrypto_ERROR_BAD_MAGIC verification string does not match. + * @retval OEMCrypto_ERROR_SYSTEM_INVALIDATED * - * Threading: + * @threading * This is a "Usage Table Function" and will not be called simultaneously * with any other function, as if the CDM holds a write lock on the OEMCrypto * system. * - * Version: + * @version * This method changed in API version 16. */ OEMCryptoResult OEMCrypto_LoadUsageTableHeader(const uint8_t* buffer, size_t buffer_length); -/* - * OEMCrypto_CreateNewUsageEntry +/** + * This creates a new usage entry. The size of the header will be increased + * by 8 bytes, and secure volatile memory will be allocated for it. The new + * entry will be associated with the given session. The status of the new + * entry will be set to "unused". OEMCrypto will set *usage_entry_number to + * be the index of the new entry. The first entry created will have index 0. + * The new entry will be initialized with a generation number equal to the + * master generation number, which will also be stored in the header's new + * slot. Then the master generation number will be incremented. Since each + * entry's generation number is less than the master generation number, the + * new entry will have a generation number that is larger than all other + * entries and larger than all previously deleted entries. This helps prevent + * a rogue application from deleting an entry and then loading an old version + * of it. * - * Description: - * This creates a new usage entry. The size of the header will be increased - * by 8 bytes, and secure volatile memory will be allocated for it. The new - * entry will be associated with the given session. The status of the new - * entry will be set to "unused". OEMCrypto will set *usage_entry_number to - * be the index of the new entry. The first entry created will have index 0. - * The new entry will be initialized with a generation number equal to the - * master generation number, which will also be stored in the header's new - * slot. Then the master generation number will be incremented. Since each - * entry's generation number is less than the master generation number, the - * new entry will have a generation number that is larger than all other - * entries and larger than all previously deleted entries. This helps prevent - * a rogue application from deleting an entry and then loading an old version - * of it. + * If the session already has a usage entry associated with it, the error + * OEMCrypto_ERROR_MULTIPLE_USAGE_ENTRIES is returned. * - * If the session already has a usage entry associated with it, the error - * OEMCrypto_ERROR_MULTIPLE_USAGE_ENTRIES is returned. + * @param[in] session: handle for the session to be used. + * @param[out] usage_entry_number: index of new usage entry. * - * Parameters: - * [in] session: handle for the session to be used. - * [out] usage_entry_number: index of new usage entry. - * - * Returns: - * OEMCrypto_SUCCESS success - * OEMCrypto_ERROR_NOT_IMPLEMENTED - some devices do not implement usage + * @retval OEMCrypto_SUCCESS success + * @retval OEMCrypto_ERROR_NOT_IMPLEMENTED some devices do not implement usage * tables. - * OEMCrypto_ERROR_INSUFFICIENT_RESOURCES - if there is no room in memory to - * increase the size of the usage table header. The CDM layer can - * delete some entries and then try again, or it can pass the error up - * to the application. - * OEMCrypto_ERROR_UNKNOWN_FAILURE - * OEMCrypto_ERROR_SESSION_LOST_STATE - * OEMCrypto_ERROR_SYSTEM_INVALIDATED - * OEMCrypto_ERROR_MULTIPLE_USAGE_ENTRIES - if there already is a usage entry - * loaded into this session + * @retval OEMCrypto_ERROR_INSUFFICIENT_RESOURCES if there is no room in + * memory to increase the size of the usage table header. The CDM layer + * can delete some entries and then try again, or it can pass the error + * up to the application. + * @retval OEMCrypto_ERROR_UNKNOWN_FAILURE + * @retval OEMCrypto_ERROR_SESSION_LOST_STATE + * @retval OEMCrypto_ERROR_SYSTEM_INVALIDATED + * @retval OEMCrypto_ERROR_MULTIPLE_USAGE_ENTRIES if there already is a usage + * entry loaded into this session * - * Threading: + * @threading * This is a "Usage Table Function" and will not be called simultaneously * with any other function, as if the CDM holds a write lock on the OEMCrypto * system. * - * Version: + * @version * This method changed in API version 13. */ OEMCryptoResult OEMCrypto_CreateNewUsageEntry(OEMCrypto_SESSION session, uint32_t* usage_entry_number); -/* - * OEMCrypto_LoadUsageEntry +/** + * This loads a usage entry saved previously by UpdateUsageEntry. The + * signature at the beginning of the buffer is verified and the buffer will + * be decrypted. Then the verification field in the entry will be verified. + * The index in the entry must match the index passed in. The generation + * number in the entry will be compared against the entry's corresponding + * generation number in the header. If it is off by 1, a warning is returned, + * but the entry is still loaded. This warning may be logged by the CDM + * layer. If the generation number is off by more than 1, an error is + * returned and the entry is not loaded. * - * Description: - * This loads a usage entry saved previously by UpdateUsageEntry. The - * signature at the beginning of the buffer is verified and the buffer will - * be decrypted. Then the verification field in the entry will be verified. - * The index in the entry must match the index passed in. The generation - * number in the entry will be compared against the entry's corresponding - * generation number in the header. If it is off by 1, a warning is returned, - * but the entry is still loaded. This warning may be logged by the CDM - * layer. If the generation number is off by more than 1, an error is - * returned and the entry is not loaded. + * OEMCrypto shall call ODK_ReloadClockValues, as described in "License + * Duration and Renewal" to set the session's clock values. * - * OEMCrypto shall call ODK_ReloadClockValues, as described in "License - * Duration and Renewal" to set the session's clock values. + * If the entry is already loaded into another open session, then this fails + * and returns OEMCrypto_ERROR_INVALID_SESSION. If the session already has a + * usage entry associated with it, the error + * OEMCrypto_ERROR_MULTIPLE_USAGE_ENTRIES is returned. * - * If the entry is already loaded into another open session, then this fails - * and returns OEMCrypto_ERROR_INVALID_SESSION. If the session already has a - * usage entry associated with it, the error - * OEMCrypto_ERROR_MULTIPLE_USAGE_ENTRIES is returned. + * Before version API 16, the usage entry stored the time that the license + * was loaded. This value is now interpreted as the time that the licence + * request was signed. This can be achieved by simply renaming the field and + * using the same value when reloading an older entry. * - * Before version API 16, the usage entry stored the time that the license - * was loaded. This value is now interpreted as the time that the licence - * request was signed. This can be achieved by simply renaming the field and - * using the same value when reloading an older entry. + * @param[in] session: handle for the session to be used. + * @param[in] usage_entry_number: index of existing usage entry. + * @param[in] buffer: pointer to memory containing encrypted usage table entry. + * @param[in] buffer_length: length of the buffer, in bytes. * - * Parameters: - * [in] session: handle for the session to be used. - * [in] usage_entry_number: index of existing usage entry. - * [in] buffer: pointer to memory containing encrypted usage table entry. - * [in] buffer_length: length of the buffer, in bytes. - * - * Returns: - * OEMCrypto_SUCCESS success - * OEMCrypto_ERROR_SHORT_BUFFER - * OEMCrypto_ERROR_NOT_IMPLEMENTED - some devices do not implement usage + * @retval OEMCrypto_SUCCESS success + * @retval OEMCrypto_ERROR_SHORT_BUFFER + * @retval OEMCrypto_ERROR_NOT_IMPLEMENTED some devices do not implement usage * tables. - * OEMCrypto_ERROR_UNKNOWN_FAILURE - index beyond end of table. - * OEMCrypto_ERROR_INVALID_SESSION - entry associated with another session or - * the index is wrong. - * OEMCrypto_WARNING_GENERATION_SKEW - if the generation number is off by - * exactly 1. - * OEMCrypto_ERROR_GENERATION_SKEW - if the generation number is off by more - * than 1. - * OEMCrypto_ERROR_SIGNATURE_FAILURE - if the signature failed. - * OEMCrypto_ERROR_BAD_MAGIC - verification string does not match. - * OEMCrypto_ERROR_SESSION_LOST_STATE - * OEMCrypto_ERROR_SYSTEM_INVALIDATED - * OEMCrypto_ERROR_MULTIPLE_USAGE_ENTRIES - if there already is a usage entry - * loaded into this session + * @retval OEMCrypto_ERROR_UNKNOWN_FAILURE index beyond end of table. + * @retval OEMCrypto_ERROR_INVALID_SESSION entry associated with another + * session or the index is wrong. + * @retval OEMCrypto_WARNING_GENERATION_SKEW if the generation number is off + * by exactly 1. + * @retval OEMCrypto_ERROR_GENERATION_SKEW if the generation number is off by + * more than 1. + * @retval OEMCrypto_ERROR_SIGNATURE_FAILURE if the signature failed. + * @retval OEMCrypto_ERROR_BAD_MAGIC verification string does not match. + * @retval OEMCrypto_ERROR_SESSION_LOST_STATE + * @retval OEMCrypto_ERROR_SYSTEM_INVALIDATED + * @retval OEMCrypto_ERROR_MULTIPLE_USAGE_ENTRIES if there already is a usage + * entry loaded into this session * - * Threading: + * @threading * This is a "Usage Table Function" and will not be called simultaneously * with any other function, as if the CDM holds a write lock on the OEMCrypto * system. * - * Version: + * @version * This method changed in API version 13. */ OEMCryptoResult OEMCrypto_LoadUsageEntry(OEMCrypto_SESSION session, @@ -4335,68 +4180,63 @@ OEMCryptoResult OEMCrypto_LoadUsageEntry(OEMCrypto_SESSION session, const uint8_t* buffer, size_t buffer_length); -/* - * OEMCrypto_UpdateUsageEntry +/** + * Updates the session's usage entry and fills buffers with the encrypted and + * signed entry and usage table header. * - * Description: - * Updates the session's usage entry and fills buffers with the encrypted and - * signed entry and usage table header. + * OEMCrypto shall call ODK_UpdateLastPlaybackTime to update the session's + * clock values, as discussed in the document "License Duration and Renewal". + * The values in the session's clock values structure are copied to the usage + * entry. * - * OEMCrypto shall call ODK_UpdateLastPlaybackTime to update the session's - * clock values, as discussed in the document "License Duration and Renewal". - * The values in the session's clock values structure are copied to the usage - * entry. + * OEMCrypto shall update all time and status values in the entry, and then + * increment the entry's generation number. The corresponding generation + * number in the usage table header is also incremented so that it matches + * the one in the entry. The master generation number in the usage table + * header is incremented and the master generation number is copied to secure + * persistent storage. OEMCrypto will encrypt and sign the entry into the + * entry_buffer, and it will encrypt and sign the usage table header into the + * header_buffer. Some actions, such as the first decrypt and deactivating an + * entry, will also increment the entry's generation number as well as + * changing the entry's status and time fields. The first decryption will + * change the status from Inactive to Active, and it will set the time stamp + * "first decrypt". * - * OEMCrypto shall update all time and status values in the entry, and then - * increment the entry's generation number. The corresponding generation - * number in the usage table header is also incremented so that it matches - * the one in the entry. The master generation number in the usage table - * header is incremented and the master generation number is copied to secure - * persistent storage. OEMCrypto will encrypt and sign the entry into the - * entry_buffer, and it will encrypt and sign the usage table header into the - * header_buffer. Some actions, such as the first decrypt and deactivating an - * entry, will also increment the entry's generation number as well as - * changing the entry's status and time fields. The first decryption will - * change the status from Inactive to Active, and it will set the time stamp - * "first decrypt". + * If the usage entry has the flag ForbidReport set, then the flag is + * cleared. It is the responsibility of the CDM layer to call this function + * and save the usage table before the next call to ReportUsage and before + * the CDM is terminated. Failure to do so will result in generation number + * skew, which will invalidate all of the usage table. * - * If the usage entry has the flag ForbidReport set, then the flag is - * cleared. It is the responsibility of the CDM layer to call this function - * and save the usage table before the next call to ReportUsage and before - * the CDM is terminated. Failure to do so will result in generation number - * skew, which will invalidate all of the usage table. + * If either entry_buffer_length or header_buffer_length is not large enough, + * they are set to the needed size, and return OEMCrypto_ERROR_SHORT_BUFFER. + * In this case, the entry is not updated, ForbidReport is not cleared, + * generation numbers are not incremented, and no other work is done. * - * If either entry_buffer_length or header_buffer_length is not large enough, - * they are set to the needed size, and return OEMCrypto_ERROR_SHORT_BUFFER. - * In this case, the entry is not updated, ForbidReport is not cleared, - * generation numbers are not incremented, and no other work is done. + * @param[in] session: handle for the session to be used. + * @param[out] header_buffer: pointer to memory where encrypted usage table + * header is written. + * @param[in,out] header_buffer_length: (in) length of the header_buffer, in + * bytes. (out) actual length of the header_buffer + * @param[out] entry_buffer: pointer to memory where encrypted usage table entry + * is written. + * @param[in,out] entry_buffer_length: (in) length of the entry_buffer, in + * bytes. (out) actual length of the entry_buffer * - * Parameters: - * [in] session: handle for the session to be used. - * [out] header_buffer: pointer to memory where encrypted usage table header - * is written. - * [in/out] header_buffer_length: (in) length of the header_buffer, in bytes. - * (out) actual length of the header_buffer - * [out] entry_buffer: pointer to memory where encrypted usage table entry is - * written. - * [in/out] entry_buffer_length: (in) length of the entry_buffer, in bytes. - * (out) actual length of the entry_buffer - * - * Returns: - * OEMCrypto_SUCCESS success - * OEMCrypto_ERROR_SHORT_BUFFER - * OEMCrypto_ERROR_NOT_IMPLEMENTED - some devices do not implement usage + * @retval OEMCrypto_SUCCESS success + * @retval OEMCrypto_ERROR_SHORT_BUFFER + * @retval OEMCrypto_ERROR_NOT_IMPLEMENTED some devices do not implement usage * tables. - * OEMCrypto_ERROR_UNKNOWN_FAILURE - * OEMCrypto_ERROR_SESSION_LOST_STATE - * OEMCrypto_ERROR_SYSTEM_INVALIDATED + * @retval OEMCrypto_ERROR_UNKNOWN_FAILURE + * @retval OEMCrypto_ERROR_SESSION_LOST_STATE + * @retval OEMCrypto_ERROR_SYSTEM_INVALIDATED * - * Threading: + * @threading * This is a "Usage Table Function" and will not be called simultaneously * with any other function, as if the CDM holds a write lock on the OEMCrypto * system. * - * Version: + * @version * This method changed in API version 16. */ OEMCryptoResult OEMCrypto_UpdateUsageEntry( @@ -4404,393 +4244,363 @@ OEMCryptoResult OEMCrypto_UpdateUsageEntry( size_t* header_buffer_length, OEMCrypto_SharedMemory* entry_buffer, size_t* entry_buffer_length); -/* - * OEMCrypto_DeactivateUsageEntry +/** + * This deactivates the usage entry associated with the current session. This + * means that the status of the usage entry is changed to InactiveUsed if it + * was Active, or InactiveUnused if it was Unused. This also increments the + * entry's generation number, and the header's master generation number. The + * corresponding generation number in the usage table header is also + * incremented so that it matches the one in the entry. The entry's flag + * ForbidReport will be set. This flag prevents an application from + * generating a report of a deactivated license without first saving the + * entry. * - * Description: - * This deactivates the usage entry associated with the current session. This - * means that the status of the usage entry is changed to InactiveUsed if it - * was Active, or InactiveUnused if it was Unused. This also increments the - * entry's generation number, and the header's master generation number. The - * corresponding generation number in the usage table header is also - * incremented so that it matches the one in the entry. The entry's flag - * ForbidReport will be set. This flag prevents an application from - * generating a report of a deactivated license without first saving the - * entry. + * OEMCrypto shall call ODK_DeactivateUsageEntry to update the session's + * clock values, as discussed in the document "License Duration and Renewal". * - * OEMCrypto shall call ODK_DeactivateUsageEntry to update the session's - * clock values, as discussed in the document "License Duration and Renewal". + * It is allowed to call this function multiple times. If the state is + * already InactiveUsed or InactiveUnused, then this function does not change + * the entry or its state. * - * It is allowed to call this function multiple times. If the state is - * already InactiveUsed or InactiveUnused, then this function does not change - * the entry or its state. + * @param[in] session: handle for the session to be used. + * @param[in] pst: pointer to memory containing Provider Session Token. + * @param[in] pst_length: length of the pst, in bytes. * - * Parameters: - * [in] session: handle for the session to be used. - * [in] pst: pointer to memory containing Provider Session Token. - * [in] pst_length: length of the pst, in bytes. + * @retval OEMCrypto_SUCCESS success + * @retval OEMCrypto_ERROR_INVALID_CONTEXT an entry was not created or loaded, + * or the pst does not match. + * @retval OEMCrypto_ERROR_NOT_IMPLEMENTED + * @retval OEMCrypto_ERROR_UNKNOWN_FAILURE + * @retval OEMCrypto_ERROR_BUFFER_TOO_LARGE + * @retval OEMCrypto_ERROR_SESSION_LOST_STATE + * @retval OEMCrypto_ERROR_SYSTEM_INVALIDATED * - * Returns: - * OEMCrypto_SUCCESS success - * OEMCrypto_ERROR_INVALID_CONTEXT - an entry was not created or loaded, or - * the pst does not match. - * OEMCrypto_ERROR_NOT_IMPLEMENTED - * OEMCrypto_ERROR_UNKNOWN_FAILURE - * OEMCrypto_ERROR_BUFFER_TOO_LARGE - * OEMCrypto_ERROR_SESSION_LOST_STATE - * OEMCrypto_ERROR_SYSTEM_INVALIDATED - * - * Buffer Sizes: + * @buffer_size * OEMCrypto shall support pst sizes of at least 255 bytes. * OEMCrypto shall return OEMCrypto_ERROR_BUFFER_TOO_LARGE if the buffer is * larger than the supported size. * - * Threading: + * @threading * This is a "Usage Table Function" and will not be called simultaneously * with any other function, as if the CDM holds a write lock on the OEMCrypto * system. * - * Version: + * @version * This method changed in API version 16. */ OEMCryptoResult OEMCrypto_DeactivateUsageEntry(OEMCrypto_SESSION session, const uint8_t* pst, size_t pst_length); -/* - * OEMCrypto_ReportUsage +/** + * All fields of OEMCrypto_PST_Report are in network byte order. * - * Description: - * All fields of OEMCrypto_PST_Report are in network byte order. + * If the buffer_length is not sufficient to hold a report structure, set + * buffer_length and return OEMCrypto_ERROR_SHORT_BUFFER. * - * If the buffer_length is not sufficient to hold a report structure, set - * buffer_length and return OEMCrypto_ERROR_SHORT_BUFFER. + * If an entry was not loaded or created with OEMCrypto_CreateNewUsageEntry + * or OEMCrypto_LoadUsageEntry, or if the pst does not match that in the + * entry, return the error OEMCrypto_ERROR_INVALID_CONTEXT. * - * If an entry was not loaded or created with OEMCrypto_CreateNewUsageEntry - * or OEMCrypto_LoadUsageEntry, or if the pst does not match that in the - * entry, return the error OEMCrypto_ERROR_INVALID_CONTEXT. + * If the usage entry's flag ForbidReport is set, indicating the entry has + * not been saved since the entry was deactivated, then the error + * OEMCrypto_ERROR_ENTRY_NEEDS_UPDATE is returned and a report is not + * generated. Similarly, if any key in the session has been used since the + * last call to OEMCrypto_UpdateUsageEntry, then the report is not generated, + * and OEMCrypto returns the error OEMCrypto_ERROR_ENTRY_NEEDS_UPDATE. * - * If the usage entry's flag ForbidReport is set, indicating the entry has - * not been saved since the entry was deactivated, then the error - * OEMCrypto_ERROR_ENTRY_NEEDS_UPDATE is returned and a report is not - * generated. Similarly, if any key in the session has been used since the - * last call to OEMCrypto_UpdateUsageEntry, then the report is not generated, - * and OEMCrypto returns the error OEMCrypto_ERROR_ENTRY_NEEDS_UPDATE. + * The pst_report is filled out by subtracting the times in the Usage Entry + * from the current time on the secure clock. This design was chosen to avoid + * a requirement to sync the device's secure clock with any external clock. * - * The pst_report is filled out by subtracting the times in the Usage Entry - * from the current time on the secure clock. This design was chosen to avoid - * a requirement to sync the device's secure clock with any external clock. + * (See drawing in "Widevine Modular DRM Security Integration Guide") * - * (See drawing in "Widevine Modular DRM Security Integration Guide") + * Valid values for status are: * - * Valid values for status are: + * - 0 = kUnused -- the keys have not been used to decrypt. + * - 1 = kActive -- the keys have been used, and have not been deactivated. + * - 2 = kInactive - deprecated. Use kInactiveUsed or kInactiveUnused. + * - 3 = kInactiveUsed -- the keys have been marked inactive after being + * active. + * - 4 = kInactiveUnused -- they keys have been marked inactive, but were + * never active. + * The clock_security_level is reported as follows: * - * - 0 = kUnused -- the keys have not been used to decrypt. - * - 1 = kActive -- the keys have been used, and have not been deactivated. - * - 2 = kInactive - deprecated. Use kInactiveUsed or kInactiveUnused. - * - 3 = kInactiveUsed -- the keys have been marked inactive after being - * active. - * - 4 = kInactiveUnused -- they keys have been marked inactive, but were - * never active. - * The clock_security_level is reported as follows: + * - 0 = Insecure Clock - clock just uses system time. + * - 1 = Secure Timer - clock runs from a secure timer which is initialized + * from system time when OEMCrypto becomes active and cannot be modified + * by user software or the user while OEMCrypto is active. A secure + * timer cannot run backwards, even while OEMCrypto is not active. + * - 2 = Secure Clock - Real-time clock set from a secure source that + * cannot be modified by user software regardless of whether OEMCrypto + * is active or inactive. The clock time can only be modified by + * tampering with the security software or hardware. + * - 3 = Hardware Secure Clock - Real-time clock set from a secure source + * that cannot be modified by user software and there are security + * features that prevent the user from modifying the clock in hardware, + * such as a tamper proof battery. + * (See drawing in "Widevine Modular DRM Security Integration Guide") * - * - 0 = Insecure Clock - clock just uses system time. - * - 1 = Secure Timer - clock runs from a secure timer which is initialized - * from system time when OEMCrypto becomes active and cannot be modified - * by user software or the user while OEMCrypto is active. A secure - * timer cannot run backwards, even while OEMCrypto is not active. - * - 2 = Secure Clock - Real-time clock set from a secure source that - * cannot be modified by user software regardless of whether OEMCrypto - * is active or inactive. The clock time can only be modified by - * tampering with the security software or hardware. - * - 3 = Hardware Secure Clock - Real-time clock set from a secure source - * that cannot be modified by user software and there are security - * features that prevent the user from modifying the clock in hardware, - * such as a tamper proof battery. - * (See drawing in "Widevine Modular DRM Security Integration Guide") + * After pst_report has been filled in, the HMAC SHA1 signature is computed + * for the buffer from bytes 20 to the end of the pst field. The signature is + * computed using the mac_key[client] which is stored in the usage table. The + * HMAC SHA1 signature is used to prevent a rogue application from using + * OMECrypto_GenerateSignature to forge a Usage Report. * - * After pst_report has been filled in, the HMAC SHA1 signature is computed - * for the buffer from bytes 20 to the end of the pst field. The signature is - * computed using the mac_key[client] which is stored in the usage table. The - * HMAC SHA1 signature is used to prevent a rogue application from using - * OMECrypto_GenerateSignature to forge a Usage Report. + * Before version 16 of this API, seconds_since_license_received was reported + * instead of seconds_since_license_signed. For any practical bookkeeping + * purposes, these events are essentially at the same time. * - * Before version 16 of this API, seconds_since_license_received was reported - * instead of seconds_since_license_signed. For any practical bookkeeping - * purposes, these events are essentially at the same time. + * Devices that do not implement a Session Usage Table may return + * OEMCrypto_ERROR_NOT_IMPLEMENTED. * - * Devices that do not implement a Session Usage Table may return - * OEMCrypto_ERROR_NOT_IMPLEMENTED. + * @param[in] session: handle for the session to be used. + * @param[in] pst: pointer to memory containing Provider Session Token. + * @param[in] pst_length: length of the pst, in bytes. + * @param[out] buffer: pointer to buffer in which usage report should be + * stored. May be null on the first call in order to find required buffer + * size. + * @param[in,out] buffer_length: (in) length of the report buffer, in bytes. + * (out) actual length of the report * - * Parameters: - * [in] session: handle for the session to be used. - * [in] pst: pointer to memory containing Provider Session Token. - * [in] pst_length: length of the pst, in bytes. - * [out] buffer: pointer to buffer in which usage report should be stored. - * May be null on the first call in order to find required buffer size. - * [in/out] buffer_length: (in) length of the report buffer, in bytes. - * (out) actual length of the report + * @retval OEMCrypto_SUCCESS success + * @retval OEMCrypto_ERROR_SHORT_BUFFER if report buffer is not large enough + * to hold the output report. + * @retval OEMCrypto_ERROR_INVALID_SESSION no open session with that id. + * @retval OEMCrypto_ERROR_INVALID_CONTEXT + * @retval OEMCrypto_ERROR_NOT_IMPLEMENTED + * @retval OEMCrypto_ERROR_UNKNOWN_FAILURE + * @retval OEMCrypto_ERROR_BUFFER_TOO_LARGE + * @retval OEMCrypto_ERROR_ENTRY_NEEDS_UPDATE if no call to UpdateUsageEntry + * since last call to Deactivate or since key use. + * @retval OEMCrypto_ERROR_WRONG_PST report asked for wrong pst. + * @retval OEMCrypto_ERROR_SESSION_LOST_STATE + * @retval OEMCrypto_ERROR_SYSTEM_INVALIDATED * - * Returns: - * OEMCrypto_SUCCESS success - * OEMCrypto_ERROR_SHORT_BUFFER - if report buffer is not large enough to - * hold the output report. - * OEMCrypto_ERROR_INVALID_SESSION - no open session with that id. - * OEMCrypto_ERROR_INVALID_CONTEXT - * OEMCrypto_ERROR_NOT_IMPLEMENTED - * OEMCrypto_ERROR_UNKNOWN_FAILURE - * OEMCrypto_ERROR_BUFFER_TOO_LARGE - * OEMCrypto_ERROR_ENTRY_NEEDS_UPDATE - if no call to UpdateUsageEntry since - * last call to Deactivate or since key use. - * OEMCrypto_ERROR_WRONG_PST - report asked for wrong pst. - * OEMCrypto_ERROR_SESSION_LOST_STATE - * OEMCrypto_ERROR_SYSTEM_INVALIDATED - * - * Buffer Sizes: + * @buffer_size * OEMCrypto shall support pst sizes of at least 255 bytes. * OEMCrypto shall return OEMCrypto_ERROR_BUFFER_TOO_LARGE if the buffer is * larger than the supported size. * - * Threading: + * @threading * This is a "Usage Table Function" and will not be called simultaneously * with any other function, as if the CDM holds a write lock on the OEMCrypto * system. * - * Version: + * @version * This method changed in API version 13. */ OEMCryptoResult OEMCrypto_ReportUsage(OEMCrypto_SESSION session, const uint8_t* pst, size_t pst_length, uint8_t* buffer, size_t* buffer_length); -/* - * OEMCrypto_MoveEntry +/** + * Moves the entry associated with the current session from one location in + * the usage table header to another. This function is used by the CDM layer + * to defragment the usage table. This does not modify any data in the entry, + * except the index and the generation number. The index in the session's + * usage entry will be changed to new_index. The generation number in + * session's usage entry and in the header for new_index will be increased to + * the master generation number, and then the master generation number is + * incremented. If there was an existing entry at the new location, it will + * be overwritten. It is an error to call this when the entry that was at + * new_index is associated with a currently open session. In this case, the + * error code OEMCrypto_ERROR_ENTRY_IN_USE is returned. It is the CDM layer's + * responsibility to call UpdateUsageEntry after moving an entry. It is an + * error for new_index to be beyond the end of the existing usage table + * header. * - * Description: - * Moves the entry associated with the current session from one location in - * the usage table header to another. This function is used by the CDM layer - * to defragment the usage table. This does not modify any data in the entry, - * except the index and the generation number. The index in the session's - * usage entry will be changed to new_index. The generation number in - * session's usage entry and in the header for new_index will be increased to - * the master generation number, and then the master generation number is - * incremented. If there was an existing entry at the new location, it will - * be overwritten. It is an error to call this when the entry that was at - * new_index is associated with a currently open session. In this case, the - * error code OEMCrypto_ERROR_ENTRY_IN_USE is returned. It is the CDM layer's - * responsibility to call UpdateUsageEntry after moving an entry. It is an - * error for new_index to be beyond the end of the existing usage table - * header. + * Devices that do not implement a Session Usage Table may return + * OEMCrypto_ERROR_NOT_IMPLEMENTED. * - * Devices that do not implement a Session Usage Table may return - * OEMCrypto_ERROR_NOT_IMPLEMENTED. + * @param[in] session: handle for the session to be used. + * @param[in] new_index: new index to be used for the session's usage entry * - * Parameters: - * [in] session: handle for the session to be used. - * [in] new_index: new index to be used for the session's usage entry + * @retval OEMCrypto_SUCCESS success + * @retval OEMCrypto_ERROR_NOT_IMPLEMENTED + * @retval OEMCrypto_ERROR_UNKNOWN_FAILURE + * @retval OEMCrypto_ERROR_BUFFER_TOO_LARGE + * @retval OEMCrypto_ERROR_ENTRY_IN_USE + * @retval OEMCrypto_ERROR_SESSION_LOST_STATE + * @retval OEMCrypto_ERROR_SYSTEM_INVALIDATED * - * Returns: - * OEMCrypto_SUCCESS success - * OEMCrypto_ERROR_NOT_IMPLEMENTED - * OEMCrypto_ERROR_UNKNOWN_FAILURE - * OEMCrypto_ERROR_BUFFER_TOO_LARGE - * OEMCrypto_ERROR_ENTRY_IN_USE - * OEMCrypto_ERROR_SESSION_LOST_STATE - * OEMCrypto_ERROR_SYSTEM_INVALIDATED - * - * Threading: + * @threading * This is a "Usage Table Function" and will not be called simultaneously * with any other function, as if the CDM holds a write lock on the OEMCrypto * system. * - * Version: + * @version * This method is new in API version 13. */ OEMCryptoResult OEMCrypto_MoveEntry(OEMCrypto_SESSION session, uint32_t new_index); -/* - * OEMCrypto_ShrinkUsageTableHeader +/** + * This shrinks the usage table and the header. This function is used by the + * CDM layer after it has defragmented the usage table and can delete unused + * entries. It is an error if any open session is associated with an entry + * that will be erased - the error OEMCrypto_ERROR_ENTRY_IN_USE shall be + * returned in this case, and the header shall not be modified. If + * new_entry_count is larger than the current size, then the header is not + * changed and the error OEMCrypto_ERROR_UNKNOWN_FAILURE is returned. If the + * header has not been previously loaded, then an error is returned. + * OEMCrypto will increment the master generation number in the header and + * store the new value in secure persistent storage. Then, OEMCrypto will + * encrypt and sign the header into the provided buffer. The generation + * numbers of all remaining entries will remain unchanged. The next time + * OEMCrypto_CreateNewUsageEntry is called, the new entry will have an index + * of new_entry_count. * - * Description: - * This shrinks the usage table and the header. This function is used by the - * CDM layer after it has defragmented the usage table and can delete unused - * entries. It is an error if any open session is associated with an entry - * that will be erased - the error OEMCrypto_ERROR_ENTRY_IN_USE shall be - * returned in this case, and the header shall not be modified. If - * new_entry_count is larger than the current size, then the header is not - * changed and the error OEMCrypto_ERROR_UNKNOWN_FAILURE is returned. If the - * header has not been previously loaded, then an error is returned. - * OEMCrypto will increment the master generation number in the header and - * store the new value in secure persistent storage. Then, OEMCrypto will - * encrypt and sign the header into the provided buffer. The generation - * numbers of all remaining entries will remain unchanged. The next time - * OEMCrypto_CreateNewUsageEntry is called, the new entry will have an index - * of new_entry_count. + * Devices that do not implement a Session Usage Table may return + * OEMCrypto_ERROR_NOT_IMPLEMENTED. * - * Devices that do not implement a Session Usage Table may return - * OEMCrypto_ERROR_NOT_IMPLEMENTED. + * If header_buffer_length is not large enough to hold the new table, it is + * set to the needed value, the generation number is not incremented, and + * OEMCrypto_ERROR_SHORT_BUFFER is returned. * - * If header_buffer_length is not large enough to hold the new table, it is - * set to the needed value, the generation number is not incremented, and - * OEMCrypto_ERROR_SHORT_BUFFER is returned. + * If the header has not been loaded or created, return the error + * OEMCrypto_ERROR_UNKNOWN_FAILURE. * - * If the header has not been loaded or created, return the error - * OEMCrypto_ERROR_UNKNOWN_FAILURE. + * @param[in] new_entry_count: number of entries in the to be in the header. + * @param[out] header_buffer: pointer to memory where encrypted usage table + * header is written. + * @param[in,out] header_buffer_length: (in) length of the header_buffer, in + * bytes. (out) actual length of the header_buffer * - * Parameters: - * [in] new_entry_count: number of entries in the to be in the header. - * [out] header_buffer: pointer to memory where encrypted usage table header - * is written. - * [in/out] header_buffer_length: (in) length of the header_buffer, in bytes. - * (out) actual length of the header_buffer + * @retval OEMCrypto_SUCCESS success + * @retval OEMCrypto_ERROR_SHORT_BUFFER + * @retval OEMCrypto_ERROR_NOT_IMPLEMENTED + * @retval OEMCrypto_ERROR_UNKNOWN_FAILURE + * @retval OEMCrypto_ERROR_ENTRY_IN_USE + * @retval OEMCrypto_ERROR_SYSTEM_INVALIDATED * - * Returns: - * OEMCrypto_SUCCESS success - * OEMCrypto_ERROR_SHORT_BUFFER - * OEMCrypto_ERROR_NOT_IMPLEMENTED - * OEMCrypto_ERROR_UNKNOWN_FAILURE - * OEMCrypto_ERROR_ENTRY_IN_USE - * OEMCrypto_ERROR_SYSTEM_INVALIDATED - * - * Threading: + * @threading * This is a "Usage Table Function" and will not be called simultaneously * with any other function, as if the CDM holds a write lock on the OEMCrypto * system. * - * Version: + * @version * This method is new in API version 13. */ OEMCryptoResult OEMCrypto_ShrinkUsageTableHeader(uint32_t new_entry_count, uint8_t* header_buffer, size_t* header_buffer_length); -/* - * OEMCrypto_RemoveSRM +/// @} + +/// @addtogroup test_verify +/// @{ + +/** + * Delete the current SRM. Any valid SRM, regardless of its version number, + * will be installable after this via OEMCrypto_LoadSRM. * - * Description: - * Delete the current SRM. Any valid SRM, regardless of its version number, - * will be installable after this via OEMCrypto_LoadSRM. + * This function should not be implemented on production devices, and will + * only be used to verify unit tests on a test device. * - * This function should not be implemented on production devices, and will - * only be used to verify unit tests on a test device. + * @retval OEMCrypto_SUCCESS if the SRM file was deleted. + * @retval OEMCrypto_ERROR_NOT_IMPLEMENTED always on production devices. * - * Parameters: - * none - * - * Returns: - * OEMCrypto_SUCCESS - if the SRM file was deleted. - * OEMCrypto_ERROR_NOT_IMPLEMENTED - always on production devices. - * - * Threading: + * @threading * This is an "Initialization and Termination Function" and will not be * called simultaneously with any other function, as if the CDM holds a write * lock on the OEMCrypto system. * - * Version: + * @version * This method is new in API version 13. */ OEMCryptoResult OEMCrypto_RemoveSRM(void); -/* - * OEMCrypto_SupportsDecryptHash +/** + * Returns the type of hash function supported for Full Decrypt Path Testing. + * A hash type of OEMCrypto_Hash_Not_Supported = 0 means this feature is not + * supported. OEMCrypto is not required by Google to support this feature, + * but support will greatly improve automated testing. A hash type of + * OEMCrypto_CRC_Clear_Buffer = 1 means the device will be able to compute + * the CRC 32 checksum of the decrypted content in the secure buffer after a + * call to OEMCrypto_DecryptCENC. Google intends to provide test applications + * on some platforms, such as Android, that will automate decryption testing + * using the CRC 32 checksum of all frames in some test content. * - * Description: - * Returns the type of hash function supported for Full Decrypt Path Testing. - * A hash type of OEMCrypto_Hash_Not_Supported = 0 means this feature is not - * supported. OEMCrypto is not required by Google to support this feature, - * but support will greatly improve automated testing. A hash type of - * OEMCrypto_CRC_Clear_Buffer = 1 means the device will be able to compute - * the CRC 32 checksum of the decrypted content in the secure buffer after a - * call to OEMCrypto_DecryptCENC. Google intends to provide test applications - * on some platforms, such as Android, that will automate decryption testing - * using the CRC 32 checksum of all frames in some test content. + * If an SOC vendor cannot support CRC 32 checksums of decrypted output, but + * can support some other hash or checksum, then the function should return + * OEMCrypto_Partner_Defined_Hash = 2 and those partners should modify the + * test application to compute the appropriate hash. An application that + * computes the CRC 32 hashes of test content and builds a hash file in the + * correct format will be provided by Widevine. The source of this + * application will be provided so that partners may modify it to compute + * their own hash format and generate their own hashes. * - * If an SOC vendor cannot support CRC 32 checksums of decrypted output, but - * can support some other hash or checksum, then the function should return - * OEMCrypto_Partner_Defined_Hash = 2 and those partners should modify the - * test application to compute the appropriate hash. An application that - * computes the CRC 32 hashes of test content and builds a hash file in the - * correct format will be provided by Widevine. The source of this - * application will be provided so that partners may modify it to compute - * their own hash format and generate their own hashes. + * @retval OEMCrypto_Hash_Not_Supported = 0; + * @retval OEMCrypto_CRC_Clear_Buffer = 1; + * @retval OEMCrypto_Partner_Defined_Hash = 2; * - * Returns: - * OEMCrypto_Hash_Not_Supported = 0; - * OEMCrypto_CRC_Clear_Buffer = 1; - * OEMCrypto_Partner_Defined_Hash = 2; - * - * Threading: + * @threading * This is a "Property Function" and may be called simultaneously with any * other property function or session function, but not any initialization or * usage table function, as if the CDM holds a read lock on the OEMCrypto * system. * - * Version: + * @version * This method is new in API version 15. */ uint32_t OEMCrypto_SupportsDecryptHash(void); -/* - * OEMCrypto_SetDecryptHash +/** + * Set the hash value for the next frame to be decrypted. This function is + * called before the first subsample is passed to OEMCrypto_DecryptCENC, when + * the subsample_flag has the bit OEMCrypto_FirstSubsample set. The hash is + * over all of the frame or sample: encrypted and clear subsamples + * concatenated together, up to, and including the subsample with the + * subsample_flag having the bit OEMCrypto_LastSubsample set. If hashing the + * output is not supported, then this will return + * OEMCrypto_ERROR_NOT_IMPLEMENTED. If the hash is ill formed or there are + * other error conditions, this returns OEMCrypto_ERROR_UNKNOWN_FAILURE. The + * length of the hash will be at most 128 bytes, and will be 4 bytes (32 + * bits) for the default CRC32 hash. * - * Description: - * Set the hash value for the next frame to be decrypted. This function is - * called before the first subsample is passed to OEMCrypto_DecryptCENC, when - * the subsample_flag has the bit OEMCrypto_FirstSubsample set. The hash is - * over all of the frame or sample: encrypted and clear subsamples - * concatenated together, up to, and including the subsample with the - * subsample_flag having the bit OEMCrypto_LastSubsample set. If hashing the - * output is not supported, then this will return - * OEMCrypto_ERROR_NOT_IMPLEMENTED. If the hash is ill formed or there are - * other error conditions, this returns OEMCrypto_ERROR_UNKNOWN_FAILURE. The - * length of the hash will be at most 128 bytes, and will be 4 bytes (32 - * bits) for the default CRC32 hash. + * This may be called before the first call to SelectKey. In that case, this + * function cannot verify that the key control block allows hash + * verification. The function DecryptCENC should verify that the key control + * bit allows hash verification when it is called. If an attempt is made to + * compute a hash when the selected key does not have the bit + * Allow_Hash_Verification set, then a hash should not be computed, and + * OEMCrypto_GetHashErrorCode should return the error + * OEMCrypto_ERROR_UNKNOWN_FAILURE. * - * This may be called before the first call to SelectKey. In that case, this - * function cannot verify that the key control block allows hash - * verification. The function DecryptCENC should verify that the key control - * bit allows hash verification when it is called. If an attempt is made to - * compute a hash when the selected key does not have the bit - * Allow_Hash_Verification set, then a hash should not be computed, and - * OEMCrypto_GetHashErrorCode should return the error - * OEMCrypto_ERROR_UNKNOWN_FAILURE. + * OEMCrypto should compute the hash of the frame and then compare it with + * the correct value. If the values differ, then OEMCrypto should latch in an + * error and save the frame number of the bad hash. It is allowed for + * OEMCrypto to postpone computation of the hash until the frame is + * displayed. This might happen if the actual decryption operation is carried + * out by a later step in the video pipeline, or if you are using a partner + * specified hash of the decoded frame. For this reason, an error state must + * be saved until the call to OEMCrypto_GetHashErrorCode is made. * - * OEMCrypto should compute the hash of the frame and then compare it with - * the correct value. If the values differ, then OEMCrypto should latch in an - * error and save the frame number of the bad hash. It is allowed for - * OEMCrypto to postpone computation of the hash until the frame is - * displayed. This might happen if the actual decryption operation is carried - * out by a later step in the video pipeline, or if you are using a partner - * specified hash of the decoded frame. For this reason, an error state must - * be saved until the call to OEMCrypto_GetHashErrorCode is made. + * @param[in] session: session id for current decrypt operation + * @param[in] frame_number: frame number for the recent DecryptCENC sample. + * @param[in] hash: hash or CRC of previously decrypted frame. + * @param[in] hash_length: length of hash, in bytes. * - * Parameters: - * [in] session: session id for current decrypt operation - * [in] frame_number: frame number for the recent DecryptCENC sample. - * [in] hash: hash or CRC of previously decrypted frame. - * [in] hash_length: length of hash, in bytes. + * @retval OEMCrypto_SUCCESS if the hash was set + * @retval OEMCrypto_ERROR_NOT_IMPLEMENTED function not implemented + * @retval OEMCrypto_ERROR_INVALID_SESSION session not open + * @retval OEMCrypto_ERROR_SHORT_BUFFER hash_length too short for supported + * hash type + * @retval OEMCrypto_ERROR_BUFFER_TOO_LARGE hash_length too long for supported + * hash type + * @retval OEMCrypto_ERROR_UNKNOWN_FAILURE other error + * @retval OEMCrypto_ERROR_SESSION_LOST_STATE + * @retval OEMCrypto_ERROR_SYSTEM_INVALIDATED * - * Returns: - * OEMCrypto_SUCCESS - if the hash was set - * OEMCrypto_ERROR_NOT_IMPLEMENTED - function not implemented - * OEMCrypto_ERROR_INVALID_SESSION - session not open - * OEMCrypto_ERROR_SHORT_BUFFER - hash_length too short for supported hash - * type - * OEMCrypto_ERROR_BUFFER_TOO_LARGE - hash_length too long for supported hash - * type - * OEMCrypto_ERROR_UNKNOWN_FAILURE - other error - * OEMCrypto_ERROR_SESSION_LOST_STATE - * OEMCrypto_ERROR_SYSTEM_INVALIDATED - * - * Threading: + * @threading * This is a "Session Function" and may be called simultaneously with session * functions for other sessions but not simultaneously with other functions * for this session. It will not be called simultaneously with initialization * or usage table functions. It is as if the CDM holds a write lock for this * session, and a read lock on the OEMCrypto system. * - * Version: + * @version * This method is new in API version 15. */ OEMCryptoResult OEMCrypto_SetDecryptHash(OEMCrypto_SESSION session, @@ -4798,128 +4608,115 @@ OEMCryptoResult OEMCrypto_SetDecryptHash(OEMCrypto_SESSION session, const uint8_t* hash, size_t hash_length); -/* - * OEMCrypto_GetHashErrorCode +/** + * If the hash set in OEMCrypto_SetDecryptHash did not match the computed + * hash, then an error code was saved internally. This function returns that + * error and the frame number of the bad hash. This will be called + * periodically, but might not be in sync with the decrypt loop. OEMCrypto + * shall not reset the error state to "no error" once any frame has failed + * verification. It should be initialized to "no error" when the session is + * first opened. If there is more than one bad frame, it is the implementer's + * choice if it is more useful to return the number of the first bad frame, + * or the most recent bad frame. * - * Description: - * If the hash set in OEMCrypto_SetDecryptHash did not match the computed - * hash, then an error code was saved internally. This function returns that - * error and the frame number of the bad hash. This will be called - * periodically, but might not be in sync with the decrypt loop. OEMCrypto - * shall not reset the error state to "no error" once any frame has failed - * verification. It should be initialized to "no error" when the session is - * first opened. If there is more than one bad frame, it is the implementer's - * choice if it is more useful to return the number of the first bad frame, - * or the most recent bad frame. + * If the hash could not be computed -- either because the + * Allow_Hash_Verification was not set in the key control block, or because + * there were other issues -- this function should return + * OEMCrypto_ERROR_UNKNOWN_FAILURE. * - * If the hash could not be computed -- either because the - * Allow_Hash_Verification was not set in the key control block, or because - * there were other issues -- this function should return - * OEMCrypto_ERROR_UNKNOWN_FAILURE. + * @param[in] session: session id for operation. + * @param[out] failed_frame_number: frame number for sample with incorrect hash. * - * Parameters: - * [in] session: session id for operation. - * [out] failed_frame_number: frame number for sample with incorrect hash. + * @retval OEMCrypto_SUCCESS if all frames have had a correct hash + * @retval OEMCrypto_ERROR_NOT_IMPLEMENTED + * @retval OEMCrypto_ERROR_BAD_HASH if any frame had an incorrect hash + * @retval OEMCrypto_ERROR_UNKNOWN_FAILURE if the hash could not be computed + * @retval OEMCrypto_ERROR_SESSION_LOST_STATE + * @retval OEMCrypto_ERROR_SYSTEM_INVALIDATED * - * Returns: - * OEMCrypto_SUCCESS - if all frames have had a correct hash - * OEMCrypto_ERROR_NOT_IMPLEMENTED - * OEMCrypto_ERROR_BAD_HASH - if any frame had an incorrect hash - * OEMCrypto_ERROR_UNKNOWN_FAILURE - if the hash could not be computed - * OEMCrypto_ERROR_SESSION_LOST_STATE - * OEMCrypto_ERROR_SYSTEM_INVALIDATED - * - * Threading: + * @threading * This is a "Session Function" and may be called simultaneously with session * functions for other sessions but not simultaneously with other functions * for this session. It will not be called simultaneously with initialization * or usage table functions. It is as if the CDM holds a write lock for this * session, and a read lock on the OEMCrypto system. * - * Version: + * @version * This method is new in API version 15. */ OEMCryptoResult OEMCrypto_GetHashErrorCode(OEMCrypto_SESSION session, uint32_t* failed_frame_number); -/* - * OEMCrypto_AllocateSecureBuffer +/** + * Allocates a secure buffer and fills out the destination buffer information + * in output_descriptor. The integer secure_fd may also be set to indicate + * the source of the buffer. OEMCrypto may use the secure_fd to help track + * the buffer if it wishes. The unit tests will pass a pointer to the same + * destination buffer description and the same secure_fd to + * OEMCrypto_FreeSecureBuffer when the buffer is to be freed. * - * Description: - * Allocates a secure buffer and fills out the destination buffer information - * in output_descriptor. The integer secure_fd may also be set to indicate - * the source of the buffer. OEMCrypto may use the secure_fd to help track - * the buffer if it wishes. The unit tests will pass a pointer to the same - * destination buffer description and the same secure_fd to - * OEMCrypto_FreeSecureBuffer when the buffer is to be freed. + * This is especially helpful if the hash functions above are supported. This + * will only be used by the OEMCrypto unit tests, so we recommend returning + * OEMCrypto_ERROR_NOT_IMPLEMENTED for production devices if performance is + * an issue. If OEMCrypto_ERROR_NOT_IMPLEMENTED is returned, then secure + * buffer unit tests will be skipped. * - * This is especially helpful if the hash functions above are supported. This - * will only be used by the OEMCrypto unit tests, so we recommend returning - * OEMCrypto_ERROR_NOT_IMPLEMENTED for production devices if performance is - * an issue. If OEMCrypto_ERROR_NOT_IMPLEMENTED is returned, then secure - * buffer unit tests will be skipped. + * @param[in] session: session id for operation. + * @param[in] buffer_size: the requested buffer size. + * @param[out] output_descriptor: the buffer descriptor for the created + * buffer. This will be passed into the OEMCrypto_DecryptCENC function. + * @param[out] secure_fd: a pointer to platform dependent file or buffer + * descriptor. This will be passed to OEMCrypto_FreeSecureBuffer. * - * Parameters: - * [in] session: session id for operation. - * [in] buffer_size: the requested buffer size. - * [out] output_descriptor: the buffer descriptor for the created buffer. - * This will be passed into the OEMCrypto_DecryptCENC function. - * [out] secure_fd: a pointer to platform dependent file or buffer - * descriptor. This will be passed to OEMCrypto_FreeSecureBuffer. + * @retval OEMCrypto_SUCCESS if the buffer was created + * @retval OEMCrypto_ERROR_NOT_IMPLEMENTED + * @retval OEMCrypto_ERROR_OUTPUT_TOO_LARGE + * @retval OEMCrypto_ERROR_UNKNOWN_FAILURE * - * Returns: - * OEMCrypto_SUCCESS - if the buffer was created - * OEMCrypto_ERROR_NOT_IMPLEMENTED - * OEMCrypto_ERROR_OUTPUT_TOO_LARGE - * OEMCrypto_ERROR_UNKNOWN_FAILURE - * - * Threading: + * @threading * This is a "Session Function" and may be called simultaneously with session * functions for other sessions but not simultaneously with other functions * for this session. It will not be called simultaneously with initialization * or usage table functions. It is as if the CDM holds a write lock for this * session, and a read lock on the OEMCrypto system. * - * Version: + * @version * This method is new in API version 16. */ OEMCryptoResult OEMCrypto_AllocateSecureBuffer( OEMCrypto_SESSION session, size_t buffer_size, OEMCrypto_DestBufferDesc* output_descriptor, int* secure_fd); -/* - * OEMCrypto_FreeSecureBuffer +/** + * Frees a secure buffer that had previously been created with + * OEMCrypto_AllocateSecureBuffer. Any return value except OEMCrypto_SUCCESS + * will cause the unit test using secure buffers to fail. * - * Description: - * Frees a secure buffer that had previously been created with - * OEMCrypto_AllocateSecureBuffer. Any return value except OEMCrypto_SUCCESS - * will cause the unit test using secure buffers to fail. + * @param[in] session: session id for operation. + * @param[out] output_descriptor: the buffer descriptor modified by + * OEMCrypto_AllocateSecureBuffer + * @param[in] secure_fd: The integer returned by OEMCrypto_AllocateSecureBuffer * - * Parameters: - * [in] session: session id for operation. - * [out] output_descriptor: the buffer descriptor modified by - * OEMCrypto_AllocateSecureBuffer - * [in] secure_fd: The integer returned by OEMCrypto_AllocateSecureBuffer + * @retval OEMCrypto_SUCCESS if the buffer was freed + * @retval OEMCrypto_ERROR_NOT_IMPLEMENTED + * @retval OEMCrypto_ERROR_UNKNOWN_FAILURE * - * Returns: - * OEMCrypto_SUCCESS - if the buffer was freed - * OEMCrypto_ERROR_NOT_IMPLEMENTED - * OEMCrypto_ERROR_UNKNOWN_FAILURE - * - * Threading: + * @threading * This is a "Session Function" and may be called simultaneously with session * functions for other sessions but not simultaneously with other functions * for this session. It will not be called simultaneously with initialization * or usage table functions. It is as if the CDM holds a write lock for this * session, and a read lock on the OEMCrypto system. * - * Version: + * @version * This method is new in API version 16. */ OEMCryptoResult OEMCrypto_FreeSecureBuffer( OEMCrypto_SESSION session, OEMCrypto_DestBufferDesc* output_descriptor, int secure_fd); +/// @} + /****************************************************************************/ /****************************************************************************/ /* The following functions are deprecated. They are not required for the diff --git a/oemcrypto/include/level3.h b/oemcrypto/include/level3.h index f71584b..846e7ee 100644 --- a/oemcrypto/include/level3.h +++ b/oemcrypto/include/level3.h @@ -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" { diff --git a/oemcrypto/odk/include/OEMCryptoCENCCommon.h b/oemcrypto/odk/include/OEMCryptoCENCCommon.h index e8f2ad9..5e54cc6 100644 --- a/oemcrypto/odk/include/OEMCryptoCENCCommon.h +++ b/oemcrypto/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. /********************************************************************* * 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_ diff --git a/oemcrypto/odk/include/core_message_deserialize.h b/oemcrypto/odk/include/core_message_deserialize.h index c2967dd..93cf800 100644 --- a/oemcrypto/odk/include/core_message_deserialize.h +++ b/oemcrypto/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_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_ diff --git a/oemcrypto/odk/include/core_message_serialize.h b/oemcrypto/odk/include/core_message_serialize.h index ffa43f5..619a085 100644 --- a/oemcrypto/odk/include/core_message_serialize.h +++ b/oemcrypto/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.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_ diff --git a/oemcrypto/odk/include/core_message_serialize_proto.h b/oemcrypto/odk/include/core_message_serialize_proto.h index 0f494de..b9dd34e 100644 --- a/oemcrypto/odk/include/core_message_serialize_proto.h +++ b/oemcrypto/odk/include/core_message_serialize_proto.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_ diff --git a/oemcrypto/odk/include/core_message_types.h b/oemcrypto/odk/include/core_message_types.h index 488e5d2..0f45f00 100644 --- a/oemcrypto/odk/include/core_message_types.h +++ b/oemcrypto/odk/include/core_message_types.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_ diff --git a/oemcrypto/odk/include/odk.h b/oemcrypto/odk/include/odk.h index 6bbf36f..588f185 100644 --- a/oemcrypto/odk/include/odk.h +++ b/oemcrypto/odk/include/odk.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 + * Widevine Core Message Serialization + * and + * License Duration and Renewal + * 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_ diff --git a/oemcrypto/odk/include/odk_attributes.h b/oemcrypto/odk/include/odk_attributes.h new file mode 100644 index 0000000..623a057 --- /dev/null +++ b/oemcrypto/odk/include/odk_attributes.h @@ -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_ diff --git a/oemcrypto/odk/include/odk_structs.h b/oemcrypto/odk/include/odk_structs.h index 5c9ff30..13bf83b 100644 --- a/oemcrypto/odk/include/odk_structs.h +++ b/oemcrypto/odk/include/odk_structs.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_ @@ -12,54 +12,54 @@ /* The version of this library. */ #define ODK_MAJOR_VERSION 16 -#define ODK_MINOR_VERSION 3 +#define ODK_MINOR_VERSION 4 /* 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.4 2020-10-07" -/* 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_ diff --git a/oemcrypto/odk/include/odk_target.h b/oemcrypto/odk/include/odk_target.h index 9225210..cb2774f 100644 --- a/oemcrypto/odk/include/odk_target.h +++ b/oemcrypto/odk/include/odk_target.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_ diff --git a/oemcrypto/odk/src/core_message_deserialize.cpp b/oemcrypto/odk/src/core_message_deserialize.cpp index 1cef5ee..47d4478 100644 --- a/oemcrypto/odk/src/core_message_deserialize.cpp +++ b/oemcrypto/odk/src/core_message_deserialize.cpp @@ -39,8 +39,8 @@ bool ParseRequest(uint32_t message_type, reinterpret_cast(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(blk); InitMessage(msg, const_cast(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 diff --git a/oemcrypto/odk/src/core_message_serialize.cpp b/oemcrypto/odk/src/core_message_serialize.cpp index 4546460..0e05330 100644 --- a/oemcrypto/odk/src/core_message_serialize.cpp +++ b/oemcrypto/odk/src/core_message_serialize.cpp @@ -50,8 +50,8 @@ bool CreateResponse(uint32_t message_type, const S& core_request, static constexpr size_t BUF_CAPACITY = 2048; std::vector buf(BUF_CAPACITY, 0); - Message* msg = nullptr; - AllocateMessage(&msg, message_block); + uint8_t blk[SIZE_OF_MESSAGE_STRUCT]; + Message* msg = reinterpret_cast(blk); InitMessage(msg, buf.data(), buf.capacity()); packer(msg, &response); if (!ValidMessage(msg)) { diff --git a/oemcrypto/odk/src/core_message_serialize_proto.cpp b/oemcrypto/odk/src/core_message_serialize_proto.cpp index cacba42..4b8da06 100644 --- a/oemcrypto/odk/src/core_message_serialize_proto.cpp +++ b/oemcrypto/odk/src/core_message_serialize_proto.cpp @@ -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; } diff --git a/oemcrypto/odk/src/odk.c b/oemcrypto/odk/src/odk.c index 42e82d5..a009e7b 100644 --- a/oemcrypto/odk/src/odk.c +++ b/oemcrypto/odk/src/odk.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. #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)); @@ -299,20 +307,24 @@ OEMCryptoResult ODK_ParseLicense( return ODK_ERROR_CORE_MESSAGE; } - if (parsed_license->nonce_required) { - if (initial_license_load) { - if (nonce_values->nonce != - license_response.request.core_message.nonce_values.nonce || - nonce_values->session_id != - license_response.request.core_message.nonce_values.session_id) { - return OEMCrypto_ERROR_INVALID_NONCE; - } - } else { /* !initial_license_load */ - nonce_values->nonce = - license_response.request.core_message.nonce_values.nonce; - nonce_values->session_id = - license_response.request.core_message.nonce_values.session_id; + /* If this is the first time we load this license, then we verify that the + * nonce values are the correct, otherwise we copy the nonce values. If the + * nonce values are not required to be correct, then we don't know if this is + * an initial load or not. In that case, we also copy the values so that we + * can use the nonce values later for a renewal. + */ + if (parsed_license->nonce_required && initial_license_load) { + if (nonce_values->nonce != + license_response.request.core_message.nonce_values.nonce || + nonce_values->session_id != + license_response.request.core_message.nonce_values.session_id) { + return OEMCrypto_ERROR_INVALID_NONCE; } + } else { /* !initial_license_load, or can't tell if initial. */ + nonce_values->nonce = + license_response.request.core_message.nonce_values.nonce; + nonce_values->session_id = + license_response.request.core_message.nonce_values.session_id; } /* For v16, in order to be backwards compatible with a v15 license server, * OEMCrypto stores a hash of the core license request and only signs the @@ -342,7 +354,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( @@ -359,9 +371,12 @@ OEMCryptoResult ODK_ParseRenewal(const uint8_t* message, size_t message_length, */ /* If a renewal request is lost in transit, we should throw it out and create * a new one. We use the timestamp to make sure we have the latest request. + * We only do this if playback has already started. This allows us to reload + * an offline license and also reload a renewal before starting playback. */ - if (clock_values->time_of_renewal_request < - renewal_response.request.playback_time) { + if (clock_values->timer_status != ODK_CLOCK_TIMER_STATUS_LICENSE_LOADED && + clock_values->time_of_renewal_request < + renewal_response.request.playback_time) { return ODK_STALE_RENEWAL; } return ODK_ComputeRenewalDuration(timer_limits, clock_values, system_time, @@ -378,8 +393,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; } diff --git a/oemcrypto/odk/src/odk_assert.h b/oemcrypto/odk/src/odk_assert.h index 6fda98b..149021f 100644 --- a/oemcrypto/odk/src/odk_assert.h +++ b/oemcrypto/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_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_ diff --git a/oemcrypto/odk/src/odk_endian.h b/oemcrypto/odk/src/odk_endian.h index 2a6f143..00dc01e 100644 --- a/oemcrypto/odk/src/odk_endian.h +++ b/oemcrypto/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. #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_ diff --git a/oemcrypto/odk/src/odk_overflow.c b/oemcrypto/odk/src/odk_overflow.c index 76c685f..a03f0f6 100644 --- a/oemcrypto/odk/src/odk_overflow.c +++ b/oemcrypto/odk/src/odk_overflow.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. #include #include diff --git a/oemcrypto/odk/src/odk_overflow.h b/oemcrypto/odk/src/odk_overflow.h index b1e03ee..6f3f994 100644 --- a/oemcrypto/odk/src/odk_overflow.h +++ b/oemcrypto/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. #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_ diff --git a/oemcrypto/odk/src/odk_serialize.c b/oemcrypto/odk/src/odk_serialize.c index efd9475..ae53e73 100644 --- a/oemcrypto/odk/src/odk_serialize.c +++ b/oemcrypto/odk/src/odk_serialize.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. /* * 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) { diff --git a/oemcrypto/odk/src/odk_serialize.h b/oemcrypto/odk/src/odk_serialize.h index f35f178..73f4abd 100644 --- a/oemcrypto/odk/src/odk_serialize.h +++ b/oemcrypto/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. /* * 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_ diff --git a/oemcrypto/odk/src/odk_structs_priv.h b/oemcrypto/odk/src/odk_structs_priv.h index 3c5a502..7b1f6de 100644 --- a/oemcrypto/odk/src/odk_structs_priv.h +++ b/oemcrypto/odk/src/odk_structs_priv.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_ diff --git a/oemcrypto/odk/src/odk_timer.c b/oemcrypto/odk/src/odk_timer.c index 0fbcf18..67c3470 100644 --- a/oemcrypto/odk/src/odk_timer.c +++ b/oemcrypto/odk/src/odk_timer.c @@ -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 #include #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; } diff --git a/oemcrypto/odk/src/odk_util.c b/oemcrypto/odk/src/odk_util.c index ff85a9c..682bf8e 100644 --- a/oemcrypto/odk/src/odk_util.c +++ b/oemcrypto/odk/src/odk_util.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. #include "odk_util.h" diff --git a/oemcrypto/odk/src/odk_util.h b/oemcrypto/odk/src/odk_util.h index 7de668e..138791f 100644 --- a/oemcrypto/odk/src/odk_util.h +++ b/oemcrypto/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. #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_ diff --git a/oemcrypto/odk/src/serialization_base.c b/oemcrypto/odk/src/serialization_base.c index 4681b54..1b32d05 100644 --- a/oemcrypto/odk/src/serialization_base.c +++ b/oemcrypto/odk/src/serialization_base.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. #include "serialization_base.h" @@ -9,7 +9,6 @@ #include #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 */ diff --git a/oemcrypto/odk/src/serialization_base.h b/oemcrypto/odk/src/serialization_base.h index c99f4d1..9e7776b 100644 --- a/oemcrypto/odk/src/serialization_base.h +++ b/oemcrypto/odk/src/serialization_base.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_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_ diff --git a/oemcrypto/odk/test/fuzzing/Android.bp b/oemcrypto/odk/test/fuzzing/Android.bp new file mode 100644 index 0000000..f0093f4 --- /dev/null +++ b/oemcrypto/odk/test/fuzzing/Android.bp @@ -0,0 +1,168 @@ +// 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. + +cc_defaults { + name: "odk_fuzz_library_defaults", + srcs: [ + "odk_fuzz_helper.cpp", + ], + include_dirs: [ + "vendor/widevine/libwvdrmengine/oemcrypto/odk/test", + "vendor/widevine/libwvdrmengine/oemcrypto/odk/include", + "vendor/widevine/libwvdrmengine/oemcrypto/odk/src", + ], +} + +cc_fuzz { + name: "odk_license_request_fuzz", + srcs: [ + "odk_license_request_fuzz.cpp", + ], + fuzz_config: { + componentid: 611718, + }, + corpus: ["corpus/little_endian_64bit/license_request_corpus/*"], + static_libs: [ + "libwv_kdo", + "libwv_odk", + ], + defaults: ["odk_fuzz_library_defaults"], + proprietary: true, +} + +cc_fuzz { + name: "odk_renewal_request_fuzz", + srcs: [ + "odk_renewal_request_fuzz.cpp", + ], + fuzz_config: { + componentid: 611718, + }, + corpus: ["corpus/little_endian_64bit/renewal_request_corpus/*"], + static_libs: [ + "libwv_kdo", + "libwv_odk", + ], + defaults: ["odk_fuzz_library_defaults"], + proprietary: true, +} + +cc_fuzz { + name: "odk_provisioning_request_fuzz", + srcs: [ + "odk_provisioning_request_fuzz.cpp", + ], + fuzz_config: { + componentid: 611718, + }, + corpus: ["corpus/little_endian_64bit/provisioning_request_corpus/*"], + static_libs: [ + "libwv_kdo", + "libwv_odk", + ], + defaults: ["odk_fuzz_library_defaults"], + proprietary: true, +} + +cc_fuzz { + name: "odk_license_response_fuzz", + srcs: [ + "odk_license_response_fuzz.cpp", + ], + fuzz_config: { + componentid: 611718, + }, + corpus: ["corpus/little_endian_64bit/license_response_corpus/*"], + static_libs: [ + "libwv_kdo", + "libwv_odk", + ], + defaults: ["odk_fuzz_library_defaults"], + proprietary: true, +} + +cc_fuzz { + name: "odk_renewal_response_fuzz", + srcs: [ + "odk_renewal_response_fuzz.cpp", + ], + fuzz_config: { + componentid: 611718, + }, + corpus: ["corpus/little_endian_64bit/renewal_response_corpus/*"], + static_libs: [ + "libwv_kdo", + "libwv_odk", + ], + defaults: ["odk_fuzz_library_defaults"], + proprietary: true, +} + +cc_fuzz { + name: "odk_provisioning_response_fuzz", + srcs: [ + "odk_provisioning_response_fuzz.cpp", + ], + fuzz_config: { + componentid: 611718, + }, + corpus: ["corpus/little_endian_64bit/provisioning_response_corpus/*"], + static_libs: [ + "libwv_kdo", + "libwv_odk", + ], + defaults: ["odk_fuzz_library_defaults"], + proprietary: true, +} + +cc_fuzz { + name: "odk_license_response_fuzz_with_mutator", + srcs: [ + "odk_license_response_fuzz_with_mutator.cpp", + ], + fuzz_config: { + componentid: 611718, + }, + corpus: ["corpus/little_endian_64bit/license_response_corpus/*"], + static_libs: [ + "libwv_kdo", + "libwv_odk", + ], + defaults: ["odk_fuzz_library_defaults"], + proprietary: true, +} + +cc_fuzz { + name: "odk_renewal_response_fuzz_with_mutator", + srcs: [ + "odk_renewal_response_fuzz_with_mutator.cpp", + ], + fuzz_config: { + componentid: 611718, + }, + corpus: ["corpus/little_endian_64bit/renewal_response_corpus/*"], + static_libs: [ + "libwv_kdo", + "libwv_odk", + ], + defaults: ["odk_fuzz_library_defaults"], + proprietary: true, +} + +cc_fuzz { + name: "odk_provisioning_response_fuzz_with_mutator", + srcs: [ + "odk_provisioning_response_fuzz_with_mutator.cpp", + ], + fuzz_config: { + componentid: 611718, + }, + corpus: ["corpus/little_endian_64bit/provisioning_response_corpus/*"], + static_libs: [ + "libwv_kdo", + "libwv_odk", + ], + defaults: ["odk_fuzz_library_defaults"], + proprietary: true, +} \ No newline at end of file diff --git a/oemcrypto/odk/test/fuzzing/README.md b/oemcrypto/odk/test/fuzzing/README.md new file mode 100644 index 0000000..eb7da4f --- /dev/null +++ b/oemcrypto/odk/test/fuzzing/README.md @@ -0,0 +1,19 @@ +# ODK Fuzzing + +## Objective + +* Run fuzzing on ODK and KDO serialize and deserialize APIs using google + supported fuzzer engines to find security vulnerabilities. Any issues found + by clusterfuzz will be reported to + [odk fuzz buganizer](https://b.corp.google.com/issues?q=componentid:425099%20status:open%20reporter:cluster-fuzz-googleplex@google.com). + +## Run fuzz target on local machine + +* In order to run fuzz target locally and see code coverage, save binary input + to be tested against fuzz target into a temporary corpus directory and + execute following commands + + ```shell + $ blaze build --config=asan-fuzzer //your:target + $ blaze-bin/your/target FULL_CORPUS_DIR + ``` diff --git a/oemcrypto/odk/test/fuzzing/corpus/little_endian_64bit/license_request_corpus/602c63d2f3d13ca3206cdf204cde24e7d8f4266c b/oemcrypto/odk/test/fuzzing/corpus/little_endian_64bit/license_request_corpus/602c63d2f3d13ca3206cdf204cde24e7d8f4266c new file mode 100644 index 0000000..dc8cabc Binary files /dev/null and b/oemcrypto/odk/test/fuzzing/corpus/little_endian_64bit/license_request_corpus/602c63d2f3d13ca3206cdf204cde24e7d8f4266c differ diff --git a/oemcrypto/odk/test/fuzzing/corpus/little_endian_64bit/license_request_corpus/8cebdcc0161125a10e19c45f055051712873de25 b/oemcrypto/odk/test/fuzzing/corpus/little_endian_64bit/license_request_corpus/8cebdcc0161125a10e19c45f055051712873de25 new file mode 100644 index 0000000..608e888 Binary files /dev/null and b/oemcrypto/odk/test/fuzzing/corpus/little_endian_64bit/license_request_corpus/8cebdcc0161125a10e19c45f055051712873de25 differ diff --git a/oemcrypto/odk/test/fuzzing/corpus/little_endian_64bit/license_response_corpus/4e578d6c9628e832c099623b44f56d95aa37f94b b/oemcrypto/odk/test/fuzzing/corpus/little_endian_64bit/license_response_corpus/4e578d6c9628e832c099623b44f56d95aa37f94b new file mode 100644 index 0000000..e319747 Binary files /dev/null and b/oemcrypto/odk/test/fuzzing/corpus/little_endian_64bit/license_response_corpus/4e578d6c9628e832c099623b44f56d95aa37f94b differ diff --git a/oemcrypto/odk/test/fuzzing/corpus/little_endian_64bit/license_response_corpus/5b693511ef850e42c5ffded171794dbeb9460cc0 b/oemcrypto/odk/test/fuzzing/corpus/little_endian_64bit/license_response_corpus/5b693511ef850e42c5ffded171794dbeb9460cc0 new file mode 100644 index 0000000..0cf77b5 Binary files /dev/null and b/oemcrypto/odk/test/fuzzing/corpus/little_endian_64bit/license_response_corpus/5b693511ef850e42c5ffded171794dbeb9460cc0 differ diff --git a/oemcrypto/odk/test/fuzzing/corpus/little_endian_64bit/license_response_corpus/b6b865b095697164ad032c2f695ed828f5754749 b/oemcrypto/odk/test/fuzzing/corpus/little_endian_64bit/license_response_corpus/b6b865b095697164ad032c2f695ed828f5754749 new file mode 100644 index 0000000..225f87a --- /dev/null +++ b/oemcrypto/odk/test/fuzzing/corpus/little_endian_64bit/license_response_corpus/b6b865b095697164ad032c2f695ed828f5754749 @@ -0,0 +1 @@ +{"componentid":425099} diff --git a/oemcrypto/odk/test/fuzzing/corpus/little_endian_64bit/license_response_corpus/dba39b6cf6524e996397ddc1e08b928b5c92bb5d b/oemcrypto/odk/test/fuzzing/corpus/little_endian_64bit/license_response_corpus/dba39b6cf6524e996397ddc1e08b928b5c92bb5d new file mode 100644 index 0000000..51f67eb Binary files /dev/null and b/oemcrypto/odk/test/fuzzing/corpus/little_endian_64bit/license_response_corpus/dba39b6cf6524e996397ddc1e08b928b5c92bb5d differ diff --git a/oemcrypto/odk/test/fuzzing/corpus/little_endian_64bit/license_response_corpus/dd1bc1827a331b7aed2a6fb6740da032123aa0a8 b/oemcrypto/odk/test/fuzzing/corpus/little_endian_64bit/license_response_corpus/dd1bc1827a331b7aed2a6fb6740da032123aa0a8 new file mode 100644 index 0000000..a3f3b26 Binary files /dev/null and b/oemcrypto/odk/test/fuzzing/corpus/little_endian_64bit/license_response_corpus/dd1bc1827a331b7aed2a6fb6740da032123aa0a8 differ diff --git a/oemcrypto/odk/test/fuzzing/corpus/little_endian_64bit/provisioning_request_corpus/53c26407b39c997143146a0dce8ff0ac11f565e1 b/oemcrypto/odk/test/fuzzing/corpus/little_endian_64bit/provisioning_request_corpus/53c26407b39c997143146a0dce8ff0ac11f565e1 new file mode 100644 index 0000000..a2d65a8 Binary files /dev/null and b/oemcrypto/odk/test/fuzzing/corpus/little_endian_64bit/provisioning_request_corpus/53c26407b39c997143146a0dce8ff0ac11f565e1 differ diff --git a/oemcrypto/odk/test/fuzzing/corpus/little_endian_64bit/provisioning_request_corpus/fab3c99d604bab7b7bf5c54c5bd995fc98d4d96f b/oemcrypto/odk/test/fuzzing/corpus/little_endian_64bit/provisioning_request_corpus/fab3c99d604bab7b7bf5c54c5bd995fc98d4d96f new file mode 100644 index 0000000..a4ded57 Binary files /dev/null and b/oemcrypto/odk/test/fuzzing/corpus/little_endian_64bit/provisioning_request_corpus/fab3c99d604bab7b7bf5c54c5bd995fc98d4d96f differ diff --git a/oemcrypto/odk/test/fuzzing/corpus/little_endian_64bit/provisioning_response_corpus/91e10d030fbdd3374e57a2720f09488f2b03ce69 b/oemcrypto/odk/test/fuzzing/corpus/little_endian_64bit/provisioning_response_corpus/91e10d030fbdd3374e57a2720f09488f2b03ce69 new file mode 100644 index 0000000..0616a29 Binary files /dev/null and b/oemcrypto/odk/test/fuzzing/corpus/little_endian_64bit/provisioning_response_corpus/91e10d030fbdd3374e57a2720f09488f2b03ce69 differ diff --git a/oemcrypto/odk/test/fuzzing/corpus/little_endian_64bit/renewal_request_corpus/12a72efb395e731ec4470b5f5b6768d6806e9131 b/oemcrypto/odk/test/fuzzing/corpus/little_endian_64bit/renewal_request_corpus/12a72efb395e731ec4470b5f5b6768d6806e9131 new file mode 100644 index 0000000..7371c1f Binary files /dev/null and b/oemcrypto/odk/test/fuzzing/corpus/little_endian_64bit/renewal_request_corpus/12a72efb395e731ec4470b5f5b6768d6806e9131 differ diff --git a/oemcrypto/odk/test/fuzzing/corpus/little_endian_64bit/renewal_request_corpus/21de033b9baf2a0e82ae3b4185b22aa0acf69bbc b/oemcrypto/odk/test/fuzzing/corpus/little_endian_64bit/renewal_request_corpus/21de033b9baf2a0e82ae3b4185b22aa0acf69bbc new file mode 100644 index 0000000..ef24b18 Binary files /dev/null and b/oemcrypto/odk/test/fuzzing/corpus/little_endian_64bit/renewal_request_corpus/21de033b9baf2a0e82ae3b4185b22aa0acf69bbc differ diff --git a/oemcrypto/odk/test/fuzzing/corpus/little_endian_64bit/renewal_request_corpus/97bf96be666434bfa93dbfb36b81baeefed14170 b/oemcrypto/odk/test/fuzzing/corpus/little_endian_64bit/renewal_request_corpus/97bf96be666434bfa93dbfb36b81baeefed14170 new file mode 100644 index 0000000..4e6f216 Binary files /dev/null and b/oemcrypto/odk/test/fuzzing/corpus/little_endian_64bit/renewal_request_corpus/97bf96be666434bfa93dbfb36b81baeefed14170 differ diff --git a/oemcrypto/odk/test/fuzzing/corpus/little_endian_64bit/renewal_request_corpus/a7b0e7dca597331d7f051204096c9d01ba6d468e b/oemcrypto/odk/test/fuzzing/corpus/little_endian_64bit/renewal_request_corpus/a7b0e7dca597331d7f051204096c9d01ba6d468e new file mode 100644 index 0000000..748c29c Binary files /dev/null and b/oemcrypto/odk/test/fuzzing/corpus/little_endian_64bit/renewal_request_corpus/a7b0e7dca597331d7f051204096c9d01ba6d468e differ diff --git a/oemcrypto/odk/test/fuzzing/corpus/little_endian_64bit/renewal_response_corpus/38df40a320f60e955006aaa294b74d45a316e50f b/oemcrypto/odk/test/fuzzing/corpus/little_endian_64bit/renewal_response_corpus/38df40a320f60e955006aaa294b74d45a316e50f new file mode 100644 index 0000000..4abb16a Binary files /dev/null and b/oemcrypto/odk/test/fuzzing/corpus/little_endian_64bit/renewal_response_corpus/38df40a320f60e955006aaa294b74d45a316e50f differ diff --git a/oemcrypto/odk/test/fuzzing/corpus/little_endian_64bit/renewal_response_corpus/9962997b5ea87005276319cbfff67884846485cf b/oemcrypto/odk/test/fuzzing/corpus/little_endian_64bit/renewal_response_corpus/9962997b5ea87005276319cbfff67884846485cf new file mode 100644 index 0000000..892a4ed Binary files /dev/null and b/oemcrypto/odk/test/fuzzing/corpus/little_endian_64bit/renewal_response_corpus/9962997b5ea87005276319cbfff67884846485cf differ diff --git a/oemcrypto/odk/test/fuzzing/corpus/little_endian_64bit/renewal_response_corpus/c84663115c890873dd585987c1223193d29aef16 b/oemcrypto/odk/test/fuzzing/corpus/little_endian_64bit/renewal_response_corpus/c84663115c890873dd585987c1223193d29aef16 new file mode 100644 index 0000000..b1fc6da Binary files /dev/null and b/oemcrypto/odk/test/fuzzing/corpus/little_endian_64bit/renewal_response_corpus/c84663115c890873dd585987c1223193d29aef16 differ diff --git a/oemcrypto/odk/test/fuzzing/corpus_generator/Android.bp b/oemcrypto/odk/test/fuzzing/corpus_generator/Android.bp new file mode 100644 index 0000000..e993971 --- /dev/null +++ b/oemcrypto/odk/test/fuzzing/corpus_generator/Android.bp @@ -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. + +// ---------------------------------------------------------------- +// Builds odk_corpus_generator shared library, which can be used with +// LD_PRELOAD command to generate corpus by intercepting oemcrypto +// unit tests. +// ---------------------------------------------------------------- +// Builds libwv_odk.so, The ODK shared Library (libwv_odk) is used +// by the OEMCrypto unit tests to generate corpus for ODK fuzz scrips. +cc_library_shared { + name: "libwv_odk_corpus_generator", + include_dirs: [ + "vendor/widevine/libwvdrmengine/oemcrypto/include", + "vendor/widevine/libwvdrmengine/oemcrypto/odk/include", + "vendor/widevine/libwvdrmengine/oemcrypto/odk/test", + ], + host_ldlibs: ["-ldl"], + srcs: [ + "odk_corpus_generator.c", + "odk_corpus_generator_helper.c", + ], + proprietary: true, + + owner: "widevine", +} diff --git a/oemcrypto/odk/test/fuzzing/corpus_generator/README.md b/oemcrypto/odk/test/fuzzing/corpus_generator/README.md new file mode 100644 index 0000000..f6df374 --- /dev/null +++ b/oemcrypto/odk/test/fuzzing/corpus_generator/README.md @@ -0,0 +1,79 @@ +# Objective + +The Idea behind the corpus generator code is to intercept OEMCrypto unit test +calls to odk APIs using LD_PRELOAD and read the data into corpus files which can +be fed as corpus to fuzzer scripts. + +LD_PRELOAD command needs to be run from cdm repository while running oemcrypto +unit tests. + +## Get OEMCrypto and Build OEMCrypto unit tests: + +* 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 + ``` + +* We need to run odk as a dynamic library in order to use LD_PRELOAD, apply + patch from go/wvgerrit/95090 to locally cloned repo which has changes to run + odk as dynamic library: + + ```shell + $ cd /path/to/cdm/repo + $ git fetch origin 209721cc901745999e08e35466e74f708321267e + $ git cherry-pick FETCH_HEAD + ``` + +* Build OEMCrypto unit tests: + + ```shell + $ cd /path/to/cdm/repo + $ export PATH_TO_CDM_DIR=.. + $ gyp --format=ninja --depth=$(pwd) oemcrypto/oemcrypto_unittests.gyp + $ ninja -C out/Default/ + ``` + +## Capture corpus for odk fuzzer by intercepting OEMCrypto unit tests: + +When we run LD_PRELOAD command odk_corpus_generator.so gets preloaded before +oemcrypto_unittests and odk_corpus_generator has functions to intercept calls to +ODK request and response APIs. Each call to odk API from oemcrypto_unittests +gets intercepted and input to ODK de serialize response APIs and output from ODK +serialize request APIs is captured in binary format and stored into corpus files + +In order to run LD_PRELOAD command, we need to compile corpus generator shared +library and need to preload that before OEMCrypto unit tests run + +* Compile shared library + + ```shell + $ cd /path/to/cdm/repo + $ gyp --format=ninja --depth=$(pwd) oemcrypto/odk/test/fuzzing/corpus_generator/odk_fuzz_corpus_generator.gyp + $ ninja -C out/Default/ + ``` + +* 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 + ``` + +LD_PRELOAD command runs oemcrypto_unittests with odk_corpus_generator as +interceptor. We should see unit tests being executed. The corpus files in binary +format will be captured into `oemcrypto/odk/test/fuzzing/corpus` path. These +files can be used as input corpus for ODK request and response fuzzer scripts. + +The generated corpus files can be minimized using go/testcorpus#minimize and +uploaded into google3 repository under following directory under respective +corpus types +`fuzzing/corpus` diff --git a/oemcrypto/odk/test/fuzzing/corpus_generator/odk_corpus_generator.c b/oemcrypto/odk/test/fuzzing/corpus_generator/odk_corpus_generator.c new file mode 100644 index 0000000..9a3e985 --- /dev/null +++ b/oemcrypto/odk/test/fuzzing/corpus_generator/odk_corpus_generator.c @@ -0,0 +1,158 @@ +// 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 +#define _GNU_SOURCE + +#include + +#include "fuzzing/corpus_generator/odk_corpus_generator_helper.h" +#include "fuzzing/odk_fuzz_structs.h" +#include "odk_structs.h" + +OEMCryptoResult ODK_PrepareCoreLicenseRequest( + uint8_t* message, size_t message_length, size_t* core_message_length, + const ODK_NonceValues* nonce_values) { + OEMCryptoResult (*original_function)(uint8_t*, size_t, size_t*, + const ODK_NonceValues*); + original_function = dlsym(RTLD_NEXT, "ODK_PrepareCoreLicenseRequest"); + OEMCryptoResult oem_crypto_result = (*original_function)( + message, message_length, core_message_length, nonce_values); + char* file_name = GetFileName("license_request_corpus"); + + // 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; +} + +OEMCryptoResult ODK_ParseLicense( + const uint8_t* message, size_t message_length, size_t core_message_length, + bool initial_license_load, bool usage_entry_present, + const uint8_t* request_hash, ODK_TimerLimits* timer_limits, + ODK_ClockValues* clock_values, ODK_NonceValues* nonce_values, + ODK_ParsedLicense* parsed_license) { + struct ODK_ParseLicense_Args parse_license_args; + parse_license_args.nonce_values = *nonce_values; + memcpy(parse_license_args.request_hash, request_hash, ODK_SHA256_HASH_SIZE); + parse_license_args.timer_limits = *timer_limits; + parse_license_args.clock_values = *clock_values; + parse_license_args.usage_entry_present = usage_entry_present; + parse_license_args.initial_license_load = initial_license_load; + OEMCryptoResult (*original_function)( + const uint8_t*, size_t, size_t, bool, bool, const uint8_t*, + ODK_TimerLimits*, ODK_ClockValues*, ODK_NonceValues*, ODK_ParsedLicense*); + original_function = dlsym(RTLD_NEXT, "ODK_ParseLicense"); + OEMCryptoResult oem_crypto_result = (*original_function)( + message, message_length, core_message_length, initial_license_load, + usage_entry_present, request_hash, timer_limits, clock_values, + nonce_values, parsed_license); + char* file_name = GetFileName("license_response_corpus"); + + // 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); + free(file_name); + return oem_crypto_result; +} + +OEMCryptoResult ODK_PrepareCoreRenewalRequest(uint8_t* message, + size_t message_length, + size_t* core_message_size, + ODK_NonceValues* nonce_values, + ODK_ClockValues* clock_values, + uint64_t system_time_seconds) { + OEMCryptoResult (*original_function)( + uint8_t*, size_t, size_t*, ODK_NonceValues*, ODK_ClockValues*, uint64_t); + original_function = dlsym(RTLD_NEXT, "ODK_PrepareCoreRenewalRequest"); + OEMCryptoResult oem_crypto_result = + (*original_function)(message, message_length, core_message_size, + 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] + AppendToFile(file_name, (const char*)clock_values, sizeof(ODK_ClockValues)); + AppendToFile(file_name, (const char*)message, *core_message_size); + free(file_name); + return oem_crypto_result; +} + +OEMCryptoResult ODK_ParseRenewal(const uint8_t* message, size_t message_length, + size_t core_message_length, + const ODK_NonceValues* nonce_values, + uint64_t system_time, + const ODK_TimerLimits* timer_limits, + ODK_ClockValues* clock_values, + uint64_t* timer_value) { + struct ODK_ParseRenewal_Args parse_renewal_args; + parse_renewal_args.nonce_values = *nonce_values; + parse_renewal_args.clock_values = *clock_values; + parse_renewal_args.timer_limits = *timer_limits; + parse_renewal_args.system_time = system_time; + OEMCryptoResult (*original_function)( + const uint8_t*, size_t, size_t, const ODK_NonceValues*, uint64_t, + const ODK_TimerLimits*, ODK_ClockValues*, uint64_t*); + original_function = dlsym(RTLD_NEXT, "ODK_ParseRenewal"); + OEMCryptoResult oem_crypto_result = (*original_function)( + message, message_length, core_message_length, nonce_values, system_time, + 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] + AppendToFile(file_name, (const char*)&parse_renewal_args, + sizeof(struct ODK_ParseRenewal_Args)); + AppendToFile(file_name, (const char*)message, core_message_length); + free(file_name); + return oem_crypto_result; +} + +OEMCryptoResult ODK_PrepareCoreProvisioningRequest( + uint8_t* message, size_t message_length, size_t* core_message_length, + const ODK_NonceValues* nonce_values, const uint8_t* device_id, + size_t device_id_length) { + OEMCryptoResult (*original_function)(uint8_t*, size_t, size_t*, + const ODK_NonceValues*, const uint8_t*, + size_t); + original_function = dlsym(RTLD_NEXT, "ODK_PrepareCoreProvisioningRequest"); + OEMCryptoResult oem_crypto_result = + (*original_function)(message, message_length, core_message_length, + nonce_values, device_id, device_id_length); + char* file_name = GetFileName("provisioning_request_corpus"); + + // 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; +} + +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) { + struct ODK_ParseProvisioning_Args parse_provisioning_args; + parse_provisioning_args.nonce_values = *nonce_values; + memcpy(parse_provisioning_args.device_id, device_id, device_id_length); + parse_provisioning_args.device_id_length = device_id_length; + OEMCryptoResult (*original_function)(const uint8_t*, size_t, size_t, + const ODK_NonceValues*, const uint8_t*, + size_t, ODK_ParsedProvisioning*); + original_function = dlsym(RTLD_NEXT, "ODK_ParseProvisioning"); + OEMCryptoResult oem_crypto_result = (*original_function)( + message, message_length, core_message_length, nonce_values, device_id, + device_id_length, parsed_response); + char* file_name = GetFileName("provisioning_response_corpus"); + + // 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); + free(file_name); + return oem_crypto_result; +} diff --git a/oemcrypto/odk/test/fuzzing/corpus_generator/odk_corpus_generator_helper.c b/oemcrypto/odk/test/fuzzing/corpus_generator/odk_corpus_generator_helper.c new file mode 100644 index 0000000..3a720d2 --- /dev/null +++ b/oemcrypto/odk/test/fuzzing/corpus_generator/odk_corpus_generator_helper.c @@ -0,0 +1,22 @@ +// 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, + const size_t message_size) { + FILE* fptr; + if ((fptr = fopen(file_name, "ab")) == NULL) { + printf("Error! opening file %s", file_name); + return; + } + fwrite(message, message_size, 1, fptr); + fclose(fptr); +} + +char* GetFileName(const char* directory) { + char* file_name; + file_name = malloc(150); + sprintf(file_name, "%s%s/%d", PATH_TO_CORPUS, directory, rand()); + return file_name; +} diff --git a/oemcrypto/odk/test/fuzzing/corpus_generator/odk_corpus_generator_helper.h b/oemcrypto/odk/test/fuzzing/corpus_generator/odk_corpus_generator_helper.h new file mode 100644 index 0000000..c2f164a --- /dev/null +++ b/oemcrypto/odk/test/fuzzing/corpus_generator/odk_corpus_generator_helper.h @@ -0,0 +1,18 @@ +// 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_ + +#define PATH_TO_CORPUS "./oemcrypto/odk/test/fuzzing/corpus/" + +#include +#include +#include + +void AppendToFile(const char* file_name, const char* message, + const size_t message_size); + +char* GetFileName(const char* directory); + +#endif // WIDEVINE_ODK_TEST_FUZZING_CORPUS_GENERATOR_ODK_CORPUS_GENERATOR_HELPER_H_ diff --git a/oemcrypto/odk/test/fuzzing/corpus_generator/odk_fuzz_corpus_generator.gyp b/oemcrypto/odk/test/fuzzing/corpus_generator/odk_fuzz_corpus_generator.gyp new file mode 100644 index 0000000..8acf1ac --- /dev/null +++ b/oemcrypto/odk/test/fuzzing/corpus_generator/odk_fuzz_corpus_generator.gyp @@ -0,0 +1,33 @@ +# 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. + +# Reference Link explaining flags for LD_PRELOAD: https://catonmat.net/simple-ld-preload-tutorial-part-two +{ + 'targets': [ + { + 'target_name': 'odk_corpus_generator', + 'type': 'shared_library', + 'cflags_cc': [ + '-g3', + '-O0', + '-fno-omit-frame-pointer', + '-Wall', + ], + 'include_dirs': [ + '../../../include', + '../../../test', + '../corpus_generator', + ], + 'ldflags': [ + '-fPIC', + ], + 'libraries': [ + '-ldl', + ], + 'sources': [ + 'odk_corpus_generator.c', + ], + } + ] +} diff --git a/oemcrypto/odk/test/fuzzing/odk_fuzz.gyp b/oemcrypto/odk/test/fuzzing/odk_fuzz.gyp new file mode 100644 index 0000000..58f00b0 --- /dev/null +++ b/oemcrypto/odk/test/fuzzing/odk_fuzz.gyp @@ -0,0 +1,40 @@ +# 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. + +#TODO(b/151858867): Fix File paths +{ + 'targets': [ + { + 'target_name': 'odk_fuzz', + 'type': 'executable', + 'includes': [ + '../src/odk.gypi', + '../kdo/oec_util.gypi', + ], + 'include_dirs': [ + '../../include', + '../include', + '../src', + '../kdo/include', + ], + 'cflags_cc': [ + '-std=c++11', + '-g3', + '-O0', + '-fsanitize=fuzzer,address,undefined', + '-fno-omit-frame-pointer', + ], + 'ldflags': [ + '-fPIC', + '-fsanitize=fuzzer,address,undefined', + ], + 'sources': [ + 'odk_fuzz.cpp', + ], + 'dependencies': [ + '../../../cdm/cdm.gyp:license_protocol' + ], + } + ] +} diff --git a/oemcrypto/odk/test/fuzzing/odk_fuzz_helper.cpp b/oemcrypto/odk/test/fuzzing/odk_fuzz_helper.cpp new file mode 100644 index 0000000..d87e464 --- /dev/null +++ b/oemcrypto/odk/test/fuzzing/odk_fuzz_helper.cpp @@ -0,0 +1,159 @@ +// 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/odk_fuzz_helper.h" + +#include "odk.h" + +namespace oemcrypto_core_message { + +bool convert_byte_to_valid_boolean(const bool* in) { + const char* buf = reinterpret_cast(in); + for (int i = 0; i < sizeof(bool); i++) { + if (buf[i]) { + return true; + } + } + return false; +} + +void ConvertDataToValidBools(ODK_ParsedLicense* t) { + // Convert boolean flags in parsed_license to valid bytes to + // avoid errors from msan + t->nonce_required = convert_byte_to_valid_boolean(&t->nonce_required); + t->timer_limits.soft_enforce_playback_duration = + convert_byte_to_valid_boolean( + &t->timer_limits.soft_enforce_playback_duration); + t->timer_limits.soft_enforce_rental_duration = convert_byte_to_valid_boolean( + &t->timer_limits.soft_enforce_rental_duration); +} + +void ConvertDataToValidBools(ODK_PreparedRenewalRequest* t UNUSED) {} + +void ConvertDataToValidBools(ODK_ParsedProvisioning* t UNUSED) {} + +OEMCryptoResult odk_serialize_LicenseRequest( + 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); +} + +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); +} + +OEMCryptoResult odk_serialize_ProvisioningRequest( + 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; + return ODK_PrepareCoreProvisioningRequest( + out, SIZE_MAX, size, nonce_values, + reinterpret_cast(device_id.data()), device_id.size()); +} + +OEMCryptoResult odk_deserialize_LicenseResponse(const uint8_t* message, + size_t core_message_length, + ODK_ParseLicense_Args* a, + ODK_NonceValues* nonce_values, + ODK_ParsedLicense* parsed_lic) { + return ODK_ParseLicense(message, SIZE_MAX, core_message_length, + static_cast(a->initial_license_load), + static_cast(a->usage_entry_present), + a->request_hash, &a->timer_limits, &a->clock_values, + nonce_values, parsed_lic); +} + +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); + const bool is_parse_renewal_response_successful = + err == ODK_SET_TIMER || err == ODK_DISABLE_TIMER || + err == ODK_TIMER_EXPIRED || err == ODK_STALE_RENEWAL; + if (!is_parse_renewal_response_successful) { + return err; + } + // In order to capture playback_time information which is part of + // renewal_msg and will be later used in kdo_serialize_RenewalResponse in + // odk_kdo method, we call Unpack_ODK_PreparedRenewalRequest private method. + // playback_time cannot be captured from publicly exposed API + // ODK_ParseRenewal. + uint8_t blk[SIZE_OF_MESSAGE_STRUCT]; + Message* msg = reinterpret_cast(blk); + InitMessage(msg, const_cast(buf), len); + SetSize(msg, len); + Unpack_ODK_PreparedRenewalRequest(msg, renewal_msg); + return OEMCrypto_SUCCESS; +} + +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); +} + +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(args->request_hash), ODK_SHA256_HASH_SIZE); + return serialize::CreateCoreLicenseResponse( + parsed_lic, core_request, core_request_sha_256, oemcrypto_core_message); +} + +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 serialize::CreateCoreRenewalResponse( + core_request, args->timer_limits.initial_renewal_duration_seconds, + oemcrypto_core_message); +} + +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(args->device_id), + args->device_id_length)}; + return serialize::CreateCoreProvisioningResponse(parsed_prov, core_request, + oemcrypto_core_message); +} +} // namespace oemcrypto_core_message diff --git a/oemcrypto/odk/test/fuzzing/odk_fuzz_helper.h b/oemcrypto/odk/test/fuzzing/odk_fuzz_helper.h new file mode 100644 index 0000000..fe90657 --- /dev/null +++ b/oemcrypto/odk/test/fuzzing/odk_fuzz_helper.h @@ -0,0 +1,205 @@ +// 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_ + +#include +#include + +#include "core_message_serialize.h" +#include "fuzzing/odk_fuzz_structs.h" +#include "odk_attributes.h" +#include "odk_serialize.h" + +namespace oemcrypto_core_message { +bool convert_byte_to_valid_boolean(const bool* in); + +OEMCryptoResult odk_serialize_LicenseRequest( + const void* in, uint8_t* out, size_t* size, + const ODK_LicenseRequest& core_license_request, + const ODK_NonceValues* nonce_values); + +OEMCryptoResult odk_serialize_RenewalRequest( + const void* in, uint8_t* out, size_t* size, + const ODK_RenewalRequest& core_renewal, ODK_NonceValues* nonce_values); + +OEMCryptoResult odk_serialize_ProvisioningRequest( + const void* in, uint8_t* out, size_t* size, + const ODK_ProvisioningRequest& core_provisioning, + const ODK_NonceValues* nonce_values); + +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); + +OEMCryptoResult odk_deserialize_RenewalResponse( + const uint8_t* buf, size_t len, ODK_ParseRenewal_Args* a, + ODK_NonceValues* nonce_values, ODK_PreparedRenewalRequest* renewal_msg); + +OEMCryptoResult odk_deserialize_ProvisioningResponse( + const uint8_t* buf, size_t len, ODK_ParseProvisioning_Args* a, + ODK_NonceValues* nonce_values, ODK_ParsedProvisioning* parsed_prov); + +bool kdo_serialize_LicenseResponse(const ODK_ParseLicense_Args* args, + const ODK_ParsedLicense& parsed_lic, + std::string* oemcrypto_core_message); + +bool kdo_serialize_RenewalResponse( + const ODK_ParseRenewal_Args* args, + const ODK_PreparedRenewalRequest& renewal_msg, + std::string* oemcrypto_core_message); + +bool kdo_serialize_ProvisioningResponse( + const ODK_ParseProvisioning_Args* args, + 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. +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). +extern "C" size_t LLVMFuzzerMutate(uint8_t* Data, size_t Size, size_t MaxSize) + __attribute__((weak)); + +template +size_t FuzzerMutateResponse(uint8_t* data, size_t size, size_t max_size, + const F& odk_deserialize_fun, + const G& kdo_serialize_fun) { + const size_t kArgsSize = sizeof(A); + 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]. + std::shared_ptr _args(new A()); + A* args = _args.get(); + memcpy(args, data, kArgsSize); + ODK_NonceValues nonce_values = args->nonce_values; + args->nonce_values.api_major_version = ODK_MAJOR_VERSION; + const uint8_t* buf = data + kArgsSize; + T t = {}; + 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 (result != OEMCrypto_SUCCESS) { + if (max_size < kTotalResponseSize) { + return 0; + } + // Initialize remaining bytes needed in data to zero. + if (size < kTotalResponseSize) { + memset(data + size, 0, kTotalResponseSize - size); + } + t = {}; + 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 + 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. + ConvertDataToValidBools(&t); + // 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. + memcpy(data + kArgsSize, oemcrypto_core_message.data(), + oemcrypto_core_message.size()); + return kArgsSize + oemcrypto_core_message.size(); +} + +/** + * 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 +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 UNUSED) { + T t = {}; + // Input byte array format: [function arguments][data to parse] + if (size < args_size) { + return; + } + const uint8_t* buf = in + args_size; + std::shared_ptr _args(new A()); + A* args = _args.get(); + memcpy(args, in, args_size); + args->nonce_values.api_major_version = ODK_MAJOR_VERSION; + ODK_NonceValues nonce_values = args->nonce_values; + + OEMCryptoResult result = + odk_fun(buf, size - args_size, args, &nonce_values, &t); + if (result != OEMCrypto_SUCCESS) { + return; + } + std::string oemcrypto_core_message; + if (!kdo_fun(args, t, &oemcrypto_core_message)) { + return; + } +} + +/** + * Template arguments: + * T: kdo deserialize output/odk serialize input structure + * F: kdo deserialize function + * G: odk serialize function + * + * raw bytes -> F deserialize -> struct T -> G serialize -> raw bytes + */ +template +static void kdo_odk(const F& kdo_fun, const G& odk_fun, const uint8_t* in, + size_t size, const size_t clock_value_size, uint8_t* out) { + if (size <= clock_value_size) { + return; + } + // Input byte array format: [Clock Values][data to parse]. + // Only Renewal Request expects clock values to be present. + std::string input(reinterpret_cast(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; + } +} +} // namespace oemcrypto_core_message +#endif // WIDEVINE_ODK_TEST_FUZZING_ODK_FUZZ_HELPER_H_ diff --git a/oemcrypto/odk/test/fuzzing/odk_fuzz_structs.h b/oemcrypto/odk/test/fuzzing/odk_fuzz_structs.h new file mode 100644 index 0000000..37d1b23 --- /dev/null +++ b/oemcrypto/odk/test/fuzzing/odk_fuzz_structs.h @@ -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. +#ifndef WIDEVINE_ODK_TEST_FUZZING_ODK_FUZZ_STRUCTS_H_ +#define WIDEVINE_ODK_TEST_FUZZING_ODK_FUZZ_STRUCTS_H_ + +#include "odk_structs.h" + +struct ODK_ParseLicense_Args { + ODK_NonceValues nonce_values; + uint8_t initial_license_load; + uint8_t usage_entry_present; + uint8_t request_hash[ODK_SHA256_HASH_SIZE]; + 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]; +}; +#endif // WIDEVINE_ODK_TEST_FUZZING_ODK_FUZZ_STRUCTS_H_ diff --git a/oemcrypto/odk/test/fuzzing/odk_license_request_fuzz.cpp b/oemcrypto/odk/test/fuzzing/odk_license_request_fuzz.cpp new file mode 100644 index 0000000..463c604 --- /dev/null +++ b/oemcrypto/odk/test/fuzzing/odk_license_request_fuzz.cpp @@ -0,0 +1,22 @@ +/* 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 + +#include "core_message_deserialize.h" +#include "fuzzing/odk_fuzz_helper.h" + +namespace oemcrypto_core_message { +using oemcrypto_core_message::deserialize::CoreLicenseRequestFromMessage; + +extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { + std::vector out(size); + const size_t kClockValueSize = 0; + kdo_odk(CoreLicenseRequestFromMessage, + odk_serialize_LicenseRequest, data, size, + kClockValueSize, out.data()); + return 0; +} +} // namespace oemcrypto_core_message diff --git a/oemcrypto/odk/test/fuzzing/odk_license_response_fuzz.cpp b/oemcrypto/odk/test/fuzzing/odk_license_response_fuzz.cpp new file mode 100644 index 0000000..12398fd --- /dev/null +++ b/oemcrypto/odk/test/fuzzing/odk_license_response_fuzz.cpp @@ -0,0 +1,20 @@ +/* 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 + +#include "fuzzing/odk_fuzz_helper.h" + +namespace oemcrypto_core_message { + +extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { + const size_t kLicenseResponseArgsSize = sizeof(ODK_ParseLicense_Args); + std::vector out(size); + odk_kdo( + odk_deserialize_LicenseResponse, kdo_serialize_LicenseResponse, data, + size, kLicenseResponseArgsSize, out.data()); + return 0; +} +} // namespace oemcrypto_core_message diff --git a/oemcrypto/odk/test/fuzzing/odk_license_response_fuzz_with_mutator.cpp b/oemcrypto/odk/test/fuzzing/odk_license_response_fuzz_with_mutator.cpp new file mode 100644 index 0000000..42472fb --- /dev/null +++ b/oemcrypto/odk/test/fuzzing/odk_license_response_fuzz_with_mutator.cpp @@ -0,0 +1,36 @@ +/* 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 + +#include "fuzzing/odk_fuzz_helper.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 UNUSED) { + const size_t kLicenseResponseArgsSize = sizeof(ODK_ParseLicense_Args); + if (size < kLicenseResponseArgsSize) { + return 0; + } + + // Mutate input data and return mutated input size. + return FuzzerMutateResponse( + data, size, max_size, odk_deserialize_LicenseResponse, + kdo_serialize_LicenseResponse); +} + +extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { + const size_t kLicenseResponseArgsSize = sizeof(ODK_ParseLicense_Args); + std::vector out(size); + odk_kdo( + odk_deserialize_LicenseResponse, kdo_serialize_LicenseResponse, data, + size, kLicenseResponseArgsSize, out.data()); + return 0; +} +} // namespace oemcrypto_core_message diff --git a/oemcrypto/odk/test/fuzzing/odk_provisioning_request_fuzz.cpp b/oemcrypto/odk/test/fuzzing/odk_provisioning_request_fuzz.cpp new file mode 100644 index 0000000..984534e --- /dev/null +++ b/oemcrypto/odk/test/fuzzing/odk_provisioning_request_fuzz.cpp @@ -0,0 +1,22 @@ +/* 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 + +#include "core_message_deserialize.h" +#include "fuzzing/odk_fuzz_helper.h" + +namespace oemcrypto_core_message { +using oemcrypto_core_message::deserialize::CoreProvisioningRequestFromMessage; + +extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { + std::vector out(size); + const size_t kClockValueSize = 0; + kdo_odk(CoreProvisioningRequestFromMessage, + odk_serialize_ProvisioningRequest, data, + size, kClockValueSize, out.data()); + return 0; +} +} // namespace oemcrypto_core_message diff --git a/oemcrypto/odk/test/fuzzing/odk_provisioning_response_fuzz.cpp b/oemcrypto/odk/test/fuzzing/odk_provisioning_response_fuzz.cpp new file mode 100644 index 0000000..90dc017 --- /dev/null +++ b/oemcrypto/odk/test/fuzzing/odk_provisioning_response_fuzz.cpp @@ -0,0 +1,21 @@ +/* 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 + +#include "fuzzing/odk_fuzz_helper.h" + +namespace oemcrypto_core_message { + +extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { + const size_t kProvisioningResponseArgsSize = + sizeof(ODK_ParseProvisioning_Args); + std::vector out(size); + odk_kdo( + odk_deserialize_ProvisioningResponse, kdo_serialize_ProvisioningResponse, + data, size, kProvisioningResponseArgsSize, out.data()); + return 0; +} +} // namespace oemcrypto_core_message diff --git a/oemcrypto/odk/test/fuzzing/odk_provisioning_response_fuzz_with_mutator.cpp b/oemcrypto/odk/test/fuzzing/odk_provisioning_response_fuzz_with_mutator.cpp new file mode 100644 index 0000000..17787a4 --- /dev/null +++ b/oemcrypto/odk/test/fuzzing/odk_provisioning_response_fuzz_with_mutator.cpp @@ -0,0 +1,38 @@ +/* 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 + +#include "fuzzing/odk_fuzz_helper.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) { + const size_t kProvisioningResponseArgsSize = + sizeof(ODK_ParseProvisioning_Args); + if (size < kProvisioningResponseArgsSize) { + return 0; + } + + // Mutate input data and return mutated input size. + return FuzzerMutateResponse( + data, size, max_size, odk_deserialize_ProvisioningResponse, + kdo_serialize_ProvisioningResponse); +} + +extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { + const size_t kProvisioningResponseArgsSize = + sizeof(ODK_ParseProvisioning_Args); + std::vector out(size); + odk_kdo( + odk_deserialize_ProvisioningResponse, kdo_serialize_ProvisioningResponse, + data, size, kProvisioningResponseArgsSize, out.data()); + return 0; +} +} // namespace oemcrypto_core_message diff --git a/oemcrypto/odk/test/fuzzing/odk_renewal_request_fuzz.cpp b/oemcrypto/odk/test/fuzzing/odk_renewal_request_fuzz.cpp new file mode 100644 index 0000000..602b37a --- /dev/null +++ b/oemcrypto/odk/test/fuzzing/odk_renewal_request_fuzz.cpp @@ -0,0 +1,22 @@ +/* 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 + +#include "core_message_deserialize.h" +#include "fuzzing/odk_fuzz_helper.h" + +namespace oemcrypto_core_message { +using oemcrypto_core_message::deserialize::CoreRenewalRequestFromMessage; + +extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { + std::vector out(size); + const size_t kClockValueSize = sizeof(ODK_ClockValues); + kdo_odk(CoreRenewalRequestFromMessage, + odk_serialize_RenewalRequest, data, size, + kClockValueSize, out.data()); + return 0; +} +} // namespace oemcrypto_core_message diff --git a/oemcrypto/odk/test/fuzzing/odk_renewal_response_fuzz.cpp b/oemcrypto/odk/test/fuzzing/odk_renewal_response_fuzz.cpp new file mode 100644 index 0000000..8d66908 --- /dev/null +++ b/oemcrypto/odk/test/fuzzing/odk_renewal_response_fuzz.cpp @@ -0,0 +1,20 @@ +/* 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 + +#include "fuzzing/odk_fuzz_helper.h" + +namespace oemcrypto_core_message { + +extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { + const size_t kRenewalResponseArgsSize = sizeof(ODK_ParseRenewal_Args); + std::vector out(size); + odk_kdo( + odk_deserialize_RenewalResponse, kdo_serialize_RenewalResponse, data, + size, kRenewalResponseArgsSize, out.data()); + return 0; +} +} // namespace oemcrypto_core_message diff --git a/oemcrypto/odk/test/fuzzing/odk_renewal_response_fuzz_with_mutator.cpp b/oemcrypto/odk/test/fuzzing/odk_renewal_response_fuzz_with_mutator.cpp new file mode 100644 index 0000000..0073c4e --- /dev/null +++ b/oemcrypto/odk/test/fuzzing/odk_renewal_response_fuzz_with_mutator.cpp @@ -0,0 +1,36 @@ +/* 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 + +#include "fuzzing/odk_fuzz_helper.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) { + const size_t kRenewalResponseArgsSize = sizeof(ODK_ParseRenewal_Args); + if (size < kRenewalResponseArgsSize) { + return 0; + } + + // Mutate input data and return mutated input size. + return FuzzerMutateResponse( + data, size, max_size, odk_deserialize_RenewalResponse, + kdo_serialize_RenewalResponse); +} + +extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { + const size_t kRenewalResponseArgsSize = sizeof(ODK_ParseRenewal_Args); + std::vector out(size); + odk_kdo( + odk_deserialize_RenewalResponse, kdo_serialize_RenewalResponse, data, + size, kRenewalResponseArgsSize, out.data()); + return 0; +} +} // namespace oemcrypto_core_message diff --git a/oemcrypto/odk/test/odk_core_message_test.cpp b/oemcrypto/odk/test/odk_core_message_test.cpp new file mode 100644 index 0000000..c824759 --- /dev/null +++ b/oemcrypto/odk/test/odk_core_message_test.cpp @@ -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(oem.c_str()); + uint8_t* message = const_cast(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(message); + VLOG(0) << absl::BytesToHexString(std::string(m, core_message_length)); +} +} // namespace wvodk_test diff --git a/oemcrypto/odk/test/odk_test.cpp b/oemcrypto/odk/test/odk_test.cpp index b5c8565..b6526df 100644 --- a/oemcrypto/odk/test/odk_test.cpp +++ b/oemcrypto/odk/test/odk_test.cpp @@ -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 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 diff --git a/oemcrypto/odk/test/odk_test_helper.h b/oemcrypto/odk/test/odk_test_helper.h index ad29e89..1d90dc0 100644 --- a/oemcrypto/odk/test/odk_test_helper.h +++ b/oemcrypto/odk/test/odk_test_helper.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_TEST_ODK_TEST_HELPER_H_ #define WIDEVINE_ODK_TEST_ODK_TEST_HELPER_H_ @@ -66,7 +66,7 @@ struct ODK_ProvisioningResponseParams { std::vector 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& fields); void ODK_ResetOdkFields(std::vector* 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& 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_ diff --git a/oemcrypto/odk/test/odk_timer_test.cpp b/oemcrypto/odk/test/odk_timer_test.cpp index c7f00ce..15a6f78 100644 --- a/oemcrypto/odk/test/odk_timer_test.cpp +++ b/oemcrypto/odk/test/odk_timer_test.cpp @@ -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, diff --git a/oemcrypto/oemcrypto_unittests.gyp b/oemcrypto/oemcrypto_unittests.gyp index f0e5ca5..6d66e97 100644 --- a/oemcrypto/oemcrypto_unittests.gyp +++ b/oemcrypto/oemcrypto_unittests.gyp @@ -5,13 +5,15 @@ # Override the variables below for the location of various gyp files. # Alternatively, set the environment variable PATH_TO_CDM_DIR to point to a # recent version of the source CDM. This *must* be a relative path. - 'boringssl_dependency%': ' 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; } @@ -102,7 +116,7 @@ int64_t CryptoEngine::MonotonicTime() { wvcdm::Clock().GetCurrentTime() + offline_time_info_.rollback_offset; static int64_t then = now; if (now < then) { - LOGW("Clock rollback detected: %lld seconds", then - now); + LOGW("Clock rollback detected: %ld seconds", then - now); offline_time_info_.rollback_offset += then - now; now = then; } @@ -216,6 +230,13 @@ OEMCryptoResult CryptoEngine::SetDestination( max_length = out_description.buffer.clear.address_length; break; case OEMCrypto_BufferType_Secure: + if (out_description.buffer.secure.handle_length < + out_description.buffer.secure.offset) { + LOGE("Secure buffer offset too large: %zu < %zu", + out_description.buffer.secure.handle_length, + out_description.buffer.secure.offset); + return OEMCrypto_ERROR_SHORT_BUFFER; + } destination_ = reinterpret_cast(out_description.buffer.secure.handle) + out_description.buffer.secure.offset; @@ -251,4 +272,8 @@ OEMCryptoResult CryptoEngine::SetDestination( return OEMCrypto_SUCCESS; } +uint32_t CryptoEngine::SessionTypeBits(SessionId sid) { + return sid >> kSessionIdTypeShift; +} + } // namespace wvoec_ref diff --git a/oemcrypto/ref/src/oemcrypto_engine_ref.h b/oemcrypto/ref/src/oemcrypto_engine_ref.h index 552f59a..299c527 100644 --- a/oemcrypto/ref/src/oemcrypto_engine_ref.h +++ b/oemcrypto/ref/src/oemcrypto_engine_ref.h @@ -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(); diff --git a/oemcrypto/ref/src/oemcrypto_key_ref.cpp b/oemcrypto/ref/src/oemcrypto_key_ref.cpp index feb2374..9d28eec 100644 --- a/oemcrypto/ref/src/oemcrypto_key_ref.cpp +++ b/oemcrypto/ref/src/oemcrypto_key_ref.cpp @@ -47,7 +47,7 @@ uint32_t KeyControlBlock::ExtractField(const std::vector& str, KeyControlBlock::KeyControlBlock( const std::vector& key_control_string) { if (key_control_string.size() < wvoec::KEY_CONTROL_SIZE) { - LOGE("KCB: BAD Size: %d (not %d)", key_control_string.size(), + LOGE("KCB: BAD Size: %zu (not %zu)", key_control_string.size(), wvoec::KEY_CONTROL_SIZE); return; } diff --git a/oemcrypto/ref/src/oemcrypto_ref.cpp b/oemcrypto/ref/src/oemcrypto_ref.cpp index 1127651..9c038b6 100644 --- a/oemcrypto/ref/src/oemcrypto_ref.cpp +++ b/oemcrypto/ref/src/oemcrypto_ref.cpp @@ -8,9 +8,9 @@ #include #include +#include #include #include -#include #include #include #include @@ -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()) { diff --git a/oemcrypto/ref/src/oemcrypto_session.cpp b/oemcrypto/ref/src/oemcrypto_session.cpp index 3f9ddf8..cbbc6fb 100644 --- a/oemcrypto/ref/src/oemcrypto_session.cpp +++ b/oemcrypto/ref/src/oemcrypto_session.cpp @@ -15,10 +15,10 @@ #include #include #include +#include #include #include #include -#include #include #include #include @@ -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& 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& 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& key, @@ -313,7 +310,7 @@ bool SessionContext::RSADeriveKeys( return false; } session_key_.resize(RSA_size(rsa_key())); - int decrypted_size = + const int decrypted_size = RSA_private_decrypt(enc_session_key.size(), &enc_session_key[0], &session_key_[0], rsa_key(), RSA_PKCS1_OAEP_PADDING); if (-1 == decrypted_size) { @@ -347,7 +344,7 @@ OEMCryptoResult SessionContext::PrepAndSignLicenseRequest( return OEMCrypto_ERROR_SHORT_BUFFER; } if (result != OEMCrypto_SUCCESS) { - LOGE("ODK error: %d", result); + LOGE("ODK error: %d", static_cast(result)); return result; } if (message == nullptr || message_length < *core_message_length || @@ -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; } @@ -395,7 +394,7 @@ OEMCryptoResult SessionContext::PrepAndSignRenewalRequest( return OEMCrypto_ERROR_SHORT_BUFFER; } if (result != OEMCrypto_SUCCESS) { - LOGE("ODK error: %d", result); + LOGE("ODK error: %d", static_cast(result)); return result; } if (message == nullptr || message_length < *core_message_length || @@ -438,7 +437,7 @@ OEMCryptoResult SessionContext::PrepAndSignProvisioningRequest( return OEMCrypto_ERROR_SHORT_BUFFER; } if (result != OEMCrypto_SUCCESS) { - LOGE("ODK error: %d", result); + LOGE("ODK error: %d", static_cast(result)); return result; } if (message == nullptr || message_length == 0 || signature == nullptr) { @@ -452,7 +451,8 @@ OEMCryptoResult SessionContext::PrepAndSignProvisioningRequest( result = GenerateCertSignature(message, message_length, signature, signature_length); } else { - LOGE("Bad prov method = %d", ce_->config_provisioning_method()); + LOGE("Bad prov method = %d", + static_cast(ce_->config_provisioning_method())); return OEMCrypto_ERROR_UNKNOWN_FAILURE; } if (result == OEMCrypto_SUCCESS) state_request_signed_ = true; @@ -508,7 +508,8 @@ size_t SessionContext::ROTSignatureSize() { return SHA256_DIGEST_LENGTH; if (ce_->config_provisioning_method() == OEMCrypto_OEMCertificate) return CertSignatureSize(); - LOGE("Bad prov method = %d", ce_->config_provisioning_method()); + LOGE("Bad prov method = %d", + static_cast(ce_->config_provisioning_method())); return 0; } @@ -522,7 +523,7 @@ OEMCryptoResult SessionContext::GenerateCertSignature( return OEMCrypto_ERROR_INVALID_CONTEXT; } if (!rsa_key()) { - LOGE("no RSA key set"); + LOGE("No RSA key set"); return OEMCrypto_ERROR_INVALID_RSA_KEY; } if (*signature_length < static_cast(RSA_size(rsa_key()))) { @@ -530,7 +531,7 @@ OEMCryptoResult SessionContext::GenerateCertSignature( return OEMCrypto_ERROR_SHORT_BUFFER; } if (allowed_schemes_ != kSign_RSASSA_PSS) { - LOGE("message signing not allowed"); + LOGE("Message signing not allowed"); return OEMCrypto_ERROR_INVALID_RSA_KEY; } @@ -572,7 +573,7 @@ OEMCryptoResult SessionContext::GenerateRSASignature( return OEMCrypto_ERROR_INVALID_CONTEXT; } if (!rsa_key()) { - LOGE("no RSA key set"); + LOGE("No RSA key set"); return OEMCrypto_ERROR_INVALID_RSA_KEY; } if (*signature_length < static_cast(RSA_size(rsa_key()))) { @@ -592,10 +593,10 @@ OEMCryptoResult SessionContext::GenerateRSASignature( return OEMCrypto_ERROR_SIGNATURE_FAILURE; } // Pad the message with PKCS1 padding, and then encrypt. - int status = RSA_private_encrypt(message_length, message, signature, - rsa_key(), RSA_PKCS1_PADDING); + const int status = RSA_private_encrypt(message_length, message, signature, + rsa_key(), RSA_PKCS1_PADDING); if (status < 0) { - LOGE("error in RSA private encrypt. status=%d", status); + LOGE("Error in RSA private encrypt. status = %d", status); dump_boringssl_error(); return OEMCrypto_ERROR_UNKNOWN_FAILURE; } @@ -675,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; @@ -710,7 +713,7 @@ OEMCryptoResult SessionContext::LoadLicense(const uint8_t* message, usage_entry_present(), license_request_hash_, &timer_limits_, &clock_values_, &nonce_values_, &parsed_response); if (result != OEMCrypto_SUCCESS) { - LOGE("ODK Error %d", result); + LOGE("ODK Error %d", static_cast(result)); return result; } // Validate message signature @@ -793,21 +796,21 @@ OEMCryptoResult SessionContext::LoadKeysNoSignature( message + srm_restriction_data.offset); return OEMCrypto_ERROR_INVALID_CONTEXT; } - uint32_t minimum_version = htonl(*reinterpret_cast( + const uint32_t minimum_version = htonl(*reinterpret_cast( message + srm_restriction_data.offset + 8)); uint16_t current_version = 0; if (OEMCrypto_SUCCESS != ce_->current_srm_version(¤t_version)) { LOGW("[LoadKeys: SRM Version not available"); srm_requirements_status_ = InvalidSRMVersion; } else if (current_version < minimum_version) { - LOGW("[LoadKeys: SRM Version is too small %d, required: %d", + LOGW("[LoadKeys: SRM Version is too small %u, required: %u", current_version, minimum_version); srm_requirements_status_ = InvalidSRMVersion; } else if (ce_->srm_blacklisted_device_attached()) { LOGW("[LoadKeys: SRM blacklisted device attached]"); srm_requirements_status_ = InvalidSRMVersion; } else { - LOGI("[LoadKeys: SRM Versions is %d, required: %d]", current_version, + LOGI("[LoadKeys: SRM Versions is %u, required: %u]", current_version, minimum_version); srm_requirements_status_ = ValidSRMVersion; } @@ -842,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; @@ -863,10 +865,15 @@ OEMCryptoResult SessionContext::LoadKeysNoSignature( message + enc_mac_keys_iv.offset + wvoec::KEY_IV_SIZE); if (!UpdateMacKeys(enc_mac_keys_str, enc_mac_key_iv_str)) { - LOGE("Failed to update mac keys.\n"); + 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_) { @@ -882,7 +889,7 @@ OEMCryptoResult SessionContext::LoadKeysNoSignature( return result; } if (!usage_entry_->SetMacKeys(mac_key_server_, mac_key_client_)) { - LOGE("LoadKeys: Usage table can't set keys.\n"); + LOGE("LoadKeys: Usage table can't set keys."); return OEMCrypto_ERROR_UNKNOWN_FAILURE; } break; @@ -891,7 +898,7 @@ OEMCryptoResult SessionContext::LoadKeysNoSignature( return OEMCrypto_ERROR_WRONG_PST; } if (!usage_entry_->VerifyMacKeys(mac_key_server_, mac_key_client_)) { - LOGE("LoadKeys: Usage table entry mac keys do not match.\n"); + LOGE("LoadKeys: Usage table entry mac keys do not match."); return OEMCrypto_ERROR_WRONG_KEYS; } if (usage_entry_->Inactive()) return OEMCrypto_ERROR_LICENSE_INACTIVE; @@ -991,11 +998,11 @@ OEMCryptoResult SessionContext::InstallKey( LOGE("Anti-rollback hardware is required but hardware not present"); return OEMCrypto_ERROR_UNKNOWN_FAILURE; } - uint8_t minimum_patch_level = (key_control_block.control_bits() & - wvoec::kControlSecurityPatchLevelMask) >> - wvoec::kControlSecurityPatchLevelShift; + const uint8_t minimum_patch_level = (key_control_block.control_bits() & + wvoec::kControlSecurityPatchLevelMask) >> + wvoec::kControlSecurityPatchLevelShift; if (minimum_patch_level > OEMCrypto_Security_Patch_Level()) { - LOGE("[InstallKey(): security patch level: %d. Minimum:%d]", + LOGE("[InstallKey(): security_patch_level = %u, minimum_patch_level = %u]", OEMCrypto_Security_Patch_Level(), minimum_patch_level); return OEMCrypto_ERROR_UNKNOWN_FAILURE; } @@ -1026,7 +1033,7 @@ OEMCryptoResult SessionContext::InstallKey( bool SessionContext::InstallRSAEncryptedKey( const uint8_t* encrypted_message_key, size_t encrypted_message_key_length) { encryption_key_.resize(RSA_size(rsa_key())); - int decrypted_size = RSA_private_decrypt( + const int decrypted_size = RSA_private_decrypt( encrypted_message_key_length, encrypted_message_key, &encryption_key_[0], rsa_key(), RSA_PKCS1_OAEP_PADDING); if (-1 == decrypted_size) { @@ -1054,7 +1061,7 @@ OEMCryptoResult SessionContext::LoadRenewal(const uint8_t* message, return OEMCrypto_ERROR_INVALID_CONTEXT; } if (!ValidateMessage(message, message_length, signature, signature_length)) { - LOGE("signature was invalid"); + LOGE("Signature was invalid"); return OEMCrypto_ERROR_SIGNATURE_FAILURE; } @@ -1120,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); @@ -1134,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); @@ -1225,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; } @@ -1247,11 +1262,12 @@ OEMCryptoResult SessionContext::Generic_Encrypt(const uint8_t* in_buffer, const std::vector& key = current_content_key()->value(); // Set the AES key. if (static_cast(key.size()) != AES_BLOCK_SIZE) { - LOGE("[Generic_Encrypt(): CONTENT_KEY has wrong size: %d", key.size()); + 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"); @@ -1290,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) { @@ -1332,7 +1349,7 @@ OEMCryptoResult SessionContext::Generic_Sign(const uint8_t* in_buffer, } const std::vector& key = current_content_key()->value(); if (static_cast(key.size()) != SHA256_DIGEST_LENGTH) { - LOGE("[Generic_Sign(): CONTENT_KEY has wrong size; %d", key.size()); + LOGE("[Generic_Sign(): CONTENT_KEY has wrong size: %zu", key.size()); return OEMCrypto_ERROR_UNKNOWN_FAILURE; } OEMCryptoResult result = CheckKeyUse("Generic_Sign", wvoec::kControlAllowSign, @@ -1368,11 +1385,11 @@ OEMCryptoResult SessionContext::Generic_Verify(const uint8_t* in_buffer, } const std::vector& key = current_content_key()->value(); if (static_cast(key.size()) != SHA256_DIGEST_LENGTH) { - LOGE("[Generic_Verify(): CONTENT_KEY has wrong size: %d", key.size()); + 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"); @@ -1521,6 +1538,11 @@ bool SessionContext::DecryptMessage(const std::vector& 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); @@ -1558,20 +1580,20 @@ OEMCryptoResult SessionContext::DecryptSamples( OEMCryptoResult result = ce_->SetDestination( subsample_dest, subsample_length, subsample.subsample_flags); if (result != OEMCrypto_SUCCESS) { - LOGE("SetDestination status: %d", result); + LOGE("SetDestination status: %d", static_cast(result)); return result; } result = DecryptSubsample(subsample, subsample_source, ce_->destination(), subsample_dest.type, subsample_iv, pattern); if (result != OEMCrypto_SUCCESS) { - LOGE("DecryptSubsample status: %d", result); + LOGE("DecryptSubsample status: %d", static_cast(result)); return result; } result = ce_->PushDestination(subsample_dest, subsample.subsample_flags); if (result != OEMCrypto_SUCCESS) { - LOGE("PushDestination status: %d", result); + LOGE("PushDestination status: %d", static_cast(result)); return result; } @@ -1630,7 +1652,7 @@ OEMCryptoResult SessionContext::DecryptSubsample( current_hash_); if (OEMCrypto_LastSubsample & subsample.subsample_flags) { if (current_hash_ != given_hash_) { - LOGE("CRC for frame %d is %08x, should be %08x\n", + LOGE("CRC for frame %u is %08x, should be %08x\n", current_frame_number_, current_hash_, given_hash_); // Update bad_frame_number_ only if this is the first bad frame. if (hash_error_ == OEMCrypto_SUCCESS) { @@ -1666,7 +1688,7 @@ OEMCryptoResult SessionContext::ChooseDecrypt( // Set the AES key. if (static_cast(content_key.size()) != AES_BLOCK_SIZE) { - LOGE("[DecryptCTR(): CONTENT_KEY has wrong size: %d", content_key.size()); + LOGE("[DecryptCTR(): CONTENT_KEY has wrong size: %zu", content_key.size()); return OEMCrypto_ERROR_DECRYPT_FAILED; } const uint8_t* key_u8 = &content_key[0]; @@ -1712,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); diff --git a/oemcrypto/ref/src/oemcrypto_usage_table_ref.cpp b/oemcrypto/ref/src/oemcrypto_usage_table_ref.cpp index db04ae7..69f2f46 100644 --- a/oemcrypto/ref/src/oemcrypto_usage_table_ref.cpp +++ b/oemcrypto/ref/src/oemcrypto_usage_table_ref.cpp @@ -13,8 +13,8 @@ #include #include +#include #include -#include #include #include @@ -116,16 +116,18 @@ OEMCryptoResult UsageTableEntry::ReportUsage(const std::vector& pst, if (recent_decrypt_) return OEMCrypto_ERROR_ENTRY_NEEDS_UPDATE; if (pst.size() == 0 || pst.size() > kMaxPSTLength || pst.size() != data_.pst_length) { - LOGE("ReportUsage: bad pst length = %d, should be %d.", pst.size(), + LOGE("ReportUsage: bad pst length = %zu, should be %u.", pst.size(), data_.pst_length); 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; } - size_t length_needed = wvcdm::Unpacked_PST_Report::report_size(pst.size()); + const size_t length_needed = + wvcdm::Unpacked_PST_Report::report_size(pst.size()); if (*buffer_length < length_needed) { *buffer_length = length_needed; return OEMCrypto_ERROR_SHORT_BUFFER; @@ -135,7 +137,7 @@ OEMCryptoResult UsageTableEntry::ReportUsage(const std::vector& pst, return OEMCrypto_ERROR_INVALID_CONTEXT; } wvcdm::Unpacked_PST_Report pst_report(buffer); - int64_t now = usage_table_->ce_->SystemTime(); + const int64_t now = usage_table_->ce_->SystemTime(); pst_report.set_seconds_since_license_received(now - data_.time_of_license_received); pst_report.set_seconds_since_first_decrypt(now - data_.time_of_first_decrypt); @@ -233,7 +235,7 @@ OEMCryptoResult UsageTableEntry::LoadData(CryptoEngine* ce, uint32_t index, ODK_ClockValues* clock_values) { if (buffer.size() < SignedEntrySize()) return OEMCrypto_ERROR_SHORT_BUFFER; if (buffer.size() > SignedEntrySize()) - LOGW("LoadUsageTableEntry: buffer is large. %d > %d", buffer.size(), + LOGW("LoadUsageTableEntry: buffer is large. %zu > %zu", buffer.size(), SignedEntrySize()); std::vector clear_buffer(buffer.size()); SignedEntryBlock* clear = @@ -282,7 +284,7 @@ OEMCryptoResult UsageTableEntry::LoadData(CryptoEngine* ce, uint32_t index, wvcdm::HexEncode(clear->verification, kMagicLength).c_str(), clear->verification, wvcdm::HexEncode(reinterpret_cast(kEntryVerification), - kMagicLength) + kMagicLength) .c_str(), reinterpret_cast(kEntryVerification)); return OEMCrypto_ERROR_BAD_MAGIC; @@ -357,10 +359,10 @@ OEMCryptoResult UsageTable::CreateNewUsageEntry( } if (!entry) return OEMCrypto_ERROR_UNKNOWN_FAILURE; if (!usage_entry_number) return OEMCrypto_ERROR_UNKNOWN_FAILURE; - uint32_t index = generation_numbers_.size(); - size_t max = ce_->max_usage_table_size(); + const size_t index = generation_numbers_.size(); + const size_t max = ce_->max_usage_table_size(); if (max > 0 && index >= max) { - LOGE("Too many usage entries: %d/%d", index, max); + LOGE("Too many usage entries: %zu/%zu", index, max); return OEMCrypto_ERROR_INSUFFICIENT_RESOURCES; } UsageTableEntry* new_entry = MakeEntry(index); @@ -377,7 +379,7 @@ OEMCryptoResult UsageTable::LoadUsageEntry( uint32_t index, const std::vector& 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; @@ -387,9 +389,9 @@ OEMCryptoResult UsageTable::LoadUsageEntry( LOGE("LoadUsageEntry: index %d used by other session.", index); return OEMCrypto_ERROR_INVALID_SESSION; } - size_t max = ce_->max_usage_table_size(); + const size_t max = ce_->max_usage_table_size(); if (max > 0 && index >= max) { - LOGE("Too many usage entries: %d/%d", index, max); + LOGE("Too many usage entries: %u/%zu", index, max); return OEMCrypto_ERROR_INSUFFICIENT_RESOURCES; } std::unique_ptr new_entry(MakeEntry(index)); @@ -417,7 +419,7 @@ OEMCryptoResult UsageTable::ShrinkUsageTableHeader( uint32_t new_table_size, uint8_t* header_buffer, size_t* header_buffer_length) { if (new_table_size > generation_numbers_.size()) { - LOGE("OEMCrypto_ShrinkUsageTableHeader: %d > %zd.", new_table_size, + LOGE("OEMCrypto_ShrinkUsageTableHeader: %u > %zu", new_table_size, generation_numbers_.size()); return OEMCrypto_ERROR_UNKNOWN_FAILURE; } @@ -433,7 +435,7 @@ OEMCryptoResult UsageTable::ShrinkUsageTableHeader( } for (size_t i = new_table_size; i < sessions_.size(); ++i) { if (sessions_[i]) { - LOGE("ShrinkUsageTableHeader: session open for %d", i); + LOGE("ShrinkUsageTableHeader: session open for %zu", i); return OEMCrypto_ERROR_ENTRY_IN_USE; } } @@ -505,8 +507,8 @@ OEMCryptoResult UsageTable::LoadUsageTableHeader( if (buffer.size() < SignedHeaderSize(0)) return OEMCrypto_ERROR_SHORT_BUFFER; size_t max = ce_->max_usage_table_size(); if (max > 0 && buffer.size() > SignedHeaderSize(max)) { - LOGE("Header too big: %zd bytes/%zd bytes", - buffer.size(), SignedHeaderSize(max)); + LOGE("Header too big: %zu bytes/%zu bytes", buffer.size(), + SignedHeaderSize(max)); return OEMCrypto_ERROR_INSUFFICIENT_RESOURCES; } std::vector clear_buffer(buffer.size()); @@ -556,7 +558,7 @@ OEMCryptoResult UsageTable::LoadUsageTableHeader( wvcdm::HexEncode(clear->verification, kMagicLength).c_str(), clear->verification, wvcdm::HexEncode(reinterpret_cast(kHeaderVerification), - kMagicLength) + kMagicLength) .c_str(), reinterpret_cast(kHeaderVerification)); return OEMCrypto_ERROR_BAD_MAGIC; @@ -567,7 +569,7 @@ OEMCryptoResult UsageTable::LoadUsageTableHeader( return OEMCrypto_ERROR_SHORT_BUFFER; } if (buffer.size() > SignedHeaderSize(clear->count)) { - LOGW("LoadUsageTableHeader: buffer is large. %d > %d", buffer.size(), + LOGW("LoadUsageTableHeader: buffer is large. %zu > %zu", buffer.size(), SignedHeaderSize(clear->count)); } @@ -594,12 +596,12 @@ OEMCryptoResult UsageTable::LoadUsageTableHeader( OEMCryptoResult UsageTable::MoveEntry(UsageTableEntry* entry, uint32_t new_index) { if (new_index >= generation_numbers_.size()) { - LOGE("MoveEntry: index beyond end of usage table %d >= %d", new_index, + LOGE("MoveEntry: index beyond end of usage table %u >= %zu", new_index, generation_numbers_.size()); return OEMCrypto_ERROR_INVALID_CONTEXT; } if (sessions_[new_index]) { - LOGE("MoveEntry: session open for %d", new_index); + LOGE("MoveEntry: session open for %u", new_index); return OEMCrypto_ERROR_ENTRY_IN_USE; } if (!entry) { @@ -690,7 +692,7 @@ OEMCryptoResult UsageTable::CreateUsageTableHeader( // Make sure there are no entries that are currently tied to an open session. for (size_t i = 0; i < sessions_.size(); ++i) { if (sessions_[i] != nullptr) { - LOGE("CreateUsageTableHeader: index %d used by session.", i); + LOGE("CreateUsageTableHeader: index %zu used by session.", i); return OEMCrypto_ERROR_INVALID_SESSION; } } diff --git a/oemcrypto/ref/src/oemcrypto_usage_table_ref.h b/oemcrypto/ref/src/oemcrypto_usage_table_ref.h index a2d87d0..d112695 100644 --- a/oemcrypto/ref/src/oemcrypto_usage_table_ref.h +++ b/oemcrypto/ref/src/oemcrypto_usage_table_ref.h @@ -9,6 +9,7 @@ #include #include +#include #include #include diff --git a/oemcrypto/test/fuzz_tests/GenerateSignatureCorpus/0ac99ac6565414c7f57a36bcf0c212327cc88ab3 b/oemcrypto/test/fuzz_tests/GenerateSignatureCorpus/0ac99ac6565414c7f57a36bcf0c212327cc88ab3 new file mode 100644 index 0000000..14f9e28 Binary files /dev/null and b/oemcrypto/test/fuzz_tests/GenerateSignatureCorpus/0ac99ac6565414c7f57a36bcf0c212327cc88ab3 differ diff --git a/oemcrypto/test/fuzz_tests/GenerateSignatureCorpus/25adc5d13d39231afeb8ed3da76a18f9658c681a b/oemcrypto/test/fuzz_tests/GenerateSignatureCorpus/25adc5d13d39231afeb8ed3da76a18f9658c681a new file mode 100644 index 0000000..ed6ddac --- /dev/null +++ b/oemcrypto/test/fuzz_tests/GenerateSignatureCorpus/25adc5d13d39231afeb8ed3da76a18f9658c681a @@ -0,0 +1 @@ +(c020:0d112d7ea200; \ No newline at end of file diff --git a/oemcrypto/test/fuzz_tests/GenerateSignatureCorpus/32556c2f870258f2b18c905c3cd017d7064927d7 b/oemcrypto/test/fuzz_tests/GenerateSignatureCorpus/32556c2f870258f2b18c905c3cd017d7064927d7 new file mode 100644 index 0000000..3ff9503 --- /dev/null +++ b/oemcrypto/test/fuzz_tests/GenerateSignatureCorpus/32556c2f870258f2b18c905c3cd017d7064927d7 @@ -0,0 +1 @@ +(e2!0;u \ No newline at end of file diff --git a/oemcrypto/test/fuzz_tests/GenerateSignatureCorpus/3cec58166305818af41d10666b1538024cfbe4ec b/oemcrypto/test/fuzz_tests/GenerateSignatureCorpus/3cec58166305818af41d10666b1538024cfbe4ec new file mode 100644 index 0000000..e453ee9 Binary files /dev/null and b/oemcrypto/test/fuzz_tests/GenerateSignatureCorpus/3cec58166305818af41d10666b1538024cfbe4ec differ diff --git a/oemcrypto/test/fuzz_tests/GenerateSignatureCorpus/3d152b926d295b49d73a968d1668c0b9125dd2da b/oemcrypto/test/fuzz_tests/GenerateSignatureCorpus/3d152b926d295b49d73a968d1668c0b9125dd2da new file mode 100644 index 0000000..b2cb12e --- /dev/null +++ b/oemcrypto/test/fuzz_tests/GenerateSignatureCorpus/3d152b926d295b49d73a968d1668c0b9125dd2da @@ -0,0 +1 @@ +0a4c08001248000000020000101907d9ffde13aa95c122678053362136bdf8408f8276e4c2d87ec52b61aa1b9f646e58734930acebe899b3e464189a14a87202fb02570640bd22ef44b2d7e3912250a230a14080112100915007caa9b5931b76a3a85f046523e10011a09393837363534333231180120002a0c313838363738373430350000 diff --git a/oemcrypto/test/fuzz_tests/GenerateSignatureCorpus/4879965ec6be329dcc7697d913b2e8971a9729d8 b/oemcrypto/test/fuzz_tests/GenerateSignatureCorpus/4879965ec6be329dcc7697d913b2e8971a9729d8 new file mode 100644 index 0000000..b1596cc --- /dev/null +++ b/oemcrypto/test/fuzz_tests/GenerateSignatureCorpus/4879965ec6be329dcc7697d913b2e8971a9729d8 @@ -0,0 +1 @@ +(2dea200;u \ No newline at end of file diff --git a/oemcrypto/test/fuzz_tests/GenerateSignatureCorpus/4cd2f2c92b644ee1284cd082feb8e6773499ebe7 b/oemcrypto/test/fuzz_tests/GenerateSignatureCorpus/4cd2f2c92b644ee1284cd082feb8e6773499ebe7 new file mode 100644 index 0000000..04a910c --- /dev/null +++ b/oemcrypto/test/fuzz_tests/GenerateSignatureCorpus/4cd2f2c92b644ee1284cd082feb8e6773499ebe7 @@ -0,0 +1 @@ +0a4c020:0d1190d79fef02570640bd22ef44b2d7e3912250a200 diff --git a/oemcrypto/test/fuzz_tests/GenerateSignatureCorpus/4d39a8df58267539e4db62ef45bda5d9573b00a4 b/oemcrypto/test/fuzz_tests/GenerateSignatureCorpus/4d39a8df58267539e4db62ef45bda5d9573b00a4 new file mode 100644 index 0000000..2a27021 --- /dev/null +++ b/oemcrypto/test/fuzz_tests/GenerateSignatureCorpus/4d39a8df58267539e4db62ef45bda5d9573b00a4 @@ -0,0 +1 @@ +e2!0;u \ No newline at end of file diff --git a/oemcrypto/test/fuzz_tests/GenerateSignatureCorpus/58e6b3a414a1e090dfc6029add0f3555ccba127f b/oemcrypto/test/fuzz_tests/GenerateSignatureCorpus/58e6b3a414a1e090dfc6029add0f3555ccba127f new file mode 100644 index 0000000..9cbe6ea --- /dev/null +++ b/oemcrypto/test/fuzz_tests/GenerateSignatureCorpus/58e6b3a414a1e090dfc6029add0f3555ccba127f @@ -0,0 +1 @@ +e \ No newline at end of file diff --git a/oemcrypto/test/fuzz_tests/GenerateSignatureCorpus/655668f52bfd904b9f658280d3f144491f1d2a36 b/oemcrypto/test/fuzz_tests/GenerateSignatureCorpus/655668f52bfd904b9f658280d3f144491f1d2a36 new file mode 100644 index 0000000..feb5341 --- /dev/null +++ b/oemcrypto/test/fuzz_tests/GenerateSignatureCorpus/655668f52bfd904b9f658280d3f144491f1d2a36 @@ -0,0 +1 @@ +(ea200;u \ No newline at end of file diff --git a/oemcrypto/test/fuzz_tests/GenerateSignatureCorpus/6923a33a0bb5c0694734b3063ecb212aa7873f5d b/oemcrypto/test/fuzz_tests/GenerateSignatureCorpus/6923a33a0bb5c0694734b3063ecb212aa7873f5d new file mode 100644 index 0000000..7ac6fcd --- /dev/null +++ b/oemcrypto/test/fuzz_tests/GenerateSignatureCorpus/6923a33a0bb5c0694734b3063ecb212aa7873f5d @@ -0,0 +1 @@ +0a(c020:0d112d7ea200; \ No newline at end of file diff --git a/oemcrypto/test/fuzz_tests/GenerateSignatureCorpus/71e064e7c3959c15c4b39d22e836fbfdc6b046b5 b/oemcrypto/test/fuzz_tests/GenerateSignatureCorpus/71e064e7c3959c15c4b39d22e836fbfdc6b046b5 new file mode 100644 index 0000000..e91578d --- /dev/null +++ b/oemcrypto/test/fuzz_tests/GenerateSignatureCorpus/71e064e7c3959c15c4b39d22e836fbfdc6b046b5 @@ -0,0 +1 @@ +0a4c000000200:0101907d9ffde02570640bd22ef44b2d7e3912250a230a1407363534333231180120002a0c313838363738373430350000 diff --git a/oemcrypto/test/fuzz_tests/GenerateSignatureCorpus/8edb36d75f26dc46aae4520b02deea1a645cfbc3 b/oemcrypto/test/fuzz_tests/GenerateSignatureCorpus/8edb36d75f26dc46aae4520b02deea1a645cfbc3 new file mode 100644 index 0000000..a4f7ea5 Binary files /dev/null and b/oemcrypto/test/fuzz_tests/GenerateSignatureCorpus/8edb36d75f26dc46aae4520b02deea1a645cfbc3 differ diff --git a/oemcrypto/test/fuzz_tests/GenerateSignatureCorpus/a121ddeb222a383990a85dfd75c93e3db6630a41 b/oemcrypto/test/fuzz_tests/GenerateSignatureCorpus/a121ddeb222a383990a85dfd75c93e3db6630a41 new file mode 100644 index 0000000..679be98 Binary files /dev/null and b/oemcrypto/test/fuzz_tests/GenerateSignatureCorpus/a121ddeb222a383990a85dfd75c93e3db6630a41 differ diff --git a/oemcrypto/test/fuzz_tests/GenerateSignatureCorpus/ad95e413da295fa777257154ed40dfcd8e32ba2b b/oemcrypto/test/fuzz_tests/GenerateSignatureCorpus/ad95e413da295fa777257154ed40dfcd8e32ba2b new file mode 100644 index 0000000..5360871 --- /dev/null +++ b/oemcrypto/test/fuzz_tests/GenerateSignatureCorpus/ad95e413da295fa777257154ed40dfcd8e32ba2b @@ -0,0 +1 @@ +0a4c000000220:01019dd79fef02570640bd22ef44b2d7e3912250a200 diff --git a/oemcrypto/test/fuzz_tests/GenerateSignatureCorpus/b65818237816f961d1138ef352d0b905d3eb9330 b/oemcrypto/test/fuzz_tests/GenerateSignatureCorpus/b65818237816f961d1138ef352d0b905d3eb9330 new file mode 100644 index 0000000..20673e1 Binary files /dev/null and b/oemcrypto/test/fuzz_tests/GenerateSignatureCorpus/b65818237816f961d1138ef352d0b905d3eb9330 differ diff --git a/oemcrypto/test/fuzz_tests/GenerateSignatureCorpus/bcadfd6aacc62927bdb3e0a9f04b9aa11c192b6d b/oemcrypto/test/fuzz_tests/GenerateSignatureCorpus/bcadfd6aacc62927bdb3e0a9f04b9aa11c192b6d new file mode 100644 index 0000000..8d6bcfc --- /dev/null +++ b/oemcrypto/test/fuzz_tests/GenerateSignatureCorpus/bcadfd6aacc62927bdb3e0a9f04b9aa11c192b6d @@ -0,0 +1 @@ +e; \ No newline at end of file diff --git a/oemcrypto/test/fuzz_tests/GenerateSignatureCorpus/c6f546c378e78c5a74b45ce792c88f925f34000f b/oemcrypto/test/fuzz_tests/GenerateSignatureCorpus/c6f546c378e78c5a74b45ce792c88f925f34000f new file mode 100644 index 0000000..39ca47b --- /dev/null +++ b/oemcrypto/test/fuzz_tests/GenerateSignatureCorpus/c6f546c378e78c5a74b45ce792c88f925f34000f @@ -0,0 +1 @@ +0a4c000000200:010197d9ffde02570640bd22ef44b2d7e3912250a230a1407363534333231180120002a0c313838363738373430350000 diff --git a/oemcrypto/test/fuzz_tests/GenerateSignatureCorpus/d6b1a032205b2b9ddeced35d07a9d7c7f27bbef2 b/oemcrypto/test/fuzz_tests/GenerateSignatureCorpus/d6b1a032205b2b9ddeced35d07a9d7c7f27bbef2 new file mode 100644 index 0000000..f6d9016 --- /dev/null +++ b/oemcrypto/test/fuzz_tests/GenerateSignatureCorpus/d6b1a032205b2b9ddeced35d07a9d7c7f27bbef2 @@ -0,0 +1 @@ +0a4c00000020000101907d9ffde02570640bd22ef44b2d7e3912250a230a14080112100915007caa9b5931b76a3a85f046523e10011a09393837363534333231180120002a0c313838363738373430350000 diff --git a/oemcrypto/test/fuzz_tests/GenerateSignatureCorpus/d7d4218b6a3c59f80d1fe0ece07ca13e5a2c209c b/oemcrypto/test/fuzz_tests/GenerateSignatureCorpus/d7d4218b6a3c59f80d1fe0ece07ca13e5a2c209c new file mode 100644 index 0000000..ad45249 --- /dev/null +++ b/oemcrypto/test/fuzz_tests/GenerateSignatureCorpus/d7d4218b6a3c59f80d1fe0ece07ca13e5a2c209c @@ -0,0 +1 @@ +0a4c020:0d112d7e3912250a200; \ No newline at end of file diff --git a/oemcrypto/test/fuzz_tests/GenerateSignatureCorpus/d992e60874495b187bc157e0f24d4fd8ad957094 b/oemcrypto/test/fuzz_tests/GenerateSignatureCorpus/d992e60874495b187bc157e0f24d4fd8ad957094 new file mode 100644 index 0000000..7cea4d6 --- /dev/null +++ b/oemcrypto/test/fuzz_tests/GenerateSignatureCorpus/d992e60874495b187bc157e0f24d4fd8ad957094 @@ -0,0 +1 @@ +0a4c08001248000000020000101907d9ffde02570640bd22ef44b2d7e3912250a230a14080112100915007caa9b5931b76a3a85f046523e10011a09393837363534333231180120002a0c313838363738373430350000 diff --git a/oemcrypto/test/fuzz_tests/GenerateSignatureCorpus/e7064f0b80f61dbc65915311032d27baa569ae2a b/oemcrypto/test/fuzz_tests/GenerateSignatureCorpus/e7064f0b80f61dbc65915311032d27baa569ae2a new file mode 100644 index 0000000..e8a0f87 --- /dev/null +++ b/oemcrypto/test/fuzz_tests/GenerateSignatureCorpus/e7064f0b80f61dbc65915311032d27baa569ae2a @@ -0,0 +1 @@ +) \ No newline at end of file diff --git a/oemcrypto/test/fuzz_tests/GenerateSignatureCorpus/ead485157c8b58596faa57d6c0818e6c5b652a9e b/oemcrypto/test/fuzz_tests/GenerateSignatureCorpus/ead485157c8b58596faa57d6c0818e6c5b652a9e new file mode 100644 index 0000000..44f8acb --- /dev/null +++ b/oemcrypto/test/fuzz_tests/GenerateSignatureCorpus/ead485157c8b58596faa57d6c0818e6c5b652a9e @@ -0,0 +1 @@ +e2; \ No newline at end of file diff --git a/oemcrypto/test/fuzz_tests/GenerateSignatureCorpus/ecca89819d61866b1f41e756edc08510f8f70747 b/oemcrypto/test/fuzz_tests/GenerateSignatureCorpus/ecca89819d61866b1f41e756edc08510f8f70747 new file mode 100644 index 0000000..508d436 Binary files /dev/null and b/oemcrypto/test/fuzz_tests/GenerateSignatureCorpus/ecca89819d61866b1f41e756edc08510f8f70747 differ diff --git a/oemcrypto/test/fuzz_tests/GenerateSignatureCorpus/example.txt b/oemcrypto/test/fuzz_tests/GenerateSignatureCorpus/example.txt new file mode 100644 index 0000000..be5ddd8 --- /dev/null +++ b/oemcrypto/test/fuzz_tests/GenerateSignatureCorpus/example.txt @@ -0,0 +1 @@ +0a4c08001248000000020000101907d9ffde13aa95c122678053362136bdf8408f8276e4c2d87ec52b61aa1b9f646e58734930acebe899b3e464189a14a87202fb02574e70640bd22ef44b2d7e3912250a230a14080112100915007caa9b5931b76a3a85f046523e10011a09393837363534333231180120002a0c313838363738373430350000 diff --git a/oemcrypto/test/fuzz_tests/README.md b/oemcrypto/test/fuzz_tests/README.md new file mode 100644 index 0000000..cd3f5b5 --- /dev/null +++ b/oemcrypto/test/fuzz_tests/README.md @@ -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/ \ + + ``` + +## 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/ \ + + ``` + +* 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) { + + 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 <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. \ No newline at end of file diff --git a/oemcrypto/test/fuzz_tests/oemcrypto_decrypt_cenc_fuzz.cc b/oemcrypto/test/fuzz_tests/oemcrypto_decrypt_cenc_fuzz.cc new file mode 100644 index 0000000..94d3136 --- /dev/null +++ b/oemcrypto/test/fuzz_tests/oemcrypto_decrypt_cenc_fuzz.cc @@ -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& 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 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 sample_descriptions(samples_length); + // Create array to maintain secure_fd buffer values for secure buffers. + vector 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 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 diff --git a/oemcrypto/test/fuzz_tests/oemcrypto_fuzz_helper.cc b/oemcrypto/test/fuzz_tests/oemcrypto_fuzz_helper.cc new file mode 100644 index 0000000..3596d61 --- /dev/null +++ b/oemcrypto/test/fuzz_tests/oemcrypto_fuzz_helper.cc @@ -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> SplitInput(const uint8_t* data, size_t size) { + std::vector> result; + auto current_pos = data; + auto end = data + size; + // Using memmem to find separator + while (const uint8_t* pos = reinterpret_cast( + 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 diff --git a/oemcrypto/test/fuzz_tests/oemcrypto_fuzz_helper.h b/oemcrypto/test/fuzz_tests/oemcrypto_fuzz_helper.h new file mode 100644 index 0000000..7dc707e --- /dev/null +++ b/oemcrypto/test/fuzz_tests/oemcrypto_fuzz_helper.h @@ -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 + +#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 +void ConvertDataToValidEnum(T max_enum_value, T* t) { + FuzzedDataProvider fuzzed_enum_data(reinterpret_cast(t), sizeof(T)); + *t = static_cast(fuzzed_enum_data.ConsumeIntegralInRange( + 0, static_cast(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> SplitInput(const uint8_t* data, size_t size); +} // namespace wvoec + +#endif // OEMCRYPTO_FUZZ_HELPER_H_ diff --git a/oemcrypto/test/fuzz_tests/oemcrypto_fuzz_structs.h b/oemcrypto/test/fuzz_tests/oemcrypto_fuzz_structs.h new file mode 100644 index 0000000..52fb400 --- /dev/null +++ b/oemcrypto/test/fuzz_tests/oemcrypto_fuzz_structs.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_ \ No newline at end of file diff --git a/oemcrypto/test/fuzz_tests/oemcrypto_fuzztests.gyp b/oemcrypto/test/fuzz_tests/oemcrypto_fuzztests.gyp new file mode 100644 index 0000000..8d818be --- /dev/null +++ b/oemcrypto/test/fuzz_tests/oemcrypto_fuzztests.gyp @@ -0,0 +1,64 @@ +# Copyright 2018 Google LLC. All Rights Reserved. This file and proprietary +#source code may only be used and distributed under the Widevine Master License +#Agreement. +# +# Builds under the CDM ./build.py (target platform) build system +# Refer to the distribution package's README for details. +{ + 'target_defaults': { + 'type': 'executable', + 'includes': [ + 'oemcrypto_fuzztests.gypi', + ], + }, + 'targets': [ + { + 'target_name': 'oemcrypto_load_license_fuzz', + 'sources': [ + 'oemcrypto_load_license_fuzz.cc', + ], + }, + { + 'target_name': 'oemcrypto_load_provisioning_fuzz', + 'sources': [ + 'oemcrypto_load_provisioning_fuzz.cc', + ], + }, + { + 'target_name': 'oemcrypto_load_renewal_fuzz', + 'sources': [ + 'oemcrypto_load_renewal_fuzz.cc', + ], + }, + { + 'target_name': 'oemcrypto_license_request_fuzz', + 'sources': [ + 'oemcrypto_license_request_fuzz.cc', + ], + }, + { + 'target_name': 'oemcrypto_provisioning_request_fuzz', + 'sources': [ + 'oemcrypto_provisioning_request_fuzz.cc', + ], + }, + { + 'target_name': 'oemcrypto_renewal_request_fuzz', + 'sources': [ + 'oemcrypto_renewal_request_fuzz.cc', + ], + }, + { + 'target_name': 'oemcrypto_decrypt_cenc_fuzz', + 'sources': [ + 'oemcrypto_decrypt_cenc_fuzz.cc', + ], + }, + { + 'target_name': 'oemcrypto_load_entitled_content_keys_fuzz', + 'sources': [ + 'oemcrypto_load_entitled_content_keys_fuzz.cc', + ], + }, + ], +} diff --git a/oemcrypto/test/fuzz_tests/oemcrypto_fuzztests.gypi b/oemcrypto/test/fuzz_tests/oemcrypto_fuzztests.gypi new file mode 100644 index 0000000..63293f4 --- /dev/null +++ b/oemcrypto/test/fuzz_tests/oemcrypto_fuzztests.gypi @@ -0,0 +1,88 @@ +# Copyright 2018 Google LLC. All Rights Reserved. This file and proprietary +#source code may only be used and distributed under the Widevine Master License +#Agreement. + +{ + 'variables': { + 'boringssl_libcrypto_path%': '../../../third_party/boringssl/boringssl.gyp:crypto', + 'boringssl_libssl_path%': '../../../third_party/boringssl/boringssl.gyp:ssl', + 'oemcrypto_dir': '../..', + 'platform_specific_dir': '../../../linux/src', + 'privacy_crypto_impl%': 'boringssl', + # Flag used to generate source based code coverage reports. + 'generate_code_coverage_report%': 'false', + 'util_dir': '../../../util', + }, + 'sources': [ + '../../odk/src/core_message_deserialize.cpp', + '../../odk/src/core_message_serialize.cpp', + '../oec_device_features.cpp', + '../oec_key_deriver.cpp', + '../oemcrypto_corpus_generator_helper.cpp', + '../oec_session_util.cpp', + '../oemcrypto_corpus_generator_helper.cpp', + 'oemcrypto_fuzz_helper.cc', + '../oemcrypto_session_tests_helper.cpp', + '<(platform_specific_dir)/file_store.cpp', + '<(platform_specific_dir)/log.cpp', + '<(util_dir)/src/platform.cpp', + '<(util_dir)/src/rw_lock.cpp', + '<(util_dir)/src/string_conversions.cpp', + '<(util_dir)/test/test_sleep.cpp', + '<(util_dir)/test/test_clock.cpp', + ], + 'include_dirs': [ + '../../../third_party/fuzz', + '<(util_dir)/include', + '<(util_dir)/test', + '<(oemcrypto_dir)/include', + '<(oemcrypto_dir)/ref/src', + '<(oemcrypto_dir)/test', + '<(oemcrypto_dir)/test/fuzz_tests', + '<(oemcrypto_dir)/odk/include', + '<(oemcrypto_dir)/odk/src', + ], + 'includes': [ + '../../../util/libssl_dependency.gypi', + '../../ref/oec_ref.gypi', + ], + 'dependencies': [ + '../../../third_party/gmock.gyp:gtest', + '../../../third_party/gmock.gyp:gmock', + ], + 'defines': [ + 'OEMCRYPTO_FUZZ_TESTS', + ], + 'conditions': [ + ['generate_code_coverage_report=="false"', { + # Include flags to build fuzzer binaries for cluster fuzz. + 'cflags_cc': [ + '-std=c++11', + '-fsanitize=fuzzer,address,undefined', + # Need -g flag to include source line numbers in error stack trace. + '-g', + ], + 'ldflags': [ + '-fPIC', + '-fsanitize=fuzzer,address,undefined', + ], + }], + ['generate_code_coverage_report=="true"', { + # Include flags to build fuzzer binaries to generate source based code coverage reports. + 'cflags_cc': [ + '-std=c++11', + '-fprofile-instr-generate', + '-fcoverage-mapping', + ], + 'ldflags': [ + '-fPIC', + '-fsanitize=fuzzer,address,undefined', + '-fprofile-instr-generate', + '-fcoverage-mapping', + ], + }], + ], # conditions + 'libraries': [ + '-lpthread', + ], +} diff --git a/oemcrypto/test/fuzz_tests/oemcrypto_generate_signature.cc b/oemcrypto/test/fuzz_tests/oemcrypto_generate_signature.cc new file mode 100644 index 0000000..202351e --- /dev/null +++ b/oemcrypto/test/fuzz_tests/oemcrypto_generate_signature.cc @@ -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 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; +} diff --git a/oemcrypto/test/fuzz_tests/oemcrypto_license_request_fuzz.cc b/oemcrypto/test/fuzz_tests/oemcrypto_license_request_fuzz.cc new file mode 100644 index 0000000..d100dde --- /dev/null +++ b/oemcrypto/test/fuzz_tests/oemcrypto_license_request_fuzz.cc @@ -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 \ No newline at end of file diff --git a/oemcrypto/test/fuzz_tests/oemcrypto_load_entitled_content_keys_fuzz.cc b/oemcrypto/test/fuzz_tests/oemcrypto_load_entitled_content_keys_fuzz.cc new file mode 100644 index 0000000..892d6e9 --- /dev/null +++ b/oemcrypto/test/fuzz_tests/oemcrypto_load_entitled_content_keys_fuzz.cc @@ -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 diff --git a/oemcrypto/test/fuzz_tests/oemcrypto_load_license_fuzz.cc b/oemcrypto/test/fuzz_tests/oemcrypto_load_license_fuzz.cc new file mode 100644 index 0000000..e125ae0 --- /dev/null +++ b/oemcrypto/test/fuzz_tests/oemcrypto_load_license_fuzz.cc @@ -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 diff --git a/oemcrypto/test/fuzz_tests/oemcrypto_load_provisioning_fuzz.cc b/oemcrypto/test/fuzz_tests/oemcrypto_load_provisioning_fuzz.cc new file mode 100644 index 0000000..739f79f --- /dev/null +++ b/oemcrypto/test/fuzz_tests/oemcrypto_load_provisioning_fuzz.cc @@ -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 diff --git a/oemcrypto/test/fuzz_tests/oemcrypto_load_renewal_fuzz.cc b/oemcrypto/test/fuzz_tests/oemcrypto_load_renewal_fuzz.cc new file mode 100644 index 0000000..f521b47 --- /dev/null +++ b/oemcrypto/test/fuzz_tests/oemcrypto_load_renewal_fuzz.cc @@ -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 \ No newline at end of file diff --git a/oemcrypto/test/fuzz_tests/oemcrypto_provisioning_request_fuzz.cc b/oemcrypto/test/fuzz_tests/oemcrypto_provisioning_request_fuzz.cc new file mode 100644 index 0000000..1cda2ab --- /dev/null +++ b/oemcrypto/test/fuzz_tests/oemcrypto_provisioning_request_fuzz.cc @@ -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 \ No newline at end of file diff --git a/oemcrypto/test/fuzz_tests/oemcrypto_renewal_request_fuzz.cc b/oemcrypto/test/fuzz_tests/oemcrypto_renewal_request_fuzz.cc new file mode 100644 index 0000000..67ecb0a --- /dev/null +++ b/oemcrypto/test/fuzz_tests/oemcrypto_renewal_request_fuzz.cc @@ -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 \ No newline at end of file diff --git a/oemcrypto/test/fuzz_tests/sample_test.cc b/oemcrypto/test/fuzz_tests/sample_test.cc new file mode 100644 index 0000000..6890393 --- /dev/null +++ b/oemcrypto/test/fuzz_tests/sample_test.cc @@ -0,0 +1,10 @@ +#include +#include + +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; +} diff --git a/oemcrypto/test/oec_decrypt_fallback_chain.cpp b/oemcrypto/test/oec_decrypt_fallback_chain.cpp index e8d8247..7c589c5 100644 --- a/oemcrypto/test/oec_decrypt_fallback_chain.cpp +++ b/oemcrypto/test/oec_decrypt_fallback_chain.cpp @@ -7,6 +7,7 @@ #include #include +#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(&cipher_mode), + sizeof(OEMCryptoCipherMode)); + + // Pattern. + AppendToFile(file_name, reinterpret_cast(pattern), + sizeof(OEMCrypto_CENCEncryptPatternDesc)); + + // Sample data for all samples. + for (size_t i = 0; i < samples_length; i++) { + AppendToFile(file_name, + reinterpret_cast(&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(&samples_description[i].subsamples[j]), + sizeof(OEMCrypto_SubSampleDescription)); + } + } +} + } // namespace wvoec diff --git a/oemcrypto/test/oec_decrypt_fallback_chain.h b/oemcrypto/test/oec_decrypt_fallback_chain.h index 0e1512b..2379daf 100644 --- a/oemcrypto/test/oec_decrypt_fallback_chain.h +++ b/oemcrypto/test/oec_decrypt_fallback_chain.h @@ -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_ diff --git a/oemcrypto/test/oec_device_features.cpp b/oemcrypto/test/oec_device_features.cpp index de1fcf4..9649ce6 100644 --- a/oemcrypto/test/oec_device_features.cpp +++ b/oemcrypto/test/oec_device_features.cpp @@ -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; @@ -129,6 +129,7 @@ std::string DeviceFeatures::RestrictFilter(const std::string& initial_filter) { if (!generic_crypto) FilterOut(&filter, "*GenericCrypto*"); if (!cast_receiver) FilterOut(&filter, "*CastReceiver*"); if (!usage_table) FilterOut(&filter, "*UsageTable*"); + if (!usage_table) FilterOut(&filter, "*BadRange_pst*"); if (derive_key_method == NO_METHOD) FilterOut(&filter, "*SessionTest*"); if (provisioning_method != OEMCrypto_OEMCertificate) FilterOut(&filter, "*Prov30*"); diff --git a/oemcrypto/test/oec_key_deriver.cpp b/oemcrypto/test/oec_key_deriver.cpp index 34fb0fe..d2bff25 100644 --- a/oemcrypto/test/oec_key_deriver.cpp +++ b/oemcrypto/test/oec_key_deriver.cpp @@ -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(padding), padding); diff --git a/oemcrypto/test/oec_session_util.cpp b/oemcrypto/test/oec_session_util.cpp index 26a98f5..5dd0299 100644 --- a/oemcrypto/test/oec_session_util.cpp +++ b/oemcrypto/test/oec_session_util.cpp @@ -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" @@ -49,12 +52,7 @@ void PrintTo(const vector& value, ostream* os) { } // namespace std namespace { -void DeleteX509Stack(STACK_OF(X509)* stack) { - sk_X509_pop_free(stack, X509_free); -} - constexpr size_t kTestSubsampleSectionSize = 256; - } // namespace namespace wvoec { @@ -167,6 +165,10 @@ void RoundTrip(gen_signature_length, + core_message_length, data); + } vector gen_signature(gen_signature_length); ASSERT_EQ(PrepAndSignRequest(session()->session_id(), data.data(), @@ -182,6 +184,27 @@ void RoundTrip +void RoundTrip::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 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 OEMCrypto_Substring RoundTripnonce(); +} + 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(&core_response_), + sizeof(ODK_ParsedProvisioning)); + AppendToFile(file_name, reinterpret_cast(&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; @@ -477,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 |= @@ -545,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(&response_data_.keys[i].control), - reinterpret_cast(&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(&response_data_.keys[i].control), + reinterpret_cast(&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); @@ -593,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(&core_response_), + sizeof(ODK_ParsedLicense)); + AppendToFile(file_name, reinterpret_cast(&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 @@ -762,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(entitled_key_data_), + sizeof(entitled_key_data_)); + AppendSeparator(file_name); + AppendToFile(file_name, reinterpret_cast(entitled_key_array_), + num_keys_); + } ASSERT_EQ(expected_sts, OEMCrypto_LoadEntitledContentKeys( license_messages_->session()->session_id(), @@ -911,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(&renewal_response_fuzz), + sizeof(renewal_response_fuzz)); + AppendToFile(file_name, + reinterpret_cast(&encrypted_response_data_), + sizeof(encrypted_response_data_)); + } if (license_messages_->api_version() < kCoreMessagesAPI) { return OEMCrypto_RefreshKeys( session->session_id(), encrypted_response_.data(), @@ -1134,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) { @@ -1158,22 +1346,25 @@ void Session::LoadOEMCert(bool verify_cert) { public_cert.data(), &public_cert_length)); ASSERT_EQ(OEMCrypto_SUCCESS, OEMCrypto_LoadOEMPrivateKey(session_id())); - // Load the certificate chain into a BoringSSL X509 Stack - const boringssl_ptr x509_stack( - sk_X509_new_null()); - ASSERT_TRUE(x509_stack.NotNull()) << "Unable to allocate X509 stack."; + // The cert is a PKCS7 signed data type. First, parse it into an OpenSSL + // structure and find the certificate list. + // + // We must make defensive copies of public_cert's properties because of how + // d2i_PKCS7() works. + const unsigned char* cert_data = + reinterpret_cast(public_cert.data()); + long cert_size = static_cast(public_cert.size()); + boringssl_ptr pkcs7( + d2i_PKCS7(NULL, &cert_data, cert_size)); + ASSERT_TRUE(pkcs7.NotNull()) << "Error parsing PKCS7 message"; + ASSERT_TRUE(PKCS7_type_is_signed(pkcs7.get())) + << "Unexpected PKCS7 message type"; - CBS pkcs7; - CBS_init(&pkcs7, public_cert.data(), public_cert.size()); - if (!PKCS7_get_certificates(x509_stack.get(), &pkcs7)) { - dump_boringssl_error(); - FAIL() << "Unable to deserialize certificate chain."; - } - - STACK_OF(X509)* certs = x509_stack.get(); + STACK_OF(X509)* certs = pkcs7->d.sign->cert; // Load the public cert's key into public_rsa_ and verify, if requested - for (size_t i = 0; certs && i < sk_X509_num(certs); i++) { + for (size_t i = 0; certs && i < static_cast(sk_X509_num(certs)); + ++i) { X509* x509_cert = sk_X509_value(certs, i); boringssl_ptr pubkey(X509_get_pubkey(x509_cert)); ASSERT_TRUE(pubkey.NotNull()); @@ -1202,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( @@ -1250,19 +1441,17 @@ bool Session::VerifyPSSSignature(EVP_PKEY* pkey, const uint8_t* message, size_t message_length, const uint8_t* signature, size_t signature_length) { - EVP_MD_CTX md_ctx_struct; - EVP_MD_CTX* md_ctx = &md_ctx_struct; - EVP_MD_CTX_init(md_ctx); + boringssl_ptr md_ctx(EVP_MD_CTX_new()); EVP_PKEY_CTX* pkey_ctx = nullptr; - if (EVP_DigestVerifyInit(md_ctx, &pkey_ctx, EVP_sha1(), + if (EVP_DigestVerifyInit(md_ctx.get(), &pkey_ctx, EVP_sha1(), nullptr /* no ENGINE */, pkey) != 1) { LOGE("EVP_DigestVerifyInit failed in VerifyPSSSignature"); goto err; } if (EVP_PKEY_CTX_set_signature_md(pkey_ctx, - const_cast(EVP_sha1())) != 1) { + const_cast(EVP_sha1())) != 1) { LOGE("EVP_PKEY_CTX_set_signature_md failed in VerifyPSSSignature"); goto err; } @@ -1277,12 +1466,12 @@ bool Session::VerifyPSSSignature(EVP_PKEY* pkey, const uint8_t* message, goto err; } - if (EVP_DigestVerifyUpdate(md_ctx, message, message_length) != 1) { + if (EVP_DigestVerifyUpdate(md_ctx.get(), message, message_length) != 1) { LOGE("EVP_DigestVerifyUpdate failed in VerifyPSSSignature"); goto err; } - if (EVP_DigestVerifyFinal(md_ctx, const_cast(signature), + if (EVP_DigestVerifyFinal(md_ctx.get(), const_cast(signature), signature_length) != 1) { LOGE( "EVP_DigestVerifyFinal failed in VerifyPSSSignature. (Probably a bad " @@ -1290,12 +1479,10 @@ bool Session::VerifyPSSSignature(EVP_PKEY* pkey, const uint8_t* message, goto err; } - EVP_MD_CTX_cleanup(md_ctx); return true; err: dump_boringssl_error(); - EVP_MD_CTX_cleanup(md_ctx); return false; } @@ -1313,18 +1500,17 @@ void Session::VerifyRSASignature(const vector& message, boringssl_ptr 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 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); @@ -1390,16 +1576,14 @@ void Session::UpdateUsageEntry(std::vector* header_buffer) { void Session::LoadUsageEntry(uint32_t index, const vector& 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* 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)); @@ -1411,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_; @@ -1448,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(computed.pst()); + char* pst_ptr = reinterpret_cast(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, @@ -1467,8 +1649,8 @@ void Session::VerifyPST(const Test_PST_Report& expected) { } std::vector 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, @@ -1490,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(in); + for (size_t i = 0; i < sizeof(bool); i++) { + if (buf[i]) { + return true; + } + } + return false; +} + +template +void WriteRequestApiCorpus(size_t signature_length, size_t core_message_length, + vector& data) { + std::string file_name; + if (std::is_same::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::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(&signature_length), + sizeof(signature_length)); + AppendToFile(file_name, reinterpret_cast(&core_message_length), + sizeof(core_message_length)); + AppendToFile(file_name, reinterpret_cast(data.data()), + data.size()); +} } // namespace wvoec diff --git a/oemcrypto/test/oec_session_util.h b/oemcrypto/test/oec_session_util.h index 54ee318..a3773fd 100644 --- a/oemcrypto/test/oec_session_util.h +++ b/oemcrypto/test/oec_session_util.h @@ -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& 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& 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* 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 +void WriteRequestApiCorpus(size_t signature_length, size_t core_message_length, + vector& data); } // namespace wvoec #endif // CDM_OEC_SESSION_UTIL_H_ diff --git a/oemcrypto/test/oemcrypto_corpus_generator_helper.cpp b/oemcrypto/test/oemcrypto_corpus_generator_helper.cpp new file mode 100644 index 0000000..0478de6 --- /dev/null +++ b/oemcrypto/test/oemcrypto_corpus_generator_helper.cpp @@ -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 +#include + +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(&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 diff --git a/oemcrypto/test/oemcrypto_corpus_generator_helper.h b/oemcrypto/test/oemcrypto_corpus_generator_helper.h new file mode 100644 index 0000000..9086a0c --- /dev/null +++ b/oemcrypto/test/oemcrypto_corpus_generator_helper.h @@ -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 +#include +#include + +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_ diff --git a/oemcrypto/test/oemcrypto_test.cpp b/oemcrypto/test/oemcrypto_test.cpp index b04eba2..3210aca 100644 --- a/oemcrypto/test/oemcrypto_test.cpp +++ b/oemcrypto/test/oemcrypto_test.cpp @@ -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 #include #include @@ -17,6 +38,7 @@ #include #include #include +#include #include #include #include @@ -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 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,19 +199,27 @@ 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"; + "OEMCrypto unit tests for API 16.4. Tests last updated 2020-10-07"; cout << " " << log_message << "\n"; LOGI("%s", log_message.c_str()); // If any of the following fail, then it is time to update the log message // above. EXPECT_EQ(ODK_MAJOR_VERSION, 16); - EXPECT_EQ(ODK_MINOR_VERSION, 3); + // Note on minor versions. Widevine requires version 16.3 or greater for CE + // CDM and Android devices. For CE CDM devices that do not support usage + // tables, we strongly recommend 16.4. + EXPECT_GE(ODK_MINOR_VERSION, 3); + EXPECT_LE(ODK_MINOR_VERSION, 4); EXPECT_EQ(kCurrentAPI, 16u); const char* level = OEMCrypto_SecurityLevel(); ASSERT_NE(nullptr, level); @@ -186,14 +245,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 +291,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(current), HDCPCapabilityAsString(current)); + printf(" Maximum HDCP Capability: 0x%02x = %s.\n", + static_cast(maximum), HDCPCapabilityAsString(maximum)); } TEST_F(OEMCryptoClientTest, CheckSRMCapabilityV13) { @@ -255,6 +318,14 @@ TEST_F(OEMCryptoClientTest, CheckSRMCapabilityV13) { OEMCrypto_LoadSRM(bad_srm.data(), bad_srm.size())); } +TEST_F(OEMCryptoClientTest, OEMCryptoMemoryLoadSrmForLargeSrm) { + auto oemcrypto_function = [](size_t buffer_length) { + vector srm_buffer(buffer_length); + return OEMCrypto_LoadSRM(srm_buffer.data(), srm_buffer.size()); + }; + TestHugeLengthDoesNotCrashAPI(oemcrypto_function, kCheckStatus); +} + TEST_F(OEMCryptoClientTest, CheckMaxNumberOfSessionsAPI10) { size_t sessions_count; ASSERT_EQ(OEMCrypto_SUCCESS, @@ -285,6 +356,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 buffer(buffer_length); + return OEMCrypto_SetSandbox(buffer.data(), buffer.size()); + }; + TestHugeLengthDoesNotCrashAPI(oemcrypto_function, !kCheckStatus); +} + // // Session Tests // @@ -375,6 +455,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 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()); @@ -509,6 +600,131 @@ TEST_F(OEMCryptoClientTest, ClearCopyTestLargeSubsample) { ASSERT_EQ(input_buffer, output_buffer); } +TEST_F(OEMCryptoClientTest, OEMCryptoMemoryCopyBufferForHugeBufferLengths) { + Session s; + ASSERT_NO_FATAL_FAILURE(s.open()); + vector input_buffer; + OEMCrypto_DestBufferDesc dest_buffer_descriptor; + dest_buffer_descriptor.type = OEMCrypto_BufferType_Secure; + + auto oemcrypto_function = [&s, &dest_buffer_descriptor, + &input_buffer](size_t buffer_length) { + input_buffer.resize(buffer_length); + int secure_fd; + OEMCryptoResult sts = OEMCrypto_AllocateSecureBuffer( + s.session_id(), buffer_length, &dest_buffer_descriptor, &secure_fd); + if (sts != OEMCrypto_SUCCESS) { + LOGI("Secure buffers are not supported."); + return sts; + } + + dest_buffer_descriptor.buffer.secure.handle_length = buffer_length; + OEMCryptoResult status = OEMCrypto_CopyBuffer( + s.session_id(), input_buffer.data(), buffer_length, + &dest_buffer_descriptor, + OEMCrypto_FirstSubsample | OEMCrypto_LastSubsample); + OEMCrypto_FreeSecureBuffer(s.session_id(), &dest_buffer_descriptor, + secure_fd); + return status; + }; + TestHugeLengthDoesNotCrashAPI(oemcrypto_function, kCheckStatus); +} + +TEST_F(OEMCryptoClientTest, + OEMCryptoMemoryCopyBufferDirectForHugeBufferLengths) { + Session s; + ASSERT_NO_FATAL_FAILURE(s.open()); + vector input_buffer; + OEMCrypto_DestBufferDesc dest_buffer_descriptor; + dest_buffer_descriptor.type = OEMCrypto_BufferType_Direct; + + auto oemcrypto_function = [&s, &dest_buffer_descriptor, + &input_buffer](size_t buffer_length) { + input_buffer.resize(buffer_length); + OEMCryptoResult status = OEMCrypto_CopyBuffer( + s.session_id(), input_buffer.data(), buffer_length, + &dest_buffer_descriptor, + OEMCrypto_FirstSubsample | OEMCrypto_LastSubsample); + return status; + }; + TestHugeLengthDoesNotCrashAPI(oemcrypto_function, kCheckStatus); +} + +TEST_F(OEMCryptoClientTest, OEMCryptoMemoryCopyBufferForOutOfRangeOffset) { + Session s; + ASSERT_NO_FATAL_FAILURE(s.open()); + vector input_buffer; + OEMCrypto_DestBufferDesc dest_buffer_descriptor; + dest_buffer_descriptor.type = OEMCrypto_BufferType_Secure; + + size_t buffer_length = KiB; + input_buffer.resize(buffer_length); + int secure_fd; + if (OEMCrypto_AllocateSecureBuffer(s.session_id(), buffer_length, + &dest_buffer_descriptor, + &secure_fd) != OEMCrypto_SUCCESS) { + LOGI("Secure buffers are not supported."); + return; + } + + dest_buffer_descriptor.buffer.secure.handle_length = buffer_length; + auto oemcrypto_function = [&s, &dest_buffer_descriptor, &input_buffer, + &buffer_length](size_t offset) { + dest_buffer_descriptor.buffer.secure.offset = offset; + return OEMCrypto_CopyBuffer( + s.session_id(), input_buffer.data(), buffer_length, + &dest_buffer_descriptor, + OEMCrypto_FirstSubsample | OEMCrypto_LastSubsample); + }; + TestHugeLengthDoesNotCrashAPI(oemcrypto_function, kCheckStatus); + OEMCrypto_FreeSecureBuffer(s.session_id(), &dest_buffer_descriptor, + secure_fd); +} + +TEST_F(OEMCryptoClientTest, + OEMCryptoMemoryCopyBufferForOutOfRangeHandleLength) { + Session s; + ASSERT_NO_FATAL_FAILURE(s.open()); + vector input_buffer; + OEMCrypto_DestBufferDesc dest_buffer_descriptor; + dest_buffer_descriptor.type = OEMCrypto_BufferType_Secure; + + size_t buffer_length = KiB; + input_buffer.resize(buffer_length); + int secure_fd; + if (OEMCrypto_AllocateSecureBuffer(s.session_id(), buffer_length, + &dest_buffer_descriptor, + &secure_fd) != OEMCrypto_SUCCESS) { + LOGI("Secure buffers are not supported."); + return; + } + + dest_buffer_descriptor.buffer.secure.handle_length = kHugeInputBufferLength; + ASSERT_NO_FATAL_FAILURE( + OEMCrypto_CopyBuffer(s.session_id(), input_buffer.data(), buffer_length, + &dest_buffer_descriptor, + OEMCrypto_FirstSubsample | OEMCrypto_LastSubsample)); + OEMCrypto_FreeSecureBuffer(s.session_id(), &dest_buffer_descriptor, + secure_fd); +} + +TEST_F(OEMCryptoClientTest, ClearCopyTestInvalidSubsampleFlag) { + uint8_t oemcrypto_invalid_subsample_flag = 85; + Session s; + ASSERT_NO_FATAL_FAILURE(s.open()); + size_t max_size = GetResourceValue(kMaxSubsampleSize); + vector input_buffer(max_size); + GetRandBytes(input_buffer.data(), input_buffer.size()); + vector output_buffer(max_size); + OEMCrypto_DestBufferDesc dest_buffer_descriptor; + dest_buffer_descriptor.type = OEMCrypto_BufferType_Clear; + dest_buffer_descriptor.buffer.clear.address = output_buffer.data(); + dest_buffer_descriptor.buffer.clear.address_length = output_buffer.size(); + ASSERT_NO_FATAL_FAILURE(OEMCrypto_CopyBuffer( + s.session_id(), input_buffer.data(), input_buffer.size(), + &dest_buffer_descriptor, oemcrypto_invalid_subsample_flag)); +} + TEST_F(OEMCryptoClientTest, CanLoadTestKeys) { ASSERT_NE(DeviceFeatures::NO_METHOD, global_features.derive_key_method) << "Session tests cannot run with out a test keybox or RSA cert."; @@ -532,6 +748,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 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 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 +786,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 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 +822,20 @@ TEST_F(OEMCryptoKeyboxTest, NormalGetKeyData) { sts = OEMCrypto_GetKeyData(key_data, &key_data_len); uint32_t* data = reinterpret_cast(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 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 +867,42 @@ TEST_F(OEMCryptoKeyboxTest, GenerateDerivedKeysFromKeyboxLargeBuffer) { enc_context.data(), enc_context.size())); } +TEST_F(OEMCryptoKeyboxTest, + OEMCryptoMemoryGenerateDerivedKeysForLargeMacContextLength) { + Session s; + ASSERT_NO_FATAL_FAILURE(s.open()); + vector mac_context; + vector 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 mac_context; + vector 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 +941,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 +1013,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 public_cert(public_cert_length); + return OEMCrypto_GetOEMPublicCertificate(public_cert.data(), + &public_cert_length); + }; + TestHugeLengthDoesNotCrashAPI(oemcrypto_function, !kCheckStatus); +} + // // AddKey Tests // @@ -836,6 +1159,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 +1173,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 +1187,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 +1224,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 +1691,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 +1879,58 @@ TEST_P(OEMCryptoLicenseTest, QueryKeyControl) { strlen(key_id), reinterpret_cast(&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 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 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(&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 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 +2129,11 @@ TEST_P(OEMCryptoSessionTestDecryptWithHDCP, DecryptAPI09) { INSTANTIATE_TEST_CASE_P(TestHDCP, OEMCryptoSessionTestDecryptWithHDCP, Range(1, 6)); +/// @} + +/// @addtogroup renewal +/// @{ + // // Load, Refresh Keys Test // @@ -1903,6 +2319,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 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(&hash), sizeof(hash))); +} + // // Decrypt Tests -- these test Decrypt CTR mode only. // @@ -2582,18 +3022,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 +3515,50 @@ TEST_F(OEMCryptoUsesCertificate, GenerateDerivedKeysLargeBuffer) { enc_context.data(), enc_context.size())); } +TEST_F(OEMCryptoUsesCertificate, + OEMCryptoMemoryDeriveKeysFromSessionKeyForHugeMacContext) { + vector session_key; + vector 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 mac_context; + vector 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 session_key; + vector 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 mac_context; + vector 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: @@ -3190,19 +3662,82 @@ class OEMCryptoLoadsCertificateAlternates : public OEMCryptoLoadsCertificate { ASSERT_NO_FATAL_FAILURE(provisioning_messages.CreateDefaultResponse()); ASSERT_NO_FATAL_FAILURE(provisioning_messages.EncryptAndSignResponse()); OEMCryptoResult sts = provisioning_messages.LoadResponse(); - encoded_rsa_key_ = provisioning_messages.encoded_rsa_key(); - wrapped_rsa_key_ = provisioning_messages.wrapped_rsa_key(); - key_loaded_ = (wrapped_rsa_key_.size() > 0); + key_loaded_ = (OEMCrypto_SUCCESS == sts); + if (key_loaded_) { + encoded_rsa_key_ = provisioning_messages.encoded_rsa_key(); + wrapped_rsa_key_ = provisioning_messages.wrapped_rsa_key(); + EXPECT_GT(wrapped_rsa_key_.size(), 0u); + EXPECT_EQ(nullptr, find(wrapped_rsa_key_, encoded_rsa_key_)); + } if (force) { EXPECT_EQ(OEMCrypto_SUCCESS, sts); - EXPECT_EQ(nullptr, find(wrapped_rsa_key_, encoded_rsa_key_)); - ASSERT_TRUE(key_loaded_); } } bool key_loaded_; }; +TEST_F(OEMCryptoLoadsCertificateAlternates, + OEMCryptoMemoryGenerateRSASignatureForLargeBuffer) { + OEMCryptoResult sts; + LoadWithAllowedSchemes(kSign_PKCS1_Block1, false); + // If the device is a cast receiver, then this scheme is required. + if (global_features.cast_receiver) ASSERT_TRUE(key_loaded_); + if (key_loaded_) { + Session s; + ASSERT_NO_FATAL_FAILURE(s.open()); + sts = OEMCrypto_LoadDRMPrivateKey(s.session_id(), OEMCrypto_RSA_Private_Key, + wrapped_rsa_key_.data(), + wrapped_rsa_key_.size()); + ASSERT_EQ(OEMCrypto_SUCCESS, sts); + + vector message_buffer(10); + size_t signature_length = 0; + sts = OEMCrypto_GenerateRSASignature(s.session_id(), message_buffer.data(), + message_buffer.size(), nullptr, + &signature_length, kSign_PKCS1_Block1); + ASSERT_EQ(OEMCrypto_ERROR_SHORT_BUFFER, sts); + ASSERT_NE(static_cast(0), signature_length); + vector signature(signature_length); + + auto oemcrypto_function = [&](size_t buffer_length) { + message_buffer.resize(buffer_length); + return OEMCrypto_GenerateRSASignature( + s.session_id(), message_buffer.data(), message_buffer.size(), + signature.data(), &signature_length, kSign_PKCS1_Block1); + }; + TestHugeLengthDoesNotCrashAPI(oemcrypto_function, kCheckStatus); + s.close(); + } +} + +TEST_F(OEMCryptoLoadsCertificateAlternates, + OEMCryptoMemoryGenerateRSASignatureForLargeSignatureLength) { + OEMCryptoResult sts; + LoadWithAllowedSchemes(kSign_PKCS1_Block1, false); + // If the device is a cast receiver, then this scheme is required. + if (global_features.cast_receiver) ASSERT_TRUE(key_loaded_); + if (key_loaded_) { + Session s; + ASSERT_NO_FATAL_FAILURE(s.open()); + sts = OEMCrypto_LoadDRMPrivateKey(s.session_id(), OEMCrypto_RSA_Private_Key, + wrapped_rsa_key_.data(), + wrapped_rsa_key_.size()); + ASSERT_EQ(OEMCrypto_SUCCESS, sts); + + vector message_buffer(50); + vector signature; + auto oemcrypto_function = [&](size_t signature_length) { + signature.resize(signature_length); + return OEMCrypto_GenerateRSASignature( + s.session_id(), message_buffer.data(), message_buffer.size(), + signature.data(), &signature_length, kSign_PKCS1_Block1); + }; + TestHugeLengthDoesNotCrashAPI(oemcrypto_function, kCheckStatus); + s.close(); + } +} + // The alternate padding is only required for cast receivers, but all devices // should forbid the alternate padding for regular certificates. TEST_F(OEMCryptoLoadsCertificateAlternates, DisallowForbiddenPaddingAPI09) { @@ -3213,7 +3748,7 @@ TEST_F(OEMCryptoLoadsCertificateAlternates, DisallowForbiddenPaddingAPI09) { // The alternate padding is only required for cast receivers, but if a device // does load an alternate certificate, it should NOT use it for generating // a license request signature. -TEST_F(OEMCryptoLoadsCertificateAlternates, TestSignaturePKCS1_API16) { +TEST_F(OEMCryptoLoadsCertificateAlternates, TestSignaturePKCS1) { // Try to load an RSA key with alternative padding schemes. This signing // scheme is used by cast receivers. LoadWithAllowedSchemes(kSign_PKCS1_Block1, false); @@ -4294,6 +4829,9 @@ class OEMCryptoGenericCryptoTest : public OEMCryptoRefreshTest { if (alter_data) { signature[0] ^= 42; } + if (signature.size() < signature_size) { + signature.resize(signature_size); + } sts = OEMCrypto_SelectKey(session_.session_id(), session_.license().keys[key_index].key_id, @@ -4369,6 +4907,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 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 +5382,11 @@ INSTANTIATE_TEST_CASE_P(TestAll, OEMCryptoGenericCryptoTest, INSTANTIATE_TEST_CASE_P(TestAll, OEMCryptoGenericCryptoKeyIdLengthTest, Range(kCurrentAPI - 1, kCurrentAPI + 1)); +/// @} + +/// @addtogroup usage_table +/// @{ + // Test usage table functionality. class LicenseWithUsageEntry { public: @@ -5883,7 +6437,7 @@ TEST_P(OEMCryptoUsageTableDefragTest, ManyUsageEntries) { OEMCryptoResult status = OEMCrypto_SUCCESS; while (successful_count < attempt_count && status == OEMCrypto_SUCCESS) { wvcdm::TestSleep::SyncFakeClock(); - LOGD("Creating license for entry %zd", successful_count); + LOGD("Creating license for entry %zu", successful_count); entries.push_back( std::unique_ptr(new LicenseWithUsageEntry())); entries.back()->set_pst("pst " + std::to_string(successful_count)); @@ -5909,7 +6463,7 @@ TEST_P(OEMCryptoUsageTableDefragTest, ManyUsageEntries) { successful_count++; } } - LOGD("successful_count = %d", successful_count); + LOGD("successful_count = %zu", successful_count); if (status != OEMCrypto_SUCCESS) { // If we failed to create this many entries because of limited resources, // then the error returned should be insufficient resources. @@ -6021,6 +6575,38 @@ TEST_P(OEMCryptoUsageTableTest, ReloadUsageTableWithSkew) { ASSERT_EQ(OEMCrypto_SUCCESS, entry.license_messages().LoadResponse()); } +TEST_P(OEMCryptoUsageTableTest, LoadAndReloadEntries) { + constexpr size_t kEntryCount = 10; + std::vector entries(kEntryCount); + + for (LicenseWithUsageEntry& entry : entries) { + entry.license_messages().set_api_version(license_api_version_); + } + + for (size_t i = 0; i < kEntryCount; ++i) { + const std::string create_description = + "Creating entry #" + std::to_string(i); + // Create and update a new entry. + LicenseWithUsageEntry& new_entry = entries[i]; + ASSERT_NO_FATAL_FAILURE(new_entry.MakeOfflineAndClose(this)) + << create_description; + // Reload all entries, starting with the most recently created. + for (size_t j = 0; j <= i; ++j) { + const std::string reload_description = + "Reloading entry #" + std::to_string(i - j) + + ", after creating entry #" + std::to_string(i); + LicenseWithUsageEntry& old_entry = entries[i - j]; + ASSERT_NO_FATAL_FAILURE(old_entry.session().open()); + ASSERT_NO_FATAL_FAILURE(old_entry.ReloadUsageEntry()) + << reload_description; + ASSERT_NO_FATAL_FAILURE( + old_entry.session().UpdateUsageEntry(&encrypted_usage_header_)) + << reload_description; + ASSERT_NO_FATAL_FAILURE(old_entry.session().close()); + } + } +} + // A usage report with the wrong pst should fail. TEST_P(OEMCryptoUsageTableTest, GenerateReportWrongPST) { LicenseWithUsageEntry entry; @@ -6186,9 +6772,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 +6916,5 @@ INSTANTIATE_TEST_CASE_P(TestAPI16, OEMCryptoUsageTableDefragTest, INSTANTIATE_TEST_CASE_P(TestAPI16, OEMCryptoUsageTableTestWallClock, Values(kCurrentAPI)); +/// @} } // namespace wvoec diff --git a/oemcrypto/test/oemcrypto_test_main.cpp b/oemcrypto/test/oemcrypto_test_main.cpp index 1cf4a59..5815fd4 100644 --- a/oemcrypto/test/oemcrypto_test_main.cpp +++ b/oemcrypto/test/oemcrypto_test_main.cpp @@ -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 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") { diff --git a/oemcrypto/test/oemcrypto_unittests.gypi b/oemcrypto/test/oemcrypto_unittests.gypi index 4482b20..ac747be 100644 --- a/oemcrypto/test/oemcrypto_unittests.gypi +++ b/oemcrypto/test/oemcrypto_unittests.gypi @@ -10,6 +10,7 @@ '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', 'wvcrc.cpp', @@ -20,6 +21,7 @@ '<(oemcrypto_dir)/include', '<(oemcrypto_dir)/ref/src', '<(oemcrypto_dir)/test', + '<(oemcrypto_dir)/test/fuzz_tests', '<(oemcrypto_dir)/odk/include', ], 'defines': [ @@ -27,6 +29,6 @@ ], 'dependencies': [ '<(oemcrypto_dir)/odk/src/odk.gyp:odk', - '<(boringssl_dependency)', ], + 'includes': [ '../../util/libssl_dependency.gypi' ], } diff --git a/util/libcrypto_dependency.gypi b/util/libcrypto_dependency.gypi new file mode 100644 index 0000000..288c91d --- /dev/null +++ b/util/libcrypto_dependency.gypi @@ -0,0 +1,19 @@ +# 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. +{ + 'conditions': [ + [ + 'privacy_crypto_impl=="openssl"', { + 'libraries': [ + '-lcrypto', + ], + }, # privacy_crypto_impl=="openssl" + 'privacy_crypto_impl=="boringssl" or privacy_crypto_impl=="apple"', { + 'dependencies': [ + '<(boringssl_libcrypto_path)', + ], # dependencies + }, # privacy_crypto_impl=="boringssl" or privacy_crypto_impl=="apple" + ], + ], # conditions +} diff --git a/util/libssl_dependency.gypi b/util/libssl_dependency.gypi new file mode 100644 index 0000000..6bd512d --- /dev/null +++ b/util/libssl_dependency.gypi @@ -0,0 +1,20 @@ +# 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. +{ + 'conditions': [ + [ + 'privacy_crypto_impl=="openssl"', { + 'libraries': [ + '-lcrypto', + '-lssl', + ], + }, # privacy_crypto_impl=="openssl" + 'privacy_crypto_impl=="boringssl" or privacy_crypto_impl=="apple"', { + 'dependencies': [ + '<(boringssl_libssl_path)', + ], # dependencies + }, # privacy_crypto_impl=="boringssl" or privacy_crypto_impl=="apple" + ], + ], # conditions +}