Source release 14.0.0
This commit is contained in:
@@ -6,9 +6,9 @@
|
||||
* Reference APIs needed to support Widevine's crypto algorithms.
|
||||
*
|
||||
* See the document "WV Modular DRM Security Integration Guide for Common
|
||||
* Encryption (CENC) -- version 13" for a description of this API. You
|
||||
* Encryption (CENC) -- version 14" for a description of this API. You
|
||||
* can find this document in the widevine repository as
|
||||
* docs/WidevineModularDRMSecurityIntegrationGuideforCENC_v13.pdf
|
||||
* docs/WidevineModularDRMSecurityIntegrationGuideforCENC_v14.pdf
|
||||
* Changes between different versions of this API are documented in the files
|
||||
* docs/Widevine_Modular_DRM_Version_*_Delta.pdf
|
||||
*
|
||||
@@ -79,6 +79,8 @@ typedef enum OEMCryptoResult {
|
||||
OEMCrypto_ERROR_ENTRY_NEEDS_UPDATE = 48,
|
||||
OEMCrypto_ERROR_ENTRY_IN_USE = 49,
|
||||
OEMCrypto_ERROR_USAGE_TABLE_UNRECOVERABLE = 50, // Reserved. Do not use.
|
||||
OEMCrypto_KEY_NOT_LOADED = 51,
|
||||
OEMCrypto_KEY_NOT_ENTITLED = 52,
|
||||
} OEMCryptoResult;
|
||||
|
||||
/*
|
||||
@@ -144,7 +146,7 @@ typedef struct {
|
||||
} buffer;
|
||||
} OEMCrypto_DestBufferDesc;
|
||||
|
||||
/** OEMCryptoCipherMode is used in LoadKeys 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 {
|
||||
@@ -152,6 +154,14 @@ typedef enum OEMCryptoCipherMode {
|
||||
OEMCrypto_CipherMode_CBC,
|
||||
} OEMCryptoCipherMode;
|
||||
|
||||
/** OEMCrypto_LicenseType is used in LoadKeys to indicate if the key objects
|
||||
* are for content keys, or for entitlement keys.
|
||||
*/
|
||||
typedef enum OEMCrypto_LicenseType {
|
||||
OEMCrypto_ContentLicense = 0,
|
||||
OEMCrypto_EntitlementLicense = 1
|
||||
} OEMCrypto_LicenseType;
|
||||
|
||||
/*
|
||||
* OEMCrypto_KeyObject
|
||||
* Points to the relevant fields for a content key. The fields are extracted
|
||||
@@ -170,8 +180,6 @@ typedef enum OEMCryptoCipherMode {
|
||||
* key_control field.
|
||||
* key_control - the key control block. It is encrypted (AES-128-CBC) with
|
||||
* the content key from the key_data field.
|
||||
* cipher_mode - whether the key should be prepared for CTR mode or CBC mode
|
||||
* when used in later calls to DecryptCENC.
|
||||
*
|
||||
* The memory for the OEMCrypto_KeyObject fields is allocated and freed
|
||||
* by the caller of OEMCrypto_LoadKeys().
|
||||
@@ -184,9 +192,31 @@ typedef struct {
|
||||
size_t key_data_length;
|
||||
const uint8_t* key_control_iv;
|
||||
const uint8_t* key_control;
|
||||
OEMCryptoCipherMode cipher_mode;
|
||||
} OEMCrypto_KeyObject;
|
||||
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
typedef struct {
|
||||
const uint8_t* entitlement_key_id;
|
||||
size_t entitlement_key_id_length;
|
||||
const uint8_t* content_key_id;
|
||||
size_t content_key_id_length;
|
||||
const uint8_t* content_key_data_iv;
|
||||
const uint8_t* content_key_data;
|
||||
size_t content_key_data_length;
|
||||
} OEMCrypto_EntitledContentKeyObject;
|
||||
|
||||
/*
|
||||
* OEMCrypto_KeyRefreshObject
|
||||
* Points to the relevant fields for renewing a content key. The fields are
|
||||
@@ -323,6 +353,16 @@ typedef enum OEMCrypto_ProvisioningMethod {
|
||||
#define OEMCrypto_Hash_Not_Supported 0
|
||||
#define OEMCrypto_HMAC_Clear_Buffer 1
|
||||
|
||||
/*
|
||||
* Return values from OEMCrypto_GetAnalogOutputFlags.
|
||||
*/
|
||||
#define OEMCrypto_No_Analog_Output 0x0
|
||||
#define OEMCrypto_Supports_Analog_Output 0x1
|
||||
#define OEMCrypto_Can_Disable_Analog_Ouptput 0x2
|
||||
#define OEMCrypto_Supports_CGMS_A 0x4
|
||||
// Unknown_Analog_Output is used only for backwards compatibility.
|
||||
#define OEMCrypto_Unknown_Analog_Output (1<<31)
|
||||
|
||||
/*
|
||||
* Obfuscation Renames.
|
||||
*/
|
||||
@@ -342,7 +382,7 @@ typedef enum OEMCrypto_ProvisioningMethod {
|
||||
#define OEMCrypto_GenerateNonce _oecc14
|
||||
#define OEMCrypto_LoadKeys_V8 _oecc15
|
||||
#define OEMCrypto_RefreshKeys _oecc16
|
||||
#define OEMCrypto_SelectKey _oecc17
|
||||
#define OEMCrypto_SelectKey_V13 _oecc17
|
||||
#define OEMCrypto_RewrapDeviceRSAKey _oecc18
|
||||
#define OEMCrypto_LoadDeviceRSAKey _oecc19
|
||||
#define OEMCrypto_GenerateRSASignature_V8 _oecc20
|
||||
@@ -367,7 +407,7 @@ typedef enum OEMCrypto_ProvisioningMethod {
|
||||
#define OEMCrypto_IsAntiRollbackHwPresent _oecc39
|
||||
#define OEMCrypto_CopyBuffer _oecc40
|
||||
#define OEMCrypto_QueryKeyControl _oecc41
|
||||
#define OEMCrypto_LoadTestKeybox _oecc42
|
||||
#define OEMCrypto_LoadTestKeybox_V13 _oecc42
|
||||
#define OEMCrypto_ForceDeleteUsageEntry _oecc43
|
||||
#define OEMCrypto_GetHDCPCapability _oecc44
|
||||
#define OEMCrypto_LoadTestRSAKey _oecc45
|
||||
@@ -381,7 +421,7 @@ typedef enum OEMCrypto_ProvisioningMethod {
|
||||
#define OEMCrypto_IsSRMUpdateSupported _oecc53
|
||||
#define OEMCrypto_GetCurrentSRMVersion _oecc54
|
||||
#define OEMCrypto_LoadSRM _oecc55
|
||||
#define OEMCrypto_LoadKeys _oecc56
|
||||
#define OEMCrypto_LoadKeys_V13 _oecc56
|
||||
#define OEMCrypto_RemoveSRM _oecc57
|
||||
#define OEMCrypto_CreateUsageTableHeader _oecc61
|
||||
#define OEMCrypto_LoadUsageTableHeader _oecc62
|
||||
@@ -393,7 +433,11 @@ typedef enum OEMCrypto_ProvisioningMethod {
|
||||
#define OEMCrypto_MoveEntry _oecc68
|
||||
#define OEMCrypto_CopyOldUsageEntry _oecc69
|
||||
#define OEMCrypto_CreateOldUsageEntry _oecc70
|
||||
|
||||
#define OEMCrypto_GetAnalogOutputFlags _oecc71
|
||||
#define OEMCrypto_LoadTestKeybox _oecc78
|
||||
#define OEMCrypto_LoadEntitledContentKeys _oecc79
|
||||
#define OEMCrypto_SelectKey _oecc81
|
||||
#define OEMCrypto_LoadKeys _oecc82
|
||||
|
||||
/*
|
||||
* OEMCrypto_Initialize
|
||||
@@ -694,6 +738,17 @@ OEMCryptoResult OEMCrypto_GenerateSignature(OEMCrypto_SESSION session,
|
||||
* Refer to document "Widevine Modular DRM Security Integration Guide for
|
||||
* CENC" for 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_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.
|
||||
*
|
||||
@@ -804,6 +859,11 @@ OEMCryptoResult OEMCrypto_GenerateSignature(OEMCrypto_SESSION session,
|
||||
* key_array (in) - set of keys to be installed.
|
||||
* pst (in) - the Provider Session Token.
|
||||
* pst_length (in) - the length of pst.
|
||||
* srm_restriction_data (in) - optional data specifying the minimum SRM
|
||||
* version.
|
||||
* license_type (in) - specifies if the license contains content keys or
|
||||
* entitlement keys.
|
||||
|
||||
*
|
||||
* Threading:
|
||||
* This function may be called simultaneously with functions on other
|
||||
@@ -826,14 +886,75 @@ OEMCryptoResult OEMCrypto_GenerateSignature(OEMCrypto_SESSION session,
|
||||
* larger than the supported size.
|
||||
*
|
||||
* Version:
|
||||
* This method changed in API version 11.
|
||||
* This method changed in API version 14.
|
||||
*/
|
||||
OEMCryptoResult OEMCrypto_LoadKeys(
|
||||
OEMCrypto_SESSION session, const uint8_t* message, size_t message_length,
|
||||
const uint8_t* signature, size_t signature_length,
|
||||
const uint8_t* enc_mac_keys_iv, const uint8_t* enc_mac_keys,
|
||||
size_t num_keys, const OEMCrypto_KeyObject* key_array, const uint8_t* pst,
|
||||
size_t pst_length, const uint8_t* srm_requirement);
|
||||
size_t pst_length, const uint8_t* srm_requirement,
|
||||
OEMCrypto_LicenseType license_type);
|
||||
|
||||
|
||||
/*
|
||||
* OEMCrypto_LoadEntitledContentKeys
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* 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, and 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 appropriate to
|
||||
* store the encrypted content key data in the key table, and defer decrypting
|
||||
* it until the function SelectKey is called.
|
||||
*
|
||||
* Parameters:
|
||||
* session (in) - handle for the session to be used.
|
||||
* num_keys (in) - number of keys present.
|
||||
* key_array (in) - set of key updates.
|
||||
*
|
||||
* Returns
|
||||
* OEMCrypto_SUCCESS success
|
||||
* OEMCrypto_ERROR_INVALID_SESSION
|
||||
* OEMCrypto_ERROR_INVALID_CONTEXT
|
||||
* OEMCrypto_ERROR_INSUFFICIENT_RESOURCES
|
||||
* OEMCrypto_ERROR_UNKNOWN_FAILURE
|
||||
* OEMCrypto_KEY_NOT_ENTITLED
|
||||
* Threading
|
||||
*
|
||||
* This function may be called simultaneously with functions on other
|
||||
* sessions, but not with other functions on this session.
|
||||
*
|
||||
* Version
|
||||
* This method is new in API version 14.
|
||||
*/
|
||||
OEMCryptoResult OEMCrypto_LoadEntitledContentKeys(
|
||||
OEMCrypto_SESSION session,
|
||||
size_t num_keys,
|
||||
const OEMCrypto_EntitledContentKeyObject* key_array);
|
||||
|
||||
/*
|
||||
* OEMCrypto_RefreshKeys
|
||||
@@ -872,6 +993,19 @@ OEMCryptoResult OEMCrypto_LoadKeys(
|
||||
* this case, key_control_iv will also be null and the control block will not
|
||||
* be encrypted.
|
||||
*
|
||||
* 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 updated.
|
||||
*
|
||||
* 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 updated.
|
||||
*
|
||||
* If the key_id is not null, and no matching entry is found in the key
|
||||
* table, then return OEMCrypto_KEY_NOT_LOADED.
|
||||
*
|
||||
* Aside from the key's duration, no other values in the key control block
|
||||
* should be updated by this function.
|
||||
*
|
||||
* Verification:
|
||||
* The following checks should be performed. If any check fails, an error is
|
||||
@@ -920,6 +1054,7 @@ OEMCryptoResult OEMCrypto_LoadKeys(
|
||||
* OEMCrypto_ERROR_INSUFFICIENT_RESOURCES
|
||||
* OEMCrypto_ERROR_UNKNOWN_FAILURE
|
||||
* OEMCrypto_ERROR_BUFFER_TOO_LARGE
|
||||
* OEMCrypto_KEY_NOT_LOADED
|
||||
*
|
||||
* Buffer Sizes
|
||||
* OEMCrypto shall support message sizes of at least 8 KiB.
|
||||
@@ -964,8 +1099,8 @@ OEMCryptoResult OEMCrypto_RefreshKeys(
|
||||
* OEMCrypto_ERROR_NO_CONTENT_KEY.
|
||||
*
|
||||
* Parameters
|
||||
* key_id (in) - The unique id of the key of interest.
|
||||
* key_id_length (in) - The length of key_id, in bytes. From 1 to 16
|
||||
* content_key_id (in) - The unique id of the content key of interest.
|
||||
* content_key_id_length (in) - The length of key_id, in bytes. From 1 to 16
|
||||
* inclusive.
|
||||
* key_control_block(out) - A caller-owned buffer.
|
||||
* key_control_block_length (in/out) - The length of key_control_block buffer.
|
||||
@@ -991,8 +1126,8 @@ OEMCryptoResult OEMCrypto_RefreshKeys(
|
||||
* This method is added in API version 10.
|
||||
*/
|
||||
OEMCryptoResult OEMCrypto_QueryKeyControl(OEMCrypto_SESSION session,
|
||||
const uint8_t* key_id,
|
||||
size_t key_id_length,
|
||||
const uint8_t* content_key_id,
|
||||
size_t content_key_id_length,
|
||||
uint8_t* key_control_block,
|
||||
size_t* key_control_block_length);
|
||||
|
||||
@@ -1018,36 +1153,49 @@ OEMCryptoResult OEMCrypto_QueryKeyControl(OEMCrypto_SESSION session,
|
||||
* Step 3: use the latched content key to decrypt (AES-128-CTR) 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. Continue to use this key until OEMCrypto_SelectKey() is
|
||||
* called again, or until OEMCrypto_CloseSession() is called.
|
||||
* OEMCrypto_Generic_Verify as specified in the key control block.
|
||||
* If the key will be used with OEMCrypto_Generic_Encrypt or
|
||||
* OEMCrypto_Generic_Decrypt, the cipher mode will always be
|
||||
* OEMCrypto_CipherMode_CBC. Continue to use this key until
|
||||
* OEMCrypto_SelectKey() is called again, or until
|
||||
* OEMCrypto_CloseSession() is called.
|
||||
*
|
||||
* Verification:
|
||||
* The following checks should be performed if is_encrypted is true. If any
|
||||
* check fails, an error is returned, and no decryption is performed.
|
||||
*
|
||||
* 1. If the current key's control block has a nonzero Duration field, then
|
||||
* 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_KEY_NOT_LOADED.
|
||||
*
|
||||
* 2. If the current key's control block has a nonzero Duration field, then
|
||||
* the API shall verify that the duration is greater than the session's
|
||||
* elapsed time clock before the key is used. OEMCrypto may return
|
||||
* OEMCrypto_ERROR_KEY_EXPIRED from OEMCrypto_SelectKey, or SelectKey may
|
||||
* return success from select key and the decrypt or generic crypto call will
|
||||
* return OEMCrypto_ERROR_KEY_EXPIRED.
|
||||
* 2. If the key control block has the bit Disable_Analog_Output set, then
|
||||
*
|
||||
* 3. 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
|
||||
* output that cannot be disabled, then the key is not selected, and
|
||||
* OEMCrypto_ERROR_ANALOG_OUTPUT is returned.
|
||||
* 3. If the key control block has HDCP required, and the device cannot
|
||||
*
|
||||
* 4. 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.
|
||||
* 4. If the key control block has a nonzero value for HDCP_Version, and the
|
||||
*
|
||||
* 5. 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.
|
||||
*
|
||||
* Parameters:
|
||||
* session (in) - crypto session identifier
|
||||
* key_id (in) - pointer to the Key ID
|
||||
* key_id_length (in) - length of the Key ID in bytes. From 1 to 16
|
||||
* content_key_id (in) - pointer to the Content Key ID
|
||||
* content_key_id_length (in) - length of the Key ID in bytes. From 1 to 16
|
||||
* inclusive.
|
||||
* cipher_mode (in) - 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.
|
||||
*
|
||||
* Threading:
|
||||
* This function may be called simultaneously with functions on other
|
||||
@@ -1066,13 +1214,15 @@ OEMCryptoResult OEMCrypto_QueryKeyControl(OEMCrypto_SESSION session,
|
||||
* OEMCrypto_ERROR_KEY_EXPIRED
|
||||
* OEMCrypto_ERROR_ANALOG_OUTPUT
|
||||
* OEMCrypto_ERROR_INSUFFICIENT_HDCP
|
||||
* OEMCrypto_KEY_NOT_LOADED
|
||||
*
|
||||
* Version:
|
||||
* This method changed in API version 8.
|
||||
* This method changed in API version 14.
|
||||
*/
|
||||
OEMCryptoResult OEMCrypto_SelectKey(OEMCrypto_SESSION session,
|
||||
const uint8_t* key_id,
|
||||
size_t key_id_length);
|
||||
const uint8_t* content_key_id,
|
||||
size_t content_key_id_length,
|
||||
OEMCryptoCipherMode cipher_mode);
|
||||
|
||||
/*
|
||||
* OEMCrypto_DecryptCENC
|
||||
@@ -1132,7 +1282,7 @@ OEMCryptoResult OEMCrypto_SelectKey(OEMCrypto_SESSION session,
|
||||
* change the status to "active" and set the time_of_first_decrypt.
|
||||
*
|
||||
* The decryption mode, either OEMCrypto_CipherMode_CTR or
|
||||
* OEMCrypto_CipherMode_CBC, was specified in the call to OEMCrypto_LoadKeys.
|
||||
* OEMCrypto_CipherMode_CBC, was 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 can be found in the
|
||||
* document Draft International Standard ISO/IEC DIS 23001-7. Search for the
|
||||
@@ -1502,7 +1652,8 @@ OEMCryptoResult OEMCrypto_GetOEMPublicCertificate(OEMCrypto_SESSION session,
|
||||
* The test keybox can be found in the reference implementation.
|
||||
*
|
||||
* Parameters
|
||||
* none
|
||||
* buffer (in) - pointer to memory containing test keybox, in binary form.
|
||||
* length (in) - length of the buffer, in bytes.
|
||||
*
|
||||
* Returns
|
||||
* OEMCrypto_SUCCESS success
|
||||
@@ -1515,7 +1666,7 @@ OEMCryptoResult OEMCrypto_GetOEMPublicCertificate(OEMCrypto_SESSION session,
|
||||
* Version
|
||||
* This method is added in API version 10.
|
||||
*/
|
||||
OEMCryptoResult OEMCrypto_LoadTestKeybox();
|
||||
OEMCryptoResult OEMCrypto_LoadTestKeybox(const uint8_t *buffer, size_t length);
|
||||
|
||||
/*
|
||||
* OEMCrypto_IsKeyboxValid
|
||||
@@ -1709,7 +1860,9 @@ OEMCryptoResult OEMCrypto_GetRandom(uint8_t* randomData, size_t dataLength);
|
||||
*
|
||||
* Parameters:
|
||||
* session (in) - crypto session identifier.
|
||||
* unaligned_nonce (in) - The nonce provided in the provisioning response.
|
||||
* unaligned_nonce (in) - The nonce provided in the provisioning
|
||||
* - response. This points to an uint32_t that might
|
||||
* - not be aligned to a word boundary.
|
||||
* encrypted_message_key (in) - message_key encrypted by private key
|
||||
* - from OEM cert.
|
||||
* encrypted_message_key_length (in) - length of encrypted_message_key in
|
||||
@@ -1842,7 +1995,9 @@ OEMCryptoResult OEMCrypto_RewrapDeviceRSAKey30(
|
||||
* - signature for message, received from the
|
||||
* - provisioning server.
|
||||
* signature_length (in) - length of the signature, in bytes.
|
||||
* unaligned_nonce (in) - The nonce provided in the provisioning response.
|
||||
* unaligned_nonce (in) - The nonce provided in the provisioning
|
||||
* - response. This points to an uint32_t that might
|
||||
* - not be aligned to a word boundary.
|
||||
* enc_rsa_key (in) - Encrypted device private RSA key received from
|
||||
* - the provisioning server. Format is PKCS#8
|
||||
* - binary DER encoded, encrypted with the derived
|
||||
@@ -2329,8 +2484,13 @@ OEMCryptoResult OEMCrypto_GetMaxNumberOfSessions(size_t* max);
|
||||
* High end devices should also support 3072 bit RSA keys. Devices that are
|
||||
* cast receivers should also support RSA cast receiver certificates.
|
||||
*
|
||||
* Parameters:
|
||||
* none
|
||||
* 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. OEMCrypto should not reject such
|
||||
* keys.
|
||||
*
|
||||
* Parameters: none
|
||||
*
|
||||
* Threading:
|
||||
* This function may be called simultaneously with any other functions.
|
||||
@@ -2521,6 +2681,7 @@ OEMCryptoResult OEMCrypto_Generic_Decrypt(
|
||||
* OEMCrypto_ERROR_INSUFFICIENT_RESOURCES
|
||||
* OEMCrypto_ERROR_UNKNOWN_FAILURE
|
||||
* OEMCrypto_ERROR_BUFFER_TOO_LARGE
|
||||
* OEMCrypto_ERROR_NOT_IMPLEMENTED
|
||||
*
|
||||
* Buffer Sizes
|
||||
* OEMCrypto shall support buffer sizes of at least 100 KiB for generic
|
||||
@@ -2586,6 +2747,7 @@ OEMCryptoResult OEMCrypto_Generic_Sign(OEMCrypto_SESSION session,
|
||||
* OEMCrypto_ERROR_INSUFFICIENT_RESOURCES
|
||||
* OEMCrypto_ERROR_UNKNOWN_FAILURE
|
||||
* OEMCrypto_ERROR_BUFFER_TOO_LARGE
|
||||
* OEMCrypto_ERROR_NOT_IMPLEMENTED
|
||||
*
|
||||
* Buffer Sizes
|
||||
* OEMCrypto shall support buffer sizes of at least 100 KiB for generic
|
||||
@@ -3261,6 +3423,38 @@ OEMCryptoResult OEMCrypto_CopyOldUsageEntry(OEMCrypto_SESSION session,
|
||||
const uint8_t*pst,
|
||||
size_t pst_length);
|
||||
|
||||
/*
|
||||
* OEMCrypto_GetAnalogOutputFlags
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* Threading:
|
||||
* This function will not be called simultaneously with any session functions.
|
||||
*
|
||||
* 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
|
||||
* output.
|
||||
* 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
|
||||
* CGMS-A, if required by the key control block
|
||||
*
|
||||
* Version:
|
||||
* This method is new in API version 14.
|
||||
*/
|
||||
uint32_t OEMCrypto_GetAnalogOutputFlags();
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -1,22 +0,0 @@
|
||||
// Copyright 2017 Google Inc. All Rights Reserved
|
||||
|
||||
/*********************************************************************
|
||||
* level3_file_system_factory.h
|
||||
*
|
||||
* Creates a single OEMCrypto_Level3FileSystem.
|
||||
*********************************************************************/
|
||||
|
||||
#ifndef LEVEL3_FILE_SYSTEM_FACTORY_H_
|
||||
#define LEVEL3_FILE_SYSTEM_FACTORY_H_
|
||||
|
||||
#include "level3_file_system.h"
|
||||
|
||||
namespace wvoec3 {
|
||||
|
||||
OEMCrypto_Level3FileSystem* createLevel3FileSystem();
|
||||
|
||||
void deleteLevel3FileSystem(OEMCrypto_Level3FileSystem* file_system);
|
||||
|
||||
}
|
||||
|
||||
#endif // LEVEL3_FILE_SYSTEM_FACTORY_H_
|
||||
@@ -1,146 +0,0 @@
|
||||
// Copyright 2017 Google Inc. All Rights Reserved.
|
||||
|
||||
/*********************************************************************
|
||||
* pst_report.h
|
||||
*
|
||||
* Reference APIs needed to support Widevine's crypto algorithms.
|
||||
*********************************************************************/
|
||||
|
||||
#ifndef PST_REPORT_H_
|
||||
#define PST_REPORT_H_
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "OEMCryptoCENC.h"
|
||||
#include "string_conversions.h" // needed for htonll64.
|
||||
|
||||
namespace wvcdm {
|
||||
|
||||
class Unpacked_PST_Report {
|
||||
public:
|
||||
// This object does not own the buffer, and does not check that buffer
|
||||
// is not null.
|
||||
Unpacked_PST_Report(uint8_t *buffer) : buffer_(buffer) {}
|
||||
|
||||
// Copy and move semantics of this class is like that of a pointer.
|
||||
Unpacked_PST_Report(const Unpacked_PST_Report& other) :
|
||||
buffer_(other.buffer_) {}
|
||||
|
||||
Unpacked_PST_Report& operator=(const Unpacked_PST_Report& other) {
|
||||
buffer_ = other.buffer_;
|
||||
return *this;
|
||||
}
|
||||
|
||||
size_t report_size() const {
|
||||
return pst_length() + kraw_pst_report_size;
|
||||
}
|
||||
|
||||
static size_t report_size(size_t pst_length) {
|
||||
return pst_length + kraw_pst_report_size;
|
||||
}
|
||||
|
||||
uint8_t status() const {
|
||||
return static_cast<uint8_t>(* (buffer_ + kstatus_offset));
|
||||
}
|
||||
|
||||
void set_status(uint8_t value) {
|
||||
buffer_[kstatus_offset] = value;
|
||||
}
|
||||
|
||||
uint8_t* signature() {
|
||||
return buffer_ + ksignature_offset;
|
||||
}
|
||||
|
||||
uint8_t clock_security_level() const {
|
||||
return static_cast<uint8_t>(* (buffer_ + kclock_security_level_offset));
|
||||
}
|
||||
|
||||
void set_clock_security_level(uint8_t value) {
|
||||
buffer_[kclock_security_level_offset] = value;
|
||||
}
|
||||
|
||||
uint8_t pst_length() const {
|
||||
return static_cast<uint8_t>(* (buffer_ + kpst_length_offset));
|
||||
}
|
||||
|
||||
void set_pst_length(uint8_t value) {
|
||||
buffer_[kpst_length_offset] = value;
|
||||
}
|
||||
|
||||
uint8_t padding() const {
|
||||
return static_cast<uint8_t>(* (buffer_ + kpadding_offset));
|
||||
}
|
||||
|
||||
void set_padding(uint8_t value) {
|
||||
buffer_[kpadding_offset] = value;
|
||||
}
|
||||
|
||||
// In host byte order.
|
||||
int64_t seconds_since_license_received() const {
|
||||
int64_t time;
|
||||
memcpy(&time, buffer_ + kseconds_since_license_received_offset,
|
||||
sizeof(int64_t));
|
||||
return wvcdm::ntohll64(time);
|
||||
}
|
||||
|
||||
// Parameter time is in host byte order.
|
||||
void set_seconds_since_license_received(int64_t time) const {
|
||||
time = wvcdm::ntohll64(time);
|
||||
memcpy(buffer_ + kseconds_since_license_received_offset, &time,
|
||||
sizeof(int64_t));
|
||||
}
|
||||
|
||||
// In host byte order.
|
||||
int64_t seconds_since_first_decrypt() const {
|
||||
int64_t time;
|
||||
memcpy(&time, buffer_ + kseconds_since_first_decrypt_offset,
|
||||
sizeof(int64_t));
|
||||
return wvcdm::ntohll64(time);
|
||||
}
|
||||
|
||||
// Parameter time is in host byte order.
|
||||
void set_seconds_since_first_decrypt(int64_t time) const {
|
||||
time = wvcdm::ntohll64(time);
|
||||
memcpy(buffer_ + kseconds_since_first_decrypt_offset, &time,
|
||||
sizeof(int64_t));
|
||||
}
|
||||
|
||||
// In host byte order.
|
||||
int64_t seconds_since_last_decrypt() const {
|
||||
int64_t time;
|
||||
memcpy(&time, buffer_ + kseconds_since_last_decrypt_offset,
|
||||
sizeof(int64_t));
|
||||
return wvcdm::ntohll64(time);
|
||||
}
|
||||
|
||||
// Parameter time is in host byte order.
|
||||
void set_seconds_since_last_decrypt(int64_t time) const {
|
||||
time = wvcdm::ntohll64(time);
|
||||
memcpy(buffer_ + kseconds_since_last_decrypt_offset, &time,
|
||||
sizeof(int64_t));
|
||||
}
|
||||
|
||||
uint8_t* pst() {
|
||||
return (buffer_ + kpst_offset);
|
||||
}
|
||||
|
||||
private:
|
||||
uint8_t *buffer_;
|
||||
|
||||
// Size of the PST_Report without the pst string.
|
||||
static const size_t kraw_pst_report_size = 48;
|
||||
static const size_t ksignature_offset = 0;
|
||||
static const size_t kstatus_offset = 20;
|
||||
static const size_t kclock_security_level_offset = 21;
|
||||
static const size_t kpst_length_offset = 22;
|
||||
static const size_t kpadding_offset = 23;
|
||||
static const size_t kseconds_since_license_received_offset = 24;
|
||||
static const size_t kseconds_since_first_decrypt_offset = 32;
|
||||
static const size_t kseconds_since_last_decrypt_offset = 40;
|
||||
static const size_t kpst_offset = 48;
|
||||
};
|
||||
} // namespace wvcdm
|
||||
|
||||
#endif // PST_REPORT_H_
|
||||
@@ -21,7 +21,7 @@ extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
|
||||
|
||||
Session s;
|
||||
s.open();
|
||||
s.GenerateDerivedKeysFromKeybox();
|
||||
s.GenerateDerivedKeysFromKeybox(session_helper.keybox_);
|
||||
|
||||
static const uint32_t SignatureBufferMaxLength = size;
|
||||
vector<uint8_t> signature(SignatureBufferMaxLength);
|
||||
|
||||
@@ -140,6 +140,7 @@ std::string DeviceFeatures::RestrictFilter(const std::string& initial_filter) {
|
||||
if (api_version < 11) FilterOut(&filter, "*API11*");
|
||||
if (api_version < 12) FilterOut(&filter, "*API12*");
|
||||
if (api_version < 13) FilterOut(&filter, "*API13*");
|
||||
if (api_version < 14) FilterOut(&filter, "*API14*");
|
||||
// Performance tests take a long time. Filter them out if they are not
|
||||
// specifically requested.
|
||||
if (filter.find("Performance") == std::string::npos) {
|
||||
@@ -172,7 +173,7 @@ void DeviceFeatures::PickDerivedKey() {
|
||||
}
|
||||
if (uses_keybox) {
|
||||
// If device uses a keybox, try to load the test keybox.
|
||||
if (OEMCrypto_ERROR_NOT_IMPLEMENTED != OEMCrypto_LoadTestKeybox()) {
|
||||
if (OEMCrypto_ERROR_NOT_IMPLEMENTED != OEMCrypto_LoadTestKeybox(NULL, 0)) {
|
||||
derive_key_method = LOAD_TEST_KEYBOX;
|
||||
} else if (IsTestKeyboxInstalled()) {
|
||||
derive_key_method = EXISTING_TEST_KEYBOX;
|
||||
@@ -187,8 +188,8 @@ bool DeviceFeatures::IsTestKeyboxInstalled() {
|
||||
size_t key_data_len = sizeof(key_data);
|
||||
if (OEMCrypto_GetKeyData(key_data, &key_data_len) != OEMCrypto_SUCCESS)
|
||||
return false;
|
||||
if (key_data_len != kKeyboxDataSize) return false;
|
||||
if (memcmp(key_data, &kKeybox[kKeyboxDataOffset], key_data_len)) return false;
|
||||
if (key_data_len != sizeof(wvcdm_test_auth::kValidKeybox01.data_)) return false;
|
||||
if (memcmp(key_data, wvcdm_test_auth::kValidKeybox01.data_, key_data_len)) return false;
|
||||
uint8_t dev_id[128] = {0};
|
||||
size_t dev_id_len = 128;
|
||||
if (OEMCrypto_GetDeviceID(dev_id, &dev_id_len) != OEMCrypto_SUCCESS)
|
||||
@@ -197,8 +198,8 @@ bool DeviceFeatures::IsTestKeyboxInstalled() {
|
||||
// multiple '\0' characters at the end of the device id.
|
||||
return 0 == strncmp(
|
||||
reinterpret_cast<const char*>(dev_id),
|
||||
reinterpret_cast<const char*>(&kKeybox[kKeyboxDeviceIdOffset]),
|
||||
kKeyboxDeviceIdSize);
|
||||
reinterpret_cast<const char*>(wvcdm_test_auth::kValidKeybox01.device_id_),
|
||||
sizeof(wvcdm_test_auth::kValidKeybox01.device_id_));
|
||||
}
|
||||
|
||||
void DeviceFeatures::FilterOut(std::string* current_filter,
|
||||
|
||||
@@ -7,6 +7,8 @@
|
||||
|
||||
#include <arpa/inet.h> // needed for ntoh()
|
||||
#include <openssl/aes.h>
|
||||
#include <openssl/bio.h>
|
||||
#include <openssl/cmac.h>
|
||||
#include <openssl/err.h>
|
||||
#include <openssl/hmac.h>
|
||||
#include <openssl/pem.h>
|
||||
@@ -17,6 +19,7 @@
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
#include <iostream>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
@@ -43,29 +46,20 @@ void PrintTo(const vector<uint8_t>& value, ostream* os) {
|
||||
namespace {
|
||||
int GetRandBytes(unsigned char* buf, int num) {
|
||||
// returns 1 on success, -1 if not supported, or 0 if other failure.
|
||||
#if (OPENSSL_VERSION_NUMBER < 0x10100000L)
|
||||
return RAND_pseudo_bytes(buf, num);
|
||||
#else
|
||||
return RAND_bytes(buf, num);
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifdef OPENSSL_IS_BORINGSSL
|
||||
void DeleteX509Stack(STACK_OF(X509)* stack) {
|
||||
sk_X509_pop_free(stack, X509_free);
|
||||
}
|
||||
|
||||
typedef size_t X509Count;
|
||||
#else
|
||||
typedef int X509Count;
|
||||
#endif
|
||||
} // namespace
|
||||
|
||||
namespace wvoec {
|
||||
|
||||
// Increment counter for AES-CTR. The CENC spec specifies we increment only
|
||||
// the low 64 bits of the IV counter, and leave the high 64 bits alone. This
|
||||
// is different from the OpenSSL implementation, so we implement the CTR loop
|
||||
// is different from the BoringSSL implementation, so we implement the CTR loop
|
||||
// ourselves.
|
||||
void ctr128_inc64(int64_t increaseBy, uint8_t* iv) {
|
||||
ASSERT_NE(static_cast<void*>(NULL), iv);
|
||||
@@ -77,19 +71,19 @@ void ctr128_inc64(int64_t increaseBy, uint8_t* iv) {
|
||||
// Some compilers don't like the macro htonl within an ASSERT_EQ.
|
||||
uint32_t htonl_fnc(uint32_t x) { return htonl(x); }
|
||||
|
||||
void dump_openssl_error() {
|
||||
void dump_boringssl_error() {
|
||||
while (unsigned long err = ERR_get_error()) {
|
||||
char buffer[120];
|
||||
ERR_error_string_n(err, buffer, sizeof(buffer));
|
||||
cout << "openssl error -- " << buffer << "\n";
|
||||
cout << "BoringSSL Error -- " << buffer << "\n";
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T, void (*func)(T*)>
|
||||
class openssl_ptr {
|
||||
class boringssl_ptr {
|
||||
public:
|
||||
explicit openssl_ptr(T* p = NULL) : ptr_(p) {}
|
||||
~openssl_ptr() {
|
||||
explicit boringssl_ptr(T* p = NULL) : ptr_(p) {}
|
||||
~boringssl_ptr() {
|
||||
if (ptr_) func(ptr_);
|
||||
}
|
||||
T& operator*() const { return *ptr_; }
|
||||
@@ -99,7 +93,7 @@ class openssl_ptr {
|
||||
|
||||
private:
|
||||
T* ptr_;
|
||||
CORE_DISALLOW_COPY_AND_ASSIGN(openssl_ptr);
|
||||
CORE_DISALLOW_COPY_AND_ASSIGN(boringssl_ptr);
|
||||
};
|
||||
|
||||
Session::Session()
|
||||
@@ -111,8 +105,9 @@ Session::Session()
|
||||
enc_key_(wvcdm::KEY_SIZE),
|
||||
public_rsa_(0),
|
||||
message_size_(sizeof(MessageData)),
|
||||
num_keys_(4) { // Most tests only use 4 keys.
|
||||
// Other tests will explicitly call set_num_keys.
|
||||
num_keys_(4), // Most tests only use 4 keys.
|
||||
// Other tests will explicitly call set_num_keys.
|
||||
has_entitlement_license_(false) {
|
||||
// Stripe the padded message.
|
||||
for (size_t i = 0; i < sizeof(padded_message_.padding); i++) {
|
||||
padded_message_.padding[i] = i % 0x100;
|
||||
@@ -180,7 +175,53 @@ void Session::FillDefaultContext(vector<uint8_t>* mac_context,
|
||||
"180120002a0c31383836373837343035000000000080");
|
||||
}
|
||||
|
||||
void Session::GenerateDerivedKeysFromKeybox() {
|
||||
void Session::DeriveKey(const uint8_t* key, const vector<uint8_t>& context,
|
||||
int counter, vector<uint8_t>* out) {
|
||||
ASSERT_FALSE(context.empty());
|
||||
ASSERT_GE(4, counter);
|
||||
ASSERT_NE(static_cast<void*>(NULL), out);
|
||||
|
||||
const EVP_CIPHER* cipher = EVP_aes_128_cbc();
|
||||
CMAC_CTX* cmac_ctx = CMAC_CTX_new();
|
||||
ASSERT_NE(static_cast<void*>(NULL), cmac_ctx);
|
||||
|
||||
ASSERT_EQ(1, CMAC_Init(cmac_ctx, key, wvcdm::KEY_SIZE, cipher, 0));
|
||||
|
||||
std::vector<uint8_t> message;
|
||||
message.push_back(counter);
|
||||
message.insert(message.end(), context.begin(), context.end());
|
||||
|
||||
ASSERT_EQ(1, CMAC_Update(cmac_ctx, &message[0], message.size()));
|
||||
|
||||
size_t reslen;
|
||||
uint8_t res[128];
|
||||
ASSERT_EQ(1, CMAC_Final(cmac_ctx, res, &reslen));
|
||||
|
||||
out->assign(res, res + reslen);
|
||||
CMAC_CTX_free(cmac_ctx);
|
||||
}
|
||||
|
||||
void Session::DeriveKeys(const uint8_t* master_key,
|
||||
const vector<uint8_t>& mac_key_context,
|
||||
const vector<uint8_t>& enc_key_context) {
|
||||
// Generate derived key for mac key
|
||||
std::vector<uint8_t> mac_key_part2;
|
||||
DeriveKey(master_key, mac_key_context, 1, &mac_key_server_);
|
||||
DeriveKey(master_key, mac_key_context, 2, &mac_key_part2);
|
||||
mac_key_server_.insert(mac_key_server_.end(), mac_key_part2.begin(),
|
||||
mac_key_part2.end());
|
||||
|
||||
DeriveKey(master_key, mac_key_context, 3, &mac_key_client_);
|
||||
DeriveKey(master_key, mac_key_context, 4, &mac_key_part2);
|
||||
mac_key_client_.insert(mac_key_client_.end(), mac_key_part2.begin(),
|
||||
mac_key_part2.end());
|
||||
|
||||
// Generate derived key for encryption key
|
||||
DeriveKey(master_key, enc_key_context, 1, &enc_key_);
|
||||
}
|
||||
|
||||
void Session::GenerateDerivedKeysFromKeybox(
|
||||
const wvcdm_test_auth::WidevineKeybox& keybox) {
|
||||
GenerateNonce();
|
||||
vector<uint8_t> mac_context;
|
||||
vector<uint8_t> enc_context;
|
||||
@@ -190,15 +231,7 @@ void Session::GenerateDerivedKeysFromKeybox() {
|
||||
mac_context.size(), &enc_context[0],
|
||||
enc_context.size()));
|
||||
|
||||
// Expected MAC and ENC keys generated from context strings
|
||||
// with test keybox "installed".
|
||||
mac_key_server_ = wvcdm::a2b_hex(
|
||||
"3CFD60254786AF350B353B4FBB700AB382558400356866BA16C256BCD8C502BF");
|
||||
|
||||
mac_key_client_ = wvcdm::a2b_hex(
|
||||
"A9DE7B3E4E199ED8D1FBC29CD6B4C772CC4538C8B0D3E208B3E76F2EC0FD6F47");
|
||||
|
||||
enc_key_ = wvcdm::a2b_hex("D0BFC35DA9E33436E81C4229E78CB9F4");
|
||||
DeriveKeys(keybox.device_key_, mac_context, enc_context);
|
||||
}
|
||||
|
||||
void Session::GenerateDerivedKeysFromSessionKey() {
|
||||
@@ -217,13 +250,7 @@ void Session::GenerateDerivedKeysFromSessionKey() {
|
||||
&mac_context[0], mac_context.size(), &enc_context[0],
|
||||
enc_context.size()));
|
||||
|
||||
// Expected MAC and ENC keys generated from context strings
|
||||
// with RSA certificate "installed".
|
||||
mac_key_server_ = wvcdm::a2b_hex(
|
||||
"1E451E59CB663DA1646194DD28880788ED8ED2EFF913CBD6A0D535D1D5A90381");
|
||||
mac_key_client_ = wvcdm::a2b_hex(
|
||||
"F9AAE74690909F2207B53B13307FCA096CA8C49CC6DFE3659873CB952889A74B");
|
||||
enc_key_ = wvcdm::a2b_hex("CB477D09014D72C9B8DCE76C33EA43B3");
|
||||
DeriveKeys(&session_key[0], mac_context, enc_context);
|
||||
}
|
||||
|
||||
void Session::LoadTestKeys(const std::string& pst, bool new_mac_keys) {
|
||||
@@ -237,7 +264,8 @@ void Session::LoadTestKeys(const std::string& pst, bool new_mac_keys) {
|
||||
&signature_[0], signature_.size(),
|
||||
encrypted_license().mac_key_iv,
|
||||
encrypted_license().mac_keys, num_keys_,
|
||||
key_array_, pst_ptr, pst.length(), NULL));
|
||||
key_array_, pst_ptr, pst.length(), NULL,
|
||||
OEMCrypto_ContentLicense));
|
||||
// Update new generated keys.
|
||||
memcpy(&mac_key_server_[0], license_.mac_keys, wvcdm::MAC_KEY_SIZE);
|
||||
memcpy(&mac_key_client_[0], license_.mac_keys + wvcdm::MAC_KEY_SIZE,
|
||||
@@ -247,11 +275,114 @@ void Session::LoadTestKeys(const std::string& pst, bool new_mac_keys) {
|
||||
OEMCrypto_SUCCESS,
|
||||
OEMCrypto_LoadKeys(session_id(), message_ptr(), message_size_,
|
||||
&signature_[0], signature_.size(), NULL, NULL,
|
||||
num_keys_, key_array_, pst_ptr, pst.length(), NULL));
|
||||
num_keys_, key_array_, pst_ptr, pst.length(), NULL,
|
||||
OEMCrypto_ContentLicense));
|
||||
}
|
||||
VerifyTestKeys();
|
||||
}
|
||||
|
||||
void Session::LoadEnitlementTestKeys(const std::string& pst,
|
||||
bool new_mac_keys,
|
||||
OEMCryptoResult expected_sts) {
|
||||
uint8_t* pst_ptr = NULL;
|
||||
if (pst.length() > 0) {
|
||||
pst_ptr = encrypted_license().pst;
|
||||
}
|
||||
if (new_mac_keys) {
|
||||
ASSERT_EQ(expected_sts,
|
||||
OEMCrypto_LoadKeys(session_id(), message_ptr(), message_size_,
|
||||
&signature_[0], signature_.size(),
|
||||
encrypted_license().mac_key_iv,
|
||||
encrypted_license().mac_keys, num_keys_,
|
||||
key_array_, pst_ptr, pst.length(), NULL,
|
||||
OEMCrypto_EntitlementLicense));
|
||||
// Update new generated keys.
|
||||
memcpy(&mac_key_server_[0], license_.mac_keys, wvcdm::MAC_KEY_SIZE);
|
||||
memcpy(&mac_key_client_[0], license_.mac_keys + wvcdm::MAC_KEY_SIZE,
|
||||
wvcdm::MAC_KEY_SIZE);
|
||||
} else {
|
||||
ASSERT_EQ(
|
||||
expected_sts,
|
||||
OEMCrypto_LoadKeys(session_id(), message_ptr(), message_size_,
|
||||
&signature_[0], signature_.size(), NULL, NULL,
|
||||
num_keys_, key_array_, pst_ptr, pst.length(), NULL,
|
||||
OEMCrypto_EntitlementLicense));
|
||||
}
|
||||
}
|
||||
|
||||
void Session::FillEntitledKeyArray() {
|
||||
has_entitlement_license_ = true;
|
||||
for (size_t i = 0; i < num_keys_; ++i) {
|
||||
EntitledContentKeyData* key_data = &entitled_key_data_[i];
|
||||
|
||||
entitled_key_array_[i].entitlement_key_id = key_array_[i].key_id;
|
||||
entitled_key_array_[i].entitlement_key_id_length =
|
||||
key_array_[i].key_id_length;
|
||||
|
||||
EXPECT_EQ(
|
||||
1, GetRandBytes(key_data->content_key_id,
|
||||
sizeof(key_data->content_key_id)));
|
||||
entitled_key_array_[i].content_key_id = key_data->content_key_id;
|
||||
entitled_key_array_[i].content_key_id_length =
|
||||
sizeof(key_data->content_key_id);
|
||||
|
||||
EXPECT_EQ(
|
||||
1, GetRandBytes(key_data->content_key_data,
|
||||
sizeof(key_data->content_key_data)));
|
||||
entitled_key_array_[i].content_key_data = key_data->content_key_data;
|
||||
entitled_key_array_[i].content_key_data_length =
|
||||
sizeof(key_data->content_key_data);
|
||||
|
||||
EXPECT_EQ(
|
||||
1, GetRandBytes(entitled_key_data_[i].content_key_data_iv,
|
||||
sizeof(entitled_key_data_[i].content_key_data_iv)));
|
||||
entitled_key_array_[i].content_key_data_iv = key_data->content_key_data_iv;
|
||||
}
|
||||
}
|
||||
|
||||
void Session::LoadEntitledContentKeys(OEMCryptoResult expected_sts) {
|
||||
// Create a copy of the stored |entitled_key_array_|.
|
||||
std::vector<OEMCrypto_EntitledContentKeyObject> encrypted_entitled_key_array;
|
||||
encrypted_entitled_key_array.resize(num_keys_);
|
||||
memcpy(&encrypted_entitled_key_array[0], &entitled_key_array_[0],
|
||||
sizeof(OEMCrypto_EntitledContentKeyObject) * num_keys_);
|
||||
|
||||
// Create a encrypted version of all of the content keys stored in
|
||||
// |entitled_key_array_|.
|
||||
std::vector<std::vector<uint8_t> > encrypted_content_keys;
|
||||
encrypted_content_keys.resize(num_keys_);
|
||||
|
||||
for (size_t i = 0; i < num_keys_; ++i) {
|
||||
// Load the entitlement key from |key_array_|.
|
||||
AES_KEY aes_key;
|
||||
AES_set_encrypt_key(&key_array_[i].key_data[0], 256, &aes_key);
|
||||
encrypted_content_keys[i].resize(
|
||||
encrypted_entitled_key_array[i].content_key_data_length);
|
||||
|
||||
// Encrypt the content key with the entitlement key.
|
||||
uint8_t iv[16];
|
||||
memcpy(&iv[0], &encrypted_entitled_key_array[i].content_key_data[0], 16);
|
||||
AES_cbc_encrypt(
|
||||
&entitled_key_array_[i].content_key_data[0],
|
||||
const_cast<uint8_t*>(
|
||||
&encrypted_entitled_key_array[i].content_key_data[0]),
|
||||
encrypted_entitled_key_array[i].content_key_data_length,
|
||||
&aes_key, iv, AES_ENCRYPT);
|
||||
|
||||
// Set the |encrypted_entitled_key_array| to point to the encrypted copy
|
||||
// of the content key.
|
||||
encrypted_entitled_key_array[i].content_key_data =
|
||||
encrypted_content_keys[i].data();
|
||||
}
|
||||
ASSERT_EQ(expected_sts,
|
||||
OEMCrypto_LoadEntitledContentKeys(
|
||||
session_id(), num_keys_, &encrypted_entitled_key_array[0]));
|
||||
if (expected_sts != OEMCrypto_SUCCESS) {
|
||||
return;
|
||||
}
|
||||
VerifyEntitlementTestKeys();
|
||||
}
|
||||
|
||||
void Session::VerifyTestKeys() {
|
||||
for (unsigned int i = 0; i < num_keys_; i++) {
|
||||
KeyControlBlock block;
|
||||
@@ -274,6 +405,29 @@ void Session::VerifyTestKeys() {
|
||||
}
|
||||
}
|
||||
|
||||
void Session::VerifyEntitlementTestKeys() {
|
||||
for (unsigned int i = 0; i < num_keys_; i++) {
|
||||
KeyControlBlock block;
|
||||
size_t size = sizeof(block);
|
||||
OEMCryptoResult sts = OEMCrypto_QueryKeyControl(
|
||||
session_id(), entitled_key_array_[i].content_key_id,
|
||||
entitled_key_array_[i].content_key_id_length,
|
||||
reinterpret_cast<uint8_t*>(&block), &size);
|
||||
if (sts != OEMCrypto_ERROR_NOT_IMPLEMENTED) {
|
||||
ASSERT_EQ(OEMCrypto_SUCCESS, sts);
|
||||
ASSERT_EQ(sizeof(block), size);
|
||||
// control duration and bits stored in network byte order. For printing
|
||||
// we change to host byte order.
|
||||
ASSERT_EQ((htonl_fnc(license_.keys[i].control.duration)),
|
||||
(htonl_fnc(block.duration)))
|
||||
<< "For key " << i;
|
||||
ASSERT_EQ(htonl_fnc(license_.keys[i].control.control_bits),
|
||||
htonl_fnc(block.control_bits))
|
||||
<< "For key " << i;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Session::RefreshTestKeys(const size_t key_count, uint32_t control_bits,
|
||||
uint32_t nonce, OEMCryptoResult expected_result) {
|
||||
// Note: we store the message in encrypted_license_, but the refresh key
|
||||
@@ -329,7 +483,57 @@ void Session::FillSimpleMessage(uint32_t duration, uint32_t control,
|
||||
sizeof(license_.keys[i].key_iv)));
|
||||
EXPECT_EQ(1, GetRandBytes(license_.keys[i].control_iv,
|
||||
sizeof(license_.keys[i].control_iv)));
|
||||
if (global_features.api_version == 13) {
|
||||
if (global_features.api_version == 14) {
|
||||
// For version 14, we require OEMCrypto to handle kc14 for all licenses.
|
||||
memcpy(license_.keys[i].control.verification, "kc14", 4);
|
||||
} else if (global_features.api_version == 13) {
|
||||
// For version 13, we require OEMCrypto to handle kc13 for all licenses.
|
||||
memcpy(license_.keys[i].control.verification, "kc13", 4);
|
||||
} else if (global_features.api_version == 12) {
|
||||
// For version 12, we require OEMCrypto to handle kc12 for all licenses.
|
||||
memcpy(license_.keys[i].control.verification, "kc12", 4);
|
||||
} else if (control & wvoec_mock::kControlSecurityPatchLevelMask) {
|
||||
// For versions before 12, we require the special key control block only
|
||||
// when there are newer features present.
|
||||
memcpy(license_.keys[i].control.verification, "kc11", 4);
|
||||
} else if (control & wvoec_mock::kControlRequireAntiRollbackHardware) {
|
||||
memcpy(license_.keys[i].control.verification, "kc10", 4);
|
||||
} else if (control & (wvoec_mock::kControlHDCPVersionMask |
|
||||
wvoec_mock::kControlReplayMask)) {
|
||||
memcpy(license_.keys[i].control.verification, "kc09", 4);
|
||||
} else {
|
||||
memcpy(license_.keys[i].control.verification, "kctl", 4);
|
||||
}
|
||||
license_.keys[i].control.duration = htonl(duration);
|
||||
license_.keys[i].control.nonce = htonl(nonce);
|
||||
license_.keys[i].control.control_bits = htonl(control);
|
||||
license_.keys[i].cipher_mode = OEMCrypto_CipherMode_CTR;
|
||||
}
|
||||
memcpy(license_.pst, pst.c_str(), min(sizeof(license_.pst), pst.length()));
|
||||
pst_ = pst;
|
||||
}
|
||||
|
||||
void Session::FillSimpleEntitlementMessage(
|
||||
uint32_t duration, uint32_t control, uint32_t nonce,
|
||||
const std::string& pst) {
|
||||
EXPECT_EQ(
|
||||
1, GetRandBytes(license_.mac_key_iv, sizeof(license_.mac_key_iv)));
|
||||
EXPECT_EQ(1, GetRandBytes(license_.mac_keys, sizeof(license_.mac_keys)));
|
||||
for (unsigned int i = 0; i < num_keys_; i++) {
|
||||
memset(license_.keys[i].key_id, 0, kTestKeyIdMaxLength);
|
||||
license_.keys[i].key_id_length = kDefaultKeyIdLength;
|
||||
memset(license_.keys[i].key_id, i, license_.keys[i].key_id_length);
|
||||
EXPECT_EQ(1, GetRandBytes(license_.keys[i].key_data,
|
||||
sizeof(license_.keys[i].key_data)));
|
||||
license_.keys[i].key_data_length = wvcdm::KEY_SIZE * 2; // AES-256 keys
|
||||
EXPECT_EQ(1, GetRandBytes(license_.keys[i].key_iv,
|
||||
sizeof(license_.keys[i].key_iv)));
|
||||
EXPECT_EQ(1, GetRandBytes(license_.keys[i].control_iv,
|
||||
sizeof(license_.keys[i].control_iv)));
|
||||
if (global_features.api_version == 14) {
|
||||
// For version 13, we require OEMCrypto to handle kc14 for all licenses.
|
||||
memcpy(license_.keys[i].control.verification, "kc14", 4);
|
||||
} else if (global_features.api_version == 13) {
|
||||
// For version 13, we require OEMCrypto to handle kc13 for all licenses.
|
||||
memcpy(license_.keys[i].control.verification, "kc13", 4);
|
||||
} else if (global_features.api_version == 12) {
|
||||
@@ -362,7 +566,10 @@ void Session::FillRefreshMessage(size_t key_count, uint32_t control_bits,
|
||||
encrypted_license().keys[i].key_id_length = license_.keys[i].key_id_length;
|
||||
memcpy(encrypted_license().keys[i].key_id, license_.keys[i].key_id,
|
||||
encrypted_license().keys[i].key_id_length);
|
||||
if (global_features.api_version == 13) {
|
||||
if (global_features.api_version == 14) {
|
||||
// For version 14, we require OEMCrypto to handle kc14 for all licenses.
|
||||
memcpy(encrypted_license().keys[i].control.verification, "kc14", 4);
|
||||
} else if (global_features.api_version == 13) {
|
||||
// For version 13, we require OEMCrypto to handle kc13 for all licenses.
|
||||
memcpy(encrypted_license().keys[i].control.verification, "kc13", 4);
|
||||
} else if (global_features.api_version == 12) {
|
||||
@@ -389,9 +596,10 @@ void Session::EncryptAndSign() {
|
||||
AES_cbc_encrypt(&license_.mac_keys[0], &encrypted_license().mac_keys[0],
|
||||
2 * wvcdm::MAC_KEY_SIZE, &aes_key, iv_buffer, AES_ENCRYPT);
|
||||
|
||||
int key_size = has_entitlement_license() ? 256 : 128;
|
||||
for (unsigned int i = 0; i < num_keys_; i++) {
|
||||
memcpy(iv_buffer, &license_.keys[i].control_iv[0], wvcdm::KEY_IV_SIZE);
|
||||
AES_set_encrypt_key(&license_.keys[i].key_data[0], 128, &aes_key);
|
||||
AES_set_encrypt_key(&license_.keys[i].key_data[0], key_size, &aes_key);
|
||||
AES_cbc_encrypt(
|
||||
reinterpret_cast<const uint8_t*>(&license_.keys[i].control),
|
||||
reinterpret_cast<uint8_t*>(&encrypted_license().keys[i].control),
|
||||
@@ -474,7 +682,6 @@ void Session::FillKeyArray(const MessageData& data,
|
||||
key_array[i].key_control_iv = data.keys[i].control_iv;
|
||||
key_array[i].key_control =
|
||||
reinterpret_cast<const uint8_t*>(&data.keys[i].control);
|
||||
key_array[i].cipher_mode = data.keys[i].cipher_mode;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -524,7 +731,8 @@ void Session::TestDecryptCTR(bool select_key_first,
|
||||
if (select_key_first) {
|
||||
// Select the key (from FillSimpleMessage)
|
||||
sts = OEMCrypto_SelectKey(session_id(), license_.keys[key_index].key_id,
|
||||
license_.keys[key_index].key_id_length);
|
||||
license_.keys[key_index].key_id_length,
|
||||
OEMCrypto_CipherMode_CTR);
|
||||
ASSERT_EQ(OEMCrypto_SUCCESS, sts);
|
||||
}
|
||||
|
||||
@@ -588,7 +796,8 @@ 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);
|
||||
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) {
|
||||
@@ -610,43 +819,30 @@ void Session::LoadOEMCert(bool verify_cert) {
|
||||
OEMCrypto_GetOEMPublicCertificate(session_id(), &public_cert[0],
|
||||
&public_cert_length));
|
||||
|
||||
// Load the certificate chain into an OpenSSL X509 Stack
|
||||
#ifdef OPENSSL_IS_BORINGSSL
|
||||
const openssl_ptr<STACK_OF(X509), DeleteX509Stack> x509_stack(
|
||||
// Load the certificate chain into a BoringSSL X509 Stack
|
||||
const boringssl_ptr<STACK_OF(X509), DeleteX509Stack> x509_stack(
|
||||
sk_X509_new_null());
|
||||
ASSERT_TRUE(x509_stack.NotNull()) << "Unable to allocate X509 stack.";
|
||||
|
||||
CBS pkcs7;
|
||||
CBS_init(&pkcs7, public_cert.data(), public_cert.size());
|
||||
if (!PKCS7_get_certificates(x509_stack.get(), &pkcs7)) {
|
||||
dump_openssl_error();
|
||||
dump_boringssl_error();
|
||||
FAIL() << "Unable to deserialize certificate chain.";
|
||||
}
|
||||
|
||||
STACK_OF(X509)* certs = x509_stack.get();
|
||||
#else
|
||||
// load the cert into rsa_key_.
|
||||
openssl_ptr<BIO, BIO_vfree> bio(
|
||||
BIO_new_mem_buf(&public_cert[0], public_cert_length));
|
||||
ASSERT_TRUE(bio.NotNull());
|
||||
openssl_ptr<PKCS7, PKCS7_free> cert(
|
||||
d2i_PKCS7_bio(bio.get(), NULL));
|
||||
ASSERT_TRUE(cert.NotNull());
|
||||
|
||||
EXPECT_EQ(OBJ_obj2nid(cert->type), NID_pkcs7_signed);
|
||||
STACK_OF(X509)* certs = cert->d.sign->cert;
|
||||
#endif
|
||||
|
||||
// Load the public cert's key into public_rsa_ and verify, if requested
|
||||
for (X509Count i = 0; certs && i < sk_X509_num(certs); i++) {
|
||||
for (size_t i = 0; certs && i < sk_X509_num(certs); i++) {
|
||||
X509* x509_cert = sk_X509_value(certs, i);
|
||||
openssl_ptr<EVP_PKEY, EVP_PKEY_free> pubkey(X509_get_pubkey(x509_cert));
|
||||
boringssl_ptr<EVP_PKEY, EVP_PKEY_free> pubkey(X509_get_pubkey(x509_cert));
|
||||
ASSERT_TRUE(pubkey.NotNull());
|
||||
if (i == 0) {
|
||||
public_rsa_ = EVP_PKEY_get1_RSA(pubkey.get());
|
||||
if (!public_rsa_) {
|
||||
cout << "d2i_RSAPrivateKey failed.\n";
|
||||
dump_openssl_error();
|
||||
dump_boringssl_error();
|
||||
ASSERT_TRUE(NULL != public_rsa_);
|
||||
}
|
||||
}
|
||||
@@ -656,9 +852,9 @@ void Session::LoadOEMCert(bool verify_cert) {
|
||||
X509_NAME* name = X509_get_subject_name(x509_cert);
|
||||
printf(" OEM Certificate Name: %s\n",
|
||||
X509_NAME_oneline(name, &buffer[0], buffer.size()));
|
||||
openssl_ptr<X509_STORE, X509_STORE_free> store(X509_STORE_new());
|
||||
boringssl_ptr<X509_STORE, X509_STORE_free> store(X509_STORE_new());
|
||||
ASSERT_TRUE(store.NotNull());
|
||||
openssl_ptr<X509_STORE_CTX, X509_STORE_CTX_free> store_ctx(
|
||||
boringssl_ptr<X509_STORE_CTX, X509_STORE_CTX_free> store_ctx(
|
||||
X509_STORE_CTX_new());
|
||||
ASSERT_TRUE(store_ctx.NotNull());
|
||||
|
||||
@@ -667,23 +863,13 @@ void Session::LoadOEMCert(bool verify_cert) {
|
||||
// TODO(fredgc): Verify cert is signed by Google.
|
||||
|
||||
int result = X509_verify_cert(store_ctx.get());
|
||||
#if (OPENSSL_VERSION_NUMBER < 0x10100000L)
|
||||
ASSERT_GE(0, result) << " OEM Cert not valid. " <<
|
||||
X509_verify_cert_error_string(store_ctx->error);
|
||||
#else
|
||||
ASSERT_GE(0, result) << " OEM Cert not valid. " <<
|
||||
X509_verify_cert_error_string(
|
||||
X509_STORE_CTX_get_error(store_ctx.get()));
|
||||
#endif
|
||||
if (result == 0) {
|
||||
#if (OPENSSL_VERSION_NUMBER < 0x10100000L)
|
||||
printf("Cert not verified: %s.\n",
|
||||
X509_verify_cert_error_string(store_ctx->error));
|
||||
#else
|
||||
printf("Cert not verified: %s.\n",
|
||||
X509_verify_cert_error_string(
|
||||
X509_STORE_CTX_get_error(store_ctx.get())));
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -767,32 +953,32 @@ void Session::RewrapRSAKey30(const struct RSAPrivateKeyMessage& encrypted,
|
||||
|
||||
void Session::PreparePublicKey(const uint8_t* rsa_key, size_t rsa_key_length) {
|
||||
if (rsa_key == NULL) {
|
||||
rsa_key = wvcdm_test_auth::kRsaPrivateKey_2048;
|
||||
rsa_key_length = wvcdm_test_auth::kRsaPrivateKeySize_2048;
|
||||
rsa_key = wvcdm_test_auth::kTestRSAPKCS8PrivateKeyInfo2_2048;
|
||||
rsa_key_length = wvcdm_test_auth::kTestRSAPKCS8PrivateKeyInfo2_2048_Size;
|
||||
}
|
||||
uint8_t* p = const_cast<uint8_t*>(rsa_key);
|
||||
openssl_ptr<BIO, BIO_vfree> bio(BIO_new_mem_buf(p, rsa_key_length));
|
||||
boringssl_ptr<BIO, BIO_vfree> bio(BIO_new_mem_buf(p, rsa_key_length));
|
||||
ASSERT_TRUE(bio.NotNull());
|
||||
openssl_ptr<PKCS8_PRIV_KEY_INFO, PKCS8_PRIV_KEY_INFO_free> pkcs8_pki(
|
||||
boringssl_ptr<PKCS8_PRIV_KEY_INFO, PKCS8_PRIV_KEY_INFO_free> pkcs8_pki(
|
||||
d2i_PKCS8_PRIV_KEY_INFO_bio(bio.get(), NULL));
|
||||
ASSERT_TRUE(pkcs8_pki.NotNull());
|
||||
openssl_ptr<EVP_PKEY, EVP_PKEY_free> evp(EVP_PKCS82PKEY(pkcs8_pki.get()));
|
||||
boringssl_ptr<EVP_PKEY, EVP_PKEY_free> evp(EVP_PKCS82PKEY(pkcs8_pki.get()));
|
||||
ASSERT_TRUE(evp.NotNull());
|
||||
if (public_rsa_) RSA_free(public_rsa_);
|
||||
public_rsa_ = EVP_PKEY_get1_RSA(evp.get());
|
||||
if (!public_rsa_) {
|
||||
cout << "d2i_RSAPrivateKey failed. ";
|
||||
dump_openssl_error();
|
||||
dump_boringssl_error();
|
||||
FAIL() << "Could not parse public RSA key.";
|
||||
}
|
||||
switch (RSA_check_key(public_rsa_)) {
|
||||
case 1: // valid.
|
||||
return;
|
||||
case 0: // not valid.
|
||||
dump_openssl_error();
|
||||
dump_boringssl_error();
|
||||
FAIL() << "[rsa key not valid] ";
|
||||
default: // -1 == check failed.
|
||||
dump_openssl_error();
|
||||
dump_boringssl_error();
|
||||
FAIL() << "[error checking rsa key] ";
|
||||
}
|
||||
}
|
||||
@@ -801,13 +987,9 @@ bool Session::VerifyPSSSignature(EVP_PKEY* pkey, const uint8_t* message,
|
||||
size_t message_length,
|
||||
const uint8_t* signature,
|
||||
size_t signature_length) {
|
||||
#if (OPENSSL_VERSION_NUMBER < 0x10100000L)
|
||||
EVP_MD_CTX md_ctx_struct;
|
||||
EVP_MD_CTX* md_ctx = &md_ctx_struct;
|
||||
EVP_MD_CTX_init(md_ctx);
|
||||
#else
|
||||
EVP_MD_CTX* md_ctx = EVP_MD_CTX_new();
|
||||
#endif
|
||||
EVP_PKEY_CTX* pkey_ctx = NULL;
|
||||
|
||||
if (EVP_DigestVerifyInit(md_ctx, &pkey_ctx, EVP_sha1(), NULL /* no ENGINE */,
|
||||
@@ -845,20 +1027,12 @@ bool Session::VerifyPSSSignature(EVP_PKEY* pkey, const uint8_t* message,
|
||||
goto err;
|
||||
}
|
||||
|
||||
#if (OPENSSL_VERSION_NUMBER < 0x10100000L)
|
||||
EVP_MD_CTX_cleanup(md_ctx);
|
||||
#else
|
||||
EVP_MD_CTX_free(md_ctx);
|
||||
#endif
|
||||
return true;
|
||||
|
||||
err:
|
||||
dump_openssl_error();
|
||||
#if (OPENSSL_VERSION_NUMBER < 0x10100000L)
|
||||
dump_boringssl_error();
|
||||
EVP_MD_CTX_cleanup(md_ctx);
|
||||
#else
|
||||
EVP_MD_CTX_free(md_ctx);
|
||||
#endif
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -874,7 +1048,7 @@ void Session::VerifyRSASignature(const vector<uint8_t>& message,
|
||||
<< RSA_size(public_rsa_) << "\n";
|
||||
|
||||
if (padding_scheme == kSign_RSASSA_PSS) {
|
||||
openssl_ptr<EVP_PKEY, EVP_PKEY_free> pkey(EVP_PKEY_new());
|
||||
boringssl_ptr<EVP_PKEY, EVP_PKEY_free> pkey(EVP_PKEY_new());
|
||||
ASSERT_EQ(1, EVP_PKEY_set1_RSA(pkey.get(), public_rsa_));
|
||||
|
||||
const bool ok = VerifyPSSSignature(pkey.get(), &message[0], message.size(),
|
||||
@@ -909,7 +1083,7 @@ bool Session::GenerateRSASessionKey(vector<uint8_t>* session_key,
|
||||
int size = static_cast<int>(RSA_size(public_rsa_));
|
||||
if (status != size) {
|
||||
cout << "GenerateRSASessionKey error encrypting session key.\n";
|
||||
dump_openssl_error();
|
||||
dump_boringssl_error();
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
@@ -1022,15 +1196,17 @@ void Session::VerifyPST(const Test_PST_Report& expected) {
|
||||
char* pst_ptr = reinterpret_cast<char *>(computed.pst());
|
||||
std::string computed_pst(pst_ptr, pst_ptr + computed.pst_length());
|
||||
ASSERT_EQ(expected.pst, computed_pst);
|
||||
EXPECT_NEAR(expected.seconds_since_license_received,
|
||||
time_t now = time(NULL);
|
||||
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);
|
||||
// Decrypt times only valid on licenses that have been active.
|
||||
if (expected.status == kActive || expected.status == kInactiveUsed) {
|
||||
EXPECT_NEAR(expected.seconds_since_first_decrypt,
|
||||
EXPECT_NEAR(expected.seconds_since_first_decrypt + age,
|
||||
computed.seconds_since_first_decrypt(),
|
||||
kUsageTableTimeTolerance);
|
||||
EXPECT_NEAR(expected.seconds_since_last_decrypt,
|
||||
EXPECT_NEAR(expected.seconds_since_last_decrypt + age,
|
||||
computed.seconds_since_last_decrypt(),
|
||||
kUsageTableTimeTolerance);
|
||||
}
|
||||
@@ -1041,7 +1217,7 @@ void Session::VerifyPST(const Test_PST_Report& expected) {
|
||||
pst_report_buffer_.size() - SHA_DIGEST_LENGTH,
|
||||
&signature[0], &md_len)) {
|
||||
cout << "Error computing HMAC.\n";
|
||||
dump_openssl_error();
|
||||
dump_boringssl_error();
|
||||
}
|
||||
EXPECT_EQ(0, memcmp(computed.signature(), &signature[0],
|
||||
SHA_DIGEST_LENGTH));
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
|
||||
#include "oec_device_features.h"
|
||||
#include "pst_report.h"
|
||||
#include "test_keybox.h"
|
||||
#include "wv_cdm_constants.h"
|
||||
|
||||
using namespace std;
|
||||
@@ -94,13 +95,21 @@ struct RSAPrivateKeyMessage {
|
||||
struct Test_PST_Report {
|
||||
Test_PST_Report(const std::string& pst_in,
|
||||
OEMCrypto_Usage_Entry_Status status_in)
|
||||
: status(status_in), pst(pst_in) {}
|
||||
: status(status_in), pst(pst_in), time_created(time(NULL)) {}
|
||||
|
||||
OEMCrypto_Usage_Entry_Status status;
|
||||
int64_t seconds_since_license_received;
|
||||
int64_t seconds_since_first_decrypt;
|
||||
int64_t seconds_since_last_decrypt;
|
||||
std::string pst;
|
||||
time_t time_created;
|
||||
};
|
||||
|
||||
struct EntitledContentKeyData {
|
||||
uint8_t entitlement_key_id[wvcdm::KEY_SIZE];
|
||||
uint8_t content_key_id[wvcdm::KEY_SIZE];
|
||||
uint8_t content_key_data_iv[wvcdm::KEY_SIZE];
|
||||
uint8_t content_key_data[wvcdm::KEY_SIZE];
|
||||
};
|
||||
|
||||
// Increment counter for AES-CTR. The CENC spec specifies we increment only
|
||||
@@ -112,8 +121,8 @@ void ctr128_inc64(int64_t increaseBy, uint8_t* iv);
|
||||
// Some compilers don't like the macro htonl within an ASSERT_EQ.
|
||||
uint32_t htonl_fnc(uint32_t x);
|
||||
|
||||
// Prints error string from openSSL
|
||||
void dump_openssl_error();
|
||||
// Prints error string from BoringSSL
|
||||
void dump_boringssl_error();
|
||||
|
||||
class Session {
|
||||
public:
|
||||
@@ -142,7 +151,8 @@ class Session {
|
||||
vector<uint8_t>* enc_context);
|
||||
// Generate known mac and enc keys using OEMCrypto_GenerateDerivedKeys and
|
||||
// also fill out enc_key_, mac_key_server_, and mac_key_client_.
|
||||
void GenerateDerivedKeysFromKeybox();
|
||||
void GenerateDerivedKeysFromKeybox(
|
||||
const wvcdm_test_auth::WidevineKeybox& keybox);
|
||||
// Generate known mac and enc keys using OEMCrypto_DeriveKeysFromSessionKey
|
||||
// and also fill out enc_key_, mac_key_server_, and mac_key_client_.
|
||||
void GenerateDerivedKeysFromSessionKey();
|
||||
@@ -151,9 +161,27 @@ class Session {
|
||||
// by FillSimpleMessage, modified if needed, and then encrypted and signed by
|
||||
// the server's mac key in EncryptAndSign.
|
||||
void LoadTestKeys(const std::string& pst = "", bool new_mac_keys = true);
|
||||
// Loads the entitlement keys in the message pointed to by message_ptr()
|
||||
// using OEMCrypto_LoadKeys. This message should have already been created
|
||||
// by FillSimpleEntitlementMessage, modified if needed, and then encrypted
|
||||
// and signed by the server's mac key in EncryptAndSign.
|
||||
void LoadEnitlementTestKeys(const std::string& pst = "",
|
||||
bool new_mac_keys = true,
|
||||
OEMCryptoResult expected_sts = OEMCrypto_SUCCESS);
|
||||
// Fills an OEMCrypto_EntitledContentKeyObject using the information from
|
||||
// the license_ and randomly generated content keys. This method should be
|
||||
// called after LoadEnitlementTestKeys.
|
||||
void FillEntitledKeyArray();
|
||||
// Encrypts and loads the entitled content keys via
|
||||
// OEMCrypto_LoadEntitledContentKeys.
|
||||
void LoadEntitledContentKeys(
|
||||
OEMCryptoResult expected_sts = OEMCrypto_SUCCESS);
|
||||
// This uses OEMCrypto_QueryKeyControl to check that the keys in OEMCrypto
|
||||
// have the correct key control data.
|
||||
void VerifyTestKeys();
|
||||
// This uses OEMCrypto_QueryKeyControl to check that the keys in OEMCrypto
|
||||
// have the correct key control data.
|
||||
void VerifyEntitlementTestKeys();
|
||||
// This creates a refresh key or license renewal message, signs it with the
|
||||
// server's mac key, and calls OEMCrypto_RefreshKeys.
|
||||
void RefreshTestKeys(const size_t key_count, uint32_t control_bits,
|
||||
@@ -166,6 +194,12 @@ class Session {
|
||||
// before being loaded in LoadTestKeys.
|
||||
void FillSimpleMessage(uint32_t duration, uint32_t control, uint32_t nonce,
|
||||
const std::string& pst = "");
|
||||
// This fills the data structure license_ with entitlement key information.
|
||||
// This data can be modified, and then should be encrypted and signed in
|
||||
// EncryptAndSign before being loaded in LoadEnitlementTestKeys.
|
||||
void FillSimpleEntitlementMessage(
|
||||
uint32_t duration, uint32_t control,
|
||||
uint32_t nonce, const std::string& pst = "");
|
||||
// Like FillSimpleMessage, this fills encrypted_license_ with data. The name
|
||||
// is a little misleading: the license renewal message is not encrypted, it
|
||||
// is just signed. The signature is computed in RefreshTestKeys, above.
|
||||
@@ -339,7 +373,18 @@ class Session {
|
||||
// The size of the encrypted message.
|
||||
size_t message_size() { return message_size_; }
|
||||
|
||||
// If this session has an entitlement license.
|
||||
bool has_entitlement_license() const { return has_entitlement_license_; }
|
||||
|
||||
private:
|
||||
// Generate mac and enc keys give the master key.
|
||||
void DeriveKeys(const uint8_t* master_key,
|
||||
const vector<uint8_t>& mac_key_context,
|
||||
const vector<uint8_t>& enc_key_context);
|
||||
// Internal utility function to derive key using CMAC-128
|
||||
void DeriveKey(const uint8_t* key, const vector<uint8_t>& context,
|
||||
int counter, vector<uint8_t>* out);
|
||||
|
||||
bool open_;
|
||||
bool forced_session_id_;
|
||||
OEMCrypto_SESSION session_id_;
|
||||
@@ -360,6 +405,13 @@ class Session {
|
||||
vector<uint8_t> encrypted_usage_entry_;
|
||||
uint32_t usage_entry_number_;
|
||||
string pst_;
|
||||
bool has_entitlement_license_;
|
||||
|
||||
// Clear Entitlement key data. This is the backing data for
|
||||
// |entitled_key_array_|.
|
||||
EntitledContentKeyData entitled_key_data_[kMaxNumKeys];
|
||||
// Entitled key object. Pointers are backed by |entitled_key_data_|.
|
||||
OEMCrypto_EntitledContentKeyObject entitled_key_array_[kMaxNumKeys];
|
||||
};
|
||||
|
||||
} // namespace wvoec
|
||||
|
||||
@@ -2,8 +2,6 @@
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include "test_keybox.h"
|
||||
|
||||
using namespace std;
|
||||
using namespace wvoec;
|
||||
|
||||
@@ -26,7 +24,7 @@ void SessionUtil::CreateWrappedRSAKeyFromKeybox(uint32_t allowed_schemes,
|
||||
bool force) {
|
||||
Session s;
|
||||
ASSERT_NO_FATAL_FAILURE(s.open());
|
||||
ASSERT_NO_FATAL_FAILURE(s.GenerateDerivedKeysFromKeybox());
|
||||
ASSERT_NO_FATAL_FAILURE(s.GenerateDerivedKeysFromKeybox(keybox_));
|
||||
// Provisioning request would be signed by the client and verified by the
|
||||
// server.
|
||||
ASSERT_NO_FATAL_FAILURE(s.VerifyClientSignature());
|
||||
@@ -83,14 +81,16 @@ void SessionUtil::CreateWrappedRSAKey(uint32_t allowed_schemes,
|
||||
}
|
||||
}
|
||||
|
||||
void SessionUtil::InstallKeybox(const uint8_t* keybox, bool good) {
|
||||
const size_t keybox_size = wvcdm_test_auth::kKeyboxSize;
|
||||
uint8_t wrapped[keybox_size];
|
||||
size_t length = keybox_size;
|
||||
void SessionUtil::InstallKeybox(const wvcdm_test_auth::WidevineKeybox& keybox,
|
||||
bool good) {
|
||||
uint8_t wrapped[sizeof(wvcdm_test_auth::WidevineKeybox)];
|
||||
size_t length = sizeof(wvcdm_test_auth::WidevineKeybox);
|
||||
keybox_ = keybox;
|
||||
ASSERT_EQ(
|
||||
OEMCrypto_SUCCESS,
|
||||
OEMCrypto_WrapKeybox(keybox, keybox_size, wrapped, &length, NULL, 0));
|
||||
OEMCryptoResult sts = OEMCrypto_InstallKeybox(wrapped, length);
|
||||
OEMCrypto_WrapKeybox(reinterpret_cast<const uint8_t*>(&keybox),
|
||||
sizeof(keybox), wrapped, &length, NULL, 0));
|
||||
OEMCryptoResult sts = OEMCrypto_InstallKeybox(wrapped, sizeof(keybox));
|
||||
if (good) {
|
||||
ASSERT_EQ(OEMCrypto_SUCCESS, sts);
|
||||
} else {
|
||||
@@ -101,16 +101,30 @@ void SessionUtil::InstallKeybox(const uint8_t* keybox, bool good) {
|
||||
void SessionUtil::EnsureTestKeys() {
|
||||
switch (global_features.derive_key_method) {
|
||||
case DeviceFeatures::LOAD_TEST_KEYBOX:
|
||||
ASSERT_EQ(OEMCrypto_SUCCESS, OEMCrypto_LoadTestKeybox());
|
||||
keybox_ = wvcdm_test_auth::kTestKeybox;
|
||||
/* Note: If you are upgrading from an older version, it may be easier to
|
||||
* force the following condition. This uses the same test keybox as we
|
||||
* used in older versions of this test.
|
||||
*/
|
||||
if (global_features.api_version < 14) {
|
||||
keybox_ = wvcdm_test_auth::kValidKeybox01;
|
||||
}
|
||||
|
||||
ASSERT_EQ(OEMCrypto_SUCCESS,
|
||||
OEMCrypto_LoadTestKeybox(
|
||||
reinterpret_cast<const uint8_t*>(&keybox_),
|
||||
sizeof(keybox_)));
|
||||
break;
|
||||
case DeviceFeatures::LOAD_TEST_RSA_KEY:
|
||||
ASSERT_EQ(OEMCrypto_SUCCESS, OEMCrypto_LoadTestRSAKey());
|
||||
break;
|
||||
case DeviceFeatures::EXISTING_TEST_KEYBOX:
|
||||
// already has test keybox.
|
||||
// already has old test keybox.
|
||||
keybox_ = wvcdm_test_auth::kValidKeybox01;
|
||||
break;
|
||||
case DeviceFeatures::FORCE_TEST_KEYBOX:
|
||||
InstallKeybox(wvcdm_test_auth::kKeybox, true);
|
||||
keybox_ = wvcdm_test_auth::kTestKeybox;
|
||||
InstallKeybox(keybox_, true);
|
||||
break;
|
||||
case DeviceFeatures::TEST_PROVISION_30:
|
||||
// Can use oem certificate to install test rsa key.
|
||||
@@ -140,7 +154,7 @@ void SessionUtil::InstallTestSessionKeys(Session* s) {
|
||||
s->GenerateDerivedKeysFromSessionKey());
|
||||
} else { // Just uses keybox. Test keybox should already be installed.
|
||||
ASSERT_NO_FATAL_FAILURE(
|
||||
s->GenerateDerivedKeysFromKeybox());
|
||||
s->GenerateDerivedKeysFromKeybox(keybox_));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
#include "oec_session_util.h"
|
||||
|
||||
#include "OEMCryptoCENC.h"
|
||||
#include "test_keybox.h"
|
||||
#include "test_rsa_key.h"
|
||||
|
||||
namespace wvoec {
|
||||
@@ -14,9 +15,10 @@ namespace wvoec {
|
||||
class SessionUtil {
|
||||
public:
|
||||
SessionUtil()
|
||||
: encoded_rsa_key_(wvcdm_test_auth::kRsaPrivateKey_2048,
|
||||
wvcdm_test_auth::kRsaPrivateKey_2048 +
|
||||
wvcdm_test_auth::kRsaPrivateKeySize_2048) {}
|
||||
: encoded_rsa_key_(
|
||||
wvcdm_test_auth::kTestRSAPKCS8PrivateKeyInfo2_2048,
|
||||
wvcdm_test_auth::kTestRSAPKCS8PrivateKeyInfo2_2048 +
|
||||
wvcdm_test_auth::kTestRSAPKCS8PrivateKeyInfo2_2048_Size) {}
|
||||
|
||||
// If force is true, we assert that the key loads successfully.
|
||||
void CreateWrappedRSAKeyFromKeybox(uint32_t allowed_schemes, bool force);
|
||||
@@ -27,7 +29,7 @@ public:
|
||||
// If force is true, we assert that the key loads successfully.
|
||||
void CreateWrappedRSAKey(uint32_t allowed_schemes, bool force);
|
||||
|
||||
void InstallKeybox(const uint8_t* keybox, bool good);
|
||||
void InstallKeybox(const wvcdm_test_auth::WidevineKeybox& keybox, bool good);
|
||||
|
||||
void EnsureTestKeys();
|
||||
|
||||
@@ -35,6 +37,7 @@ public:
|
||||
|
||||
std::vector<uint8_t> encoded_rsa_key_;
|
||||
std::vector<uint8_t> wrapped_rsa_key_;
|
||||
wvcdm_test_auth::WidevineKeybox keybox_;
|
||||
};
|
||||
|
||||
} // namespace wvoec
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -15,6 +15,8 @@
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include "OEMCryptoCENC.h"
|
||||
#include "test_keybox.h"
|
||||
#include "test_rsa_key.h"
|
||||
|
||||
#include "log.h"
|
||||
|
||||
@@ -105,7 +107,9 @@ TEST_F(OEMCryptoAndroidMNCTest, MinVersionNumber10) {
|
||||
|
||||
TEST_F(OEMCryptoAndroidMNCTest, LoadTestKeybox) {
|
||||
if (OEMCrypto_Keybox == OEMCrypto_GetProvisioningMethod()) {
|
||||
OEMCryptoResult status = OEMCrypto_LoadTestKeybox();
|
||||
OEMCryptoResult status = OEMCrypto_LoadTestKeybox(
|
||||
reinterpret_cast<const uint8_t*>(&kTestKeybox),
|
||||
sizeof(kTestKeybox)));
|
||||
// OEMCrypto may return success or not implemented.
|
||||
if (status == OEMCrypto_SUCCESS) {
|
||||
LOGV("OEMCrypto_LoadTestKeybox is implemented.");
|
||||
@@ -149,4 +153,12 @@ TEST_F(OEMCryptoAndroidOCTest, MinVersionNumber13) {
|
||||
ASSERT_GE(version, 13u);
|
||||
}
|
||||
|
||||
// These tests are required for Pi MR1 Android devices.
|
||||
class OEMCryptoAndroidPiMR1Test : public OEMCryptoAndroidOCTest {};
|
||||
|
||||
TEST_F(OEMCryptoAndroidPiMR1Test, MinVersionNumber14) {
|
||||
uint32_t version = OEMCrypto_APIVersion();
|
||||
ASSERT_GE(version, 14u);
|
||||
}
|
||||
|
||||
} // namespace wvoec
|
||||
|
||||
Reference in New Issue
Block a user