Update unit tests to 2020-03-27 version
This CL updates the following: - Some robustness improvements to the ODK library. - Unit tests assume that license release does not have a core message. - Added version string to unit tests. The version string of the unit tests is now: OEMCrypto unit tests for API 16.2. Tests last updated 2020-03-27
This commit is contained in:
Binary file not shown.
@@ -131,8 +131,9 @@ typedef struct {
|
|||||||
* Fields:
|
* Fields:
|
||||||
* [in] input_data: An unaligned pointer to this sample from the stream.
|
* [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] input_data_length: The length of this sample in the stream, in bytes.
|
||||||
* [in] output: A caller-owned descriptor that specifies the handling of the
|
* [in] output_descriptor: A caller-owned descriptor that specifies the
|
||||||
* decrypted byte stream. See OEMCrypto_DestbufferDesc for details.
|
* handling of the decrypted byte stream. See OEMCrypto_DestbufferDesc for
|
||||||
|
* details.
|
||||||
*
|
*
|
||||||
* Version:
|
* Version:
|
||||||
* This struct changed in API version 16.
|
* This struct changed in API version 16.
|
||||||
@@ -599,7 +600,7 @@ OEMCryptoResult OEMCrypto_Terminate(void);
|
|||||||
* This function shall call ODK_InitializeSessionValues to initialize the
|
* This function shall call ODK_InitializeSessionValues to initialize the
|
||||||
* session's clock values, timer values, and nonce values.
|
* session's clock values, timer values, and nonce values.
|
||||||
* ODK_InitializeSessionValues is described in the document "License Duration
|
* ODK_InitializeSessionValues is described in the document "License Duration
|
||||||
* and Renewal", to initialize the sessions clock values.
|
* and Renewal", to initialize the session's clock values.
|
||||||
*
|
*
|
||||||
* Parameters:
|
* Parameters:
|
||||||
* [out] session: an opaque handle that the crypto firmware uses to identify
|
* [out] session: an opaque handle that the crypto firmware uses to identify
|
||||||
@@ -824,9 +825,9 @@ OEMCryptoResult OEMCrypto_DeriveKeysFromSessionKey(
|
|||||||
* waits at least one second before requesting more nonces, then OEMCrypto
|
* waits at least one second before requesting more nonces, then OEMCrypto
|
||||||
* will reset the error condition and generate valid nonces again.
|
* will reset the error condition and generate valid nonces again.
|
||||||
*
|
*
|
||||||
* The nonce should be stored in the sessions ODK_NonceValue field by calling
|
* The nonce should be stored in the session's ODK_NonceValue field by
|
||||||
* the function ODK_SetNonceValue(&nonce_values, nonce). The ODK functions
|
* calling the function ODK_SetNonceValue(&nonce_values, nonce). The ODK
|
||||||
* are documented in "Widevine Core Message Serialization".
|
* functions are documented in "Widevine Core Message Serialization".
|
||||||
*
|
*
|
||||||
* This function shall only be called at most once per open session. It shall
|
* 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
|
* only be called before signing either a provisioning request or a license
|
||||||
@@ -872,7 +873,7 @@ OEMCryptoResult OEMCrypto_GenerateNonce(OEMCrypto_SESSION session,
|
|||||||
* Message Serialization".
|
* Message Serialization".
|
||||||
*
|
*
|
||||||
* The message body is the buffer starting at message + core_message_size,
|
* The message body is the buffer starting at message + core_message_size,
|
||||||
* and with length message_length-core_message_size. The reason OEMCrypto
|
* 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
|
* 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.
|
* device to request a license from a v15 license server.
|
||||||
*
|
*
|
||||||
@@ -882,12 +883,12 @@ OEMCryptoResult OEMCrypto_GenerateNonce(OEMCrypto_SESSION session,
|
|||||||
*
|
*
|
||||||
* OEMCrypto shall compute a hash of the core license request. The core
|
* OEMCrypto shall compute a hash of the core license request. The core
|
||||||
* license request is the buffer starting at message and with length
|
* license request is the buffer starting at message and with length
|
||||||
* core_message_size. The has will be saved with the session and verified
|
* core_message_size. The hash will be saved with the session and verified
|
||||||
* that it matches a hash in the license response.
|
* that it matches a hash in the license response.
|
||||||
*
|
*
|
||||||
* OEMCrypto shall also call the function ODK_InitializeClockValues,
|
* OEMCrypto shall also call the function ODK_InitializeClockValues,
|
||||||
* described in the document "License Duration and Renewal", to initialize
|
* described in the document "License Duration and Renewal", to initialize
|
||||||
* the sessions clock values.
|
* the session's clock values.
|
||||||
*
|
*
|
||||||
* Refer to the Signing Messages Sent to a Server section above for more
|
* Refer to the Signing Messages Sent to a Server section above for more
|
||||||
* details about the signature algorithm.
|
* details about the signature algorithm.
|
||||||
@@ -897,6 +898,7 @@ OEMCryptoResult OEMCrypto_GenerateNonce(OEMCrypto_SESSION session,
|
|||||||
* signature_length to the size needed to receive the output signature.
|
* signature_length to the size needed to receive the output signature.
|
||||||
*
|
*
|
||||||
* Parameters:
|
* Parameters:
|
||||||
|
* [in] session: handle for the session to be used.
|
||||||
* [in/out] message: Pointer to memory for the entire message. Modified by
|
* [in/out] message: Pointer to memory for the entire message. Modified by
|
||||||
* OEMCrypto via the ODK library.
|
* OEMCrypto via the ODK library.
|
||||||
* [in] message_length: length of the entire message buffer.
|
* [in] message_length: length of the entire message buffer.
|
||||||
@@ -960,7 +962,7 @@ OEMCryptoResult OEMCrypto_PrepAndSignLicenseRequest(
|
|||||||
* If nonce_values.api_major_version is 15, then OEMCrypto shall compute the
|
* 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.
|
* 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
|
* 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
|
* 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
|
* license loaded, it will use the usage entries client mac key to sign the
|
||||||
* message body.
|
* message body.
|
||||||
*
|
*
|
||||||
@@ -984,6 +986,7 @@ OEMCryptoResult OEMCrypto_PrepAndSignLicenseRequest(
|
|||||||
* signature_length to the size needed to receive the output signature.
|
* signature_length to the size needed to receive the output signature.
|
||||||
*
|
*
|
||||||
* Parameters:
|
* Parameters:
|
||||||
|
* [in] session: handle for the session to be used.
|
||||||
* [in/out] message: Pointer to memory for the entire message. Modified by
|
* [in/out] message: Pointer to memory for the entire message. Modified by
|
||||||
* OEMCrypto via the ODK library.
|
* OEMCrypto via the ODK library.
|
||||||
* [in] message_length: length of the entire message buffer.
|
* [in] message_length: length of the entire message buffer.
|
||||||
@@ -1029,19 +1032,19 @@ OEMCryptoResult OEMCrypto_PrepAndSignRenewalRequest(
|
|||||||
* OEMCrypto_PrepAndSignProvisioningRequest
|
* OEMCrypto_PrepAndSignProvisioningRequest
|
||||||
*
|
*
|
||||||
* Description:
|
* Description:
|
||||||
* OEMCrypto will use ODK_PrepareCoreRenewalRequest, as described in the
|
* OEMCrypto will use OEMCrypto_PrepAndSignProvisioningRequest, as described
|
||||||
* document "Widevine Core Message Serialization", to prepare the core
|
* in the document "Widevine Core Message Serialization", to prepare the core
|
||||||
* message. If it returns an error, the error should be returned by OEMCrypto
|
* 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
|
* to the CDM layer. If it returns OEMCrypto_SUCCESS, then OEMCrypto shall
|
||||||
* sign compute the signature of the entire message. The entire message is
|
* sign compute the signature of the entire message. The entire message is
|
||||||
* the buffer starting at message with length message_length.
|
* the buffer starting at message with length message_length.
|
||||||
*
|
*
|
||||||
* For a device that has a keybox, i.e. Provisioning 2.0, OEMCrypto will sign
|
* For a device that has a keybox, i.e. Provisioning 2.0, OEMCrypto will sign
|
||||||
* the response with the session's derived client mac key from the previous
|
* the request with the session's derived client mac key from the previous
|
||||||
* call to OEMCrypto_GenerateDerivedKeys.
|
* call to OEMCrypto_GenerateDerivedKeys.
|
||||||
*
|
*
|
||||||
* For a device that has an OEM Certificate, i.e. Provisioning 3.0, OEMCrypto
|
* For a device that has an OEM Certificate, i.e. Provisioning 3.0, OEMCrypto
|
||||||
* will sign the response with the private key associated with the OEM
|
* will sign the request with the private key associated with the OEM
|
||||||
* Certificate. The key shall have been loaded by a previous call to
|
* Certificate. The key shall have been loaded by a previous call to
|
||||||
* OEMCrypto_LoadDRMPrivateKey.
|
* OEMCrypto_LoadDRMPrivateKey.
|
||||||
*
|
*
|
||||||
@@ -1053,6 +1056,7 @@ OEMCryptoResult OEMCrypto_PrepAndSignRenewalRequest(
|
|||||||
* signature_length to the size needed to receive the output signature.
|
* signature_length to the size needed to receive the output signature.
|
||||||
*
|
*
|
||||||
* Parameters:
|
* Parameters:
|
||||||
|
* [in] session: handle for the session to be used.
|
||||||
* [in/out] message: Pointer to memory for the entire message. Modified by
|
* [in/out] message: Pointer to memory for the entire message. Modified by
|
||||||
* OEMCrypto via the ODK library.
|
* OEMCrypto via the ODK library.
|
||||||
* [in] message_length: length of the entire message buffer.
|
* [in] message_length: length of the entire message buffer.
|
||||||
@@ -1210,18 +1214,18 @@ OEMCryptoResult OEMCrypto_LoadSRM(const uint8_t* buffer, size_t buffer_length);
|
|||||||
* OEMCrypto_ERROR_LICENSE_RELOAD.
|
* OEMCrypto_ERROR_LICENSE_RELOAD.
|
||||||
* 3. The enc_mac_keys substring must either have zero length, or satisfy
|
* 3. The enc_mac_keys substring must either have zero length, or satisfy
|
||||||
* the range check. I.e. (offset < message_length) && (offset + length
|
* the range check. I.e. (offset < message_length) && (offset + length
|
||||||
* < message_length) && (offset < offset+length),and offset+length does
|
* < message_length) && (offset < offset + length),and offset + length
|
||||||
* not cause an integer overflow. If it does not have zero length, then
|
* does not cause an integer overflow. If it does not have zero length,
|
||||||
* enc_mac_keys_iv must not have zero length, and must also satisfy the
|
* then enc_mac_keys_iv must not have zero length, and must also satisfy
|
||||||
* range check. If not, return OEMCrypto_ERROR_INVALID_CONTEXT. If the
|
* the range check. If not, return OEMCrypto_ERROR_INVALID_CONTEXT. If
|
||||||
* length is zero, then OEMCrypto may assume that the offset is also
|
* the length is zero, then OEMCrypto may assume that the offset is also
|
||||||
* zero.
|
* zero.
|
||||||
* 4. The API shall verify that each substring in each KeyObject points to
|
* 4. The API shall verify that each substring in each KeyObject points to
|
||||||
* a location in the message. I.e. (offset < message_length) &&
|
* a location in the message. I.e. (offset < message_length) &&
|
||||||
* (offset + length < message_length) && (offset < offset+length) and
|
* (offset + length < message_length) && (offset < offset + length) and
|
||||||
* offset+length does not cause an integer overflow, for each of key_id,
|
* offset + length does not cause an integer overflow, for each of
|
||||||
* key_data_iv, key_data, key_control_iv, key_control. If not, return
|
* key_id, key_data_iv, key_data, key_control_iv, key_control. If not,
|
||||||
* OEMCrypto_ERROR_INVALID_CONTEXT.
|
* return OEMCrypto_ERROR_INVALID_CONTEXT.
|
||||||
* 5. Each key's control block, after decryption, shall have a valid
|
* 5. Each key's control block, after decryption, shall have a valid
|
||||||
* verification field. If not, return OEMCrypto_ERROR_INVALID_CONTEXT.
|
* verification field. If not, return OEMCrypto_ERROR_INVALID_CONTEXT.
|
||||||
* 6. If any key control block has the Nonce_Enabled bit set, that key's
|
* 6. If any key control block has the Nonce_Enabled bit set, that key's
|
||||||
@@ -1459,18 +1463,18 @@ OEMCryptoResult OEMCrypto_LoadKeys(
|
|||||||
* OEMCrypto_ERROR_LICENSE_RELOAD.
|
* OEMCrypto_ERROR_LICENSE_RELOAD.
|
||||||
* 15. The enc_mac_keys substring must either have zero length, or satisfy
|
* 15. The enc_mac_keys substring must either have zero length, or satisfy
|
||||||
* the range check. I.e. (offset < message_length) && (offset + length
|
* the range check. I.e. (offset < message_length) && (offset + length
|
||||||
* < message_length) && (offset < offset+length),and offset+length does
|
* < message_length) && (offset < offset + length),and offset + length
|
||||||
* not cause an integer overflow. If it does not have zero length, then
|
* does not cause an integer overflow. If it does not have zero length,
|
||||||
* enc_mac_keys_iv must not have zero length, and must also satisfy the
|
* then enc_mac_keys_iv must not have zero length, and must also satisfy
|
||||||
* range check. If not, return OEMCrypto_ERROR_INVALID_CONTEXT. If the
|
* the range check. If not, return OEMCrypto_ERROR_INVALID_CONTEXT. If
|
||||||
* length is zero, then OEMCrypto may assume that the offset is also
|
* the length is zero, then OEMCrypto may assume that the offset is also
|
||||||
* zero.
|
* zero.
|
||||||
* 16. The API shall verify that each substring in each KeyObject points to
|
* 16. The API shall verify that each substring in each KeyObject points to
|
||||||
* a location in the message. I.e. (offset < message_length) &&
|
* a location in the message. I.e. (offset < message_length) &&
|
||||||
* (offset + length < message_length) && (offset < offset+length) and
|
* (offset + length < message_length) && (offset < offset + length) and
|
||||||
* offset+length does not cause an integer overflow, for each of key_id,
|
* offset + length does not cause an integer overflow, for each of
|
||||||
* key_data_iv, key_data, key_control_iv, key_control. If not, return
|
* key_id, key_data_iv, key_data, key_control_iv, key_control. If not,
|
||||||
* OEMCrypto_ERROR_INVALID_CONTEXT.
|
* return OEMCrypto_ERROR_INVALID_CONTEXT.
|
||||||
* 17. Each key's control block, after decryption, shall have a valid
|
* 17. Each key's control block, after decryption, shall have a valid
|
||||||
* verification field. If not, return OEMCrypto_ERROR_INVALID_CONTEXT.
|
* verification field. If not, return OEMCrypto_ERROR_INVALID_CONTEXT.
|
||||||
* 18. If any key control block has the Nonce_Enabled bit set, that key's
|
* 18. If any key control block has the Nonce_Enabled bit set, that key's
|
||||||
@@ -2480,8 +2484,9 @@ OEMCryptoResult OEMCrypto_DecryptCENC(
|
|||||||
* [in] session: crypto session identifier.
|
* [in] session: crypto session identifier.
|
||||||
* [in] data_addr: An unaligned pointer to the buffer to be copied.
|
* [in] data_addr: An unaligned pointer to the buffer to be copied.
|
||||||
* [in] data_addr_length: The length of the buffer, in bytes.
|
* [in] data_addr_length: The length of the buffer, in bytes.
|
||||||
* [in] out_buffer: A caller-owned descriptor that specifies the handling of
|
* [in] out_buffer_descriptor: A caller-owned descriptor that specifies the
|
||||||
* the byte stream. See OEMCrypto_DestbufferDesc for details.
|
* handling of the byte stream. See OEMCrypto_DestbufferDesc for
|
||||||
|
* details.
|
||||||
* [in] subsample_flags: bitwise flags indicating if this is the first,
|
* [in] subsample_flags: bitwise flags indicating if this is the first,
|
||||||
* middle, or last subsample in a chunk of data. 1 = first subsample, 2
|
* middle, or last subsample in a chunk of data. 1 = first subsample, 2
|
||||||
* = last subsample, 3 = both first and last subsample, 0 = neither
|
* = last subsample, 3 = both first and last subsample, 0 = neither
|
||||||
@@ -2995,17 +3000,17 @@ OEMCryptoResult OEMCrypto_IsKeyboxOrOEMCertValid(void);
|
|||||||
* OEMCrypto_GetDeviceID
|
* OEMCrypto_GetDeviceID
|
||||||
*
|
*
|
||||||
* Description:
|
* Description:
|
||||||
* Retrieve DeviceID from the Keybox. For devices that have an OEM
|
* Return a device unique id. For devices with a keybox, retrieve the
|
||||||
* Certificate instead of a keybox, this function may return
|
* DeviceID from the Keybox. For devices that have an OEM Certificate instead
|
||||||
* OEMCrypto_ERROR_NOT_IMPLEMENTED. If the function is implemented on an OEM
|
* of a keybox, it should set the device ID to a device-unique string, such
|
||||||
* Certificate device, it should set the device ID to a device-unique string,
|
* as the device serial number. The ID should be device-unique and it should
|
||||||
* such as the device serial number. The ID should be device-unique and it
|
* be stable -- i.e. it should not change across a device reboot or a system
|
||||||
* should be stable -- i.e. it should not change across a device reboot or a
|
* upgrade. This shall match the device id found in the core provisioning
|
||||||
* system upgrade. This shall match the device id found in the core
|
* request message. The maximum length of the device id is 64 bytes. The
|
||||||
* provisioning request message.
|
* device ID field in a keybox is 32 bytes.
|
||||||
*
|
*
|
||||||
* Parameters:
|
* Parameters:
|
||||||
* [out] device_id - pointer to the buffer that receives the Device ID
|
* [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
|
* [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.
|
* buffer. On output, the number of bytes written into the buffer.
|
||||||
*
|
*
|
||||||
@@ -3601,7 +3606,7 @@ OEMCryptoResult OEMCrypto_GetMaxNumberOfSessions(size_t* max);
|
|||||||
* with block type 1 padding.
|
* with block type 1 padding.
|
||||||
* - 0x100 = OEMCrypto_Supports_ECC_secp256r1 - Elliptic Curve secp256r1
|
* - 0x100 = OEMCrypto_Supports_ECC_secp256r1 - Elliptic Curve secp256r1
|
||||||
* - 0x200 = OEMCrypto_Supports_ECC_secp384r1 - Elliptic Curve secp384r1
|
* - 0x200 = OEMCrypto_Supports_ECC_secp384r1 - Elliptic Curve secp384r1
|
||||||
* - 0x200 = OEMCrypto_Supports_ECC_secp521r1 - Elliptic Curve secp521r1
|
* - 0x400 = OEMCrypto_Supports_ECC_secp521r1 - Elliptic Curve secp521r1
|
||||||
*
|
*
|
||||||
* Threading:
|
* Threading:
|
||||||
* This is a "Property Function" and may be called simultaneously with any
|
* This is a "Property Function" and may be called simultaneously with any
|
||||||
@@ -3802,7 +3807,8 @@ uint32_t OEMCrypto_GetAnalogOutputFlags(void);
|
|||||||
* |Minimum Generic crypto buffer |10 KiB |100 KiB |500 KiB |1 MiB |
|
* |Minimum Generic crypto buffer |10 KiB |100 KiB |500 KiB |1 MiB |
|
||||||
* |size | | | | |
|
* |size | | | | |
|
||||||
* +--------------------------------+---------+----------+---------+---------+
|
* +--------------------------------+---------+----------+---------+---------+
|
||||||
* |Minimum number of open sessions |10 |20 |30 |40 |
|
* |Minimum number of concurrent |10 |20 |30 |40 |
|
||||||
|
* |sessions | | | | |
|
||||||
* +--------------------------------+---------+----------+---------+---------+
|
* +--------------------------------+---------+----------+---------+---------+
|
||||||
* |Minimum number of keys per |4 |20 |20 |30 |
|
* |Minimum number of keys per |4 |20 |20 |30 |
|
||||||
* |session | | | | |
|
* |session | | | | |
|
||||||
@@ -3928,12 +3934,12 @@ uint32_t OEMCrypto_ResourceRatingTier(void);
|
|||||||
* [in] core_message_length: length of the core submessage, in bytes.
|
* [in] core_message_length: length of the core submessage, in bytes.
|
||||||
* [in] signature: pointer to memory containing the signature.
|
* [in] signature: pointer to memory containing the signature.
|
||||||
* [in] signature_length: length of the signature, in bytes.
|
* [in] signature_length: length of the signature, in bytes.
|
||||||
* [out] wrapped_rsa_key: pointer to buffer in which encrypted RSA key should
|
* [out] wrapped_private_key: pointer to buffer in which encrypted RSA or ECC
|
||||||
* be stored. May be null on the first call in order to find required
|
* private key should be stored. May be null on the first call in order
|
||||||
* buffer size.
|
* to find required buffer size.
|
||||||
* [in/out] wrapped_rsa_key_length: (in) length of the encrypted RSA key, in
|
* [in/out] wrapped_private_key_length: (in) length of the encrypted private
|
||||||
* bytes.
|
* key, in bytes.
|
||||||
* (out) actual length of the encrypted RSA key
|
* (out) actual length of the encrypted private key
|
||||||
*
|
*
|
||||||
* Returns:
|
* Returns:
|
||||||
* OEMCrypto_SUCCESS success
|
* OEMCrypto_SUCCESS success
|
||||||
@@ -4284,7 +4290,7 @@ OEMCryptoResult OEMCrypto_CreateNewUsageEntry(OEMCrypto_SESSION session,
|
|||||||
* OEMCrypto_LoadUsageEntry
|
* OEMCrypto_LoadUsageEntry
|
||||||
*
|
*
|
||||||
* Description:
|
* Description:
|
||||||
* This loads a usage table saved previously by UpdateUsageEntry. The
|
* This loads a usage entry saved previously by UpdateUsageEntry. The
|
||||||
* signature at the beginning of the buffer is verified and the buffer will
|
* 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.
|
* 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
|
* The index in the entry must match the index passed in. The generation
|
||||||
@@ -4477,7 +4483,7 @@ OEMCryptoResult OEMCrypto_DeactivateUsageEntry(OEMCrypto_SESSION session,
|
|||||||
* buffer_length and return OEMCrypto_ERROR_SHORT_BUFFER.
|
* buffer_length and return OEMCrypto_ERROR_SHORT_BUFFER.
|
||||||
*
|
*
|
||||||
* If an entry was not loaded or created with OEMCrypto_CreateNewUsageEntry
|
* If an entry was not loaded or created with OEMCrypto_CreateNewUsageEntry
|
||||||
* or OEMCRypto_LoadUsageEntry, or if the pst does not match that in the
|
* or OEMCrypto_LoadUsageEntry, or if the pst does not match that in the
|
||||||
* entry, return the error OEMCrypto_ERROR_INVALID_CONTEXT.
|
* entry, return the error OEMCrypto_ERROR_INVALID_CONTEXT.
|
||||||
*
|
*
|
||||||
* If the usage entry's flag ForbidReport is set, indicating the entry has
|
* If the usage entry's flag ForbidReport is set, indicating the entry has
|
||||||
@@ -4489,7 +4495,7 @@ OEMCryptoResult OEMCrypto_DeactivateUsageEntry(OEMCrypto_SESSION session,
|
|||||||
*
|
*
|
||||||
* The pst_report is filled out by subtracting the times in the Usage Entry
|
* 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
|
* from the current time on the secure clock. This design was chosen to avoid
|
||||||
* the device's secure clock with any external clock.
|
* 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")
|
||||||
*
|
*
|
||||||
@@ -4857,11 +4863,11 @@ OEMCryptoResult OEMCrypto_GetHashErrorCode(OEMCrypto_SESSION session,
|
|||||||
*
|
*
|
||||||
* Description:
|
* Description:
|
||||||
* Allocates a secure buffer and fills out the destination buffer information
|
* Allocates a secure buffer and fills out the destination buffer information
|
||||||
* in output. The integer secure_fd may also be set to indicate the source of
|
* in output_descriptor. The integer secure_fd may also be set to indicate
|
||||||
* the buffer. OEMCrypto may use the secure_fd to help track the buffer if it
|
* the source of the buffer. OEMCrypto may use the secure_fd to help track
|
||||||
* wishes. The unit tests will pass a pointer to the same destination buffer
|
* the buffer if it wishes. The unit tests will pass a pointer to the same
|
||||||
* description and the same secure_fd to OEMCrypto_FreeSecureBuffer when the
|
* destination buffer description and the same secure_fd to
|
||||||
* buffer is to be freed.
|
* OEMCrypto_FreeSecureBuffer when the buffer is to be freed.
|
||||||
*
|
*
|
||||||
* This is especially helpful if the hash functions above are supported. This
|
* 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
|
* will only be used by the OEMCrypto unit tests, so we recommend returning
|
||||||
@@ -4872,9 +4878,9 @@ OEMCryptoResult OEMCrypto_GetHashErrorCode(OEMCrypto_SESSION session,
|
|||||||
* Parameters:
|
* Parameters:
|
||||||
* [in] session: session id for operation.
|
* [in] session: session id for operation.
|
||||||
* [in] buffer_size: the requested buffer size.
|
* [in] buffer_size: the requested buffer size.
|
||||||
* [out] output: the buffer descriptor for the created buffer. This will be
|
* [out] output_descriptor: the buffer descriptor for the created buffer.
|
||||||
* passed into the OEMCrypto_DecryptCENC function.
|
* This will be passed into the OEMCrypto_DecryptCENC function.
|
||||||
* [out] secure_fd: a pointer to platform dependant file or buffer
|
* [out] secure_fd: a pointer to platform dependent file or buffer
|
||||||
* descriptor. This will be passed to OEMCrypto_FreeSecureBuffer.
|
* descriptor. This will be passed to OEMCrypto_FreeSecureBuffer.
|
||||||
*
|
*
|
||||||
* Returns:
|
* Returns:
|
||||||
@@ -4907,7 +4913,7 @@ OEMCryptoResult OEMCrypto_AllocateSecureBuffer(
|
|||||||
*
|
*
|
||||||
* Parameters:
|
* Parameters:
|
||||||
* [in] session: session id for operation.
|
* [in] session: session id for operation.
|
||||||
* [out] output: the buffer descriptor modified by
|
* [out] output_descriptor: the buffer descriptor modified by
|
||||||
* OEMCrypto_AllocateSecureBuffer
|
* OEMCrypto_AllocateSecureBuffer
|
||||||
* [in] secure_fd: The integer returned by OEMCrypto_AllocateSecureBuffer
|
* [in] secure_fd: The integer returned by OEMCrypto_AllocateSecureBuffer
|
||||||
*
|
*
|
||||||
@@ -4941,41 +4947,52 @@ OEMCryptoResult OEMCrypto_GenerateSignature(OEMCrypto_SESSION session,
|
|||||||
size_t message_length,
|
size_t message_length,
|
||||||
uint8_t* signature,
|
uint8_t* signature,
|
||||||
size_t* signature_length);
|
size_t* signature_length);
|
||||||
|
|
||||||
OEMCryptoResult OEMCrypto_RewrapDeviceRSAKey30(
|
OEMCryptoResult OEMCrypto_RewrapDeviceRSAKey30(
|
||||||
OEMCrypto_SESSION session, const uint32_t* unaligned_nonce,
|
OEMCrypto_SESSION session, const uint32_t* unaligned_nonce,
|
||||||
const uint8_t* encrypted_message_key, size_t encrypted_message_key_length,
|
const uint8_t* encrypted_message_key, size_t encrypted_message_key_length,
|
||||||
const uint8_t* enc_rsa_key, size_t enc_rsa_key_length,
|
const uint8_t* enc_rsa_key, size_t enc_rsa_key_length,
|
||||||
const uint8_t* enc_rsa_key_iv, uint8_t* wrapped_rsa_key,
|
const uint8_t* enc_rsa_key_iv, uint8_t* wrapped_rsa_key,
|
||||||
size_t* wrapped_rsa_key_length);
|
size_t* wrapped_rsa_key_length);
|
||||||
|
|
||||||
OEMCryptoResult OEMCrypto_RewrapDeviceRSAKey(
|
OEMCryptoResult OEMCrypto_RewrapDeviceRSAKey(
|
||||||
OEMCrypto_SESSION session, const uint8_t* message, size_t message_length,
|
OEMCrypto_SESSION session, const uint8_t* message, size_t message_length,
|
||||||
const uint8_t* signature, size_t signature_length,
|
const uint8_t* signature, size_t signature_length,
|
||||||
const uint32_t* unaligned_nonce, const uint8_t* enc_rsa_key,
|
const uint32_t* unaligned_nonce, const uint8_t* enc_rsa_key,
|
||||||
size_t enc_rsa_key_length, const uint8_t* enc_rsa_key_iv,
|
size_t enc_rsa_key_length, const uint8_t* enc_rsa_key_iv,
|
||||||
uint8_t* wrapped_rsa_key, size_t* wrapped_rsa_key_length);
|
uint8_t* wrapped_rsa_key, size_t* wrapped_rsa_key_length);
|
||||||
|
|
||||||
OEMCryptoResult OEMCrypto_UpdateUsageTable(void);
|
OEMCryptoResult OEMCrypto_UpdateUsageTable(void);
|
||||||
|
|
||||||
OEMCryptoResult OEMCrypto_DeleteUsageEntry(OEMCrypto_SESSION, const uint8_t*,
|
OEMCryptoResult OEMCrypto_DeleteUsageEntry(OEMCrypto_SESSION, const uint8_t*,
|
||||||
size_t, const uint8_t*, size_t,
|
size_t, const uint8_t*, size_t,
|
||||||
const uint8_t*, size_t);
|
const uint8_t*, size_t);
|
||||||
|
|
||||||
OEMCryptoResult OEMCrypto_ForceDeleteUsageEntry(const uint8_t*, size_t);
|
OEMCryptoResult OEMCrypto_ForceDeleteUsageEntry(const uint8_t*, size_t);
|
||||||
|
|
||||||
OEMCryptoResult OEMCrypto_CopyOldUsageEntry(OEMCrypto_SESSION session,
|
OEMCryptoResult OEMCrypto_CopyOldUsageEntry(OEMCrypto_SESSION session,
|
||||||
const uint8_t* pst,
|
const uint8_t* pst,
|
||||||
size_t pst_length);
|
size_t pst_length);
|
||||||
|
|
||||||
OEMCryptoResult OEMCrypto_DeleteOldUsageTable(void);
|
OEMCryptoResult OEMCrypto_DeleteOldUsageTable(void);
|
||||||
|
|
||||||
OEMCryptoResult OEMCrypto_CreateOldUsageEntry(
|
OEMCryptoResult OEMCrypto_CreateOldUsageEntry(
|
||||||
uint64_t time_since_license_received, uint64_t time_since_first_decrypt,
|
uint64_t time_since_license_received, uint64_t time_since_first_decrypt,
|
||||||
uint64_t time_since_last_decrypt, OEMCrypto_Usage_Entry_Status status,
|
uint64_t time_since_last_decrypt, OEMCrypto_Usage_Entry_Status status,
|
||||||
uint8_t* server_mac_key, uint8_t* client_mac_key, const uint8_t* pst,
|
uint8_t* server_mac_key, uint8_t* client_mac_key, const uint8_t* pst,
|
||||||
size_t pst_length);
|
size_t pst_length);
|
||||||
|
|
||||||
OEMCryptoResult OEMCrypto_GenerateDerivedKeys_V15(
|
OEMCryptoResult OEMCrypto_GenerateDerivedKeys_V15(
|
||||||
OEMCrypto_SESSION session, const uint8_t* mac_key_context,
|
OEMCrypto_SESSION session, const uint8_t* mac_key_context,
|
||||||
uint32_t mac_key_context_length, const uint8_t* enc_key_context,
|
uint32_t mac_key_context_length, const uint8_t* enc_key_context,
|
||||||
uint32_t enc_key_context_length);
|
uint32_t enc_key_context_length);
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
size_t encrypt; // number of 16 byte blocks to decrypt.
|
size_t encrypt; // number of 16 byte blocks to decrypt.
|
||||||
size_t skip; // number of 16 byte blocks to leave in clear.
|
size_t skip; // number of 16 byte blocks to leave in clear.
|
||||||
size_t offset; // offset into the pattern in blocks for this call.
|
size_t offset; // offset into the pattern in blocks for this call.
|
||||||
} OEMCrypto_CENCEncryptPatternDesc_V15;
|
} OEMCrypto_CENCEncryptPatternDesc_V15;
|
||||||
|
|
||||||
OEMCryptoResult OEMCrypto_DecryptCENC_V15(
|
OEMCryptoResult OEMCrypto_DecryptCENC_V15(
|
||||||
OEMCrypto_SESSION session, const uint8_t* data_addr, size_t data_length,
|
OEMCrypto_SESSION session, const uint8_t* data_addr, size_t data_length,
|
||||||
bool is_encrypted, const uint8_t* iv,
|
bool is_encrypted, const uint8_t* iv,
|
||||||
@@ -4983,9 +5000,11 @@ OEMCryptoResult OEMCrypto_DecryptCENC_V15(
|
|||||||
OEMCrypto_DestBufferDesc* out_buffer_descriptor,
|
OEMCrypto_DestBufferDesc* out_buffer_descriptor,
|
||||||
const OEMCrypto_CENCEncryptPatternDesc_V15* pattern,
|
const OEMCrypto_CENCEncryptPatternDesc_V15* pattern,
|
||||||
uint8_t subsample_flags);
|
uint8_t subsample_flags);
|
||||||
|
|
||||||
OEMCryptoResult OEMCrypto_GetOEMPublicCertificate_V15(
|
OEMCryptoResult OEMCrypto_GetOEMPublicCertificate_V15(
|
||||||
OEMCrypto_SESSION session, uint8_t* public_cert,
|
OEMCrypto_SESSION session, uint8_t* public_cert,
|
||||||
size_t* public_cert_length);
|
size_t* public_cert_length);
|
||||||
|
|
||||||
OEMCryptoResult OEMCrypto_LoadDeviceRSAKey(OEMCrypto_SESSION session,
|
OEMCryptoResult OEMCrypto_LoadDeviceRSAKey(OEMCrypto_SESSION session,
|
||||||
const uint8_t* wrapped_rsa_key,
|
const uint8_t* wrapped_rsa_key,
|
||||||
size_t wrapped_rsa_key_length);
|
size_t wrapped_rsa_key_length);
|
||||||
|
|||||||
@@ -81,6 +81,7 @@ cc_test {
|
|||||||
|
|
||||||
srcs: [
|
srcs: [
|
||||||
"test/odk_test.cpp",
|
"test/odk_test.cpp",
|
||||||
|
"test/odk_test_helper.cpp",
|
||||||
"test/odk_timer_test.cpp",
|
"test/odk_timer_test.cpp",
|
||||||
],
|
],
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
The ODK Library is used to generate and parse core OEMCrypto messages.
|
The ODK Library is used to generate and parse core OEMCrypto messages for
|
||||||
|
OEMCrypto v16 and above.
|
||||||
|
|
||||||
This library is used by both OEMCrypto on a device, and by Widvine license and
|
This library is used by both OEMCrypto on a device, and by Widevine license and
|
||||||
provisioning servers.
|
provisioning servers.
|
||||||
|
|
||||||
The source of truth for these files is in the server code base on piper. Do not
|
The source of truth for these files is in the server code base on piper. Do not
|
||||||
|
|||||||
@@ -35,7 +35,7 @@ bool CreateResponse(uint32_t message_type, const S& core_request,
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto* header = reinterpret_cast<ODK_CoreMessage*>(&response);
|
auto* header = &response.request.core_message;
|
||||||
header->message_type = message_type;
|
header->message_type = message_type;
|
||||||
header->nonce_values.api_major_version = core_request.api_major_version;
|
header->nonce_values.api_major_version = core_request.api_major_version;
|
||||||
header->nonce_values.api_minor_version = core_request.api_minor_version;
|
header->nonce_values.api_minor_version = core_request.api_minor_version;
|
||||||
|
|||||||
@@ -138,10 +138,8 @@ bool CreateCoreLicenseResponseFromProto(const std::string& serialized_license,
|
|||||||
parsed_lic.nonce_required = nonce_required;
|
parsed_lic.nonce_required = nonce_required;
|
||||||
const auto& policy = lic.policy();
|
const auto& policy = lic.policy();
|
||||||
ODK_TimerLimits& timer_limits = parsed_lic.timer_limits;
|
ODK_TimerLimits& timer_limits = parsed_lic.timer_limits;
|
||||||
// TODO(b/148241181): add field to protobuf.
|
timer_limits.soft_enforce_rental_duration =
|
||||||
// timer_limits.soft_enforce_rental_duration =
|
policy.soft_enforce_rental_duration();
|
||||||
// policy.soft_enforce_rental_duration();
|
|
||||||
timer_limits.soft_enforce_rental_duration = true;
|
|
||||||
timer_limits.soft_enforce_playback_duration =
|
timer_limits.soft_enforce_playback_duration =
|
||||||
policy.soft_enforce_playback_duration();
|
policy.soft_enforce_playback_duration();
|
||||||
timer_limits.earliest_playback_start_seconds = 0;
|
timer_limits.earliest_playback_start_seconds = 0;
|
||||||
|
|||||||
@@ -15,48 +15,63 @@
|
|||||||
#include "odk_util.h"
|
#include "odk_util.h"
|
||||||
#include "serialization_base.h"
|
#include "serialization_base.h"
|
||||||
|
|
||||||
#define ODK_LICENSE_REQUEST_SIZE 20
|
|
||||||
#define ODK_RENEWAL_REQUEST_SIZE 28
|
|
||||||
#define ODK_PROVISIONING_REQUEST_SIZE 88
|
|
||||||
|
|
||||||
/* @ private odk functions */
|
/* @ private odk functions */
|
||||||
|
|
||||||
static OEMCryptoResult ODK_PrepareRequest(uint8_t* buffer, size_t buffer_length,
|
static OEMCryptoResult ODK_PrepareRequest(
|
||||||
size_t* core_message_length,
|
uint8_t* message, size_t message_length, size_t* core_message_length,
|
||||||
uint32_t message_type,
|
uint32_t message_type, const ODK_NonceValues* nonce_values,
|
||||||
const ODK_NonceValues* nonce_values,
|
void* prepared_request_buffer, size_t prepared_request_buffer_length) {
|
||||||
ODK_CoreMessage* core_message) {
|
|
||||||
if (nonce_values == NULL || core_message_length == NULL ||
|
if (nonce_values == NULL || core_message_length == NULL ||
|
||||||
core_message == NULL || *core_message_length > buffer_length) {
|
prepared_request_buffer == NULL ||
|
||||||
|
*core_message_length > message_length) {
|
||||||
return ODK_ERROR_CORE_MESSAGE;
|
return ODK_ERROR_CORE_MESSAGE;
|
||||||
}
|
}
|
||||||
|
|
||||||
Message* msg = NULL;
|
Message* msg = NULL;
|
||||||
AllocateMessage(&msg, message_block);
|
AllocateMessage(&msg, message_block);
|
||||||
InitMessage(msg, buffer, *core_message_length);
|
InitMessage(msg, message, *core_message_length);
|
||||||
|
|
||||||
|
/* The core message should be at the beginning of the buffer, and with a
|
||||||
|
* shorter length. */
|
||||||
|
if (sizeof(ODK_CoreMessage) > prepared_request_buffer_length) {
|
||||||
|
return ODK_ERROR_CORE_MESSAGE;
|
||||||
|
}
|
||||||
|
ODK_CoreMessage* core_message = (ODK_CoreMessage*)prepared_request_buffer;
|
||||||
*core_message = (ODK_CoreMessage){
|
*core_message = (ODK_CoreMessage){
|
||||||
message_type,
|
message_type,
|
||||||
0,
|
0,
|
||||||
*nonce_values,
|
*nonce_values,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/* Set core message length, and pack prepared request into message if the
|
||||||
|
* message buffer has been correctly initialized by the caller. */
|
||||||
switch (message_type) {
|
switch (message_type) {
|
||||||
case ODK_License_Request_Type: {
|
case ODK_License_Request_Type: {
|
||||||
core_message->message_length = ODK_LICENSE_REQUEST_SIZE;
|
core_message->message_length = ODK_LICENSE_REQUEST_SIZE;
|
||||||
|
if (sizeof(ODK_PreparedLicenseRequest) > prepared_request_buffer_length) {
|
||||||
|
return ODK_ERROR_CORE_MESSAGE;
|
||||||
|
}
|
||||||
Pack_ODK_PreparedLicenseRequest(
|
Pack_ODK_PreparedLicenseRequest(
|
||||||
msg, (ODK_PreparedLicenseRequest*)core_message);
|
msg, (ODK_PreparedLicenseRequest*)prepared_request_buffer);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case ODK_Renewal_Request_Type: {
|
case ODK_Renewal_Request_Type: {
|
||||||
core_message->message_length = ODK_RENEWAL_REQUEST_SIZE;
|
core_message->message_length = ODK_RENEWAL_REQUEST_SIZE;
|
||||||
|
if (sizeof(ODK_PreparedRenewalRequest) > prepared_request_buffer_length) {
|
||||||
|
return ODK_ERROR_CORE_MESSAGE;
|
||||||
|
}
|
||||||
Pack_ODK_PreparedRenewalRequest(
|
Pack_ODK_PreparedRenewalRequest(
|
||||||
msg, (ODK_PreparedRenewalRequest*)core_message);
|
msg, (ODK_PreparedRenewalRequest*)prepared_request_buffer);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case ODK_Provisioning_Request_Type: {
|
case ODK_Provisioning_Request_Type: {
|
||||||
core_message->message_length = ODK_PROVISIONING_REQUEST_SIZE;
|
core_message->message_length = ODK_PROVISIONING_REQUEST_SIZE;
|
||||||
|
if (sizeof(ODK_PreparedProvisioningRequest) >
|
||||||
|
prepared_request_buffer_length) {
|
||||||
|
return ODK_ERROR_CORE_MESSAGE;
|
||||||
|
}
|
||||||
Pack_ODK_PreparedProvisioningRequest(
|
Pack_ODK_PreparedProvisioningRequest(
|
||||||
msg, (ODK_PreparedProvisioningRequest*)core_message);
|
msg, (ODK_PreparedProvisioningRequest*)prepared_request_buffer);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
default: {
|
default: {
|
||||||
@@ -65,43 +80,59 @@ static OEMCryptoResult ODK_PrepareRequest(uint8_t* buffer, size_t buffer_length,
|
|||||||
}
|
}
|
||||||
|
|
||||||
*core_message_length = core_message->message_length;
|
*core_message_length = core_message->message_length;
|
||||||
if (GetStatus(msg) != MESSAGE_STATUS_OK ||
|
if (GetStatus(msg) != MESSAGE_STATUS_OK) {
|
||||||
GetSize(msg) != *core_message_length) {
|
/* This is to indicate the caller that the core_message_length has been
|
||||||
|
* appropriately set, but the message buffer is either empty or too small,
|
||||||
|
* which needs to be initialized and filled in the subsequent call. */
|
||||||
return OEMCrypto_ERROR_SHORT_BUFFER;
|
return OEMCrypto_ERROR_SHORT_BUFFER;
|
||||||
}
|
}
|
||||||
|
if (GetSize(msg) != *core_message_length) {
|
||||||
|
/* This should not happen. Something is wrong. */
|
||||||
|
return ODK_ERROR_CORE_MESSAGE;
|
||||||
|
}
|
||||||
return OEMCrypto_SUCCESS;
|
return OEMCrypto_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
static OEMCryptoResult ODK_ParseResponse(const uint8_t* buf,
|
static OEMCryptoResult ODK_ParseResponse(
|
||||||
size_t message_length,
|
const uint8_t* message, size_t message_length, size_t core_message_length,
|
||||||
size_t core_message_length,
|
uint32_t message_type, const ODK_NonceValues* nonce_values,
|
||||||
uint32_t message_type,
|
void* response_buffer, uint32_t response_buffer_length) {
|
||||||
const ODK_NonceValues* nonce_values,
|
if (message == NULL || response_buffer == NULL ||
|
||||||
ODK_CoreMessage* const core_message) {
|
core_message_length > message_length) {
|
||||||
if (core_message_length > message_length) {
|
|
||||||
return ODK_ERROR_CORE_MESSAGE;
|
return ODK_ERROR_CORE_MESSAGE;
|
||||||
}
|
}
|
||||||
|
|
||||||
Message* msg = NULL;
|
Message* msg = NULL;
|
||||||
AllocateMessage(&msg, message_block);
|
AllocateMessage(&msg, message_block);
|
||||||
/* We initialize the message buffer with a size of the entire message
|
/* We initialize the message buffer with a size of the entire message
|
||||||
* length. */
|
* length. */
|
||||||
InitMessage(msg, (uint8_t*)buf, message_length);
|
InitMessage(msg, (uint8_t*)message, message_length);
|
||||||
/* The core message should be at the beginning of the buffer, and with a
|
/* 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. */
|
* shorter length. The core message is the part we are parsing. */
|
||||||
SetSize(msg, core_message_length);
|
SetSize(msg, core_message_length);
|
||||||
|
|
||||||
|
/* Parse message and unpack it into response buffer. */
|
||||||
switch (message_type) {
|
switch (message_type) {
|
||||||
case ODK_License_Response_Type: {
|
case ODK_License_Response_Type: {
|
||||||
Unpack_ODK_LicenseResponse(msg, (ODK_LicenseResponse*)core_message);
|
if (sizeof(ODK_LicenseResponse) > response_buffer_length) {
|
||||||
|
return ODK_ERROR_CORE_MESSAGE;
|
||||||
|
}
|
||||||
|
Unpack_ODK_LicenseResponse(msg, (ODK_LicenseResponse*)response_buffer);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case ODK_Renewal_Response_Type: {
|
case ODK_Renewal_Response_Type: {
|
||||||
Unpack_ODK_RenewalResponse(msg, (ODK_RenewalResponse*)core_message);
|
if (sizeof(ODK_RenewalResponse) > response_buffer_length) {
|
||||||
|
return ODK_ERROR_CORE_MESSAGE;
|
||||||
|
}
|
||||||
|
Unpack_ODK_RenewalResponse(msg, (ODK_RenewalResponse*)response_buffer);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case ODK_Provisioning_Response_Type: {
|
case ODK_Provisioning_Response_Type: {
|
||||||
Unpack_ODK_ProvisioningResponse(msg,
|
if (sizeof(ODK_ProvisioningResponse) > response_buffer_length) {
|
||||||
(ODK_ProvisioningResponse*)core_message);
|
return ODK_ERROR_CORE_MESSAGE;
|
||||||
|
}
|
||||||
|
Unpack_ODK_ProvisioningResponse(
|
||||||
|
msg, (ODK_ProvisioningResponse*)response_buffer);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
default: {
|
default: {
|
||||||
@@ -109,6 +140,7 @@ static OEMCryptoResult ODK_ParseResponse(const uint8_t* buf,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ODK_CoreMessage* core_message = (ODK_CoreMessage*)response_buffer;
|
||||||
if (GetStatus(msg) != MESSAGE_STATUS_OK ||
|
if (GetStatus(msg) != MESSAGE_STATUS_OK ||
|
||||||
message_type != core_message->message_type ||
|
message_type != core_message->message_type ||
|
||||||
GetOffset(msg) != core_message->message_length) {
|
GetOffset(msg) != core_message->message_length) {
|
||||||
@@ -117,12 +149,7 @@ static OEMCryptoResult ODK_ParseResponse(const uint8_t* buf,
|
|||||||
|
|
||||||
if (nonce_values) {
|
if (nonce_values) {
|
||||||
/* always verify nonce_values for Renewal and Provisioning responses */
|
/* always verify nonce_values for Renewal and Provisioning responses */
|
||||||
if (nonce_values->api_major_version !=
|
if (!ODK_NonceValuesEqual(nonce_values, &(core_message->nonce_values))) {
|
||||||
core_message->nonce_values.api_major_version ||
|
|
||||||
nonce_values->api_minor_version !=
|
|
||||||
core_message->nonce_values.api_minor_version ||
|
|
||||||
nonce_values->nonce != core_message->nonce_values.nonce ||
|
|
||||||
nonce_values->session_id != core_message->nonce_values.session_id) {
|
|
||||||
return OEMCrypto_ERROR_INVALID_NONCE;
|
return OEMCrypto_ERROR_INVALID_NONCE;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -137,12 +164,15 @@ static OEMCryptoResult ODK_ParseResponse(const uint8_t* buf,
|
|||||||
OEMCryptoResult ODK_PrepareCoreLicenseRequest(
|
OEMCryptoResult ODK_PrepareCoreLicenseRequest(
|
||||||
uint8_t* message, size_t message_length, size_t* core_message_length,
|
uint8_t* message, size_t message_length, size_t* core_message_length,
|
||||||
const ODK_NonceValues* nonce_values) {
|
const ODK_NonceValues* nonce_values) {
|
||||||
|
if (core_message_length == NULL || nonce_values == NULL) {
|
||||||
|
return ODK_ERROR_CORE_MESSAGE;
|
||||||
|
}
|
||||||
ODK_PreparedLicenseRequest license_request = {
|
ODK_PreparedLicenseRequest license_request = {
|
||||||
{0},
|
{0},
|
||||||
};
|
};
|
||||||
return ODK_PrepareRequest(message, message_length, core_message_length,
|
return ODK_PrepareRequest(
|
||||||
ODK_License_Request_Type, nonce_values,
|
message, message_length, core_message_length, ODK_License_Request_Type,
|
||||||
&license_request.core_message);
|
nonce_values, &license_request, sizeof(ODK_PreparedLicenseRequest));
|
||||||
}
|
}
|
||||||
|
|
||||||
OEMCryptoResult ODK_PrepareCoreRenewalRequest(uint8_t* message,
|
OEMCryptoResult ODK_PrepareCoreRenewalRequest(uint8_t* message,
|
||||||
@@ -151,8 +181,22 @@ OEMCryptoResult ODK_PrepareCoreRenewalRequest(uint8_t* message,
|
|||||||
ODK_NonceValues* nonce_values,
|
ODK_NonceValues* nonce_values,
|
||||||
ODK_ClockValues* clock_values,
|
ODK_ClockValues* clock_values,
|
||||||
uint64_t system_time_seconds) {
|
uint64_t system_time_seconds) {
|
||||||
if (nonce_values == NULL || clock_values == NULL)
|
if (core_message_size == NULL || nonce_values == NULL ||
|
||||||
|
clock_values == NULL) {
|
||||||
return ODK_ERROR_CORE_MESSAGE;
|
return ODK_ERROR_CORE_MESSAGE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* If the license has not been loaded, then this is release instead of a
|
||||||
|
* renewal. All releases use v15. */
|
||||||
|
if (clock_values->timer_status == ODK_CLOCK_TIMER_STATUS_LICENSE_NOT_LOADED ||
|
||||||
|
clock_values->timer_status == ODK_CLOCK_TIMER_STATUS_LICENSE_INACTIVE) {
|
||||||
|
nonce_values->api_major_version = 15;
|
||||||
|
}
|
||||||
|
if (nonce_values->api_major_version < 16) {
|
||||||
|
*core_message_size = 0;
|
||||||
|
return OEMCrypto_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
ODK_PreparedRenewalRequest renewal_request = {{0}, 0};
|
ODK_PreparedRenewalRequest renewal_request = {{0}, 0};
|
||||||
/* First, we compute the time this request was made relative to the playback
|
/* First, we compute the time this request was made relative to the playback
|
||||||
* clock. */
|
* clock. */
|
||||||
@@ -168,19 +212,24 @@ OEMCryptoResult ODK_PrepareCoreRenewalRequest(uint8_t* message,
|
|||||||
return ODK_ERROR_CORE_MESSAGE;
|
return ODK_ERROR_CORE_MESSAGE;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Save time for this request so that we can verify the response. This makes
|
/* Save time for this request so that we can verify the response. This makes
|
||||||
* all earlier requests invalid. If preparing this request fails, then all
|
* all earlier requests invalid. If preparing this request fails, then all
|
||||||
* requests will be bad. */
|
* requests will be bad. */
|
||||||
clock_values->time_of_renewal_request = renewal_request.playback_time;
|
clock_values->time_of_renewal_request = renewal_request.playback_time;
|
||||||
return ODK_PrepareRequest(message, message_length, core_message_size,
|
|
||||||
ODK_Renewal_Request_Type, nonce_values,
|
return ODK_PrepareRequest(
|
||||||
&renewal_request.core_message);
|
message, message_length, core_message_size, ODK_Renewal_Request_Type,
|
||||||
|
nonce_values, &renewal_request, sizeof(ODK_PreparedRenewalRequest));
|
||||||
}
|
}
|
||||||
|
|
||||||
OEMCryptoResult ODK_PrepareCoreProvisioningRequest(
|
OEMCryptoResult ODK_PrepareCoreProvisioningRequest(
|
||||||
uint8_t* message, size_t message_length, size_t* core_message_length,
|
uint8_t* message, size_t message_length, size_t* core_message_length,
|
||||||
const ODK_NonceValues* nonce_values, const uint8_t* device_id,
|
const ODK_NonceValues* nonce_values, const uint8_t* device_id,
|
||||||
size_t device_id_length) {
|
size_t device_id_length) {
|
||||||
|
if (core_message_length == NULL || nonce_values == NULL) {
|
||||||
|
return ODK_ERROR_CORE_MESSAGE;
|
||||||
|
}
|
||||||
ODK_PreparedProvisioningRequest provisioning_request = {
|
ODK_PreparedProvisioningRequest provisioning_request = {
|
||||||
{0},
|
{0},
|
||||||
0,
|
0,
|
||||||
@@ -195,10 +244,11 @@ OEMCryptoResult ODK_PrepareCoreProvisioningRequest(
|
|||||||
}
|
}
|
||||||
return ODK_PrepareRequest(message, message_length, core_message_length,
|
return ODK_PrepareRequest(message, message_length, core_message_length,
|
||||||
ODK_Provisioning_Request_Type, nonce_values,
|
ODK_Provisioning_Request_Type, nonce_values,
|
||||||
&provisioning_request.core_message);
|
&provisioning_request,
|
||||||
|
sizeof(ODK_PreparedProvisioningRequest));
|
||||||
}
|
}
|
||||||
|
|
||||||
/* @@ parse request functions */
|
/* @@ parse response functions */
|
||||||
|
|
||||||
OEMCryptoResult ODK_ParseLicense(
|
OEMCryptoResult ODK_ParseLicense(
|
||||||
const uint8_t* message, size_t message_length, size_t core_message_length,
|
const uint8_t* message, size_t message_length, size_t core_message_length,
|
||||||
@@ -214,7 +264,7 @@ OEMCryptoResult ODK_ParseLicense(
|
|||||||
ODK_LicenseResponse license_response = {{{0}}, parsed_license, {0}};
|
ODK_LicenseResponse license_response = {{{0}}, parsed_license, {0}};
|
||||||
const OEMCryptoResult err = ODK_ParseResponse(
|
const OEMCryptoResult err = ODK_ParseResponse(
|
||||||
message, message_length, core_message_length, ODK_License_Response_Type,
|
message, message_length, core_message_length, ODK_License_Response_Type,
|
||||||
NULL, &license_response.request.core_message);
|
NULL, &license_response, sizeof(ODK_LicenseResponse));
|
||||||
|
|
||||||
if (err != OEMCrypto_SUCCESS) {
|
if (err != OEMCrypto_SUCCESS) {
|
||||||
return err;
|
return err;
|
||||||
@@ -279,11 +329,11 @@ OEMCryptoResult ODK_ParseRenewal(const uint8_t* message, size_t message_length,
|
|||||||
{{0}, 0},
|
{{0}, 0},
|
||||||
0,
|
0,
|
||||||
};
|
};
|
||||||
OEMCryptoResult err = ODK_ParseResponse(
|
const OEMCryptoResult err = ODK_ParseResponse(
|
||||||
message, message_length, core_message_length, ODK_Renewal_Response_Type,
|
message, message_length, core_message_length, ODK_Renewal_Response_Type,
|
||||||
nonce_values, &renewal_response.request.core_message);
|
nonce_values, &renewal_response, sizeof(ODK_RenewalResponse));
|
||||||
|
|
||||||
if (err) {
|
if (err != OEMCrypto_SUCCESS) {
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -318,24 +368,25 @@ OEMCryptoResult ODK_ParseProvisioning(
|
|||||||
return ODK_ERROR_CORE_MESSAGE;
|
return ODK_ERROR_CORE_MESSAGE;
|
||||||
}
|
}
|
||||||
|
|
||||||
OEMCryptoResult err =
|
const OEMCryptoResult err = ODK_ParseResponse(
|
||||||
ODK_ParseResponse(message, message_length, core_message_length,
|
message, message_length, core_message_length,
|
||||||
ODK_Provisioning_Response_Type, nonce_values,
|
ODK_Provisioning_Response_Type, nonce_values, &provisioning_response,
|
||||||
&provisioning_response.request.core_message);
|
sizeof(ODK_ProvisioningResponse));
|
||||||
|
|
||||||
if (err) {
|
if (err != OEMCrypto_SUCCESS) {
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (memcmp(device_id, provisioning_response.request.device_id,
|
if (crypto_memcmp(device_id, provisioning_response.request.device_id,
|
||||||
device_id_length)) {
|
device_id_length) != 0) {
|
||||||
return ODK_ERROR_CORE_MESSAGE;
|
return ODK_ERROR_CORE_MESSAGE;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint8_t zero[ODK_DEVICE_ID_LEN_MAX] = {0};
|
const uint8_t zero[ODK_DEVICE_ID_LEN_MAX] = {0};
|
||||||
/* check bytes beyond device_id_length are 0 */
|
/* check bytes beyond device_id_length are 0 */
|
||||||
if (memcmp(zero, provisioning_response.request.device_id + device_id_length,
|
if (crypto_memcmp(zero,
|
||||||
ODK_DEVICE_ID_LEN_MAX - device_id_length)) {
|
provisioning_response.request.device_id + device_id_length,
|
||||||
|
ODK_DEVICE_ID_LEN_MAX - device_id_length) != 0) {
|
||||||
return ODK_ERROR_CORE_MESSAGE;
|
return ODK_ERROR_CORE_MESSAGE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -60,6 +60,15 @@ typedef struct {
|
|||||||
ODK_ParsedProvisioning* parsed_provisioning;
|
ODK_ParsedProvisioning* parsed_provisioning;
|
||||||
} ODK_ProvisioningResponse;
|
} 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. */
|
||||||
|
#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. */
|
/* These are the possible timer status values. */
|
||||||
#define ODK_CLOCK_TIMER_STATUS_UNDEFINED 0 /* Should not happen. */
|
#define ODK_CLOCK_TIMER_STATUS_UNDEFINED 0 /* Should not happen. */
|
||||||
/* When the structure has been initialized, but no license is loaded. */
|
/* When the structure has been initialized, but no license is loaded. */
|
||||||
|
|||||||
@@ -4,7 +4,6 @@
|
|||||||
|
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
#include "odk.h"
|
#include "odk.h"
|
||||||
#include "odk_overflow.h"
|
#include "odk_overflow.h"
|
||||||
#include "odk_structs_priv.h"
|
#include "odk_structs_priv.h"
|
||||||
@@ -12,8 +11,7 @@
|
|||||||
/* Private function. Checks to see if the license is active. Returns
|
/* Private function. Checks to see if the license is active. Returns
|
||||||
* ODK_TIMER_EXPIRED if the license is valid but inactive. Returns
|
* ODK_TIMER_EXPIRED if the license is valid but inactive. Returns
|
||||||
* OEMCrypto_SUCCESS if the license is active. Returns
|
* OEMCrypto_SUCCESS if the license is active. Returns
|
||||||
* OEMCrypto_ERROR_UNKNOWN_FAILURE on other errors. This also updates the
|
* OEMCrypto_ERROR_UNKNOWN_FAILURE on other errors. */
|
||||||
* timer_status if appropriate. */
|
|
||||||
static OEMCryptoResult ODK_LicenseActive(const ODK_TimerLimits* timer_limits,
|
static OEMCryptoResult ODK_LicenseActive(const ODK_TimerLimits* timer_limits,
|
||||||
ODK_ClockValues* clock_values) {
|
ODK_ClockValues* clock_values) {
|
||||||
/* Check some basic errors. */
|
/* Check some basic errors. */
|
||||||
@@ -26,7 +24,6 @@ static OEMCryptoResult ODK_LicenseActive(const ODK_TimerLimits* timer_limits,
|
|||||||
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
|
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
|
||||||
}
|
}
|
||||||
if (clock_values->status > kActive) {
|
if (clock_values->status > kActive) {
|
||||||
clock_values->timer_status = ODK_CLOCK_TIMER_STATUS_LICENSE_INACTIVE;
|
|
||||||
return ODK_TIMER_EXPIRED;
|
return ODK_TIMER_EXPIRED;
|
||||||
}
|
}
|
||||||
return OEMCrypto_SUCCESS;
|
return OEMCrypto_SUCCESS;
|
||||||
@@ -157,7 +154,9 @@ static OEMCryptoResult ODK_CheckPlaybackWindow(
|
|||||||
* have already computed the timer limit. */
|
* have already computed the timer limit. */
|
||||||
static void ODK_UpdateTimerStatusForRenewal(ODK_ClockValues* clock_values,
|
static void ODK_UpdateTimerStatusForRenewal(ODK_ClockValues* clock_values,
|
||||||
uint32_t new_status) {
|
uint32_t new_status) {
|
||||||
if (clock_values == NULL) return; /* should not happen. */
|
if (clock_values == NULL) {
|
||||||
|
return; /* should not happen. */
|
||||||
|
}
|
||||||
if (clock_values->timer_status == ODK_CLOCK_TIMER_STATUS_LICENSE_LOADED) {
|
if (clock_values->timer_status == ODK_CLOCK_TIMER_STATUS_LICENSE_LOADED) {
|
||||||
/* Signal that the timer is already set. */
|
/* Signal that the timer is already set. */
|
||||||
clock_values->timer_status = ODK_CLOCK_TIMER_STATUS_RENEWAL_LOADED;
|
clock_values->timer_status = ODK_CLOCK_TIMER_STATUS_RENEWAL_LOADED;
|
||||||
@@ -174,17 +173,21 @@ OEMCryptoResult ODK_ComputeRenewalDuration(const ODK_TimerLimits* timer_limits,
|
|||||||
uint64_t system_time_seconds,
|
uint64_t system_time_seconds,
|
||||||
uint64_t new_renewal_duration,
|
uint64_t new_renewal_duration,
|
||||||
uint64_t* timer_value) {
|
uint64_t* timer_value) {
|
||||||
if (timer_limits == NULL || clock_values == NULL)
|
if (timer_limits == NULL || clock_values == NULL) {
|
||||||
return OEMCrypto_ERROR_INVALID_CONTEXT; /* should not happen. */
|
return OEMCrypto_ERROR_INVALID_CONTEXT; /* should not happen. */
|
||||||
|
}
|
||||||
/* If this is before the license was signed, something is odd. Return an
|
/* If this is before the license was signed, something is odd. Return an
|
||||||
* error. */
|
* error. */
|
||||||
if (system_time_seconds < clock_values->time_of_license_signed)
|
if (system_time_seconds < clock_values->time_of_license_signed) {
|
||||||
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
|
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
|
||||||
|
}
|
||||||
|
|
||||||
const OEMCryptoResult license_status =
|
const OEMCryptoResult license_status =
|
||||||
ODK_LicenseActive(timer_limits, clock_values);
|
ODK_LicenseActive(timer_limits, clock_values);
|
||||||
/* If the license is not active, then we cannot renew the license. */
|
/* If the license is not active, then we cannot renew the license. */
|
||||||
if (license_status != OEMCrypto_SUCCESS) return license_status;
|
if (license_status != OEMCrypto_SUCCESS) {
|
||||||
|
return license_status;
|
||||||
|
}
|
||||||
|
|
||||||
/* We start with the new renewal duration as the new timer limit. */
|
/* We start with the new renewal duration as the new timer limit. */
|
||||||
uint64_t new_timer_value = new_renewal_duration;
|
uint64_t new_timer_value = new_renewal_duration;
|
||||||
@@ -193,9 +196,12 @@ OEMCryptoResult ODK_ComputeRenewalDuration(const ODK_TimerLimits* timer_limits,
|
|||||||
* new_timer_value. */
|
* new_timer_value. */
|
||||||
const OEMCryptoResult rental_status = ODK_CheckRentalWindow(
|
const OEMCryptoResult rental_status = ODK_CheckRentalWindow(
|
||||||
timer_limits, clock_values, system_time_seconds, &new_timer_value);
|
timer_limits, clock_values, system_time_seconds, &new_timer_value);
|
||||||
|
|
||||||
/* If the rental status forbids playback, then we're done. */
|
/* If the rental status forbids playback, then we're done. */
|
||||||
if ((rental_status != ODK_DISABLE_TIMER) && (rental_status != ODK_SET_TIMER))
|
if ((rental_status != ODK_DISABLE_TIMER) &&
|
||||||
|
(rental_status != ODK_SET_TIMER)) {
|
||||||
return rental_status;
|
return rental_status;
|
||||||
|
}
|
||||||
|
|
||||||
/* If playback has already started and it has hard enforcement, then check
|
/* If playback has already started and it has hard enforcement, then check
|
||||||
* total playback window. */
|
* total playback window. */
|
||||||
@@ -207,8 +213,9 @@ OEMCryptoResult ODK_ComputeRenewalDuration(const ODK_TimerLimits* timer_limits,
|
|||||||
/* If the timer limits forbid playback in the playback window, then we're
|
/* If the timer limits forbid playback in the playback window, then we're
|
||||||
* done. */
|
* done. */
|
||||||
if ((playback_status != ODK_DISABLE_TIMER) &&
|
if ((playback_status != ODK_DISABLE_TIMER) &&
|
||||||
(playback_status != ODK_SET_TIMER))
|
(playback_status != ODK_SET_TIMER)) {
|
||||||
return playback_status;
|
return playback_status;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* If new_timer_value is infinite (represented by 0), then there are no
|
/* If new_timer_value is infinite (represented by 0), then there are no
|
||||||
@@ -219,6 +226,7 @@ OEMCryptoResult ODK_ComputeRenewalDuration(const ODK_TimerLimits* timer_limits,
|
|||||||
ODK_CLOCK_TIMER_STATUS_UNLIMITED);
|
ODK_CLOCK_TIMER_STATUS_UNLIMITED);
|
||||||
return ODK_DISABLE_TIMER;
|
return ODK_DISABLE_TIMER;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* If the caller gave us a pointer to store the new timer value. Fill it. */
|
/* If the caller gave us a pointer to store the new timer value. Fill it. */
|
||||||
if (timer_value != NULL) {
|
if (timer_value != NULL) {
|
||||||
*timer_value = new_timer_value;
|
*timer_value = new_timer_value;
|
||||||
@@ -241,8 +249,9 @@ OEMCryptoResult ODK_InitializeSessionValues(ODK_TimerLimits* timer_limits,
|
|||||||
ODK_NonceValues* nonce_values,
|
ODK_NonceValues* nonce_values,
|
||||||
uint32_t api_major_version,
|
uint32_t api_major_version,
|
||||||
uint32_t session_id) {
|
uint32_t session_id) {
|
||||||
if (clock_values == NULL || clock_values == NULL || nonce_values == NULL)
|
if (timer_limits == NULL || clock_values == NULL || nonce_values == NULL) {
|
||||||
return OEMCrypto_ERROR_INVALID_CONTEXT;
|
return OEMCrypto_ERROR_INVALID_CONTEXT;
|
||||||
|
}
|
||||||
/* Check that the API version passed in from OEMCrypto matches the version of
|
/* Check that the API version passed in from OEMCrypto matches the version of
|
||||||
* this ODK library. */
|
* this ODK library. */
|
||||||
if (api_major_version != ODK_MAJOR_VERSION) {
|
if (api_major_version != ODK_MAJOR_VERSION) {
|
||||||
@@ -269,7 +278,9 @@ OEMCryptoResult ODK_InitializeSessionValues(ODK_TimerLimits* timer_limits,
|
|||||||
* OEMCrypto_GenerateNonce. */
|
* OEMCrypto_GenerateNonce. */
|
||||||
OEMCryptoResult ODK_SetNonceValues(ODK_NonceValues* nonce_values,
|
OEMCryptoResult ODK_SetNonceValues(ODK_NonceValues* nonce_values,
|
||||||
uint32_t nonce) {
|
uint32_t nonce) {
|
||||||
if (nonce_values == NULL) return OEMCrypto_ERROR_INVALID_CONTEXT;
|
if (nonce_values == NULL) {
|
||||||
|
return OEMCrypto_ERROR_INVALID_CONTEXT;
|
||||||
|
}
|
||||||
/* Setting the nonce should only happen once per session. */
|
/* Setting the nonce should only happen once per session. */
|
||||||
if (nonce_values->nonce != 0) {
|
if (nonce_values->nonce != 0) {
|
||||||
return OEMCrypto_ERROR_INVALID_CONTEXT;
|
return OEMCrypto_ERROR_INVALID_CONTEXT;
|
||||||
@@ -281,7 +292,9 @@ OEMCryptoResult ODK_SetNonceValues(ODK_NonceValues* nonce_values,
|
|||||||
/* This is called when OEMCrypto signs a license. */
|
/* This is called when OEMCrypto signs a license. */
|
||||||
OEMCryptoResult ODK_InitializeClockValues(ODK_ClockValues* clock_values,
|
OEMCryptoResult ODK_InitializeClockValues(ODK_ClockValues* clock_values,
|
||||||
uint64_t system_time_seconds) {
|
uint64_t system_time_seconds) {
|
||||||
if (clock_values == NULL) return OEMCrypto_ERROR_INVALID_CONTEXT;
|
if (clock_values == NULL) {
|
||||||
|
return OEMCrypto_ERROR_INVALID_CONTEXT;
|
||||||
|
}
|
||||||
clock_values->time_of_license_signed = system_time_seconds;
|
clock_values->time_of_license_signed = system_time_seconds;
|
||||||
clock_values->time_of_first_decrypt = 0;
|
clock_values->time_of_first_decrypt = 0;
|
||||||
clock_values->time_of_last_decrypt = 0;
|
clock_values->time_of_last_decrypt = 0;
|
||||||
@@ -298,7 +311,9 @@ OEMCryptoResult ODK_ReloadClockValues(ODK_ClockValues* clock_values,
|
|||||||
uint64_t time_of_last_decrypt,
|
uint64_t time_of_last_decrypt,
|
||||||
enum OEMCrypto_Usage_Entry_Status status,
|
enum OEMCrypto_Usage_Entry_Status status,
|
||||||
uint64_t system_time_seconds) {
|
uint64_t system_time_seconds) {
|
||||||
if (clock_values == NULL) return OEMCrypto_ERROR_INVALID_CONTEXT;
|
if (clock_values == NULL) {
|
||||||
|
return OEMCrypto_ERROR_INVALID_CONTEXT;
|
||||||
|
}
|
||||||
clock_values->time_of_license_signed = time_of_license_signed;
|
clock_values->time_of_license_signed = time_of_license_signed;
|
||||||
clock_values->time_of_first_decrypt = time_of_first_decrypt;
|
clock_values->time_of_first_decrypt = time_of_first_decrypt;
|
||||||
clock_values->time_of_last_decrypt = time_of_last_decrypt;
|
clock_values->time_of_last_decrypt = time_of_last_decrypt;
|
||||||
@@ -313,8 +328,9 @@ OEMCryptoResult ODK_AttemptFirstPlayback(uint64_t system_time_seconds,
|
|||||||
const ODK_TimerLimits* timer_limits,
|
const ODK_TimerLimits* timer_limits,
|
||||||
ODK_ClockValues* clock_values,
|
ODK_ClockValues* clock_values,
|
||||||
uint64_t* timer_value) {
|
uint64_t* timer_value) {
|
||||||
if (clock_values == NULL || timer_limits == NULL)
|
if (clock_values == NULL || timer_limits == NULL) {
|
||||||
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
|
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
|
||||||
|
}
|
||||||
/* All times are relative to when the license was signed. */
|
/* All times are relative to when the license was signed. */
|
||||||
uint64_t rental_time = 0;
|
uint64_t rental_time = 0;
|
||||||
if (odk_sub_overflow_u64(system_time_seconds,
|
if (odk_sub_overflow_u64(system_time_seconds,
|
||||||
@@ -328,7 +344,9 @@ OEMCryptoResult ODK_AttemptFirstPlayback(uint64_t system_time_seconds,
|
|||||||
}
|
}
|
||||||
/* If the license is inactive or not loaded, then playback is not allowed. */
|
/* If the license is inactive or not loaded, then playback is not allowed. */
|
||||||
OEMCryptoResult status = ODK_LicenseActive(timer_limits, clock_values);
|
OEMCryptoResult status = ODK_LicenseActive(timer_limits, clock_values);
|
||||||
if (status != OEMCrypto_SUCCESS) return status;
|
if (status != OEMCrypto_SUCCESS) {
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
|
||||||
/* We start with the initial renewal duration as the timer limit. */
|
/* We start with the initial renewal duration as the timer limit. */
|
||||||
uint64_t new_timer_value = timer_limits->initial_renewal_duration_seconds;
|
uint64_t new_timer_value = timer_limits->initial_renewal_duration_seconds;
|
||||||
@@ -348,7 +366,9 @@ OEMCryptoResult ODK_AttemptFirstPlayback(uint64_t system_time_seconds,
|
|||||||
* new_timer_value. */
|
* new_timer_value. */
|
||||||
status = ODK_CheckRentalWindow(timer_limits, clock_values,
|
status = ODK_CheckRentalWindow(timer_limits, clock_values,
|
||||||
system_time_seconds, &new_timer_value);
|
system_time_seconds, &new_timer_value);
|
||||||
if ((status != ODK_DISABLE_TIMER) && (status != ODK_SET_TIMER)) return status;
|
if ((status != ODK_DISABLE_TIMER) && (status != ODK_SET_TIMER)) {
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
|
||||||
/* If playback has not already started, then this is the first playback. */
|
/* If playback has not already started, then this is the first playback. */
|
||||||
if (clock_values->time_of_first_decrypt == 0) {
|
if (clock_values->time_of_first_decrypt == 0) {
|
||||||
@@ -360,7 +380,9 @@ OEMCryptoResult ODK_AttemptFirstPlayback(uint64_t system_time_seconds,
|
|||||||
* restrictions. This might decrease new_timer_value. */
|
* restrictions. This might decrease new_timer_value. */
|
||||||
status = ODK_CheckPlaybackWindow(timer_limits, clock_values,
|
status = ODK_CheckPlaybackWindow(timer_limits, clock_values,
|
||||||
system_time_seconds, &new_timer_value);
|
system_time_seconds, &new_timer_value);
|
||||||
if ((status != ODK_DISABLE_TIMER) && (status != ODK_SET_TIMER)) return status;
|
if ((status != ODK_DISABLE_TIMER) && (status != ODK_SET_TIMER)) {
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
|
||||||
/* We know we are allowed to decrypt. The rest computes the timer duration. */
|
/* We know we are allowed to decrypt. The rest computes the timer duration. */
|
||||||
clock_values->time_of_last_decrypt = system_time_seconds;
|
clock_values->time_of_last_decrypt = system_time_seconds;
|
||||||
@@ -390,7 +412,9 @@ OEMCryptoResult ODK_UpdateLastPlaybackTime(uint64_t system_time_seconds,
|
|||||||
const ODK_TimerLimits* timer_limits,
|
const ODK_TimerLimits* timer_limits,
|
||||||
ODK_ClockValues* clock_values) {
|
ODK_ClockValues* clock_values) {
|
||||||
OEMCryptoResult status = ODK_LicenseActive(timer_limits, clock_values);
|
OEMCryptoResult status = ODK_LicenseActive(timer_limits, clock_values);
|
||||||
if (status != OEMCrypto_SUCCESS) return status;
|
if (status != OEMCrypto_SUCCESS) {
|
||||||
|
return status;
|
||||||
|
}
|
||||||
switch (clock_values->timer_status) {
|
switch (clock_values->timer_status) {
|
||||||
case ODK_CLOCK_TIMER_STATUS_UNLIMITED:
|
case ODK_CLOCK_TIMER_STATUS_UNLIMITED:
|
||||||
break;
|
break;
|
||||||
@@ -413,7 +437,9 @@ OEMCryptoResult ODK_UpdateLastPlaybackTime(uint64_t system_time_seconds,
|
|||||||
|
|
||||||
/* This is called from OEMCrypto_DeactivateUsageEntry. */
|
/* This is called from OEMCrypto_DeactivateUsageEntry. */
|
||||||
OEMCryptoResult ODK_DeactivateUsageEntry(ODK_ClockValues* clock_values) {
|
OEMCryptoResult ODK_DeactivateUsageEntry(ODK_ClockValues* clock_values) {
|
||||||
if (clock_values == NULL) return OEMCrypto_ERROR_UNKNOWN_FAILURE;
|
if (clock_values == NULL) {
|
||||||
|
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
|
||||||
|
}
|
||||||
if (clock_values->status == kUnused) {
|
if (clock_values->status == kUnused) {
|
||||||
clock_values->status = kInactiveUnused;
|
clock_values->status = kInactiveUnused;
|
||||||
} else if (clock_values->status == kActive) {
|
} else if (clock_values->status == kActive) {
|
||||||
@@ -430,8 +456,9 @@ OEMCryptoResult ODK_InitializeV15Values(ODK_TimerLimits* timer_limits,
|
|||||||
ODK_NonceValues* nonce_values,
|
ODK_NonceValues* nonce_values,
|
||||||
uint32_t key_duration,
|
uint32_t key_duration,
|
||||||
uint64_t system_time_seconds) {
|
uint64_t system_time_seconds) {
|
||||||
if (clock_values == NULL || clock_values == NULL || nonce_values == NULL)
|
if (timer_limits == NULL || clock_values == NULL || nonce_values == NULL) {
|
||||||
return OEMCrypto_ERROR_INVALID_CONTEXT;
|
return OEMCrypto_ERROR_INVALID_CONTEXT;
|
||||||
|
}
|
||||||
timer_limits->soft_enforce_playback_duration = false;
|
timer_limits->soft_enforce_playback_duration = false;
|
||||||
timer_limits->soft_enforce_rental_duration = false;
|
timer_limits->soft_enforce_rental_duration = false;
|
||||||
timer_limits->earliest_playback_start_seconds = 0;
|
timer_limits->earliest_playback_start_seconds = 0;
|
||||||
@@ -458,12 +485,14 @@ OEMCryptoResult ODK_RefreshV15Values(const ODK_TimerLimits* timer_limits,
|
|||||||
uint64_t system_time_seconds,
|
uint64_t system_time_seconds,
|
||||||
uint32_t new_key_duration,
|
uint32_t new_key_duration,
|
||||||
uint64_t* timer_value) {
|
uint64_t* timer_value) {
|
||||||
if (timer_limits == NULL || clock_values == NULL || nonce_values == NULL)
|
if (timer_limits == NULL || clock_values == NULL || nonce_values == NULL) {
|
||||||
return OEMCrypto_ERROR_INVALID_CONTEXT;
|
return OEMCrypto_ERROR_INVALID_CONTEXT;
|
||||||
if (nonce_values->api_major_version != 15)
|
}
|
||||||
|
if (nonce_values->api_major_version != 15) {
|
||||||
return OEMCrypto_ERROR_INVALID_NONCE;
|
return OEMCrypto_ERROR_INVALID_NONCE;
|
||||||
|
}
|
||||||
if (clock_values->status > kActive) {
|
if (clock_values->status > kActive) {
|
||||||
clock_values->timer_status = ODK_TIMER_EXPIRED;
|
clock_values->timer_status = ODK_CLOCK_TIMER_STATUS_LICENSE_INACTIVE;
|
||||||
return ODK_TIMER_EXPIRED;
|
return ODK_TIMER_EXPIRED;
|
||||||
}
|
}
|
||||||
return ODK_ComputeRenewalDuration(timer_limits, clock_values,
|
return ODK_ComputeRenewalDuration(timer_limits, clock_values,
|
||||||
|
|||||||
@@ -23,3 +23,12 @@ int crypto_memcmp(const void* in_a, const void* in_b, size_t len) {
|
|||||||
}
|
}
|
||||||
return x;
|
return x;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool ODK_NonceValuesEqual(const ODK_NonceValues* a, const ODK_NonceValues* b) {
|
||||||
|
if (a == NULL || b == NULL) {
|
||||||
|
return (a == b);
|
||||||
|
}
|
||||||
|
return (a->api_major_version == b->api_major_version &&
|
||||||
|
a->api_minor_version == b->api_minor_version &&
|
||||||
|
a->nonce == b->nonce && a->session_id == b->session_id);
|
||||||
|
}
|
||||||
|
|||||||
@@ -8,6 +8,8 @@
|
|||||||
#include <stddef.h>
|
#include <stddef.h>
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
|
||||||
|
#include "odk_structs.h"
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
extern "C" {
|
extern "C" {
|
||||||
#endif
|
#endif
|
||||||
@@ -18,6 +20,8 @@ extern "C" {
|
|||||||
* return value when a != b is undefined, other than being non-zero. */
|
* return value when a != b is undefined, other than being non-zero. */
|
||||||
int crypto_memcmp(const void* a, const void* b, size_t len);
|
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
|
#ifdef __cplusplus
|
||||||
} /* extern "C" */
|
} /* extern "C" */
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -20,8 +20,12 @@ struct _Message {
|
|||||||
MessageStatus status;
|
MessageStatus status;
|
||||||
};
|
};
|
||||||
|
|
||||||
odk_static_assert(SIZE_OF_MESSAGE_STRUCT >= sizeof(Message),
|
/* TODO(b/150776214): this can be removed once AllocateMessage gets cleaned up
|
||||||
"SIZE_OF_MESSAGE_STRUCT too small");
|
*/
|
||||||
|
/*
|
||||||
|
* odk_static_assert(SIZE_OF_MESSAGE_STRUCT >= sizeof(struct _Message),
|
||||||
|
* "SIZE_OF_MESSAGE_STRUCT too small");
|
||||||
|
*/
|
||||||
|
|
||||||
bool ValidMessage(Message* message) {
|
bool ValidMessage(Message* message) {
|
||||||
if (message == NULL) {
|
if (message == NULL) {
|
||||||
@@ -235,7 +239,8 @@ size_t GetSize(Message* message) {
|
|||||||
|
|
||||||
void SetSize(Message* message, size_t size) {
|
void SetSize(Message* message, size_t size) {
|
||||||
if (message == NULL) return;
|
if (message == NULL) return;
|
||||||
if (size > message->capacity) message->status = MESSAGE_STATUS_OVERFLOW_ERROR;
|
if (size > message->capacity)
|
||||||
|
message->status = MESSAGE_STATUS_OVERFLOW_ERROR;
|
||||||
else
|
else
|
||||||
message->size = size;
|
message->size = size;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -28,10 +28,20 @@ extern "C" {
|
|||||||
*/
|
*/
|
||||||
#define AllocateMessage(msg, blk) \
|
#define AllocateMessage(msg, blk) \
|
||||||
uint8_t blk[SIZE_OF_MESSAGE_STRUCT]; \
|
uint8_t blk[SIZE_OF_MESSAGE_STRUCT]; \
|
||||||
*(msg) = (Message*)(blk);
|
*(msg) = (Message*)(blk)
|
||||||
|
|
||||||
typedef struct _Message Message;
|
typedef struct _Message Message;
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
MESSAGE_STATUS_OK,
|
||||||
|
MESSAGE_STATUS_UNKNOWN_ERROR,
|
||||||
|
MESSAGE_STATUS_OVERFLOW_ERROR,
|
||||||
|
MESSAGE_STATUS_UNDERFLOW_ERROR,
|
||||||
|
MESSAGE_STATUS_PARSE_ERROR,
|
||||||
|
MESSAGE_STATUS_NULL_POINTER_ERROR,
|
||||||
|
MESSAGE_STATUS_API_VALUE_ERROR
|
||||||
|
} MessageStatus;
|
||||||
|
|
||||||
bool ValidMessage(Message* message);
|
bool ValidMessage(Message* message);
|
||||||
|
|
||||||
void Pack_enum(Message* message, int value);
|
void Pack_enum(Message* message, int value);
|
||||||
@@ -51,16 +61,6 @@ void UnpackArray(Message* message, uint8_t* address,
|
|||||||
size_t size); /* copy out */
|
size_t size); /* copy out */
|
||||||
void Unpack_OEMCrypto_Substring(Message* msg, OEMCrypto_Substring* obj);
|
void Unpack_OEMCrypto_Substring(Message* msg, OEMCrypto_Substring* obj);
|
||||||
|
|
||||||
typedef enum {
|
|
||||||
MESSAGE_STATUS_OK,
|
|
||||||
MESSAGE_STATUS_UNKNOWN_ERROR,
|
|
||||||
MESSAGE_STATUS_OVERFLOW_ERROR,
|
|
||||||
MESSAGE_STATUS_UNDERFLOW_ERROR,
|
|
||||||
MESSAGE_STATUS_PARSE_ERROR,
|
|
||||||
MESSAGE_STATUS_NULL_POINTER_ERROR,
|
|
||||||
MESSAGE_STATUS_API_VALUE_ERROR
|
|
||||||
} MessageStatus;
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Create a message from a buffer. The message structure consumes the first
|
* Create a message from a buffer. The message structure consumes the first
|
||||||
* sizeof(Message) bytes of the buffer. The caller is responsible for ensuring
|
* sizeof(Message) bytes of the buffer. The caller is responsible for ensuring
|
||||||
|
|||||||
@@ -9,7 +9,7 @@
|
|||||||
#include <cstring>
|
#include <cstring>
|
||||||
#include <functional>
|
#include <functional>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <string>
|
#include <vector>
|
||||||
|
|
||||||
#include "OEMCryptoCENCCommon.h"
|
#include "OEMCryptoCENCCommon.h"
|
||||||
#include "core_message_deserialize.h"
|
#include "core_message_deserialize.h"
|
||||||
@@ -20,210 +20,307 @@
|
|||||||
#include "odk_structs.h"
|
#include "odk_structs.h"
|
||||||
#include "odk_structs_priv.h"
|
#include "odk_structs_priv.h"
|
||||||
|
|
||||||
// TODO(b/147297226): remove this: using namespace std;
|
typedef std::function<void(const uint8_t*, uint8_t*, size_t, size_t)>
|
||||||
// TODO(b/147297226): remove this: using namespace oec_util;
|
roundtrip_fun;
|
||||||
|
|
||||||
typedef std::function<size_t(const uint8_t*, uint8_t*, size_t)> roundtrip_fun;
|
using oemcrypto_core_message::ODK_LicenseRequest;
|
||||||
|
using oemcrypto_core_message::ODK_ProvisioningRequest;
|
||||||
|
using oemcrypto_core_message::ODK_RenewalRequest;
|
||||||
|
|
||||||
// @ kdo deserialize; odk derialize
|
using oemcrypto_core_message::deserialize::CoreLicenseRequestFromMessage;
|
||||||
static OEMCryptoResult odk_fun_LicenseRequest(
|
using oemcrypto_core_message::deserialize::CoreProvisioningRequestFromMessage;
|
||||||
uint8_t* out, size_t* size, uint32_t api_version, uint32_t nonce,
|
using oemcrypto_core_message::deserialize::CoreRenewalRequestFromMessage;
|
||||||
uint32_t session_id, const ODK_LicenseRequest& /*core_license_request*/) {
|
|
||||||
return ODK_PrepareCoreLicenseRequest(out, SIZE_MAX, size, api_version, nonce,
|
using oemcrypto_core_message::serialize::CreateCoreLicenseResponse;
|
||||||
session_id);
|
using oemcrypto_core_message::serialize::CreateCoreProvisioningResponse;
|
||||||
|
using oemcrypto_core_message::serialize::CreateCoreRenewalResponse;
|
||||||
|
|
||||||
|
// @ kdo deserialize; odk serialize
|
||||||
|
static OEMCryptoResult odk_serialize_LicenseRequest(
|
||||||
|
const void* in, uint8_t* out, size_t* size,
|
||||||
|
const ODK_LicenseRequest& core_license_request,
|
||||||
|
const ODK_NonceValues* nonce_values) {
|
||||||
|
return ODK_PrepareCoreLicenseRequest(out, SIZE_MAX, size, nonce_values);
|
||||||
}
|
}
|
||||||
|
|
||||||
static OEMCryptoResult odk_fun_RenewalRequest(
|
static OEMCryptoResult odk_serialize_RenewalRequest(
|
||||||
uint8_t* out, size_t* size, uint32_t api_version, uint32_t nonce,
|
const void* in, uint8_t* out, size_t* size,
|
||||||
uint32_t session_id, const ODK_RenewalRequest& core_renewal) {
|
const ODK_RenewalRequest& core_renewal, ODK_NonceValues* nonce_values) {
|
||||||
// todo: fuzz ODK_ClockValues
|
ODK_ClockValues clock{};
|
||||||
ODK_ClockValues clock = {};
|
memcpy(&clock, in, sizeof(ODK_ClockValues));
|
||||||
uint64_t system_time_seconds = core_renewal.playback_time;
|
uint64_t system_time_seconds = core_renewal.playback_time_seconds;
|
||||||
return ODK_PrepareCoreRenewalRequest(out, SIZE_MAX, size, api_version, nonce,
|
return ODK_PrepareCoreRenewalRequest(out, SIZE_MAX, size, nonce_values,
|
||||||
session_id, &clock, system_time_seconds);
|
&clock, system_time_seconds);
|
||||||
}
|
}
|
||||||
|
|
||||||
static OEMCryptoResult odk_fun_ProvisioningRequest(
|
static OEMCryptoResult odk_serialize_ProvisioningRequest(
|
||||||
uint8_t* out, size_t* size, uint32_t api_version, uint32_t nonce,
|
const void* in, uint8_t* out, size_t* size,
|
||||||
uint32_t session_id, const ODK_ProvisioningRequest& core_provisioning) {
|
const ODK_ProvisioningRequest& core_provisioning,
|
||||||
|
const ODK_NonceValues* nonce_values) {
|
||||||
const std::string& device_id = core_provisioning.device_id;
|
const std::string& device_id = core_provisioning.device_id;
|
||||||
return ODK_PrepareCoreProvisioningRequest(
|
return ODK_PrepareCoreProvisioningRequest(
|
||||||
out, SIZE_MAX, size, api_version, nonce, session_id,
|
out, SIZE_MAX, size, nonce_values,
|
||||||
reinterpret_cast<const uint8_t*>(device_id.data()), device_id.size());
|
reinterpret_cast<const uint8_t*>(device_id.data()), device_id.size());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Template arguments:
|
||||||
|
* T: kdo deserialize output/odk serialize input structure
|
||||||
|
* F: kdo deserialize function
|
||||||
|
* G: odk serialize function
|
||||||
|
*
|
||||||
|
* raw bytes -> F deserialize -> struct T -> G serialize -> raw bytes
|
||||||
|
*/
|
||||||
template <typename T, typename F, typename G>
|
template <typename T, typename F, typename G>
|
||||||
static roundtrip_fun kdo_odk(const F& kdo_fun, const G& odk_fun) {
|
static roundtrip_fun kdo_odk(const F& kdo_fun, const G& odk_fun) {
|
||||||
auto roundtrip = [&](const uint8_t* in, uint8_t* out, size_t size) -> size_t {
|
auto roundtrip = [&](const uint8_t* in, uint8_t* out, size_t size,
|
||||||
std::string input(reinterpret_cast<const char*>(in), size);
|
size_t clock_value_size) -> void {
|
||||||
|
if (size <= clock_value_size) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// Input byte array format: [Clock Values][data to parse]
|
||||||
|
std::string input(reinterpret_cast<const char*>(in) + clock_value_size,
|
||||||
|
size - clock_value_size);
|
||||||
T t = {};
|
T t = {};
|
||||||
if (!kdo_fun(input, &t)) {
|
if (!kdo_fun(input, &t)) {
|
||||||
return 0;
|
return;
|
||||||
}
|
}
|
||||||
OEMCryptoResult err =
|
ODK_NonceValues nonce_values = {t.api_minor_version, t.api_major_version,
|
||||||
odk_fun(out, &size, t.api_version, t.nonce, t.session_id, t);
|
t.nonce, t.session_id};
|
||||||
return OEMCrypto_SUCCESS == err ? size : 0;
|
OEMCryptoResult err = odk_fun(in, out, &size, t, &nonce_values);
|
||||||
|
if (OEMCrypto_SUCCESS != err) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
assert(0 == memcmp(in + clock_value_size, out, size));
|
||||||
};
|
};
|
||||||
return roundtrip;
|
return roundtrip;
|
||||||
}
|
}
|
||||||
|
|
||||||
// @ odk deserialize; kdo serialize
|
// @ odk deserialize; kdo serialize
|
||||||
namespace {
|
namespace {
|
||||||
struct ODK_Common_Args {
|
|
||||||
uint32_t api_version;
|
|
||||||
uint32_t nonce;
|
|
||||||
uint32_t session_id;
|
|
||||||
};
|
|
||||||
struct ODK_ParseLicense_Args {
|
struct ODK_ParseLicense_Args {
|
||||||
ODK_Common_Args common;
|
ODK_NonceValues nonce_values;
|
||||||
uint8_t initial_license_load;
|
uint8_t initial_license_load;
|
||||||
uint8_t usage_entry_present;
|
uint8_t usage_entry_present;
|
||||||
|
uint8_t request_hash[32];
|
||||||
|
ODK_TimerLimits timer_limits;
|
||||||
|
ODK_ClockValues clock_values;
|
||||||
};
|
};
|
||||||
struct ODK_ParseRenewal_Args {
|
struct ODK_ParseRenewal_Args {
|
||||||
ODK_Common_Args common;
|
ODK_NonceValues nonce_values;
|
||||||
uint64_t system_time;
|
uint64_t system_time;
|
||||||
ODK_TimerLimits timer_limits;
|
ODK_TimerLimits timer_limits;
|
||||||
ODK_ClockValues clock_values;
|
ODK_ClockValues clock_values;
|
||||||
};
|
};
|
||||||
struct ODK_ParseProvisioning_Args {
|
struct ODK_ParseProvisioning_Args {
|
||||||
ODK_Common_Args common;
|
ODK_NonceValues nonce_values;
|
||||||
size_t device_id_length;
|
size_t device_id_length;
|
||||||
uint8_t device_id[64];
|
uint8_t device_id[64];
|
||||||
};
|
};
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
static OEMCryptoResult odk_fun_LicenseResponse(
|
bool convert_byte_to_valid_boolean(const bool* in) {
|
||||||
const uint8_t* message, size_t message_length, uint32_t api_version,
|
const int value = *reinterpret_cast<const int*>(in);
|
||||||
uint32_t nonce, uint32_t session_id, const ODK_ParseLicense_Args* a,
|
return value != 0;
|
||||||
ODK_ParsedLicense& parsed_lic) {
|
|
||||||
return ODK_ParseLicense(
|
|
||||||
message, message_length, api_version, nonce, session_id,
|
|
||||||
static_cast<bool>(a->initial_license_load),
|
|
||||||
static_cast<bool>(a->usage_entry_present), &parsed_lic);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool kdo_fun_LicenseResponse(const ODK_ParseLicense_Args* args,
|
static OEMCryptoResult odk_deserialize_LicenseResponse(
|
||||||
const ODK_ParsedLicense& parsed_lic,
|
const uint8_t* message, size_t core_message_length,
|
||||||
std::string* oemcrypto_core_message) {
|
ODK_ParseLicense_Args* a, ODK_NonceValues* nonce_values,
|
||||||
const auto& common = args->common;
|
ODK_ParsedLicense* parsed_lic) {
|
||||||
ODK_LicenseRequest core_request{common.api_version, common.nonce,
|
return ODK_ParseLicense(message, SIZE_MAX, core_message_length,
|
||||||
common.session_id};
|
static_cast<bool>(a->initial_license_load),
|
||||||
return CreateCoreLicenseResponse(parsed_lic, core_request,
|
static_cast<bool>(a->usage_entry_present),
|
||||||
oemcrypto_core_message);
|
a->request_hash, &a->timer_limits, &a->clock_values,
|
||||||
|
nonce_values, parsed_lic);
|
||||||
}
|
}
|
||||||
|
|
||||||
static OEMCryptoResult odk_fun_RenewalResponse(
|
static bool kdo_serialize_LicenseResponse(const ODK_ParseLicense_Args* args,
|
||||||
const uint8_t* buf, size_t len, uint32_t api_version, uint32_t nonce,
|
const ODK_ParsedLicense& parsed_lic,
|
||||||
uint32_t session_id, ODK_ParseRenewal_Args* a,
|
std::string* oemcrypto_core_message) {
|
||||||
ODK_PreparedRenewalRequest& renewal_msg) {
|
const auto& nonce_values = args->nonce_values;
|
||||||
|
ODK_LicenseRequest core_request{nonce_values.api_minor_version,
|
||||||
|
nonce_values.api_major_version,
|
||||||
|
nonce_values.nonce, nonce_values.session_id};
|
||||||
|
std::string core_request_sha_256(
|
||||||
|
reinterpret_cast<const char*>(args->request_hash), 32);
|
||||||
|
return CreateCoreLicenseResponse(
|
||||||
|
parsed_lic, core_request, core_request_sha_256, oemcrypto_core_message);
|
||||||
|
}
|
||||||
|
|
||||||
|
static OEMCryptoResult odk_deserialize_RenewalResponse(
|
||||||
|
const uint8_t* buf, size_t len, ODK_ParseRenewal_Args* a,
|
||||||
|
ODK_NonceValues* nonce_values, ODK_PreparedRenewalRequest* renewal_msg) {
|
||||||
|
/* Address Sanitizer doesn't like values other than 0 OR 1 for boolean
|
||||||
|
* variables. Input from fuzzer can be parsed and any random bytes can be
|
||||||
|
* assigned to boolean variables. Using the workaround to mitigate sanitizer
|
||||||
|
* errors in fuzzer code and converting random bytes to 0 OR 1.
|
||||||
|
* This has no negative security impact*/
|
||||||
|
a->timer_limits.soft_enforce_playback_duration =
|
||||||
|
convert_byte_to_valid_boolean(
|
||||||
|
&a->timer_limits.soft_enforce_playback_duration);
|
||||||
|
a->timer_limits.soft_enforce_rental_duration = convert_byte_to_valid_boolean(
|
||||||
|
&a->timer_limits.soft_enforce_rental_duration);
|
||||||
uint64_t timer_value = 0;
|
uint64_t timer_value = 0;
|
||||||
OEMCryptoResult err =
|
OEMCryptoResult err =
|
||||||
ODK_ParseRenewal(buf, len, api_version, nonce, session_id, a->system_time,
|
ODK_ParseRenewal(buf, SIZE_MAX, len, nonce_values, a->system_time,
|
||||||
&a->timer_limits, &a->clock_values, &timer_value);
|
&a->timer_limits, &a->clock_values, &timer_value);
|
||||||
if (OEMCrypto_SUCCESS == err) {
|
if (OEMCrypto_SUCCESS == err) {
|
||||||
Message* msg = nullptr;
|
Message* msg = nullptr;
|
||||||
AllocateMessage(&msg, message_block);
|
AllocateMessage(&msg, message_block);
|
||||||
InitMessage(msg, const_cast<uint8_t*>(buf), len);
|
InitMessage(msg, const_cast<uint8_t*>(buf), len);
|
||||||
SetSize(msg, len);
|
SetSize(msg, len);
|
||||||
Unpack_ODK_PreparedRenewalRequest(msg, &renewal_msg);
|
Unpack_ODK_PreparedRenewalRequest(msg, renewal_msg);
|
||||||
assert(ValidMessage(msg));
|
assert(ValidMessage(msg));
|
||||||
}
|
}
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool kdo_fun_RenewalResponse(
|
static bool kdo_serialize_RenewalResponse(
|
||||||
const ODK_ParseRenewal_Args* args,
|
const ODK_ParseRenewal_Args* args,
|
||||||
const ODK_PreparedRenewalRequest& renewal_msg,
|
const ODK_PreparedRenewalRequest& renewal_msg,
|
||||||
std::string* oemcrypto_core_message) {
|
std::string* oemcrypto_core_message) {
|
||||||
const auto& common = args->common;
|
const auto& nonce_values = args->nonce_values;
|
||||||
ODK_RenewalRequest core_request{common.api_version, common.nonce,
|
ODK_RenewalRequest core_request{
|
||||||
common.session_id, renewal_msg.playback_time};
|
nonce_values.api_minor_version, nonce_values.api_major_version,
|
||||||
return CreateCoreRenewalResponse(core_request, oemcrypto_core_message);
|
nonce_values.nonce, nonce_values.session_id, renewal_msg.playback_time};
|
||||||
|
return CreateCoreRenewalResponse(
|
||||||
|
core_request, args->timer_limits.initial_renewal_duration_seconds,
|
||||||
|
oemcrypto_core_message);
|
||||||
}
|
}
|
||||||
|
|
||||||
static OEMCryptoResult odk_fun_ProvisioningResponse(
|
static OEMCryptoResult odk_deserialize_ProvisioningResponse(
|
||||||
const uint8_t* buf, size_t len, uint32_t api_version, uint32_t nonce,
|
const uint8_t* buf, size_t len, ODK_ParseProvisioning_Args* a,
|
||||||
uint32_t session_id, ODK_ParseProvisioning_Args* a,
|
ODK_NonceValues* nonce_values, ODK_ParsedProvisioning* parsed_prov) {
|
||||||
ODK_ParsedProvisioning& parsed_prov) {
|
return ODK_ParseProvisioning(buf, SIZE_MAX, len, nonce_values, a->device_id,
|
||||||
return ODK_ParseProvisioning(buf, len, api_version, nonce, session_id,
|
a->device_id_length, parsed_prov);
|
||||||
a->device_id, a->device_id_length, &parsed_prov);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool kdo_fun_ProvisioningResponse(
|
static bool kdo_serialize_ProvisioningResponse(
|
||||||
const ODK_ParseProvisioning_Args* args,
|
const ODK_ParseProvisioning_Args* args,
|
||||||
const ODK_ParsedProvisioning& parsed_prov,
|
const ODK_ParsedProvisioning& parsed_prov,
|
||||||
std::string* oemcrypto_core_message) {
|
std::string* oemcrypto_core_message) {
|
||||||
const auto& common = args->common;
|
const auto& nonce_values = args->nonce_values;
|
||||||
assert(args->device_id_length <= sizeof(args->device_id));
|
if (args->device_id_length > sizeof(args->device_id)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
ODK_ProvisioningRequest core_request{
|
ODK_ProvisioningRequest core_request{
|
||||||
common.api_version, common.nonce, common.session_id,
|
nonce_values.api_minor_version, nonce_values.api_major_version,
|
||||||
|
nonce_values.nonce, nonce_values.session_id,
|
||||||
std::string(reinterpret_cast<const char*>(args->device_id),
|
std::string(reinterpret_cast<const char*>(args->device_id),
|
||||||
args->device_id_length)};
|
args->device_id_length)};
|
||||||
return CreateCoreProvisioningResponse(parsed_prov, core_request,
|
return CreateCoreProvisioningResponse(parsed_prov, core_request,
|
||||||
oemcrypto_core_message);
|
oemcrypto_core_message);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Template arguments:
|
||||||
|
* A: struct holding function arguments
|
||||||
|
* T: odk deserialize output/kdo serialize input structure
|
||||||
|
* F: odk deserialize function
|
||||||
|
* G: kdo serialize function
|
||||||
|
*
|
||||||
|
* raw bytes -> F deserialize -> struct T -> G serialize -> raw bytes
|
||||||
|
*/
|
||||||
template <typename A, typename T, typename F, typename G>
|
template <typename A, typename T, typename F, typename G>
|
||||||
static roundtrip_fun odk_kdo(const F& odk_fun, const G& kdo_fun) {
|
static roundtrip_fun odk_kdo(const F& odk_fun, const G& kdo_fun) {
|
||||||
auto roundtrip = [&](const uint8_t* in, uint8_t* out, size_t size) -> size_t {
|
auto roundtrip = [&](const uint8_t* in, uint8_t* out, size_t size,
|
||||||
if (sizeof(A) > size) {
|
size_t args_size) -> void {
|
||||||
return 0;
|
// Input byte array format: [function arguments][data to parse]
|
||||||
|
if (args_size > size) {
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
T t = {};
|
T t = {};
|
||||||
const uint8_t* buf = in + sizeof(A);
|
const uint8_t* buf = in + args_size;
|
||||||
size_t len = size - sizeof(A);
|
|
||||||
std::shared_ptr<A> _args(new A());
|
std::shared_ptr<A> _args(new A());
|
||||||
A* args = _args.get();
|
A* args = _args.get();
|
||||||
memcpy(args, in, sizeof(A));
|
memcpy(args, in, args_size);
|
||||||
const auto& common = args->common;
|
args->nonce_values.api_major_version = ODK_MAJOR_VERSION;
|
||||||
OEMCryptoResult err = odk_fun(buf, len, common.api_version, common.nonce,
|
args->nonce_values.api_minor_version = ODK_MINOR_VERSION;
|
||||||
common.session_id, args, t);
|
/*
|
||||||
if (err != OEMCrypto_SUCCESS) {
|
* Input random bytes from autofuzz are interpreted by this script as
|
||||||
return 0;
|
* [function args][data to parse]. Odk deserialize functions
|
||||||
}
|
* expect the nonce values in function args to match with those
|
||||||
|
* in data to parse which is not possible with random bytes.
|
||||||
|
* We follow two pass approach.
|
||||||
|
*
|
||||||
|
* 1st pass - We copy random bytes into struct t and call kdo serialize
|
||||||
|
* with function args which will create oemcrypto core message using nonce
|
||||||
|
* from function args. Now we have a valid oemcrypto core message which is
|
||||||
|
* formed using nonce_values from function args which acts as input bytes
|
||||||
|
* for 2nd pass
|
||||||
|
*
|
||||||
|
* 2nd pass - oemcrypto core message from 1st pass guarantees that
|
||||||
|
* nonce_values in [function args] and core message match. we call
|
||||||
|
* odk_deserialize using nonce from function args and oemcrypto core message
|
||||||
|
* from 1st pass. Then we call kdo function which generates oemcrypto core
|
||||||
|
* message2, which should be equal to oemcrypto_core_message which was input
|
||||||
|
* to 2nd pass
|
||||||
|
*/
|
||||||
|
// TODO(ellurubharath): Use structure aware fuzzing
|
||||||
|
// 1st pass
|
||||||
|
memcpy(&t, buf, sizeof(t));
|
||||||
std::string oemcrypto_core_message;
|
std::string oemcrypto_core_message;
|
||||||
if (!kdo_fun(args, t, &oemcrypto_core_message)) {
|
if (!kdo_fun(args, t, &oemcrypto_core_message)) {
|
||||||
return 0;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
assert(oemcrypto_core_message.size() <= size);
|
assert(oemcrypto_core_message.size() <= size);
|
||||||
memcpy(out, oemcrypto_core_message.data(), oemcrypto_core_message.size());
|
|
||||||
return oemcrypto_core_message.size();
|
// 2nd pass
|
||||||
|
ODK_NonceValues nonce_values = args->nonce_values;
|
||||||
|
OEMCryptoResult result =
|
||||||
|
odk_fun(reinterpret_cast<const uint8_t*>(oemcrypto_core_message.data()),
|
||||||
|
oemcrypto_core_message.size(), args, &nonce_values, &t);
|
||||||
|
if (result != OEMCrypto_SUCCESS) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
std::string oemcrypto_core_message2;
|
||||||
|
if (!kdo_fun(args, t, &oemcrypto_core_message2)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
assert(oemcrypto_core_message == oemcrypto_core_message2);
|
||||||
};
|
};
|
||||||
return roundtrip;
|
return roundtrip;
|
||||||
}
|
}
|
||||||
|
|
||||||
// @ fuzz raw -> parsed -> raw
|
// @ fuzz raw -> parsed -> raw
|
||||||
static void verify_roundtrip(const uint8_t* in, size_t size,
|
static void verify_roundtrip(const uint8_t* in, size_t size,
|
||||||
roundtrip_fun roundtrip) {
|
roundtrip_fun roundtrip, size_t args_size) {
|
||||||
std::vector<uint8_t> _out(size);
|
std::vector<uint8_t> _out(size);
|
||||||
auto out = _out.data();
|
auto out = _out.data();
|
||||||
size_t n = roundtrip(in, out, size);
|
roundtrip(in, out, size, args_size);
|
||||||
assert(!n || (n <= size && 0 == memcmp(in, out, n)));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Entry point for fuzzer, data is random bytes program gets from autofuzzer
|
||||||
extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
|
extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
|
||||||
|
verify_roundtrip(data, size,
|
||||||
|
kdo_odk<ODK_LicenseRequest>(CoreLicenseRequestFromMessage,
|
||||||
|
odk_serialize_LicenseRequest),
|
||||||
|
0);
|
||||||
|
verify_roundtrip(data, size,
|
||||||
|
kdo_odk<ODK_RenewalRequest>(CoreRenewalRequestFromMessage,
|
||||||
|
odk_serialize_RenewalRequest),
|
||||||
|
sizeof(ODK_ClockValues));
|
||||||
verify_roundtrip(
|
verify_roundtrip(
|
||||||
data, size,
|
data, size,
|
||||||
kdo_odk<ODK_LicenseRequest>(ParseLicenseRequest, odk_fun_LicenseRequest));
|
kdo_odk<ODK_ProvisioningRequest>(CoreProvisioningRequestFromMessage,
|
||||||
|
odk_serialize_ProvisioningRequest),
|
||||||
|
0);
|
||||||
verify_roundtrip(
|
verify_roundtrip(
|
||||||
data, size,
|
data, size,
|
||||||
kdo_odk<ODK_RenewalRequest>(ParseRenewalRequest, odk_fun_RenewalRequest));
|
odk_kdo<ODK_ParseLicense_Args, ODK_ParsedLicense>(
|
||||||
verify_roundtrip(data, size,
|
odk_deserialize_LicenseResponse, kdo_serialize_LicenseResponse),
|
||||||
kdo_odk<ODK_ProvisioningRequest>(
|
sizeof(ODK_ParseLicense_Args));
|
||||||
ParseProvisioningRequest, odk_fun_ProvisioningRequest));
|
|
||||||
verify_roundtrip(data, size,
|
|
||||||
odk_kdo<ODK_ParseLicense_Args, ODK_ParsedLicense>(
|
|
||||||
odk_fun_LicenseResponse, kdo_fun_LicenseResponse));
|
|
||||||
verify_roundtrip(data, size,
|
|
||||||
odk_kdo<ODK_ParseRenewal_Args, ODK_PreparedRenewalRequest>(
|
|
||||||
odk_fun_RenewalResponse, kdo_fun_RenewalResponse));
|
|
||||||
verify_roundtrip(
|
verify_roundtrip(
|
||||||
data, size,
|
data, size,
|
||||||
odk_kdo<ODK_ParseProvisioning_Args, ODK_ParsedProvisioning>(
|
odk_kdo<ODK_ParseRenewal_Args, ODK_PreparedRenewalRequest>(
|
||||||
odk_fun_ProvisioningResponse, kdo_fun_ProvisioningResponse));
|
odk_deserialize_RenewalResponse, kdo_serialize_RenewalResponse),
|
||||||
|
sizeof(ODK_ParseRenewal_Args));
|
||||||
|
verify_roundtrip(data, size,
|
||||||
|
odk_kdo<ODK_ParseProvisioning_Args, ODK_ParsedProvisioning>(
|
||||||
|
odk_deserialize_ProvisioningResponse,
|
||||||
|
kdo_serialize_ProvisioningResponse),
|
||||||
|
sizeof(ODK_ParseProvisioning_Args));
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -5,6 +5,8 @@
|
|||||||
{
|
{
|
||||||
'sources': [
|
'sources': [
|
||||||
'odk_test.cpp',
|
'odk_test.cpp',
|
||||||
|
'odk_test_helper.cpp',
|
||||||
|
'odk_test_helper.h',
|
||||||
'odk_timer_test.cpp',
|
'odk_timer_test.cpp',
|
||||||
],
|
],
|
||||||
}
|
}
|
||||||
|
|||||||
488
oemcrypto/odk/test/odk_test_helper.cpp
Normal file
488
oemcrypto/odk/test/odk_test_helper.cpp
Normal file
@@ -0,0 +1,488 @@
|
|||||||
|
// 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_test_helper.h"
|
||||||
|
|
||||||
|
#include <endian.h>
|
||||||
|
|
||||||
|
#include <cstdint>
|
||||||
|
#include <cstdlib>
|
||||||
|
#include <cstring>
|
||||||
|
#include <fstream>
|
||||||
|
#include <iostream>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#include "OEMCryptoCENCCommon.h"
|
||||||
|
#include "gtest/gtest.h"
|
||||||
|
#include "odk_structs.h"
|
||||||
|
#include "odk_structs_priv.h"
|
||||||
|
|
||||||
|
namespace wvodk_test {
|
||||||
|
|
||||||
|
void ODK_SetDefaultCoreFields(ODK_CoreMessage* core_message,
|
||||||
|
uint32_t message_type) {
|
||||||
|
ASSERT_TRUE(core_message != nullptr);
|
||||||
|
core_message->message_type = message_type;
|
||||||
|
core_message->message_length = 0;
|
||||||
|
core_message->nonce_values.api_minor_version = ODK_MINOR_VERSION;
|
||||||
|
core_message->nonce_values.api_major_version = ODK_MAJOR_VERSION;
|
||||||
|
core_message->nonce_values.nonce = 0xdeadbeef;
|
||||||
|
core_message->nonce_values.session_id = 0xcafebabe;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ODK_SetDefaultLicenseResponseParams(ODK_LicenseResponseParams* params) {
|
||||||
|
ODK_SetDefaultCoreFields(&(params->core_message), ODK_License_Response_Type);
|
||||||
|
params->initial_license_load = true;
|
||||||
|
params->usage_entry_present = true;
|
||||||
|
params->parsed_license = {
|
||||||
|
.enc_mac_keys_iv = {.offset = 0, .length = 1},
|
||||||
|
.enc_mac_keys = {.offset = 2, .length = 3},
|
||||||
|
.pst = {.offset = 4, .length = 5},
|
||||||
|
.srm_restriction_data = {.offset = 6, .length = 7},
|
||||||
|
.license_type = OEMCrypto_EntitlementLicense,
|
||||||
|
.nonce_required = true,
|
||||||
|
.timer_limits =
|
||||||
|
{
|
||||||
|
.soft_enforce_rental_duration = true,
|
||||||
|
.soft_enforce_playback_duration = false,
|
||||||
|
.earliest_playback_start_seconds = 10,
|
||||||
|
.rental_duration_seconds = 11,
|
||||||
|
.total_playback_duration_seconds = 12,
|
||||||
|
.initial_renewal_duration_seconds = 13,
|
||||||
|
},
|
||||||
|
.key_array_length = 3,
|
||||||
|
.key_array =
|
||||||
|
{
|
||||||
|
{
|
||||||
|
.key_id = {.offset = 15, .length = 16},
|
||||||
|
.key_data_iv = {.offset = 17, .length = 18},
|
||||||
|
.key_data = {.offset = 19, .length = 20},
|
||||||
|
.key_control_iv = {.offset = 21, .length = 22},
|
||||||
|
.key_control = {.offset = 23, .length = 24},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.key_id = {.offset = 25, .length = 26},
|
||||||
|
.key_data_iv = {.offset = 27, .length = 28},
|
||||||
|
.key_data = {.offset = 29, .length = 30},
|
||||||
|
.key_control_iv = {.offset = 31, .length = 32},
|
||||||
|
.key_control = {.offset = 33, .length = 34},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.key_id = {.offset = 35, .length = 36},
|
||||||
|
.key_data_iv = {.offset = 37, .length = 38},
|
||||||
|
.key_data = {.offset = 39, .length = 40},
|
||||||
|
.key_control_iv = {.offset = 41, .length = 42},
|
||||||
|
.key_control = {.offset = 43, .length = 44},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
memset(params->request_hash, 0xaa, sizeof(params->request_hash));
|
||||||
|
params->extra_fields = {
|
||||||
|
{ODK_SUBSTRING, &(params->parsed_license.enc_mac_keys_iv),
|
||||||
|
".enc_mac_keys_iv"},
|
||||||
|
{ODK_SUBSTRING, &(params->parsed_license.enc_mac_keys), ".enc_mac_keys"},
|
||||||
|
{ODK_SUBSTRING, &(params->parsed_license.pst), ".pst"},
|
||||||
|
{ODK_SUBSTRING, &(params->parsed_license.srm_restriction_data),
|
||||||
|
".srm_restriction_data"},
|
||||||
|
{ODK_UINT32, &(params->parsed_license.license_type), ".license_type"},
|
||||||
|
{ODK_UINT32, &(params->parsed_license.nonce_required), ".nonce_required"},
|
||||||
|
{ODK_UINT32,
|
||||||
|
&(params->parsed_license.timer_limits.soft_enforce_rental_duration),
|
||||||
|
".soft_enforce_rental_duration"},
|
||||||
|
{ODK_UINT32,
|
||||||
|
&(params->parsed_license.timer_limits.soft_enforce_playback_duration),
|
||||||
|
".soft_enforce_playback_duration"},
|
||||||
|
{ODK_UINT64,
|
||||||
|
&(params->parsed_license.timer_limits.earliest_playback_start_seconds),
|
||||||
|
".earliest_playback_start_seconds"},
|
||||||
|
{ODK_UINT64,
|
||||||
|
&(params->parsed_license.timer_limits.rental_duration_seconds),
|
||||||
|
".rental_duration_seconds"},
|
||||||
|
{ODK_UINT64,
|
||||||
|
&(params->parsed_license.timer_limits.total_playback_duration_seconds),
|
||||||
|
".total_playback_duration_seconds"},
|
||||||
|
{ODK_UINT64,
|
||||||
|
&(params->parsed_license.timer_limits.initial_renewal_duration_seconds),
|
||||||
|
".initial_renewal_duration_seconds"},
|
||||||
|
{ODK_UINT32, &(params->parsed_license.key_array_length),
|
||||||
|
".key_array_length"},
|
||||||
|
{ODK_SUBSTRING, &(params->parsed_license.key_array[0].key_id), ".key_id"},
|
||||||
|
{ODK_SUBSTRING, &(params->parsed_license.key_array[0].key_data_iv),
|
||||||
|
".key_data_iv"},
|
||||||
|
{ODK_SUBSTRING, &(params->parsed_license.key_array[0].key_data),
|
||||||
|
".key_data"},
|
||||||
|
{ODK_SUBSTRING, &(params->parsed_license.key_array[0].key_control_iv),
|
||||||
|
".key_control_iv"},
|
||||||
|
{ODK_SUBSTRING, &(params->parsed_license.key_array[0].key_control),
|
||||||
|
".key_control"},
|
||||||
|
{ODK_SUBSTRING, &(params->parsed_license.key_array[1].key_id), ".key_id"},
|
||||||
|
{ODK_SUBSTRING, &(params->parsed_license.key_array[1].key_data_iv),
|
||||||
|
".key_data_iv"},
|
||||||
|
{ODK_SUBSTRING, &(params->parsed_license.key_array[1].key_data),
|
||||||
|
".key_data"},
|
||||||
|
{ODK_SUBSTRING, &(params->parsed_license.key_array[1].key_control_iv),
|
||||||
|
".key_control_iv"},
|
||||||
|
{ODK_SUBSTRING, &(params->parsed_license.key_array[1].key_control),
|
||||||
|
".key_control"},
|
||||||
|
{ODK_SUBSTRING, &(params->parsed_license.key_array[2].key_id), ".key_id"},
|
||||||
|
{ODK_SUBSTRING, &(params->parsed_license.key_array[2].key_data_iv),
|
||||||
|
".key_data_iv"},
|
||||||
|
{ODK_SUBSTRING, &(params->parsed_license.key_array[2].key_data),
|
||||||
|
".key_data"},
|
||||||
|
{ODK_SUBSTRING, &(params->parsed_license.key_array[2].key_control_iv),
|
||||||
|
".key_control_iv"},
|
||||||
|
{ODK_SUBSTRING, &(params->parsed_license.key_array[2].key_control),
|
||||||
|
".key_control"},
|
||||||
|
{ODK_HASH, params->request_hash, ".request_hash"},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
void ODK_SetDefaultRenewalResponseParams(ODK_RenewalResponseParams* params) {
|
||||||
|
ODK_SetDefaultCoreFields(&(params->core_message), ODK_Renewal_Response_Type);
|
||||||
|
params->system_time = 0xfaceb00c;
|
||||||
|
params->playback_clock = 10;
|
||||||
|
params->playback_timer = 20;
|
||||||
|
params->renewal_duration = 300;
|
||||||
|
params->extra_fields = {
|
||||||
|
{ODK_UINT64, &(params->playback_clock), "playback_clock"},
|
||||||
|
{ODK_UINT64, &(params->renewal_duration), "renewal_duration"},
|
||||||
|
};
|
||||||
|
params->timer_limits = {
|
||||||
|
.soft_enforce_rental_duration = false,
|
||||||
|
.soft_enforce_playback_duration = false,
|
||||||
|
.earliest_playback_start_seconds = 0,
|
||||||
|
.rental_duration_seconds = 1000,
|
||||||
|
.total_playback_duration_seconds = 2000,
|
||||||
|
.initial_renewal_duration_seconds = 300,
|
||||||
|
};
|
||||||
|
params->clock_values = {
|
||||||
|
.time_of_license_signed =
|
||||||
|
params->system_time - params->playback_clock - 42,
|
||||||
|
.time_of_first_decrypt = params->system_time - params->playback_clock,
|
||||||
|
.time_of_last_decrypt = params->system_time - params->playback_clock,
|
||||||
|
.time_of_renewal_request = params->playback_clock,
|
||||||
|
.time_when_timer_expires = params->system_time + params->playback_timer,
|
||||||
|
.timer_status = ODK_CLOCK_TIMER_STATUS_ACTIVE,
|
||||||
|
.status = kActive,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
void ODK_SetDefaultProvisioningResponseParams(
|
||||||
|
ODK_ProvisioningResponseParams* params) {
|
||||||
|
ODK_SetDefaultCoreFields(&(params->core_message),
|
||||||
|
ODK_Provisioning_Response_Type);
|
||||||
|
params->device_id_length = ODK_DEVICE_ID_LEN_MAX / 2;
|
||||||
|
memset(params->device_id, 0xff, params->device_id_length);
|
||||||
|
memset(params->device_id + params->device_id_length, 0,
|
||||||
|
ODK_DEVICE_ID_LEN_MAX - params->device_id_length);
|
||||||
|
params->parsed_provisioning = {
|
||||||
|
.enc_private_key = {.offset = 0, .length = 1},
|
||||||
|
.enc_private_key_iv = {.offset = 2, .length = 3},
|
||||||
|
.encrypted_message_key = {.offset = 4, .length = 5},
|
||||||
|
};
|
||||||
|
params->extra_fields = {
|
||||||
|
{ODK_UINT32, &(params->device_id_length), "device_id_length"},
|
||||||
|
{ODK_DEVICEID, params->device_id, "device_id"},
|
||||||
|
{ODK_UINT32, &(params->parsed_provisioning).key_type, "key_type"},
|
||||||
|
{ODK_SUBSTRING, &(params->parsed_provisioning).enc_private_key,
|
||||||
|
"enc_private_key"},
|
||||||
|
{ODK_SUBSTRING, &(params->parsed_provisioning).enc_private_key_iv,
|
||||||
|
"enc_private_key_iv"},
|
||||||
|
{ODK_SUBSTRING, &(params->parsed_provisioning).encrypted_message_key,
|
||||||
|
"encrypted_message_key"},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t ODK_FieldLength(ODK_FieldType type) {
|
||||||
|
switch (type) {
|
||||||
|
case ODK_UINT16:
|
||||||
|
return sizeof(uint16_t);
|
||||||
|
case ODK_UINT32:
|
||||||
|
return sizeof(uint32_t);
|
||||||
|
case ODK_UINT64:
|
||||||
|
return sizeof(uint64_t);
|
||||||
|
case ODK_SUBSTRING:
|
||||||
|
return sizeof(uint32_t) + sizeof(uint32_t);
|
||||||
|
case ODK_DEVICEID:
|
||||||
|
return ODK_DEVICE_ID_LEN_MAX;
|
||||||
|
case ODK_HASH:
|
||||||
|
return ODK_SHA256_HASH_SIZE;
|
||||||
|
default:
|
||||||
|
return SIZE_MAX;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t ODK_AllocSize(ODK_FieldType type) {
|
||||||
|
if (type == ODK_SUBSTRING) {
|
||||||
|
return sizeof(OEMCrypto_Substring);
|
||||||
|
}
|
||||||
|
return ODK_FieldLength(type);
|
||||||
|
}
|
||||||
|
|
||||||
|
OEMCryptoResult ODK_WriteSingleField(uint8_t* buf, const ODK_Field* field) {
|
||||||
|
if (buf == nullptr || field == nullptr || field->value == nullptr) {
|
||||||
|
return ODK_ERROR_CORE_MESSAGE;
|
||||||
|
}
|
||||||
|
switch (field->type) {
|
||||||
|
case ODK_UINT16: {
|
||||||
|
const uint16_t u16 = htobe16(*static_cast<uint16_t*>(field->value));
|
||||||
|
memcpy(buf, &u16, sizeof(u16));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case ODK_UINT32: {
|
||||||
|
const uint32_t u32 = htobe32(*static_cast<uint32_t*>(field->value));
|
||||||
|
memcpy(buf, &u32, sizeof(u32));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case ODK_UINT64: {
|
||||||
|
const uint64_t u64 = htobe64(*static_cast<uint64_t*>(field->value));
|
||||||
|
memcpy(buf, &u64, sizeof(u64));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case ODK_SUBSTRING: {
|
||||||
|
OEMCrypto_Substring* s = static_cast<OEMCrypto_Substring*>(field->value);
|
||||||
|
const uint32_t off = htobe32(s->offset);
|
||||||
|
const uint32_t len = htobe32(s->length);
|
||||||
|
memcpy(buf, &off, sizeof(off));
|
||||||
|
memcpy(buf + sizeof(off), &len, sizeof(len));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case ODK_DEVICEID:
|
||||||
|
case ODK_HASH: {
|
||||||
|
const size_t field_len = ODK_FieldLength(field->type);
|
||||||
|
const uint8_t* const id = static_cast<uint8_t*>(field->value);
|
||||||
|
memcpy(buf, id, field_len);
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
return ODK_ERROR_CORE_MESSAGE;
|
||||||
|
}
|
||||||
|
return OEMCrypto_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
OEMCryptoResult ODK_ReadSingleField(const uint8_t* buf,
|
||||||
|
const ODK_Field* field) {
|
||||||
|
if (buf == nullptr || field == nullptr || field->value == nullptr) {
|
||||||
|
return ODK_ERROR_CORE_MESSAGE;
|
||||||
|
}
|
||||||
|
switch (field->type) {
|
||||||
|
case ODK_UINT16: {
|
||||||
|
memcpy(field->value, buf, sizeof(uint16_t));
|
||||||
|
uint16_t* u16p = static_cast<uint16_t*>(field->value);
|
||||||
|
*u16p = be16toh(*u16p);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case ODK_UINT32: {
|
||||||
|
memcpy(field->value, buf, sizeof(uint32_t));
|
||||||
|
uint32_t* u32p = static_cast<uint32_t*>(field->value);
|
||||||
|
*u32p = be32toh(*u32p);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case ODK_UINT64: {
|
||||||
|
memcpy(field->value, buf, sizeof(uint64_t));
|
||||||
|
uint64_t* u64p = static_cast<uint64_t*>(field->value);
|
||||||
|
*u64p = be64toh(*u64p);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case ODK_SUBSTRING: {
|
||||||
|
OEMCrypto_Substring* s = static_cast<OEMCrypto_Substring*>(field->value);
|
||||||
|
uint32_t off = 0;
|
||||||
|
uint32_t len = 0;
|
||||||
|
memcpy(&off, buf, sizeof(off));
|
||||||
|
memcpy(&len, buf + sizeof(off), sizeof(len));
|
||||||
|
s->offset = be32toh(off);
|
||||||
|
s->length = be32toh(len);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case ODK_DEVICEID:
|
||||||
|
case ODK_HASH: {
|
||||||
|
const size_t field_len = ODK_FieldLength(field->type);
|
||||||
|
uint8_t* const id = static_cast<uint8_t*>(field->value);
|
||||||
|
memcpy(id, buf, field_len);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
return ODK_ERROR_CORE_MESSAGE;
|
||||||
|
}
|
||||||
|
return OEMCrypto_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
OEMCryptoResult ODK_DumpSingleField(const uint8_t* buf,
|
||||||
|
const ODK_Field* field) {
|
||||||
|
if (buf == nullptr || field == nullptr || field->value == nullptr) {
|
||||||
|
return ODK_ERROR_CORE_MESSAGE;
|
||||||
|
}
|
||||||
|
switch (field->type) {
|
||||||
|
case ODK_UINT16: {
|
||||||
|
uint16_t val;
|
||||||
|
memcpy(&val, buf, sizeof(uint16_t));
|
||||||
|
val = be16toh(val);
|
||||||
|
std::cerr << field->name << ": " << val << " = 0x" << std::hex << val
|
||||||
|
<< "\n";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case ODK_UINT32: {
|
||||||
|
uint32_t val;
|
||||||
|
memcpy(&val, buf, sizeof(uint32_t));
|
||||||
|
val = be32toh(val);
|
||||||
|
std::cerr << field->name << ": " << val << " = 0x" << std::hex << val
|
||||||
|
<< "\n";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case ODK_UINT64: {
|
||||||
|
uint64_t val;
|
||||||
|
memcpy(&val, buf, sizeof(uint64_t));
|
||||||
|
val = be64toh(val);
|
||||||
|
std::cerr << field->name << ": " << val << " = 0x" << std::hex << val
|
||||||
|
<< "\n";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case ODK_SUBSTRING: {
|
||||||
|
uint32_t off = 0;
|
||||||
|
uint32_t len = 0;
|
||||||
|
memcpy(&off, buf, sizeof(off));
|
||||||
|
memcpy(&len, buf + sizeof(off), sizeof(len));
|
||||||
|
std::cerr << field->name << ": (off=" << off << ", len=" << len << ")\n";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case ODK_DEVICEID:
|
||||||
|
case ODK_HASH: {
|
||||||
|
const size_t field_len = ODK_FieldLength(field->type);
|
||||||
|
std::cerr << field->name << ": ";
|
||||||
|
for (size_t i = 0; i < field_len; i++) {
|
||||||
|
std::cerr << std::hex << std::setfill('0') << std::setw(2)
|
||||||
|
<< static_cast<unsigned int>(buf[i]);
|
||||||
|
}
|
||||||
|
std::cerr << "\n";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
return ODK_ERROR_CORE_MESSAGE;
|
||||||
|
}
|
||||||
|
std::cerr << std::dec; // Return to normal.
|
||||||
|
return OEMCrypto_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Parameters:
|
||||||
|
* [in] size_in: buffer size
|
||||||
|
* [out] size_out: bytes processed
|
||||||
|
*/
|
||||||
|
OEMCryptoResult ODK_IterFields(ODK_FieldMode mode, uint8_t* buf,
|
||||||
|
const size_t size_in, size_t* size_out,
|
||||||
|
const std::vector<ODK_Field>& fields) {
|
||||||
|
if (buf == nullptr || size_out == nullptr) {
|
||||||
|
return ODK_ERROR_CORE_MESSAGE;
|
||||||
|
}
|
||||||
|
size_t off = 0, off2 = 0;
|
||||||
|
for (size_t i = 0; i < fields.size(); i++) {
|
||||||
|
if (__builtin_add_overflow(off, ODK_FieldLength(fields[i].type), &off2) ||
|
||||||
|
off2 > size_in) {
|
||||||
|
return ODK_ERROR_CORE_MESSAGE;
|
||||||
|
}
|
||||||
|
uintptr_t base = reinterpret_cast<uintptr_t>(buf);
|
||||||
|
if (__builtin_add_overflow(base, off, &base)) {
|
||||||
|
return ODK_ERROR_CORE_MESSAGE;
|
||||||
|
}
|
||||||
|
uint8_t* const buf_off = buf + off;
|
||||||
|
switch (mode) {
|
||||||
|
case ODK_WRITE:
|
||||||
|
ODK_WriteSingleField(buf_off, &fields[i]);
|
||||||
|
break;
|
||||||
|
case ODK_READ:
|
||||||
|
ODK_ReadSingleField(buf_off, &fields[i]);
|
||||||
|
break;
|
||||||
|
case ODK_DUMP:
|
||||||
|
ODK_DumpSingleField(buf_off, &fields[i]);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
return ODK_ERROR_CORE_MESSAGE;
|
||||||
|
}
|
||||||
|
off = off2;
|
||||||
|
}
|
||||||
|
*size_out = off;
|
||||||
|
if (*size_out > size_in) {
|
||||||
|
return ODK_ERROR_CORE_MESSAGE;
|
||||||
|
}
|
||||||
|
return OEMCrypto_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ODK_ExpectEqualBuf(const void* s1, const void* s2, size_t n,
|
||||||
|
const std::vector<ODK_Field>& fields) {
|
||||||
|
if (memcmp(s1, s2, n) != 0) {
|
||||||
|
const void* buffers[] = {s1, s2};
|
||||||
|
for (int i = 0; i < 2; i++) {
|
||||||
|
char _tmp[] = "/tmp/fileXXXXXX";
|
||||||
|
const int temp_fd = mkstemp(_tmp);
|
||||||
|
if (temp_fd >= 0) {
|
||||||
|
close(temp_fd);
|
||||||
|
} else {
|
||||||
|
std::cerr << "Failed to open temp file." << std::endl;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
std::string tmp(_tmp);
|
||||||
|
std::fstream out(tmp, std::ios::out | std::ios::binary);
|
||||||
|
out.write(static_cast<const char*>(buffers[i]), n);
|
||||||
|
out.close();
|
||||||
|
std::cerr << "buffer " << i << " dumped to " << tmp << std::endl;
|
||||||
|
size_t bytes_written;
|
||||||
|
uint8_t* buf =
|
||||||
|
const_cast<uint8_t*>(reinterpret_cast<const uint8_t*>(buffers[i]));
|
||||||
|
ODK_IterFields(ODK_DUMP, buf, n, &bytes_written, fields);
|
||||||
|
}
|
||||||
|
FAIL();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ODK_ResetOdkFields(std::vector<ODK_Field>* fields) {
|
||||||
|
if (fields == nullptr) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
for (auto& field : *fields) {
|
||||||
|
if (field.value != nullptr) {
|
||||||
|
const size_t size = ODK_AllocSize(field.type);
|
||||||
|
memset(field.value, 0, size);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ODK_BuildMessageBuffer(ODK_CoreMessage* core_message,
|
||||||
|
const std::vector<ODK_Field>& extra_fields,
|
||||||
|
uint8_t** buf, uint32_t* buf_size) {
|
||||||
|
ASSERT_TRUE(core_message != nullptr);
|
||||||
|
ASSERT_TRUE(buf_size != nullptr);
|
||||||
|
std::vector<ODK_Field> total_fields = {
|
||||||
|
{ODK_UINT32, &(core_message->message_type), "message_type"},
|
||||||
|
{ODK_UINT32, &(core_message->message_length), "message_size"},
|
||||||
|
{ODK_UINT16, &(core_message->nonce_values.api_minor_version),
|
||||||
|
"api_minor_version"},
|
||||||
|
{ODK_UINT16, &(core_message->nonce_values.api_major_version),
|
||||||
|
"api_major_version"},
|
||||||
|
{ODK_UINT32, &(core_message->nonce_values.nonce), "nonce"},
|
||||||
|
{ODK_UINT32, &(core_message->nonce_values.session_id), "session_id"},
|
||||||
|
};
|
||||||
|
|
||||||
|
uint32_t header_size = 0;
|
||||||
|
for (auto& field : total_fields) {
|
||||||
|
header_size += ODK_FieldLength(field.type);
|
||||||
|
}
|
||||||
|
|
||||||
|
total_fields.insert(total_fields.end(), extra_fields.begin(),
|
||||||
|
extra_fields.end());
|
||||||
|
for (auto& field : total_fields) {
|
||||||
|
*buf_size += ODK_FieldLength(field.type);
|
||||||
|
}
|
||||||
|
// update message_size
|
||||||
|
*(reinterpret_cast<uint32_t*>(total_fields[1].value)) = *buf_size;
|
||||||
|
|
||||||
|
*buf = new uint8_t[*buf_size]{};
|
||||||
|
size_t bytes_written = 0;
|
||||||
|
// serialize ODK fields to message buffer
|
||||||
|
EXPECT_EQ(OEMCrypto_SUCCESS, ODK_IterFields(ODK_WRITE, *buf, SIZE_MAX,
|
||||||
|
&bytes_written, total_fields));
|
||||||
|
EXPECT_EQ(bytes_written, *buf_size);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace wvodk_test
|
||||||
99
oemcrypto/odk/test/odk_test_helper.h
Normal file
99
oemcrypto/odk/test/odk_test_helper.h
Normal file
@@ -0,0 +1,99 @@
|
|||||||
|
/* 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_
|
||||||
|
|
||||||
|
#include <cstdint>
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#include "odk_structs.h"
|
||||||
|
#include "odk_structs_priv.h"
|
||||||
|
|
||||||
|
namespace wvodk_test {
|
||||||
|
|
||||||
|
enum ODK_FieldType {
|
||||||
|
ODK_UINT16,
|
||||||
|
ODK_UINT32,
|
||||||
|
ODK_UINT64,
|
||||||
|
ODK_SUBSTRING,
|
||||||
|
ODK_DEVICEID,
|
||||||
|
ODK_HASH,
|
||||||
|
ODK_NUMTYPES,
|
||||||
|
};
|
||||||
|
|
||||||
|
enum ODK_FieldMode {
|
||||||
|
ODK_READ,
|
||||||
|
ODK_WRITE,
|
||||||
|
ODK_DUMP,
|
||||||
|
};
|
||||||
|
|
||||||
|
struct ODK_Field {
|
||||||
|
ODK_FieldType type;
|
||||||
|
void* value;
|
||||||
|
std::string name;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct ODK_LicenseResponseParams {
|
||||||
|
ODK_CoreMessage core_message;
|
||||||
|
bool initial_license_load;
|
||||||
|
bool usage_entry_present;
|
||||||
|
uint8_t request_hash[ODK_SHA256_HASH_SIZE];
|
||||||
|
ODK_TimerLimits timer_limits;
|
||||||
|
ODK_ClockValues clock_values;
|
||||||
|
ODK_ParsedLicense parsed_license;
|
||||||
|
std::vector<ODK_Field> extra_fields;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct ODK_RenewalResponseParams {
|
||||||
|
ODK_CoreMessage core_message;
|
||||||
|
uint64_t system_time;
|
||||||
|
uint64_t playback_clock;
|
||||||
|
uint64_t renewal_duration;
|
||||||
|
ODK_TimerLimits timer_limits;
|
||||||
|
ODK_ClockValues clock_values;
|
||||||
|
uint64_t playback_timer;
|
||||||
|
std::vector<ODK_Field> extra_fields;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct ODK_ProvisioningResponseParams {
|
||||||
|
ODK_CoreMessage core_message;
|
||||||
|
uint8_t device_id[ODK_DEVICE_ID_LEN_MAX];
|
||||||
|
uint32_t device_id_length;
|
||||||
|
ODK_ParsedProvisioning parsed_provisioning;
|
||||||
|
std::vector<ODK_Field> extra_fields;
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Default values in core_message for testing */
|
||||||
|
void ODK_SetDefaultCoreFields(ODK_CoreMessage* core_message,
|
||||||
|
uint32_t message_type);
|
||||||
|
void ODK_SetDefaultLicenseResponseParams(ODK_LicenseResponseParams* params);
|
||||||
|
void ODK_SetDefaultRenewalResponseParams(ODK_RenewalResponseParams* params);
|
||||||
|
void ODK_SetDefaultProvisioningResponseParams(
|
||||||
|
ODK_ProvisioningResponseParams* params);
|
||||||
|
|
||||||
|
size_t ODK_FieldLength(ODK_FieldType type);
|
||||||
|
size_t ODK_AllocSize(ODK_FieldType type);
|
||||||
|
|
||||||
|
/* Copy ODK_Field to buf */
|
||||||
|
OEMCryptoResult ODK_WriteSingleField(uint8_t* buf, const ODK_Field* 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,
|
||||||
|
const size_t size_in, size_t* size_out,
|
||||||
|
const std::vector<ODK_Field>& fields);
|
||||||
|
void ODK_ExpectEqualBuf(const void* s1, const void* s2, size_t n,
|
||||||
|
const std::vector<ODK_Field>& fields);
|
||||||
|
void ODK_ResetOdkFields(std::vector<ODK_Field>* fields);
|
||||||
|
|
||||||
|
/* Serialize core_message and extra_fields into buf */
|
||||||
|
void ODK_BuildMessageBuffer(ODK_CoreMessage* core_message,
|
||||||
|
const std::vector<ODK_Field>& extra_fields,
|
||||||
|
uint8_t** buf, uint32_t* buf_size);
|
||||||
|
|
||||||
|
} /* namespace wvodk_test */
|
||||||
|
|
||||||
|
#endif /* WIDEVINE_ODK_TEST_ODK_TEST_HELPER_H_ */
|
||||||
@@ -33,6 +33,9 @@
|
|||||||
'test/oemcrypto_unittests.gypi',
|
'test/oemcrypto_unittests.gypi',
|
||||||
'ref/oec_ref.gypi',
|
'ref/oec_ref.gypi',
|
||||||
],
|
],
|
||||||
|
'libraries': [
|
||||||
|
'-lpthread',
|
||||||
|
],
|
||||||
'dependencies': [
|
'dependencies': [
|
||||||
'<(boringssl_dependency)',
|
'<(boringssl_dependency)',
|
||||||
'<(gtest_dependency)',
|
'<(gtest_dependency)',
|
||||||
|
|||||||
@@ -95,19 +95,22 @@ SessionContext* CryptoEngine::FindSession(SessionId sid) {
|
|||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
int64_t CryptoEngine::OnlineTime() {
|
int64_t CryptoEngine::MonotonicTime() {
|
||||||
// Use the monotonic clock for times that don't have to be stable across
|
// Use the monotonic clock for times that don't have to be stable across
|
||||||
// device boots.
|
// device boots.
|
||||||
int64_t now = wvcdm::Clock().GetCurrentTime();
|
int64_t now =
|
||||||
|
wvcdm::Clock().GetCurrentTime() + offline_time_info_.rollback_offset;
|
||||||
static int64_t then = now;
|
static int64_t then = now;
|
||||||
if (now < then) now = then;
|
if (now < then) {
|
||||||
|
offline_time_info_.rollback_offset += then - now;
|
||||||
|
now = then;
|
||||||
|
}
|
||||||
then = now;
|
then = now;
|
||||||
return now;
|
return now;
|
||||||
}
|
}
|
||||||
|
|
||||||
int64_t CryptoEngine::RollbackCorrectedOfflineTime() {
|
int64_t CryptoEngine::SystemTime() {
|
||||||
// Add any time offsets in the past to the current time.
|
const int64_t current_time = MonotonicTime();
|
||||||
int64_t current_time = OnlineTime() + offline_time_info_.rollback_offset;
|
|
||||||
// Write time info to disk if kTimeInfoUpdateWindowInSeconds has elapsed since
|
// Write time info to disk if kTimeInfoUpdateWindowInSeconds has elapsed since
|
||||||
// last write.
|
// last write.
|
||||||
if (current_time - offline_time_info_.previous_time >
|
if (current_time - offline_time_info_.previous_time >
|
||||||
@@ -125,9 +128,9 @@ std::string CryptoEngine::GetUsageTimeFileFullPath() const {
|
|||||||
// TODO(fredgc, jfore): Address how this property is presented to the ref.
|
// TODO(fredgc, jfore): Address how this property is presented to the ref.
|
||||||
// For now, the file path is empty.
|
// For now, the file path is empty.
|
||||||
/*if (!wvcdm::Properties::GetDeviceFilesBasePath(wvcdm::kSecurityLevelL3,
|
/*if (!wvcdm::Properties::GetDeviceFilesBasePath(wvcdm::kSecurityLevelL3,
|
||||||
&file_path)) {
|
&file_path)) {
|
||||||
LOGE("RollbackCorrectedOfflineTime: Unable to get base path");
|
LOGE("Unable to get base path");
|
||||||
}*/
|
}*/
|
||||||
return file_path + kStoredUsageTimeFileName;
|
return file_path + kStoredUsageTimeFileName;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -148,8 +151,7 @@ bool CryptoEngine::LoadOfflineTimeInfo(const std::string& file_path) {
|
|||||||
std::unique_ptr<wvcdm::File> file =
|
std::unique_ptr<wvcdm::File> file =
|
||||||
file_system->Open(file_path, wvcdm::FileSystem::kReadOnly);
|
file_system->Open(file_path, wvcdm::FileSystem::kReadOnly);
|
||||||
if (!file) {
|
if (!file) {
|
||||||
LOGE("RollbackCorrectedOfflineTime: File open failed: %s",
|
LOGE("File open failed: %s", file_path.c_str());
|
||||||
file_path.c_str());
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
// Load time info from previous call.
|
// Load time info from previous call.
|
||||||
@@ -164,7 +166,7 @@ bool CryptoEngine::LoadOfflineTimeInfo(const std::string& file_path) {
|
|||||||
|
|
||||||
// Detect offline time rollback after loading from disk.
|
// Detect offline time rollback after loading from disk.
|
||||||
// Add any time offsets in the past to the current time.
|
// Add any time offsets in the past to the current time.
|
||||||
int64_t current_time = OnlineTime() + offline_time_info_.rollback_offset;
|
int64_t current_time = MonotonicTime();
|
||||||
if (offline_time_info_.previous_time > current_time) {
|
if (offline_time_info_.previous_time > current_time) {
|
||||||
// Current time is earlier than the previously saved time. Time has been
|
// Current time is earlier than the previously saved time. Time has been
|
||||||
// rolled back. Update the rollback offset.
|
// rolled back. Update the rollback offset.
|
||||||
@@ -184,7 +186,7 @@ bool CryptoEngine::SaveOfflineTimeInfo(const std::string& file_path) {
|
|||||||
// earlier offline rollback, the rollback offset will be updated in
|
// earlier offline rollback, the rollback offset will be updated in
|
||||||
// LoadOfflineTimeInfo(). It guarantees that the current time to be saved
|
// LoadOfflineTimeInfo(). It guarantees that the current time to be saved
|
||||||
// will never go back.
|
// will never go back.
|
||||||
int64_t current_time = OnlineTime() + offline_time_info_.rollback_offset;
|
const int64_t current_time = MonotonicTime();
|
||||||
// The new previous_time will either stay the same or move forward.
|
// The new previous_time will either stay the same or move forward.
|
||||||
if (current_time > offline_time_info_.previous_time)
|
if (current_time > offline_time_info_.previous_time)
|
||||||
offline_time_info_.previous_time = current_time;
|
offline_time_info_.previous_time = current_time;
|
||||||
@@ -213,8 +215,7 @@ bool CryptoEngine::SaveOfflineTimeInfo(const std::string& file_path) {
|
|||||||
file = file_system->Open(
|
file = file_system->Open(
|
||||||
file_path, wvcdm::FileSystem::kCreate | wvcdm::FileSystem::kTruncate);
|
file_path, wvcdm::FileSystem::kCreate | wvcdm::FileSystem::kTruncate);
|
||||||
if (!file) {
|
if (!file) {
|
||||||
LOGE("RollbackCorrectedOfflineTime: File open failed: %s",
|
LOGE("File open failed: %s", file_path.c_str());
|
||||||
file_path.c_str());
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
file->Write(reinterpret_cast<char*>(&encrypted_buffer[0]), sizeof(TimeInfo));
|
file->Write(reinterpret_cast<char*>(&encrypted_buffer[0]), sizeof(TimeInfo));
|
||||||
|
|||||||
@@ -104,8 +104,7 @@ class CryptoEngine {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// The OEMCrypto system time. Prevents time rollback.
|
// The OEMCrypto system time. Prevents time rollback.
|
||||||
// TODO(b/145836634): Combine RollbackCorrectedOfflineTime with OnlineTime().
|
int64_t SystemTime();
|
||||||
int64_t SystemTime() { return RollbackCorrectedOfflineTime(); }
|
|
||||||
|
|
||||||
// Verify that this nonce does not collide with another nonce in any session.
|
// Verify that this nonce does not collide with another nonce in any session.
|
||||||
virtual bool NonceCollision(uint32_t nonce);
|
virtual bool NonceCollision(uint32_t nonce);
|
||||||
@@ -224,11 +223,8 @@ class CryptoEngine {
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
// System clock, measuring time in seconds.
|
// System clock, measuring time in seconds, including anti-rollback offset.
|
||||||
int64_t OnlineTime();
|
int64_t MonotonicTime();
|
||||||
|
|
||||||
// System clock with antirollback protection, measuring time in seconds.
|
|
||||||
int64_t RollbackCorrectedOfflineTime();
|
|
||||||
|
|
||||||
bool LoadOfflineTimeInfo(const std::string& file_path);
|
bool LoadOfflineTimeInfo(const std::string& file_path);
|
||||||
bool SaveOfflineTimeInfo(const std::string& file_path);
|
bool SaveOfflineTimeInfo(const std::string& file_path);
|
||||||
|
|||||||
@@ -890,7 +890,7 @@ static OEMCryptoResult OEMCrypto_RewrapDeviceRSAKey30(
|
|||||||
// For the reference implementation, the wrapped key and the encrypted
|
// For the reference implementation, the wrapped key and the encrypted
|
||||||
// key are the same size -- just encrypted with different keys.
|
// 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.
|
// We add 32 bytes for a context, 32 for iv, and 32 bytes for a signature.
|
||||||
// Important: This layout must match OEMCrypto_LoadDeviceRSAKey below.
|
// Important: This layout must match OEMCrypto_LoadDRMPrivateKey below.
|
||||||
const size_t buffer_size = enc_rsa_key_length + sizeof(WrappedRSAKey);
|
const size_t buffer_size = enc_rsa_key_length + sizeof(WrappedRSAKey);
|
||||||
|
|
||||||
if (wrapped_rsa_key == nullptr || *wrapped_rsa_key_length < buffer_size) {
|
if (wrapped_rsa_key == nullptr || *wrapped_rsa_key_length < buffer_size) {
|
||||||
@@ -1007,7 +1007,7 @@ static OEMCryptoResult OEMCrypto_RewrapDeviceRSAKey(
|
|||||||
// For the reference implementation, the wrapped key and the encrypted
|
// For the reference implementation, the wrapped key and the encrypted
|
||||||
// key are the same size -- just encrypted with different keys.
|
// 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.
|
// We add 32 bytes for a context, 32 for iv, and 32 bytes for a signature.
|
||||||
// Important: This layout must match OEMCrypto_LoadDeviceRSAKey below.
|
// Important: This layout must match OEMCrypto_LoadDRMPrivateKey below.
|
||||||
const size_t buffer_size = enc_rsa_key_length + sizeof(WrappedRSAKey);
|
const size_t buffer_size = enc_rsa_key_length + sizeof(WrappedRSAKey);
|
||||||
|
|
||||||
if (wrapped_rsa_key == nullptr || *wrapped_rsa_key_length < buffer_size) {
|
if (wrapped_rsa_key == nullptr || *wrapped_rsa_key_length < buffer_size) {
|
||||||
@@ -1134,7 +1134,7 @@ OEMCRYPTO_API OEMCryptoResult OEMCrypto_LoadProvisioning(
|
|||||||
// For the reference implementation, the wrapped key and the encrypted
|
// For the reference implementation, the wrapped key and the encrypted
|
||||||
// key are the same size -- just encrypted with different keys.
|
// 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.
|
// We add 32 bytes for a context, 32 for iv, and 32 bytes for a signature.
|
||||||
// Important: This layout must match OEMCrypto_LoadDeviceRSAKey below.
|
// Important: This layout must match OEMCrypto_LoadDRMPrivateKey below.
|
||||||
const size_t buffer_size =
|
const size_t buffer_size =
|
||||||
parsed_response.enc_private_key.length + sizeof(WrappedRSAKey);
|
parsed_response.enc_private_key.length + sizeof(WrappedRSAKey);
|
||||||
|
|
||||||
@@ -1170,24 +1170,28 @@ OEMCRYPTO_API OEMCryptoResult OEMCrypto_LoadProvisioning(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
OEMCRYPTO_API OEMCryptoResult OEMCrypto_LoadDeviceRSAKey(
|
OEMCRYPTO_API OEMCryptoResult OEMCrypto_LoadDRMPrivateKey(
|
||||||
OEMCrypto_SESSION session, const uint8_t* wrapped_rsa_key,
|
OEMCrypto_SESSION session, OEMCrypto_PrivateKeyType key_type,
|
||||||
size_t wrapped_rsa_key_length) {
|
const uint8_t* wrapped_rsa_key, size_t wrapped_rsa_key_length) {
|
||||||
if (wrapped_rsa_key == nullptr) {
|
if (wrapped_rsa_key == nullptr) {
|
||||||
LOGE("[OEMCrypto_LoadDeviceRSAKey(): OEMCrypto_ERROR_INVALID_CONTEXT]");
|
LOGE("OEMCrypto_ERROR_INVALID_CONTEXT nullptr");
|
||||||
return OEMCrypto_ERROR_INVALID_CONTEXT;
|
return OEMCrypto_ERROR_INVALID_CONTEXT;
|
||||||
}
|
}
|
||||||
if (crypto_engine == nullptr) {
|
if (crypto_engine == nullptr) {
|
||||||
LOGE("OEMCrypto_LoadDeviceRSAKey: OEMCrypto Not Initialized.");
|
LOGE("OEMCrypto Not Initialized.");
|
||||||
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
|
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
|
||||||
}
|
}
|
||||||
|
if (key_type != OEMCrypto_RSA_Private_Key) {
|
||||||
|
LOGE("ECC keys not yet supported in reference code.");
|
||||||
|
return OEMCrypto_ERROR_NOT_IMPLEMENTED;
|
||||||
|
}
|
||||||
if (crypto_engine->config_provisioning_method() == OEMCrypto_DrmCertificate) {
|
if (crypto_engine->config_provisioning_method() == OEMCrypto_DrmCertificate) {
|
||||||
// If we are using a baked in cert, the "wrapped RSA key" should actually be
|
// If we are using a baked in cert, the "wrapped RSA key" should actually be
|
||||||
// the magic value for baked-in certificates.
|
// the magic value for baked-in certificates.
|
||||||
if (wrapped_rsa_key_length != sizeof(kBakedInCertificateMagicBytes) ||
|
if (wrapped_rsa_key_length != sizeof(kBakedInCertificateMagicBytes) ||
|
||||||
memcmp(kBakedInCertificateMagicBytes, wrapped_rsa_key,
|
memcmp(kBakedInCertificateMagicBytes, wrapped_rsa_key,
|
||||||
wrapped_rsa_key_length) != 0) {
|
wrapped_rsa_key_length) != 0) {
|
||||||
LOGE("OEMCrypto_LoadDeviceRSAKey: Baked in Cert has wrong size.");
|
LOGE("Baked in Cert has wrong size.");
|
||||||
return OEMCrypto_ERROR_INVALID_RSA_KEY;
|
return OEMCrypto_ERROR_INVALID_RSA_KEY;
|
||||||
} else {
|
} else {
|
||||||
return OEMCrypto_SUCCESS;
|
return OEMCrypto_SUCCESS;
|
||||||
@@ -1196,13 +1200,13 @@ OEMCRYPTO_API OEMCryptoResult OEMCrypto_LoadDeviceRSAKey(
|
|||||||
const WrappedRSAKey* wrapped =
|
const WrappedRSAKey* wrapped =
|
||||||
reinterpret_cast<const WrappedRSAKey*>(wrapped_rsa_key);
|
reinterpret_cast<const WrappedRSAKey*>(wrapped_rsa_key);
|
||||||
if (!crypto_engine->ValidRootOfTrust()) {
|
if (!crypto_engine->ValidRootOfTrust()) {
|
||||||
LOGE("[OEMCrypto_LoadDeviceRSAKey(): ERROR_KEYBOX_INVALID]");
|
LOGE("ERROR_KEYBOX_INVALID");
|
||||||
return OEMCrypto_ERROR_KEYBOX_INVALID;
|
return OEMCrypto_ERROR_KEYBOX_INVALID;
|
||||||
}
|
}
|
||||||
|
|
||||||
SessionContext* session_ctx = crypto_engine->FindSession(session);
|
SessionContext* session_ctx = crypto_engine->FindSession(session);
|
||||||
if (session_ctx == nullptr || !session_ctx->isValid()) {
|
if (session_ctx == nullptr || !session_ctx->isValid()) {
|
||||||
LOGE("[OEMCrypto_LoadDeviceRSAKey(): ERROR_INVALID_SESSION]");
|
LOGE("ERROR_INVALID_SESSION");
|
||||||
return OEMCrypto_ERROR_INVALID_SESSION;
|
return OEMCrypto_ERROR_INVALID_SESSION;
|
||||||
}
|
}
|
||||||
const std::vector<uint8_t> context(
|
const std::vector<uint8_t> context(
|
||||||
@@ -1216,7 +1220,7 @@ OEMCRYPTO_API OEMCryptoResult OEMCrypto_LoadDeviceRSAKey(
|
|||||||
if (!session_ctx->ValidateMessage(
|
if (!session_ctx->ValidateMessage(
|
||||||
wrapped->context, wrapped_rsa_key_length - sizeof(wrapped->signature),
|
wrapped->context, wrapped_rsa_key_length - sizeof(wrapped->signature),
|
||||||
wrapped->signature, sizeof(wrapped->signature))) {
|
wrapped->signature, sizeof(wrapped->signature))) {
|
||||||
LOGE("[LoadDeviceRSAKey(): Could not verify signature]");
|
LOGE("Could not verify signature");
|
||||||
return OEMCrypto_ERROR_SIGNATURE_FAILURE;
|
return OEMCrypto_ERROR_SIGNATURE_FAILURE;
|
||||||
}
|
}
|
||||||
// Decrypt RSA key.
|
// Decrypt RSA key.
|
||||||
|
|||||||
@@ -24,6 +24,7 @@
|
|||||||
#include <openssl/sha.h>
|
#include <openssl/sha.h>
|
||||||
#include <openssl/x509.h>
|
#include <openssl/x509.h>
|
||||||
|
|
||||||
|
#include "advance_iv_ctr.h"
|
||||||
#include "disallow_copy_and_assign.h"
|
#include "disallow_copy_and_assign.h"
|
||||||
#include "keys.h"
|
#include "keys.h"
|
||||||
#include "log.h"
|
#include "log.h"
|
||||||
@@ -66,25 +67,6 @@ void advance_dest_buffer(OEMCrypto_DestBufferDesc* dest_buffer, size_t bytes) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Advance an IV according to ISO-CENC's CTR modes. The lower half of the IV is
|
|
||||||
// split off and treated as an unsigned 64-bit integer, then incremented by the
|
|
||||||
// number of complete crypto blocks decrypted. The resulting value is then
|
|
||||||
// copied back into the IV over the previous lower half.
|
|
||||||
void advance_iv_ctr(uint8_t (*subsample_iv)[wvoec::KEY_IV_SIZE], size_t bytes) {
|
|
||||||
uint64_t counter;
|
|
||||||
// Per its type, sizeof(*subsample_iv) == wvoec::KEY_IV_SIZE
|
|
||||||
static_assert(sizeof(counter) * 2 == wvoec::KEY_IV_SIZE,
|
|
||||||
"A uint64_t failed to be half the size of an AES-128 IV.");
|
|
||||||
constexpr size_t half_iv_size = wvoec::KEY_IV_SIZE / 2;
|
|
||||||
memcpy(&counter, &(*subsample_iv)[half_iv_size], half_iv_size);
|
|
||||||
|
|
||||||
const size_t increment =
|
|
||||||
bytes / wvoec::AES_128_BLOCK_SIZE; // The truncation here is intentional
|
|
||||||
counter = wvcdm::htonll64(wvcdm::ntohll64(counter) + increment);
|
|
||||||
|
|
||||||
memcpy(&(*subsample_iv)[half_iv_size], &counter, half_iv_size);
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
namespace wvoec_ref {
|
namespace wvoec_ref {
|
||||||
@@ -208,6 +190,7 @@ SessionContext::SessionContext(CryptoEngine* ce, SessionId sid,
|
|||||||
id_(sid),
|
id_(sid),
|
||||||
current_content_key_(nullptr),
|
current_content_key_(nullptr),
|
||||||
session_keys_(nullptr),
|
session_keys_(nullptr),
|
||||||
|
license_request_hash_(),
|
||||||
rsa_key_(rsa_key),
|
rsa_key_(rsa_key),
|
||||||
allowed_schemes_(kSign_RSASSA_PSS),
|
allowed_schemes_(kSign_RSASSA_PSS),
|
||||||
decrypt_started_(false),
|
decrypt_started_(false),
|
||||||
@@ -228,14 +211,6 @@ SessionContext::SessionContext(CryptoEngine* ce, SessionId sid,
|
|||||||
}
|
}
|
||||||
|
|
||||||
SessionContext::~SessionContext() {
|
SessionContext::~SessionContext() {
|
||||||
if (usage_entry_) {
|
|
||||||
delete usage_entry_;
|
|
||||||
usage_entry_ = nullptr;
|
|
||||||
}
|
|
||||||
if (session_keys_) {
|
|
||||||
delete session_keys_;
|
|
||||||
session_keys_ = nullptr;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Internal utility function to derive key using CMAC-128
|
// Internal utility function to derive key using CMAC-128
|
||||||
@@ -795,11 +770,11 @@ OEMCryptoResult SessionContext::LoadKeysNoSignature(
|
|||||||
if (session_keys_ == nullptr) {
|
if (session_keys_ == nullptr) {
|
||||||
switch (license_type) {
|
switch (license_type) {
|
||||||
case OEMCrypto_ContentLicense:
|
case OEMCrypto_ContentLicense:
|
||||||
session_keys_ = new ContentKeysContext();
|
session_keys_.reset(new ContentKeysContext());
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case OEMCrypto_EntitlementLicense:
|
case OEMCrypto_EntitlementLicense:
|
||||||
session_keys_ = new EntitlementKeysContext();
|
session_keys_.reset(new EntitlementKeysContext());
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
@@ -933,7 +908,8 @@ OEMCryptoResult SessionContext::LoadEntitledContentKeys(
|
|||||||
if (!key_array) {
|
if (!key_array) {
|
||||||
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
|
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
|
||||||
}
|
}
|
||||||
if (!session_keys_ || session_keys_->type() != OEMCrypto_EntitlementLicense) {
|
if (session_keys_ == nullptr ||
|
||||||
|
session_keys_->type() != OEMCrypto_EntitlementLicense) {
|
||||||
return OEMCrypto_ERROR_INVALID_CONTEXT;
|
return OEMCrypto_ERROR_INVALID_CONTEXT;
|
||||||
}
|
}
|
||||||
for (size_t i = 0; i < key_array_length; ++i) {
|
for (size_t i = 0; i < key_array_length; ++i) {
|
||||||
@@ -1512,8 +1488,8 @@ OEMCryptoResult SessionContext::UpdateUsageEntry(uint8_t* header_buffer,
|
|||||||
return OEMCrypto_ERROR_INVALID_CONTEXT;
|
return OEMCrypto_ERROR_INVALID_CONTEXT;
|
||||||
}
|
}
|
||||||
return ce_->usage_table().UpdateUsageEntry(
|
return ce_->usage_table().UpdateUsageEntry(
|
||||||
this, usage_entry_, header_buffer, header_buffer_length, entry_buffer,
|
this, usage_entry_.get(), header_buffer, header_buffer_length,
|
||||||
entry_buffer_length, &clock_values_);
|
entry_buffer, entry_buffer_length, &clock_values_);
|
||||||
}
|
}
|
||||||
|
|
||||||
OEMCryptoResult SessionContext::DeactivateUsageEntry(
|
OEMCryptoResult SessionContext::DeactivateUsageEntry(
|
||||||
@@ -1532,7 +1508,7 @@ OEMCryptoResult SessionContext::ReportUsage(const std::vector<uint8_t>& pst,
|
|||||||
|
|
||||||
OEMCryptoResult SessionContext::MoveEntry(uint32_t new_index) {
|
OEMCryptoResult SessionContext::MoveEntry(uint32_t new_index) {
|
||||||
if (!usage_entry_) return OEMCrypto_ERROR_INVALID_CONTEXT;
|
if (!usage_entry_) return OEMCrypto_ERROR_INVALID_CONTEXT;
|
||||||
return ce_->usage_table().MoveEntry(usage_entry_, new_index);
|
return ce_->usage_table().MoveEntry(usage_entry_.get(), new_index);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Internal utility function to decrypt the message
|
// Internal utility function to decrypt the message
|
||||||
@@ -1604,8 +1580,8 @@ OEMCryptoResult SessionContext::DecryptSamples(
|
|||||||
advance_dest_buffer(&subsample_dest, subsample_length);
|
advance_dest_buffer(&subsample_dest, subsample_length);
|
||||||
if (subsample.num_bytes_encrypted > 0 &&
|
if (subsample.num_bytes_encrypted > 0 &&
|
||||||
current_content_key()->ctr_mode()) {
|
current_content_key()->ctr_mode()) {
|
||||||
advance_iv_ctr(&subsample_iv,
|
wvcdm::AdvanceIvCtr(&subsample_iv, subsample.block_offset +
|
||||||
subsample.block_offset + subsample.num_bytes_encrypted);
|
subsample.num_bytes_encrypted);
|
||||||
}
|
}
|
||||||
} // Subsample loop
|
} // Subsample loop
|
||||||
} // Sample loop
|
} // Sample loop
|
||||||
|
|||||||
@@ -267,7 +267,7 @@ class SessionContext {
|
|||||||
std::vector<uint8_t> encryption_key_;
|
std::vector<uint8_t> encryption_key_;
|
||||||
std::vector<uint8_t> session_key_;
|
std::vector<uint8_t> session_key_;
|
||||||
const Key* current_content_key_;
|
const Key* current_content_key_;
|
||||||
SessionContextKeys* session_keys_;
|
std::unique_ptr<SessionContextKeys> session_keys_;
|
||||||
ODK_NonceValues nonce_values_;
|
ODK_NonceValues nonce_values_;
|
||||||
uint8_t license_request_hash_[ODK_SHA256_HASH_SIZE];
|
uint8_t license_request_hash_[ODK_SHA256_HASH_SIZE];
|
||||||
RSA_shared_ptr rsa_key_;
|
RSA_shared_ptr rsa_key_;
|
||||||
@@ -275,7 +275,7 @@ class SessionContext {
|
|||||||
bool decrypt_started_; // If the license has been used in this session.
|
bool decrypt_started_; // If the license has been used in this session.
|
||||||
ODK_TimerLimits timer_limits_;
|
ODK_TimerLimits timer_limits_;
|
||||||
ODK_ClockValues clock_values_;
|
ODK_ClockValues clock_values_;
|
||||||
UsageTableEntry* usage_entry_;
|
std::unique_ptr<UsageTableEntry> usage_entry_;
|
||||||
SRMVersionStatus srm_requirements_status_;
|
SRMVersionStatus srm_requirements_status_;
|
||||||
enum UsageEntryStatus {
|
enum UsageEntryStatus {
|
||||||
kNoUsageEntry, // No entry loaded for this session.
|
kNoUsageEntry, // No entry loaded for this session.
|
||||||
|
|||||||
@@ -348,9 +348,9 @@ UsageTableEntry* UsageTable::MakeEntry(uint32_t index) {
|
|||||||
return new UsageTableEntry(this, index, master_generation_number_);
|
return new UsageTableEntry(this, index, master_generation_number_);
|
||||||
}
|
}
|
||||||
|
|
||||||
OEMCryptoResult UsageTable::CreateNewUsageEntry(SessionContext* session,
|
OEMCryptoResult UsageTable::CreateNewUsageEntry(
|
||||||
UsageTableEntry** entry,
|
SessionContext* session, std::unique_ptr<UsageTableEntry>* entry,
|
||||||
uint32_t* usage_entry_number) {
|
uint32_t* usage_entry_number) {
|
||||||
if (!header_loaded_) {
|
if (!header_loaded_) {
|
||||||
LOGE("CreateNewUsageEntry: Header not loaded.");
|
LOGE("CreateNewUsageEntry: Header not loaded.");
|
||||||
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
|
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
|
||||||
@@ -367,16 +367,15 @@ OEMCryptoResult UsageTable::CreateNewUsageEntry(SessionContext* session,
|
|||||||
generation_numbers_.push_back(master_generation_number_);
|
generation_numbers_.push_back(master_generation_number_);
|
||||||
sessions_.push_back(session);
|
sessions_.push_back(session);
|
||||||
master_generation_number_++;
|
master_generation_number_++;
|
||||||
*entry = new_entry;
|
entry->reset(new_entry);
|
||||||
*usage_entry_number = index;
|
*usage_entry_number = index;
|
||||||
return OEMCrypto_SUCCESS;
|
return OEMCrypto_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
OEMCryptoResult UsageTable::LoadUsageEntry(SessionContext* session,
|
OEMCryptoResult UsageTable::LoadUsageEntry(
|
||||||
UsageTableEntry** entry,
|
SessionContext* session, std::unique_ptr<UsageTableEntry>* entry,
|
||||||
uint32_t index,
|
uint32_t index, const std::vector<uint8_t>& buffer,
|
||||||
const std::vector<uint8_t>& buffer,
|
ODK_ClockValues* clock_values) {
|
||||||
ODK_ClockValues* clock_values) {
|
|
||||||
if (!header_loaded_) {
|
if (!header_loaded_) {
|
||||||
LOGE("CreateNewUsageEntry: Header not loaded.");
|
LOGE("CreateNewUsageEntry: Header not loaded.");
|
||||||
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
|
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
|
||||||
@@ -393,12 +392,11 @@ OEMCryptoResult UsageTable::LoadUsageEntry(SessionContext* session,
|
|||||||
LOGE("Too many usage entries: %d/%d", index, max);
|
LOGE("Too many usage entries: %d/%d", index, max);
|
||||||
return OEMCrypto_ERROR_INSUFFICIENT_RESOURCES;
|
return OEMCrypto_ERROR_INSUFFICIENT_RESOURCES;
|
||||||
}
|
}
|
||||||
UsageTableEntry* new_entry = MakeEntry(index);
|
std::unique_ptr<UsageTableEntry> new_entry(MakeEntry(index));
|
||||||
|
|
||||||
OEMCryptoResult status =
|
OEMCryptoResult status =
|
||||||
new_entry->LoadData(ce_, index, buffer, clock_values);
|
new_entry->LoadData(ce_, index, buffer, clock_values);
|
||||||
if (status != OEMCrypto_SUCCESS) {
|
if (status != OEMCrypto_SUCCESS) {
|
||||||
delete new_entry;
|
|
||||||
return status;
|
return status;
|
||||||
}
|
}
|
||||||
if (new_entry->generation_number() != generation_numbers_[index]) {
|
if (new_entry->generation_number() != generation_numbers_[index]) {
|
||||||
@@ -406,13 +404,12 @@ OEMCryptoResult UsageTable::LoadUsageEntry(SessionContext* session,
|
|||||||
generation_numbers_[index]);
|
generation_numbers_[index]);
|
||||||
if ((new_entry->generation_number() + 1 < generation_numbers_[index]) ||
|
if ((new_entry->generation_number() + 1 < generation_numbers_[index]) ||
|
||||||
(new_entry->generation_number() - 1 > generation_numbers_[index])) {
|
(new_entry->generation_number() - 1 > generation_numbers_[index])) {
|
||||||
delete new_entry;
|
|
||||||
return OEMCrypto_ERROR_GENERATION_SKEW;
|
return OEMCrypto_ERROR_GENERATION_SKEW;
|
||||||
}
|
}
|
||||||
status = OEMCrypto_WARNING_GENERATION_SKEW;
|
status = OEMCrypto_WARNING_GENERATION_SKEW;
|
||||||
}
|
}
|
||||||
sessions_[index] = session;
|
sessions_[index] = session;
|
||||||
*entry = new_entry;
|
*entry = std::move(new_entry);
|
||||||
return status;
|
return status;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -88,10 +88,11 @@ class UsageTable {
|
|||||||
virtual ~UsageTable();
|
virtual ~UsageTable();
|
||||||
|
|
||||||
OEMCryptoResult CreateNewUsageEntry(SessionContext* session,
|
OEMCryptoResult CreateNewUsageEntry(SessionContext* session,
|
||||||
UsageTableEntry** entry,
|
std::unique_ptr<UsageTableEntry>* entry,
|
||||||
uint32_t* usage_entry_number);
|
uint32_t* usage_entry_number);
|
||||||
OEMCryptoResult LoadUsageEntry(SessionContext* session,
|
OEMCryptoResult LoadUsageEntry(SessionContext* session,
|
||||||
UsageTableEntry** entry, uint32_t index,
|
std::unique_ptr<UsageTableEntry>* entry,
|
||||||
|
uint32_t index,
|
||||||
const std::vector<uint8_t>& buffer,
|
const std::vector<uint8_t>& buffer,
|
||||||
ODK_ClockValues* clock_values);
|
ODK_ClockValues* clock_values);
|
||||||
OEMCryptoResult UpdateUsageEntry(
|
OEMCryptoResult UpdateUsageEntry(
|
||||||
|
|||||||
@@ -85,7 +85,7 @@ static std::string GetSSLError() {
|
|||||||
static bool DeriveKey(const std::vector<uint8_t>& key,
|
static bool DeriveKey(const std::vector<uint8_t>& key,
|
||||||
const std::vector<uint8_t>& context,
|
const std::vector<uint8_t>& context,
|
||||||
std::vector<uint8_t>* out) {
|
std::vector<uint8_t>* out) {
|
||||||
if (key.empty() || context.empty() || out == NULL) {
|
if (key.empty() || context.empty() || out == nullptr) {
|
||||||
std::cerr << "DeriveKey(): Invalid inputs" << std::endl;
|
std::cerr << "DeriveKey(): Invalid inputs" << std::endl;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -216,7 +216,7 @@ void ProvisioningRoundTrip::PrepareSession(
|
|||||||
|
|
||||||
void ProvisioningRoundTrip::VerifyRequestSignature(
|
void ProvisioningRoundTrip::VerifyRequestSignature(
|
||||||
const vector<uint8_t>& data, const vector<uint8_t>& generated_signature,
|
const vector<uint8_t>& data, const vector<uint8_t>& generated_signature,
|
||||||
size_t core_message_length) {
|
size_t /* core_message_length */) {
|
||||||
if (global_features.provisioning_method == OEMCrypto_OEMCertificate) {
|
if (global_features.provisioning_method == OEMCrypto_OEMCertificate) {
|
||||||
session()->VerifyRSASignature(data, generated_signature.data(),
|
session()->VerifyRSASignature(data, generated_signature.data(),
|
||||||
generated_signature.size(), kSign_RSASSA_PSS);
|
generated_signature.size(), kSign_RSASSA_PSS);
|
||||||
@@ -333,21 +333,16 @@ OEMCryptoResult ProvisioningRoundTrip::LoadResponse(Session* session) {
|
|||||||
// If this platform does not support v15 functions, we just need to stub these
|
// If this platform does not support v15 functions, we just need to stub these
|
||||||
// out so that the tests compile.
|
// out so that the tests compile.
|
||||||
OEMCryptoResult OEMCrypto_RewrapDeviceRSAKey30_V15(
|
OEMCryptoResult OEMCrypto_RewrapDeviceRSAKey30_V15(
|
||||||
OEMCrypto_SESSION session, const uint32_t* unaligned_nonce,
|
OEMCrypto_SESSION, const uint32_t*, const uint8_t*, size_t, const uint8_t*,
|
||||||
const uint8_t* encrypted_message_key, size_t encrypted_message_key_length,
|
size_t, const uint8_t*, uint8_t*, size_t*) {
|
||||||
const uint8_t* enc_rsa_key, size_t enc_rsa_key_length,
|
|
||||||
const uint8_t* enc_rsa_key_iv, uint8_t* wrapped_rsa_key,
|
|
||||||
size_t* wrapped_rsa_key_length) {
|
|
||||||
LOGE("Support for v15 functions not included. Define TEST_OEMCRYPTO_V15.");
|
LOGE("Support for v15 functions not included. Define TEST_OEMCRYPTO_V15.");
|
||||||
return OEMCrypto_ERROR_NOT_IMPLEMENTED;
|
return OEMCrypto_ERROR_NOT_IMPLEMENTED;
|
||||||
}
|
}
|
||||||
|
|
||||||
OEMCryptoResult OEMCrypto_RewrapDeviceRSAKey_V15(
|
OEMCryptoResult OEMCrypto_RewrapDeviceRSAKey_V15(
|
||||||
OEMCrypto_SESSION session, const uint8_t* message, size_t message_length,
|
OEMCrypto_SESSION, const uint8_t*, size_t, const uint8_t*, size_t,
|
||||||
const uint8_t* signature, size_t signature_length,
|
const uint32_t*, const uint8_t*, size_t, const uint8_t*, uint8_t*,
|
||||||
const uint32_t* unaligned_nonce, const uint8_t* enc_rsa_key,
|
size_t*) {
|
||||||
size_t enc_rsa_key_length, const uint8_t* enc_rsa_key_iv,
|
|
||||||
uint8_t* wrapped_rsa_key, size_t* wrapped_rsa_key_length) {
|
|
||||||
LOGE("Support for v15 functions not included. Define TEST_OEMCRYPTO_V15.");
|
LOGE("Support for v15 functions not included. Define TEST_OEMCRYPTO_V15.");
|
||||||
return OEMCrypto_ERROR_NOT_IMPLEMENTED;
|
return OEMCrypto_ERROR_NOT_IMPLEMENTED;
|
||||||
}
|
}
|
||||||
@@ -591,9 +586,6 @@ void LicenseRoundTrip::EncryptAndSignResponse() {
|
|||||||
memcpy(encrypted_response_.data() + serialized_core_message_.size(),
|
memcpy(encrypted_response_.data() + serialized_core_message_.size(),
|
||||||
reinterpret_cast<const uint8_t*>(&encrypted_response_data_),
|
reinterpret_cast<const uint8_t*>(&encrypted_response_data_),
|
||||||
sizeof(encrypted_response_data_));
|
sizeof(encrypted_response_data_));
|
||||||
if (global_features.provisioning_method == OEMCrypto_OEMCertificate) {
|
|
||||||
session()->GenerateDerivedKeysFromSessionKey();
|
|
||||||
}
|
|
||||||
session()->key_deriver().ServerSignBuffer(encrypted_response_.data(),
|
session()->key_deriver().ServerSignBuffer(encrypted_response_.data(),
|
||||||
encrypted_response_.size(),
|
encrypted_response_.size(),
|
||||||
&response_signature_);
|
&response_signature_);
|
||||||
@@ -831,8 +823,8 @@ void RenewalRoundTrip::VerifyRequestSignature(
|
|||||||
|
|
||||||
void RenewalRoundTrip::FillAndVerifyCoreRequest(
|
void RenewalRoundTrip::FillAndVerifyCoreRequest(
|
||||||
const std::string& core_message_string) {
|
const std::string& core_message_string) {
|
||||||
if (license_messages_->api_version() < kCoreMessagesAPI) {
|
if (license_messages_->api_version() < kCoreMessagesAPI || is_release_) {
|
||||||
// For v15, we expect that no core request was created.
|
// For v15 or for a release, we expect that no core request was created.
|
||||||
EXPECT_FALSE(
|
EXPECT_FALSE(
|
||||||
oemcrypto_core_message::deserialize::CoreRenewalRequestFromMessage(
|
oemcrypto_core_message::deserialize::CoreRenewalRequestFromMessage(
|
||||||
core_message_string, &core_request_));
|
core_message_string, &core_request_));
|
||||||
@@ -842,18 +834,14 @@ void RenewalRoundTrip::FillAndVerifyCoreRequest(
|
|||||||
core_message_string, &core_request_));
|
core_message_string, &core_request_));
|
||||||
EXPECT_EQ(license_messages_->core_request().api_major_version,
|
EXPECT_EQ(license_messages_->core_request().api_major_version,
|
||||||
core_request_.api_major_version);
|
core_request_.api_major_version);
|
||||||
if (!is_release_) {
|
EXPECT_EQ(license_messages_->core_request().nonce, core_request_.nonce);
|
||||||
// For a license release, we do not expect the nonce to be correct. That
|
EXPECT_EQ(license_messages_->core_request().session_id,
|
||||||
// is because a release might be sent without loading the license first.
|
core_request_.session_id);
|
||||||
EXPECT_EQ(license_messages_->core_request().nonce, core_request_.nonce);
|
|
||||||
EXPECT_EQ(license_messages_->core_request().session_id,
|
|
||||||
core_request_.session_id);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void RenewalRoundTrip::CreateDefaultResponse() {
|
void RenewalRoundTrip::CreateDefaultResponse() {
|
||||||
if (license_messages_->api_version() < kCoreMessagesAPI) {
|
if (license_messages_->api_version() < kCoreMessagesAPI || is_release_) {
|
||||||
uint32_t control = 0;
|
uint32_t control = 0;
|
||||||
uint32_t nonce = 0;
|
uint32_t nonce = 0;
|
||||||
// If this is a v15 device, and a v15 license, and the license used a nonce,
|
// If this is a v15 device, and a v15 license, and the license used a nonce,
|
||||||
@@ -868,12 +856,9 @@ void RenewalRoundTrip::CreateDefaultResponse() {
|
|||||||
constexpr size_t index = 0;
|
constexpr size_t index = 0;
|
||||||
response_data_.keys[index].key_id_length = 0;
|
response_data_.keys[index].key_id_length = 0;
|
||||||
response_data_.keys[index].key_id[0] = '\0';
|
response_data_.keys[index].key_id[0] = '\0';
|
||||||
std::string kcVersion =
|
const uint32_t renewal_api =
|
||||||
"kc" + std::to_string(core_request_.api_major_version);
|
std::max<uint32_t>(core_request_.api_major_version, 15u);
|
||||||
if (global_features.api_version < kCoreMessagesAPI) {
|
std::string kcVersion = "kc" + std::to_string(renewal_api);
|
||||||
// For v15 or earlier devices, we use the api of the device.
|
|
||||||
kcVersion = "kc" + std::to_string(global_features.api_version);
|
|
||||||
}
|
|
||||||
memcpy(response_data_.keys[index].control.verification, kcVersion.c_str(),
|
memcpy(response_data_.keys[index].control.verification, kcVersion.c_str(),
|
||||||
4);
|
4);
|
||||||
const uint32_t duration = static_cast<uint32_t>(
|
const uint32_t duration = static_cast<uint32_t>(
|
||||||
@@ -1371,8 +1356,9 @@ bool Session::GenerateRSASessionKey(vector<uint8_t>* session_key,
|
|||||||
|
|
||||||
void Session::InstallRSASessionTestKey(const vector<uint8_t>& wrapped_rsa_key) {
|
void Session::InstallRSASessionTestKey(const vector<uint8_t>& wrapped_rsa_key) {
|
||||||
ASSERT_EQ(OEMCrypto_SUCCESS,
|
ASSERT_EQ(OEMCrypto_SUCCESS,
|
||||||
OEMCrypto_LoadDeviceRSAKey(session_id(), wrapped_rsa_key.data(),
|
OEMCrypto_LoadDRMPrivateKey(session_id(), OEMCrypto_RSA_Private_Key,
|
||||||
wrapped_rsa_key.size()));
|
wrapped_rsa_key.data(),
|
||||||
|
wrapped_rsa_key.size()));
|
||||||
}
|
}
|
||||||
|
|
||||||
void Session::CreateNewUsageEntry(OEMCryptoResult* status) {
|
void Session::CreateNewUsageEntry(OEMCryptoResult* status) {
|
||||||
@@ -1402,13 +1388,6 @@ void Session::UpdateUsageEntry(std::vector<uint8_t>* header_buffer) {
|
|||||||
encrypted_usage_entry_.data(), &entry_buffer_length));
|
encrypted_usage_entry_.data(), &entry_buffer_length));
|
||||||
}
|
}
|
||||||
|
|
||||||
void Session::DeactivateUsageEntry(const std::string& pst) {
|
|
||||||
ASSERT_EQ(OEMCrypto_SUCCESS,
|
|
||||||
OEMCrypto_DeactivateUsageEntry(
|
|
||||||
session_id(), reinterpret_cast<const uint8_t*>(pst.c_str()),
|
|
||||||
pst.length()));
|
|
||||||
}
|
|
||||||
|
|
||||||
void Session::LoadUsageEntry(uint32_t index, const vector<uint8_t>& buffer) {
|
void Session::LoadUsageEntry(uint32_t index, const vector<uint8_t>& buffer) {
|
||||||
usage_entry_number_ = index;
|
usage_entry_number_ = index;
|
||||||
encrypted_usage_entry_ = buffer;
|
encrypted_usage_entry_ = buffer;
|
||||||
|
|||||||
@@ -177,7 +177,6 @@ class RoundTrip {
|
|||||||
CoreRequest& core_request() { return core_request_; }
|
CoreRequest& core_request() { return core_request_; }
|
||||||
CoreResponse& core_response() { return core_response_; }
|
CoreResponse& core_response() { return core_response_; }
|
||||||
ResponseData& response_data() { return response_data_; }
|
ResponseData& response_data() { return response_data_; }
|
||||||
ResponseData& encrypted_response_data() { return encrypted_response_data_; }
|
|
||||||
std::vector<uint8_t>& encrypted_response_buffer() {
|
std::vector<uint8_t>& encrypted_response_buffer() {
|
||||||
return encrypted_response_;
|
return encrypted_response_;
|
||||||
}
|
}
|
||||||
@@ -284,7 +283,8 @@ class LicenseRoundTrip
|
|||||||
update_mac_keys_(true),
|
update_mac_keys_(true),
|
||||||
api_version_(kCurrentAPI),
|
api_version_(kCurrentAPI),
|
||||||
expect_request_has_correct_nonce_(true),
|
expect_request_has_correct_nonce_(true),
|
||||||
license_type_(OEMCrypto_ContentLicense) {}
|
license_type_(OEMCrypto_ContentLicense),
|
||||||
|
request_hash_() {}
|
||||||
void CreateDefaultResponse() override;
|
void CreateDefaultResponse() override;
|
||||||
// Create a license with four keys. Each key is responsible for one of generic
|
// 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
|
// encrypt (key 0), decrypt (key 1), sign (key 2) and verify (key 3). Each key
|
||||||
@@ -527,8 +527,6 @@ class Session {
|
|||||||
void ReloadUsageEntry() { LoadUsageEntry(*this); }
|
void ReloadUsageEntry() { LoadUsageEntry(*this); }
|
||||||
// Update the usage entry and save the header to the specified buffer.
|
// Update the usage entry and save the header to the specified buffer.
|
||||||
void UpdateUsageEntry(std::vector<uint8_t>* header_buffer);
|
void UpdateUsageEntry(std::vector<uint8_t>* header_buffer);
|
||||||
// Deactivate this session's usage entry.
|
|
||||||
void DeactivateUsageEntry(const std::string& pst);
|
|
||||||
// The usage entry number for this session's usage entry.
|
// The usage entry number for this session's usage entry.
|
||||||
uint32_t usage_entry_number() const { return usage_entry_number_; }
|
uint32_t usage_entry_number() const { return usage_entry_number_; }
|
||||||
void set_usage_entry_number(uint32_t v) { usage_entry_number_ = v; }
|
void set_usage_entry_number(uint32_t v) { usage_entry_number_ = v; }
|
||||||
|
|||||||
@@ -15,9 +15,9 @@
|
|||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
# include <windows.h>
|
# include <windows.h>
|
||||||
#else
|
#else
|
||||||
# include <sys/time.h>
|
# include <sys/time.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#include <gtest/gtest.h>
|
#include <gtest/gtest.h>
|
||||||
@@ -46,9 +46,9 @@
|
|||||||
using ::testing::Bool;
|
using ::testing::Bool;
|
||||||
using ::testing::Combine;
|
using ::testing::Combine;
|
||||||
using ::testing::Range;
|
using ::testing::Range;
|
||||||
|
using ::testing::tuple;
|
||||||
using ::testing::Values;
|
using ::testing::Values;
|
||||||
using ::testing::WithParamInterface;
|
using ::testing::WithParamInterface;
|
||||||
using ::testing::tuple;
|
|
||||||
using namespace std;
|
using namespace std;
|
||||||
|
|
||||||
namespace std { // GTest wants PrintTo to be in the std namespace.
|
namespace std { // GTest wants PrintTo to be in the std namespace.
|
||||||
@@ -92,11 +92,11 @@ constexpr size_t MiB = 1024 * 1024;
|
|||||||
// depending on the resource rating reported by OEMCrypto. This function looks
|
// depending on the resource rating reported by OEMCrypto. This function looks
|
||||||
// up the required value for the specified resource for the target OEMCrypto
|
// up the required value for the specified resource for the target OEMCrypto
|
||||||
// library.
|
// library.
|
||||||
template<typename T, size_t N>
|
template <typename T, size_t N>
|
||||||
T GetResourceValue(T (&resource_values)[N]) {
|
T GetResourceValue(T (&resource_values)[N]) {
|
||||||
if (global_features.resource_rating < 1) return resource_values[0];
|
if (global_features.resource_rating < 1) return resource_values[0];
|
||||||
if (global_features.resource_rating > N) return resource_values[N-1];
|
if (global_features.resource_rating > N) return resource_values[N - 1];
|
||||||
return resource_values[global_features.resource_rating-1];
|
return resource_values[global_features.resource_rating - 1];
|
||||||
}
|
}
|
||||||
|
|
||||||
// After API 16, we require 300 entries in the usage table. Before API 16, we
|
// After API 16, we require 300 entries in the usage table. Before API 16, we
|
||||||
@@ -124,8 +124,8 @@ const size_t kLargeMessageSize[] = { 8*KiB, 8*KiB, 16*KiB, 32*KiB};
|
|||||||
|
|
||||||
/** @return The Unix time of the given time point. */
|
/** @return The Unix time of the given time point. */
|
||||||
template <typename Duration>
|
template <typename Duration>
|
||||||
uint64_t UnixTime(const std::chrono::time_point<std::chrono::system_clock,
|
uint64_t UnixTime(
|
||||||
Duration>& point) {
|
const std::chrono::time_point<std::chrono::system_clock, Duration>& point) {
|
||||||
return point.time_since_epoch() / std::chrono::seconds(1);
|
return point.time_since_epoch() / std::chrono::seconds(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -143,7 +143,8 @@ void AddNativeTime(int64_t delta_seconds, NativeTime* time) {
|
|||||||
ASSERT_TRUE(SystemTimeToFileTime(time, &file_time));
|
ASSERT_TRUE(SystemTimeToFileTime(time, &file_time));
|
||||||
uint64_t long_time = static_cast<uint64_t>(file_time.dwLowDateTime) |
|
uint64_t long_time = static_cast<uint64_t>(file_time.dwLowDateTime) |
|
||||||
(static_cast<uint64_t>(file_time.dwHighDateTime) << 32);
|
(static_cast<uint64_t>(file_time.dwHighDateTime) << 32);
|
||||||
long_time += delta_seconds * 1e7; // long_time is in 100-nanosecond intervals.
|
long_time +=
|
||||||
|
delta_seconds * 1e7; // long_time is in 100-nanosecond intervals.
|
||||||
file_time.dwLowDateTime = long_time & ((1ull << 32) - 1);
|
file_time.dwLowDateTime = long_time & ((1ull << 32) - 1);
|
||||||
file_time.dwHighDateTime = long_time >> 32;
|
file_time.dwHighDateTime = long_time >> 32;
|
||||||
ASSERT_TRUE(FileTimeToSystemTime(&file_time, time));
|
ASSERT_TRUE(FileTimeToSystemTime(&file_time, time));
|
||||||
@@ -189,6 +190,15 @@ class OEMCryptoClientTest : public ::testing::Test, public SessionUtil {
|
|||||||
// This test is first, becuase it might give an idea why other
|
// This test is first, becuase it might give an idea why other
|
||||||
// tests are failing when the device has the wrong keybox installed.
|
// tests are failing when the device has the wrong keybox installed.
|
||||||
TEST_F(OEMCryptoClientTest, VersionNumber) {
|
TEST_F(OEMCryptoClientTest, VersionNumber) {
|
||||||
|
const std::string log_message =
|
||||||
|
"OEMCrypto unit tests for API 16.2. Tests last updated 2020-03-27";
|
||||||
|
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, 2);
|
||||||
|
EXPECT_EQ(kCurrentAPI, 16u);
|
||||||
const char* level = OEMCrypto_SecurityLevel();
|
const char* level = OEMCrypto_SecurityLevel();
|
||||||
ASSERT_NE(nullptr, level);
|
ASSERT_NE(nullptr, level);
|
||||||
ASSERT_EQ('L', level[0]);
|
ASSERT_EQ('L', level[0]);
|
||||||
@@ -196,13 +206,13 @@ TEST_F(OEMCryptoClientTest, VersionNumber) {
|
|||||||
uint32_t version = OEMCrypto_APIVersion();
|
uint32_t version = OEMCrypto_APIVersion();
|
||||||
cout << " OEMCrypto API version is " << version << endl;
|
cout << " OEMCrypto API version is " << version << endl;
|
||||||
if (OEMCrypto_SupportsUsageTable()) {
|
if (OEMCrypto_SupportsUsageTable()) {
|
||||||
cout << " OEMCrypto supports usage tables." << endl;
|
cout << " OEMCrypto supports usage tables" << endl;
|
||||||
} else {
|
} else {
|
||||||
cout << " OEMCrypto does not support usage tables." << endl;
|
cout << " OEMCrypto does not support usage tables" << endl;
|
||||||
}
|
}
|
||||||
if (version >= 15) {
|
if (version >= 15) {
|
||||||
cout << " Resource Rating Tier: "
|
const uint32_t tier = OEMCrypto_ResourceRatingTier();
|
||||||
<< OEMCrypto_ResourceRatingTier() << endl;
|
cout << " Resource Rating Tier: " << tier << endl;
|
||||||
const char* build_info = OEMCrypto_BuildInformation();
|
const char* build_info = OEMCrypto_BuildInformation();
|
||||||
ASSERT_NE(nullptr, build_info);
|
ASSERT_NE(nullptr, build_info);
|
||||||
ASSERT_TRUE(strnlen(build_info, 256) <= 256)
|
ASSERT_TRUE(strnlen(build_info, 256) <= 256)
|
||||||
@@ -344,8 +354,7 @@ TEST_F(OEMCryptoClientTest, MaxSessionsOpenCloseAPI10) {
|
|||||||
ASSERT_GE(max_sessions, required_number);
|
ASSERT_GE(max_sessions, required_number);
|
||||||
// We allow GetMaxNumberOfSessions to return an estimate. This tests with a
|
// We allow GetMaxNumberOfSessions to return an estimate. This tests with a
|
||||||
// pad of 5%. Even if it's just an estimate, we still require 8 sessions.
|
// pad of 5%. Even if it's just an estimate, we still require 8 sessions.
|
||||||
size_t max_sessions_with_pad =
|
size_t max_sessions_with_pad = max(max_sessions * 19 / 20, required_number);
|
||||||
max(max_sessions * 19 / 20, required_number);
|
|
||||||
vector<OEMCrypto_SESSION> sessions;
|
vector<OEMCrypto_SESSION> sessions;
|
||||||
// Limit the number of sessions for testing.
|
// Limit the number of sessions for testing.
|
||||||
const size_t kMaxNumberOfSessionsForTesting = 0x100u;
|
const size_t kMaxNumberOfSessionsForTesting = 0x100u;
|
||||||
@@ -631,7 +640,7 @@ TEST_F(OEMCryptoKeyboxTest, GenerateDerivedKeysFromKeyboxLargeBuffer) {
|
|||||||
ASSERT_EQ(OEMCrypto_SUCCESS,
|
ASSERT_EQ(OEMCrypto_SUCCESS,
|
||||||
OEMCrypto_GenerateDerivedKeys(
|
OEMCrypto_GenerateDerivedKeys(
|
||||||
s.session_id(), mac_context.data(), mac_context.size(),
|
s.session_id(), mac_context.data(), mac_context.size(),
|
||||||
enc_context.data(),enc_context.size()));
|
enc_context.data(), enc_context.size()));
|
||||||
}
|
}
|
||||||
|
|
||||||
// This class is for tests that have an OEM Certificate instead of a keybox.
|
// This class is for tests that have an OEM Certificate instead of a keybox.
|
||||||
@@ -660,7 +669,6 @@ TEST_F(OEMCryptoProv30Test, GetDeviceId) {
|
|||||||
ASSERT_EQ(OEMCrypto_SUCCESS, sts);
|
ASSERT_EQ(OEMCrypto_SUCCESS, sts);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// The OEM certificate must be valid.
|
// The OEM certificate must be valid.
|
||||||
TEST_F(OEMCryptoProv30Test, CertValidAPI15) {
|
TEST_F(OEMCryptoProv30Test, CertValidAPI15) {
|
||||||
ASSERT_EQ(OEMCrypto_SUCCESS, OEMCrypto_IsKeyboxOrOEMCertValid());
|
ASSERT_EQ(OEMCrypto_SUCCESS, OEMCrypto_IsKeyboxOrOEMCertValid());
|
||||||
@@ -866,6 +874,25 @@ TEST_P(OEMCryptoLicenseTest, LoadKeyNoNonce) {
|
|||||||
ASSERT_EQ(OEMCrypto_SUCCESS, license_messages_.LoadResponse());
|
ASSERT_EQ(OEMCrypto_SUCCESS, license_messages_.LoadResponse());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Verify that a preloaded license may be loaded without first signing the
|
||||||
|
// request. This test is important for the preloaded licenses used by ATSC and
|
||||||
|
// CAS.
|
||||||
|
TEST_P(OEMCryptoLicenseTest, LoadKeyWithNoRequest) {
|
||||||
|
if (license_api_version_ > global_features.api_version) {
|
||||||
|
// We should not attempt to preload a license with an API higher than that
|
||||||
|
// of OEMCrypto.
|
||||||
|
license_api_version_ = global_features.api_version;
|
||||||
|
license_messages_.set_api_version(license_api_version_);
|
||||||
|
}
|
||||||
|
license_messages_.set_control(0);
|
||||||
|
// 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;
|
||||||
|
ASSERT_NO_FATAL_FAILURE(license_messages_.CreateDefaultResponse());
|
||||||
|
ASSERT_NO_FATAL_FAILURE(license_messages_.EncryptAndSignResponse());
|
||||||
|
ASSERT_EQ(OEMCrypto_SUCCESS, license_messages_.LoadResponse());
|
||||||
|
}
|
||||||
|
|
||||||
// Verify that a license may be loaded with a nonce.
|
// Verify that a license may be loaded with a nonce.
|
||||||
TEST_P(OEMCryptoLicenseTest, LoadKeyWithNonce) {
|
TEST_P(OEMCryptoLicenseTest, LoadKeyWithNonce) {
|
||||||
ASSERT_NO_FATAL_FAILURE(session_.GenerateNonce());
|
ASSERT_NO_FATAL_FAILURE(session_.GenerateNonce());
|
||||||
@@ -1083,7 +1110,9 @@ TEST_P(OEMCryptoLicenseTest, LoadKeyWithBadRange_pst) {
|
|||||||
// The IV should not be identical to the data right before the encrypted mac
|
// The IV should not be identical to the data right before the encrypted mac
|
||||||
// keys. This requirement was added in 15.2, so it frequently fails on
|
// keys. This requirement was added in 15.2, so it frequently fails on
|
||||||
// production devices.
|
// production devices.
|
||||||
TEST_F(OEMCryptoLicenseTestAPI15, LoadKeyWithSuspiciousIV) {
|
// This test is being restricted to v16 devices on rvc-dev branch because we
|
||||||
|
// only required v15.1 on Android for Q.
|
||||||
|
TEST_F(OEMCryptoLicenseTestAPI15, LoadKeyWithSuspiciousIVAPI16) {
|
||||||
ASSERT_NO_FATAL_FAILURE(session_.GenerateNonce());
|
ASSERT_NO_FATAL_FAILURE(session_.GenerateNonce());
|
||||||
ASSERT_NO_FATAL_FAILURE(license_messages_.SignAndVerifyRequest());
|
ASSERT_NO_FATAL_FAILURE(license_messages_.SignAndVerifyRequest());
|
||||||
ASSERT_NO_FATAL_FAILURE(license_messages_.CreateDefaultResponse());
|
ASSERT_NO_FATAL_FAILURE(license_messages_.CreateDefaultResponse());
|
||||||
@@ -1319,7 +1348,7 @@ TEST_P(OEMCryptoLicenseTestRangeAPI, LoadKeys) {
|
|||||||
INSTANTIATE_TEST_CASE_P(TestAll, OEMCryptoLicenseTestRangeAPI,
|
INSTANTIATE_TEST_CASE_P(TestAll, OEMCryptoLicenseTestRangeAPI,
|
||||||
Range<uint32_t>(10, kCurrentAPI + 2));
|
Range<uint32_t>(10, kCurrentAPI + 2));
|
||||||
|
|
||||||
TEST_P(OEMCryptoLicenseTest, LoadKeysBadSignature) {
|
TEST_P(OEMCryptoLicenseTest, LoadKeysBadSignatureAPI16) {
|
||||||
ASSERT_NO_FATAL_FAILURE(session_.GenerateNonce());
|
ASSERT_NO_FATAL_FAILURE(session_.GenerateNonce());
|
||||||
ASSERT_NO_FATAL_FAILURE(license_messages_.SignAndVerifyRequest());
|
ASSERT_NO_FATAL_FAILURE(license_messages_.SignAndVerifyRequest());
|
||||||
ASSERT_NO_FATAL_FAILURE(license_messages_.CreateDefaultResponse());
|
ASSERT_NO_FATAL_FAILURE(license_messages_.CreateDefaultResponse());
|
||||||
@@ -1360,7 +1389,9 @@ TEST_P(OEMCryptoLicenseTest, LoadKeyNoKeyWithNonce) {
|
|||||||
|
|
||||||
// SelectKey should fail if we attempt to select a key that has not been loaded.
|
// SelectKey should fail if we attempt to select a key that has not been loaded.
|
||||||
// Also, the error should be NO_CONTENT_KEY.
|
// Also, the error should be NO_CONTENT_KEY.
|
||||||
TEST_P(OEMCryptoLicenseTest, SelectKeyNotThereAPI15) {
|
// This test should pass for v15 devices, except that the exact error code was
|
||||||
|
// not specified until v16.
|
||||||
|
TEST_P(OEMCryptoLicenseTest, SelectKeyNotThereAPI16) {
|
||||||
ASSERT_NO_FATAL_FAILURE(session_.GenerateNonce());
|
ASSERT_NO_FATAL_FAILURE(session_.GenerateNonce());
|
||||||
ASSERT_NO_FATAL_FAILURE(license_messages_.SignAndVerifyRequest());
|
ASSERT_NO_FATAL_FAILURE(license_messages_.SignAndVerifyRequest());
|
||||||
ASSERT_NO_FATAL_FAILURE(license_messages_.CreateDefaultResponse());
|
ASSERT_NO_FATAL_FAILURE(license_messages_.CreateDefaultResponse());
|
||||||
@@ -1684,22 +1715,28 @@ class OEMCryptoSessionTestDecryptWithHDCP : public OEMCryptoSessionTests,
|
|||||||
ASSERT_NO_FATAL_FAILURE(license_messages.EncryptAndSignResponse());
|
ASSERT_NO_FATAL_FAILURE(license_messages.EncryptAndSignResponse());
|
||||||
ASSERT_EQ(OEMCrypto_SUCCESS, license_messages.LoadResponse());
|
ASSERT_EQ(OEMCrypto_SUCCESS, license_messages.LoadResponse());
|
||||||
|
|
||||||
if (version > maximum) {
|
if (version > current) {
|
||||||
ASSERT_NO_FATAL_FAILURE(
|
|
||||||
s.TestDecryptCTR(true, OEMCrypto_ERROR_INSUFFICIENT_HDCP));
|
|
||||||
} else if (version > current) {
|
|
||||||
if (global_features.api_version >= 16) {
|
if (global_features.api_version >= 16) {
|
||||||
// Can provide either OEMCrypto_WARNING_MIXED_OUTPUT_PROTECTION or
|
// Can provide either OEMCrypto_WARNING_MIXED_OUTPUT_PROTECTION or
|
||||||
// OEMCrypto_ERROR_INSUFFICIENT_HDCP. TestDecryptCTR allows either to be
|
// OEMCrypto_ERROR_INSUFFICIENT_HDCP. TestDecryptCTR allows either to be
|
||||||
// reported if OEMCrypto_WARNING_MIXED_OUTPUT_PROTECTION is expected.
|
// reported if OEMCrypto_WARNING_MIXED_OUTPUT_PROTECTION is expected.
|
||||||
ASSERT_NO_FATAL_FAILURE(
|
ASSERT_NO_FATAL_FAILURE(
|
||||||
s.TestDecryptCTR(true, OEMCrypto_WARNING_MIXED_OUTPUT_PROTECTION));
|
s.TestDecryptCTR(true, OEMCrypto_WARNING_MIXED_OUTPUT_PROTECTION))
|
||||||
|
<< "Failed when current HDCP = " << HDCPCapabilityAsString(current)
|
||||||
|
<< ", maximum HDCP = " << HDCPCapabilityAsString(maximum)
|
||||||
|
<< ", license HDCP = " << HDCPCapabilityAsString(version);
|
||||||
} else {
|
} else {
|
||||||
ASSERT_NO_FATAL_FAILURE(
|
ASSERT_NO_FATAL_FAILURE(
|
||||||
s.TestDecryptCTR(true, OEMCrypto_ERROR_INSUFFICIENT_HDCP));
|
s.TestDecryptCTR(true, OEMCrypto_ERROR_INSUFFICIENT_HDCP))
|
||||||
|
<< "Failed when current HDCP = " << HDCPCapabilityAsString(current)
|
||||||
|
<< ", maximum HDCP = " << HDCPCapabilityAsString(maximum)
|
||||||
|
<< ", license HDCP = " << HDCPCapabilityAsString(version);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
ASSERT_NO_FATAL_FAILURE(s.TestDecryptCTR(true, OEMCrypto_SUCCESS));
|
ASSERT_NO_FATAL_FAILURE(s.TestDecryptCTR(true, OEMCrypto_SUCCESS))
|
||||||
|
<< "Failed when current HDCP = " << HDCPCapabilityAsString(current)
|
||||||
|
<< ", maximum HDCP = " << HDCPCapabilityAsString(maximum)
|
||||||
|
<< ", license HDCP = " << HDCPCapabilityAsString(version);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -2635,7 +2672,8 @@ TEST_F(OEMCryptoLoadsCertificate, SignProvisioningRequest) {
|
|||||||
ASSERT_NO_FATAL_FAILURE(provisioning_messages.SignAndVerifyRequest());
|
ASSERT_NO_FATAL_FAILURE(provisioning_messages.SignAndVerifyRequest());
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(OEMCryptoLoadsCertificate, SignLargeProvisioningRequest) {
|
// This tests a large message size. The size is larger than we required in v15.
|
||||||
|
TEST_F(OEMCryptoLoadsCertificate, SignLargeProvisioningRequestAPI16) {
|
||||||
Session s;
|
Session s;
|
||||||
ASSERT_NO_FATAL_FAILURE(s.open());
|
ASSERT_NO_FATAL_FAILURE(s.open());
|
||||||
if (global_features.provisioning_method == OEMCrypto_OEMCertificate) {
|
if (global_features.provisioning_method == OEMCrypto_OEMCertificate) {
|
||||||
@@ -2751,7 +2789,8 @@ TEST_F(OEMCryptoLoadsCertificate, CertificateProvisionBadRange5Prov30_API16) {
|
|||||||
|
|
||||||
// Test that RewrapDeviceRSAKey verifies the message signature.
|
// Test that RewrapDeviceRSAKey verifies the message signature.
|
||||||
// TODO(b/144186970): This test should also run on Prov 3.0 devices.
|
// TODO(b/144186970): This test should also run on Prov 3.0 devices.
|
||||||
TEST_F(OEMCryptoLoadsCertificate, CertificateProvisionBadSignatureKeyboxTest) {
|
TEST_F(OEMCryptoLoadsCertificate,
|
||||||
|
CertificateProvisionBadSignatureKeyboxTestAPI16) {
|
||||||
Session s;
|
Session s;
|
||||||
ProvisioningRoundTrip provisioning_messages(&s, encoded_rsa_key_);
|
ProvisioningRoundTrip provisioning_messages(&s, encoded_rsa_key_);
|
||||||
provisioning_messages.PrepareSession(keybox_);
|
provisioning_messages.PrepareSession(keybox_);
|
||||||
@@ -2793,7 +2832,8 @@ TEST_F(OEMCryptoLoadsCertificate, CertificateProvisionBadRSAKey) {
|
|||||||
|
|
||||||
// Test that RewrapDeviceRSAKey verifies the RSA key is valid.
|
// Test that RewrapDeviceRSAKey verifies the RSA key is valid.
|
||||||
// TODO(b/144186970): This test should also run on Prov 3.0 devices.
|
// TODO(b/144186970): This test should also run on Prov 3.0 devices.
|
||||||
TEST_F(OEMCryptoLoadsCertificate, CertificateProvisionBadRSAKeyKeyboxTest) {
|
TEST_F(OEMCryptoLoadsCertificate,
|
||||||
|
CertificateProvisionBadRSAKeyKeyboxTestAPI16) {
|
||||||
Session s;
|
Session s;
|
||||||
ProvisioningRoundTrip provisioning_messages(&s, encoded_rsa_key_);
|
ProvisioningRoundTrip provisioning_messages(&s, encoded_rsa_key_);
|
||||||
provisioning_messages.PrepareSession(keybox_);
|
provisioning_messages.PrepareSession(keybox_);
|
||||||
@@ -2834,8 +2874,9 @@ TEST_F(OEMCryptoLoadsCertificate, LoadWrappedRSAKey) {
|
|||||||
CreateWrappedRSAKey();
|
CreateWrappedRSAKey();
|
||||||
Session s;
|
Session s;
|
||||||
ASSERT_NO_FATAL_FAILURE(s.open());
|
ASSERT_NO_FATAL_FAILURE(s.open());
|
||||||
sts = OEMCrypto_LoadDeviceRSAKey(s.session_id(), wrapped_rsa_key_.data(),
|
sts = OEMCrypto_LoadDRMPrivateKey(s.session_id(), OEMCrypto_RSA_Private_Key,
|
||||||
wrapped_rsa_key_.size());
|
wrapped_rsa_key_.data(),
|
||||||
|
wrapped_rsa_key_.size());
|
||||||
ASSERT_EQ(OEMCrypto_SUCCESS, sts);
|
ASSERT_EQ(OEMCrypto_SUCCESS, sts);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2843,12 +2884,12 @@ TEST_F(OEMCryptoLoadsCertificate, LoadWrappedRSAKey) {
|
|||||||
TEST_F(OEMCryptoLoadsCertificate, TestLargeRSAKey3072) {
|
TEST_F(OEMCryptoLoadsCertificate, TestLargeRSAKey3072) {
|
||||||
encoded_rsa_key_.assign(kTestRSAPKCS8PrivateKeyInfo3_3072,
|
encoded_rsa_key_.assign(kTestRSAPKCS8PrivateKeyInfo3_3072,
|
||||||
kTestRSAPKCS8PrivateKeyInfo3_3072 +
|
kTestRSAPKCS8PrivateKeyInfo3_3072 +
|
||||||
sizeof(kTestRSAPKCS8PrivateKeyInfo3_3072));
|
sizeof(kTestRSAPKCS8PrivateKeyInfo3_3072));
|
||||||
CreateWrappedRSAKey();
|
CreateWrappedRSAKey();
|
||||||
Session s;
|
Session s;
|
||||||
ASSERT_NO_FATAL_FAILURE(s.open());
|
ASSERT_NO_FATAL_FAILURE(s.open());
|
||||||
ASSERT_NO_FATAL_FAILURE(s.PreparePublicKey(encoded_rsa_key_.data(),
|
ASSERT_NO_FATAL_FAILURE(
|
||||||
encoded_rsa_key_.size()));
|
s.PreparePublicKey(encoded_rsa_key_.data(), encoded_rsa_key_.size()));
|
||||||
ASSERT_NO_FATAL_FAILURE(s.InstallRSASessionTestKey(wrapped_rsa_key_));
|
ASSERT_NO_FATAL_FAILURE(s.InstallRSASessionTestKey(wrapped_rsa_key_));
|
||||||
|
|
||||||
LicenseRoundTrip license_messages(&s);
|
LicenseRoundTrip license_messages(&s);
|
||||||
@@ -2869,8 +2910,8 @@ TEST_F(OEMCryptoLoadsCertificate, TestCarmichaelRSAKey) {
|
|||||||
CreateWrappedRSAKey();
|
CreateWrappedRSAKey();
|
||||||
Session s;
|
Session s;
|
||||||
ASSERT_NO_FATAL_FAILURE(s.open());
|
ASSERT_NO_FATAL_FAILURE(s.open());
|
||||||
ASSERT_NO_FATAL_FAILURE(s.PreparePublicKey(encoded_rsa_key_.data(),
|
ASSERT_NO_FATAL_FAILURE(
|
||||||
encoded_rsa_key_.size()));
|
s.PreparePublicKey(encoded_rsa_key_.data(), encoded_rsa_key_.size()));
|
||||||
ASSERT_NO_FATAL_FAILURE(s.InstallRSASessionTestKey(wrapped_rsa_key_));
|
ASSERT_NO_FATAL_FAILURE(s.InstallRSASessionTestKey(wrapped_rsa_key_));
|
||||||
|
|
||||||
LicenseRoundTrip license_messages(&s);
|
LicenseRoundTrip license_messages(&s);
|
||||||
@@ -2888,11 +2929,12 @@ TEST_F(OEMCryptoLoadsCertificate, TestMultipleRSAKeys) {
|
|||||||
Session s1; // Session s1 loads the default rsa key, but doesn't use it
|
Session s1; // Session s1 loads the default rsa key, but doesn't use it
|
||||||
// until after s2 uses its key.
|
// until after s2 uses its key.
|
||||||
ASSERT_NO_FATAL_FAILURE(s1.open());
|
ASSERT_NO_FATAL_FAILURE(s1.open());
|
||||||
ASSERT_NO_FATAL_FAILURE(s1.PreparePublicKey(encoded_rsa_key_.data(),
|
ASSERT_NO_FATAL_FAILURE(
|
||||||
encoded_rsa_key_.size()));
|
s1.PreparePublicKey(encoded_rsa_key_.data(), encoded_rsa_key_.size()));
|
||||||
ASSERT_EQ(OEMCrypto_SUCCESS,
|
ASSERT_EQ(OEMCrypto_SUCCESS,
|
||||||
OEMCrypto_LoadDeviceRSAKey(s1.session_id(), wrapped_rsa_key_.data(),
|
OEMCrypto_LoadDRMPrivateKey(
|
||||||
wrapped_rsa_key_.size()));
|
s1.session_id(), OEMCrypto_RSA_Private_Key,
|
||||||
|
wrapped_rsa_key_.data(), wrapped_rsa_key_.size()));
|
||||||
|
|
||||||
Session s2; // Session s2 uses a different rsa key.
|
Session s2; // Session s2 uses a different rsa key.
|
||||||
encoded_rsa_key_.assign(kTestRSAPKCS8PrivateKeyInfo4_2048,
|
encoded_rsa_key_.assign(kTestRSAPKCS8PrivateKeyInfo4_2048,
|
||||||
@@ -2900,8 +2942,8 @@ TEST_F(OEMCryptoLoadsCertificate, TestMultipleRSAKeys) {
|
|||||||
sizeof(kTestRSAPKCS8PrivateKeyInfo4_2048));
|
sizeof(kTestRSAPKCS8PrivateKeyInfo4_2048));
|
||||||
CreateWrappedRSAKey();
|
CreateWrappedRSAKey();
|
||||||
ASSERT_NO_FATAL_FAILURE(s2.open());
|
ASSERT_NO_FATAL_FAILURE(s2.open());
|
||||||
ASSERT_NO_FATAL_FAILURE(s2.PreparePublicKey(encoded_rsa_key_.data(),
|
ASSERT_NO_FATAL_FAILURE(
|
||||||
encoded_rsa_key_.size()));
|
s2.PreparePublicKey(encoded_rsa_key_.data(), encoded_rsa_key_.size()));
|
||||||
ASSERT_NO_FATAL_FAILURE(s2.InstallRSASessionTestKey(wrapped_rsa_key_));
|
ASSERT_NO_FATAL_FAILURE(s2.InstallRSASessionTestKey(wrapped_rsa_key_));
|
||||||
LicenseRoundTrip license_messages2(&s2);
|
LicenseRoundTrip license_messages2(&s2);
|
||||||
ASSERT_NO_FATAL_FAILURE(s2.GenerateNonce());
|
ASSERT_NO_FATAL_FAILURE(s2.GenerateNonce());
|
||||||
@@ -2975,8 +3017,9 @@ TEST_F(OEMCryptoLoadsCertificate, RSAPerformance) {
|
|||||||
while (clock.now() - start_time < kTestDuration) {
|
while (clock.now() - start_time < kTestDuration) {
|
||||||
Session s;
|
Session s;
|
||||||
ASSERT_NO_FATAL_FAILURE(s.open());
|
ASSERT_NO_FATAL_FAILURE(s.open());
|
||||||
sts = OEMCrypto_LoadDeviceRSAKey(s.session_id(), wrapped_rsa_key_.data(),
|
sts = OEMCrypto_LoadDRMPrivateKey(s.session_id(), OEMCrypto_RSA_Private_Key,
|
||||||
wrapped_rsa_key_.size());
|
wrapped_rsa_key_.data(),
|
||||||
|
wrapped_rsa_key_.size());
|
||||||
ASSERT_EQ(OEMCrypto_SUCCESS, sts);
|
ASSERT_EQ(OEMCrypto_SUCCESS, sts);
|
||||||
const size_t size = 50;
|
const size_t size = 50;
|
||||||
vector<uint8_t> licenseRequest(size);
|
vector<uint8_t> licenseRequest(size);
|
||||||
@@ -3002,12 +3045,13 @@ TEST_F(OEMCryptoLoadsCertificate, RSAPerformance) {
|
|||||||
Session s;
|
Session s;
|
||||||
ASSERT_NO_FATAL_FAILURE(s.open());
|
ASSERT_NO_FATAL_FAILURE(s.open());
|
||||||
ASSERT_EQ(OEMCrypto_SUCCESS,
|
ASSERT_EQ(OEMCrypto_SUCCESS,
|
||||||
OEMCrypto_LoadDeviceRSAKey(s.session_id(), wrapped_rsa_key_.data(),
|
OEMCrypto_LoadDRMPrivateKey(
|
||||||
wrapped_rsa_key_.size()));
|
s.session_id(), OEMCrypto_RSA_Private_Key,
|
||||||
|
wrapped_rsa_key_.data(), wrapped_rsa_key_.size()));
|
||||||
vector<uint8_t> session_key;
|
vector<uint8_t> session_key;
|
||||||
vector<uint8_t> enc_session_key;
|
vector<uint8_t> enc_session_key;
|
||||||
ASSERT_NO_FATAL_FAILURE(s.PreparePublicKey(encoded_rsa_key_.data(),
|
ASSERT_NO_FATAL_FAILURE(
|
||||||
encoded_rsa_key_.size()));
|
s.PreparePublicKey(encoded_rsa_key_.data(), encoded_rsa_key_.size()));
|
||||||
ASSERT_TRUE(s.GenerateRSASessionKey(&session_key, &enc_session_key));
|
ASSERT_TRUE(s.GenerateRSASessionKey(&session_key, &enc_session_key));
|
||||||
vector<uint8_t> mac_context;
|
vector<uint8_t> mac_context;
|
||||||
vector<uint8_t> enc_context;
|
vector<uint8_t> enc_context;
|
||||||
@@ -3035,10 +3079,9 @@ TEST_F(OEMCryptoLoadsCertificate, RSAPerformance) {
|
|||||||
while (clock.now() - start_time < kTestDuration) {
|
while (clock.now() - start_time < kTestDuration) {
|
||||||
ASSERT_EQ(OEMCrypto_SUCCESS,
|
ASSERT_EQ(OEMCrypto_SUCCESS,
|
||||||
OEMCrypto_DeriveKeysFromSessionKey(
|
OEMCrypto_DeriveKeysFromSessionKey(
|
||||||
s.session_id(),
|
s.session_id(), enc_session_key.data(),
|
||||||
enc_session_key.data(), enc_session_key.size(),
|
enc_session_key.size(), mac_context.data(),
|
||||||
mac_context.data(), mac_context.size(),
|
mac_context.size(), enc_context.data(), enc_context.size()));
|
||||||
enc_context.data(), enc_context.size()));
|
|
||||||
count++;
|
count++;
|
||||||
}
|
}
|
||||||
delta_time = clock.now() - start_time;
|
delta_time = clock.now() - start_time;
|
||||||
@@ -3068,9 +3111,8 @@ TEST_F(OEMCryptoUsesCertificate, GenerateDerivedKeysLargeBuffer) {
|
|||||||
}
|
}
|
||||||
ASSERT_EQ(OEMCrypto_SUCCESS,
|
ASSERT_EQ(OEMCrypto_SUCCESS,
|
||||||
OEMCrypto_DeriveKeysFromSessionKey(
|
OEMCrypto_DeriveKeysFromSessionKey(
|
||||||
session_.session_id(),
|
session_.session_id(), enc_session_key.data(),
|
||||||
enc_session_key.data(), enc_session_key.size(),
|
enc_session_key.size(), mac_context.data(), mac_context.size(),
|
||||||
mac_context.data(), mac_context.size(),
|
|
||||||
enc_context.data(), enc_context.size()));
|
enc_context.data(), enc_context.size()));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -3081,8 +3123,9 @@ class OEMCryptoLoadsCertificateAlternates : public OEMCryptoLoadsCertificate {
|
|||||||
OEMCryptoResult sts;
|
OEMCryptoResult sts;
|
||||||
Session s;
|
Session s;
|
||||||
ASSERT_NO_FATAL_FAILURE(s.open());
|
ASSERT_NO_FATAL_FAILURE(s.open());
|
||||||
sts = OEMCrypto_LoadDeviceRSAKey(s.session_id(), wrapped_rsa_key_.data(),
|
sts = OEMCrypto_LoadDRMPrivateKey(s.session_id(), OEMCrypto_RSA_Private_Key,
|
||||||
wrapped_rsa_key_.size());
|
wrapped_rsa_key_.data(),
|
||||||
|
wrapped_rsa_key_.size());
|
||||||
ASSERT_EQ(OEMCrypto_SUCCESS, sts);
|
ASSERT_EQ(OEMCrypto_SUCCESS, sts);
|
||||||
|
|
||||||
// Sign a Message
|
// Sign a Message
|
||||||
@@ -3090,19 +3133,16 @@ class OEMCryptoLoadsCertificateAlternates : public OEMCryptoLoadsCertificate {
|
|||||||
GetRandBytes(licenseRequest.data(), licenseRequest.size());
|
GetRandBytes(licenseRequest.data(), licenseRequest.size());
|
||||||
size_t signature_length = 256;
|
size_t signature_length = 256;
|
||||||
vector<uint8_t> signature(signature_length);
|
vector<uint8_t> signature(signature_length);
|
||||||
sts = OEMCrypto_GenerateRSASignature(s.session_id(), licenseRequest.data(),
|
sts = OEMCrypto_GenerateRSASignature(
|
||||||
licenseRequest.size(),
|
s.session_id(), licenseRequest.data(), licenseRequest.size(),
|
||||||
signature.data(), &signature_length,
|
signature.data(), &signature_length, scheme);
|
||||||
scheme);
|
|
||||||
// Allow OEMCrypto to request a full buffer.
|
// Allow OEMCrypto to request a full buffer.
|
||||||
if (sts == OEMCrypto_ERROR_SHORT_BUFFER) {
|
if (sts == OEMCrypto_ERROR_SHORT_BUFFER) {
|
||||||
ASSERT_NE(static_cast<size_t>(0), signature_length);
|
ASSERT_NE(static_cast<size_t>(0), signature_length);
|
||||||
signature.assign(signature_length, 0);
|
signature.assign(signature_length, 0);
|
||||||
sts = OEMCrypto_GenerateRSASignature(s.session_id(),
|
sts = OEMCrypto_GenerateRSASignature(
|
||||||
licenseRequest.data(),
|
s.session_id(), licenseRequest.data(), licenseRequest.size(),
|
||||||
licenseRequest.size(),
|
signature.data(), &signature_length, scheme);
|
||||||
signature.data(), &signature_length,
|
|
||||||
scheme);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
EXPECT_NE(OEMCrypto_SUCCESS, sts)
|
EXPECT_NE(OEMCrypto_SUCCESS, sts)
|
||||||
@@ -3116,8 +3156,9 @@ class OEMCryptoLoadsCertificateAlternates : public OEMCryptoLoadsCertificate {
|
|||||||
OEMCryptoResult sts;
|
OEMCryptoResult sts;
|
||||||
Session s;
|
Session s;
|
||||||
ASSERT_NO_FATAL_FAILURE(s.open());
|
ASSERT_NO_FATAL_FAILURE(s.open());
|
||||||
sts = OEMCrypto_LoadDeviceRSAKey(s.session_id(), wrapped_rsa_key_.data(),
|
sts = OEMCrypto_LoadDRMPrivateKey(s.session_id(), OEMCrypto_RSA_Private_Key,
|
||||||
wrapped_rsa_key_.size());
|
wrapped_rsa_key_.data(),
|
||||||
|
wrapped_rsa_key_.size());
|
||||||
ASSERT_EQ(OEMCrypto_SUCCESS, sts);
|
ASSERT_EQ(OEMCrypto_SUCCESS, sts);
|
||||||
|
|
||||||
vector<uint8_t> licenseRequest(size);
|
vector<uint8_t> licenseRequest(size);
|
||||||
@@ -3137,8 +3178,8 @@ class OEMCryptoLoadsCertificateAlternates : public OEMCryptoLoadsCertificate {
|
|||||||
ASSERT_EQ(OEMCrypto_SUCCESS, sts)
|
ASSERT_EQ(OEMCrypto_SUCCESS, sts)
|
||||||
<< "Failed to sign with padding scheme=" << (int)scheme
|
<< "Failed to sign with padding scheme=" << (int)scheme
|
||||||
<< ", size=" << (int)size;
|
<< ", size=" << (int)size;
|
||||||
ASSERT_NO_FATAL_FAILURE(s.PreparePublicKey(encoded_rsa_key_.data(),
|
ASSERT_NO_FATAL_FAILURE(
|
||||||
encoded_rsa_key_.size()));
|
s.PreparePublicKey(encoded_rsa_key_.data(), encoded_rsa_key_.size()));
|
||||||
ASSERT_NO_FATAL_FAILURE(s.VerifyRSASignature(licenseRequest, signature,
|
ASSERT_NO_FATAL_FAILURE(s.VerifyRSASignature(licenseRequest, signature,
|
||||||
signature_length, scheme));
|
signature_length, scheme));
|
||||||
delete[] signature;
|
delete[] signature;
|
||||||
@@ -3148,14 +3189,15 @@ class OEMCryptoLoadsCertificateAlternates : public OEMCryptoLoadsCertificate {
|
|||||||
OEMCryptoResult sts;
|
OEMCryptoResult sts;
|
||||||
Session s;
|
Session s;
|
||||||
ASSERT_NO_FATAL_FAILURE(s.open());
|
ASSERT_NO_FATAL_FAILURE(s.open());
|
||||||
sts = OEMCrypto_LoadDeviceRSAKey(s.session_id(), wrapped_rsa_key_.data(),
|
sts = OEMCrypto_LoadDRMPrivateKey(s.session_id(), OEMCrypto_RSA_Private_Key,
|
||||||
wrapped_rsa_key_.size());
|
wrapped_rsa_key_.data(),
|
||||||
|
wrapped_rsa_key_.size());
|
||||||
ASSERT_EQ(OEMCrypto_SUCCESS, sts);
|
ASSERT_EQ(OEMCrypto_SUCCESS, sts);
|
||||||
s.GenerateNonce();
|
s.GenerateNonce();
|
||||||
vector<uint8_t> session_key;
|
vector<uint8_t> session_key;
|
||||||
vector<uint8_t> enc_session_key;
|
vector<uint8_t> enc_session_key;
|
||||||
ASSERT_NO_FATAL_FAILURE(s.PreparePublicKey(encoded_rsa_key_.data(),
|
ASSERT_NO_FATAL_FAILURE(
|
||||||
encoded_rsa_key_.size()));
|
s.PreparePublicKey(encoded_rsa_key_.data(), encoded_rsa_key_.size()));
|
||||||
ASSERT_TRUE(s.GenerateRSASessionKey(&session_key, &enc_session_key));
|
ASSERT_TRUE(s.GenerateRSASessionKey(&session_key, &enc_session_key));
|
||||||
vector<uint8_t> mac_context;
|
vector<uint8_t> mac_context;
|
||||||
vector<uint8_t> enc_context;
|
vector<uint8_t> enc_context;
|
||||||
@@ -3359,7 +3401,7 @@ class OEMCryptoCastReceiverTest : public OEMCryptoLoadsCertificateAlternates {
|
|||||||
"01" // 1
|
"01" // 1
|
||||||
"05" // null object. (field=parameter?)
|
"05" // null object. (field=parameter?)
|
||||||
"00" // size of null object
|
"00" // size of null object
|
||||||
);
|
);
|
||||||
|
|
||||||
vector<uint8_t> pkey = wvcdm::a2b_hex("020100"); // integer, version = 0.
|
vector<uint8_t> pkey = wvcdm::a2b_hex("020100"); // integer, version = 0.
|
||||||
pkey = concat(pkey, field_n);
|
pkey = concat(pkey, field_n);
|
||||||
@@ -3383,8 +3425,9 @@ class OEMCryptoCastReceiverTest : public OEMCryptoLoadsCertificateAlternates {
|
|||||||
OEMCryptoResult sts;
|
OEMCryptoResult sts;
|
||||||
Session s;
|
Session s;
|
||||||
ASSERT_NO_FATAL_FAILURE(s.open());
|
ASSERT_NO_FATAL_FAILURE(s.open());
|
||||||
sts = OEMCrypto_LoadDeviceRSAKey(s.session_id(), wrapped_rsa_key_.data(),
|
sts = OEMCrypto_LoadDRMPrivateKey(s.session_id(), OEMCrypto_RSA_Private_Key,
|
||||||
wrapped_rsa_key_.size());
|
wrapped_rsa_key_.data(),
|
||||||
|
wrapped_rsa_key_.size());
|
||||||
ASSERT_EQ(OEMCrypto_SUCCESS, sts);
|
ASSERT_EQ(OEMCrypto_SUCCESS, sts);
|
||||||
|
|
||||||
// The application will compute the SHA-1 Hash of the message, so this
|
// The application will compute the SHA-1 Hash of the message, so this
|
||||||
@@ -3416,8 +3459,8 @@ class OEMCryptoCastReceiverTest : public OEMCryptoLoadsCertificateAlternates {
|
|||||||
ASSERT_EQ(OEMCrypto_SUCCESS, sts)
|
ASSERT_EQ(OEMCrypto_SUCCESS, sts)
|
||||||
<< "Failed to sign with padding scheme=" << (int)scheme
|
<< "Failed to sign with padding scheme=" << (int)scheme
|
||||||
<< ", size=" << (int)message.size();
|
<< ", size=" << (int)message.size();
|
||||||
ASSERT_NO_FATAL_FAILURE(s.PreparePublicKey(encoded_rsa_key_.data(),
|
ASSERT_NO_FATAL_FAILURE(
|
||||||
encoded_rsa_key_.size()));
|
s.PreparePublicKey(encoded_rsa_key_.data(), encoded_rsa_key_.size()));
|
||||||
|
|
||||||
// Verify that the signature matches the official test vector.
|
// Verify that the signature matches the official test vector.
|
||||||
ASSERT_EQ(correct_signature.size(), signature_length);
|
ASSERT_EQ(correct_signature.size(), signature_length);
|
||||||
@@ -3426,8 +3469,8 @@ class OEMCryptoCastReceiverTest : public OEMCryptoLoadsCertificateAlternates {
|
|||||||
|
|
||||||
// Also verify that our verification algorithm agrees. This is not needed
|
// Also verify that our verification algorithm agrees. This is not needed
|
||||||
// to test OEMCrypto, but it does verify that this test is valid.
|
// to test OEMCrypto, but it does verify that this test is valid.
|
||||||
ASSERT_NO_FATAL_FAILURE(s.VerifyRSASignature(
|
ASSERT_NO_FATAL_FAILURE(s.VerifyRSASignature(digest, signature.data(),
|
||||||
digest, signature.data(), signature_length, scheme));
|
signature_length, scheme));
|
||||||
ASSERT_NO_FATAL_FAILURE(s.VerifyRSASignature(
|
ASSERT_NO_FATAL_FAILURE(s.VerifyRSASignature(
|
||||||
digest, correct_signature.data(), correct_signature.size(), scheme));
|
digest, correct_signature.data(), correct_signature.size(), scheme));
|
||||||
}
|
}
|
||||||
@@ -3435,7 +3478,8 @@ class OEMCryptoCastReceiverTest : public OEMCryptoLoadsCertificateAlternates {
|
|||||||
|
|
||||||
// CAST Receivers should report that they support cast certificates.
|
// CAST Receivers should report that they support cast certificates.
|
||||||
TEST_F(OEMCryptoCastReceiverTest, SupportsCertificatesAPI13) {
|
TEST_F(OEMCryptoCastReceiverTest, SupportsCertificatesAPI13) {
|
||||||
ASSERT_NE(0u, OEMCrypto_Supports_RSA_CAST & OEMCrypto_SupportedCertificates());
|
ASSERT_NE(0u,
|
||||||
|
OEMCrypto_Supports_RSA_CAST & OEMCrypto_SupportedCertificates());
|
||||||
}
|
}
|
||||||
|
|
||||||
// # PKCS#1 v1.5 Signature Example 15.1
|
// # PKCS#1 v1.5 Signature Example 15.1
|
||||||
@@ -4215,10 +4259,9 @@ class OEMCryptoGenericCryptoTest : public OEMCryptoRefreshTest {
|
|||||||
OEMCrypto_CipherMode_CTR);
|
OEMCrypto_CipherMode_CTR);
|
||||||
ASSERT_EQ(OEMCrypto_SUCCESS, sts);
|
ASSERT_EQ(OEMCrypto_SUCCESS, sts);
|
||||||
vector<uint8_t> encrypted(buffer_length);
|
vector<uint8_t> encrypted(buffer_length);
|
||||||
sts =
|
sts = OEMCrypto_Generic_Encrypt(session_.session_id(), clear_buffer_.data(),
|
||||||
OEMCrypto_Generic_Encrypt(session_.session_id(), clear_buffer_.data(),
|
buffer_length, iv_, algorithm,
|
||||||
buffer_length, iv_, algorithm,
|
encrypted.data());
|
||||||
encrypted.data());
|
|
||||||
EXPECT_NE(OEMCrypto_SUCCESS, sts);
|
EXPECT_NE(OEMCrypto_SUCCESS, sts);
|
||||||
expected_encrypted.resize(buffer_length);
|
expected_encrypted.resize(buffer_length);
|
||||||
EXPECT_NE(encrypted, expected_encrypted);
|
EXPECT_NE(encrypted, expected_encrypted);
|
||||||
@@ -4237,10 +4280,9 @@ class OEMCryptoGenericCryptoTest : public OEMCryptoRefreshTest {
|
|||||||
OEMCrypto_CipherMode_CTR);
|
OEMCrypto_CipherMode_CTR);
|
||||||
ASSERT_EQ(OEMCrypto_SUCCESS, sts);
|
ASSERT_EQ(OEMCrypto_SUCCESS, sts);
|
||||||
vector<uint8_t> resultant(encrypted.size());
|
vector<uint8_t> resultant(encrypted.size());
|
||||||
sts =
|
sts = OEMCrypto_Generic_Decrypt(session_.session_id(), encrypted.data(),
|
||||||
OEMCrypto_Generic_Decrypt(session_.session_id(), encrypted.data(),
|
buffer_length, iv_, algorithm,
|
||||||
buffer_length, iv_, algorithm,
|
resultant.data());
|
||||||
resultant.data());
|
|
||||||
EXPECT_NE(OEMCrypto_SUCCESS, sts);
|
EXPECT_NE(OEMCrypto_SUCCESS, sts);
|
||||||
EXPECT_NE(clear_buffer_, resultant);
|
EXPECT_NE(clear_buffer_, resultant);
|
||||||
}
|
}
|
||||||
@@ -4310,11 +4352,11 @@ TEST_P(OEMCryptoGenericCryptoTest, GenericKeyEncrypt) {
|
|||||||
session_.license().keys[key_index].key_id_length,
|
session_.license().keys[key_index].key_id_length,
|
||||||
OEMCrypto_CipherMode_CTR));
|
OEMCrypto_CipherMode_CTR));
|
||||||
vector<uint8_t> encrypted(clear_buffer_.size());
|
vector<uint8_t> encrypted(clear_buffer_.size());
|
||||||
ASSERT_EQ(OEMCrypto_SUCCESS,
|
ASSERT_EQ(
|
||||||
OEMCrypto_Generic_Encrypt(
|
OEMCrypto_SUCCESS,
|
||||||
session_.session_id(), clear_buffer_.data(),
|
OEMCrypto_Generic_Encrypt(
|
||||||
clear_buffer_.size(), iv_, OEMCrypto_AES_CBC_128_NO_PADDING,
|
session_.session_id(), clear_buffer_.data(), clear_buffer_.size(),
|
||||||
encrypted.data()));
|
iv_, OEMCrypto_AES_CBC_128_NO_PADDING, encrypted.data()));
|
||||||
ASSERT_EQ(expected_encrypted, encrypted);
|
ASSERT_EQ(expected_encrypted, encrypted);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -4346,8 +4388,8 @@ TEST_P(OEMCryptoGenericCryptoTest, GenericKeyEncryptSameBufferAPI12) {
|
|||||||
vector<uint8_t> buffer = clear_buffer_;
|
vector<uint8_t> buffer = clear_buffer_;
|
||||||
ASSERT_EQ(OEMCrypto_SUCCESS,
|
ASSERT_EQ(OEMCrypto_SUCCESS,
|
||||||
OEMCrypto_Generic_Encrypt(
|
OEMCrypto_Generic_Encrypt(
|
||||||
session_.session_id(), buffer.data(), buffer.size(),
|
session_.session_id(), buffer.data(), buffer.size(), iv_,
|
||||||
iv_, OEMCrypto_AES_CBC_128_NO_PADDING, buffer.data()));
|
OEMCrypto_AES_CBC_128_NO_PADDING, buffer.data()));
|
||||||
ASSERT_EQ(expected_encrypted, buffer);
|
ASSERT_EQ(expected_encrypted, buffer);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -4474,11 +4516,10 @@ TEST_P(OEMCryptoGenericCryptoTest, GenericKeyVerify) {
|
|||||||
session_.license().keys[key_index].key_id,
|
session_.license().keys[key_index].key_id,
|
||||||
session_.license().keys[key_index].key_id_length,
|
session_.license().keys[key_index].key_id_length,
|
||||||
OEMCrypto_CipherMode_CTR));
|
OEMCrypto_CipherMode_CTR));
|
||||||
ASSERT_EQ(OEMCrypto_SUCCESS,
|
ASSERT_EQ(OEMCrypto_SUCCESS, OEMCrypto_Generic_Verify(
|
||||||
OEMCrypto_Generic_Verify(
|
session_.session_id(), clear_buffer_.data(),
|
||||||
session_.session_id(), clear_buffer_.data(),
|
clear_buffer_.size(), OEMCrypto_HMAC_SHA256,
|
||||||
clear_buffer_.size(), OEMCrypto_HMAC_SHA256, signature.data(),
|
signature.data(), signature.size()));
|
||||||
signature.size()));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test that the Generic_Verify function fails when not allowed.
|
// Test that the Generic_Verify function fails when not allowed.
|
||||||
@@ -4507,11 +4548,11 @@ TEST_P(OEMCryptoGenericCryptoTest, GenericKeyEncryptLargeBuffer) {
|
|||||||
session_.license().keys[key_index].key_id_length,
|
session_.license().keys[key_index].key_id_length,
|
||||||
OEMCrypto_CipherMode_CTR));
|
OEMCrypto_CipherMode_CTR));
|
||||||
vector<uint8_t> encrypted(clear_buffer_.size());
|
vector<uint8_t> encrypted(clear_buffer_.size());
|
||||||
ASSERT_EQ(OEMCrypto_SUCCESS,
|
ASSERT_EQ(
|
||||||
OEMCrypto_Generic_Encrypt(
|
OEMCrypto_SUCCESS,
|
||||||
session_.session_id(), clear_buffer_.data(),
|
OEMCrypto_Generic_Encrypt(
|
||||||
clear_buffer_.size(), iv_, OEMCrypto_AES_CBC_128_NO_PADDING,
|
session_.session_id(), clear_buffer_.data(), clear_buffer_.size(),
|
||||||
encrypted.data()));
|
iv_, OEMCrypto_AES_CBC_128_NO_PADDING, encrypted.data()));
|
||||||
ASSERT_EQ(expected_encrypted, encrypted);
|
ASSERT_EQ(expected_encrypted, encrypted);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -4579,11 +4620,10 @@ TEST_P(OEMCryptoGenericCryptoTest, GenericKeyVerifyLargeBuffer) {
|
|||||||
session_.license().keys[key_index].key_id,
|
session_.license().keys[key_index].key_id,
|
||||||
session_.license().keys[key_index].key_id_length,
|
session_.license().keys[key_index].key_id_length,
|
||||||
OEMCrypto_CipherMode_CTR));
|
OEMCrypto_CipherMode_CTR));
|
||||||
ASSERT_EQ(OEMCrypto_SUCCESS,
|
ASSERT_EQ(OEMCrypto_SUCCESS, OEMCrypto_Generic_Verify(
|
||||||
OEMCrypto_Generic_Verify(
|
session_.session_id(), clear_buffer_.data(),
|
||||||
session_.session_id(), clear_buffer_.data(),
|
clear_buffer_.size(), OEMCrypto_HMAC_SHA256,
|
||||||
clear_buffer_.size(), OEMCrypto_HMAC_SHA256, signature.data(),
|
signature.data(), signature.size()));
|
||||||
signature.size()));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test Generic_Encrypt when the key duration has expired.
|
// Test Generic_Encrypt when the key duration has expired.
|
||||||
@@ -4604,11 +4644,11 @@ TEST_P(OEMCryptoGenericCryptoTest, KeyDurationEncrypt) {
|
|||||||
session_.license().keys[key_index].key_id,
|
session_.license().keys[key_index].key_id,
|
||||||
session_.license().keys[key_index].key_id_length,
|
session_.license().keys[key_index].key_id_length,
|
||||||
OEMCrypto_CipherMode_CTR));
|
OEMCrypto_CipherMode_CTR));
|
||||||
ASSERT_EQ(OEMCrypto_SUCCESS,
|
ASSERT_EQ(
|
||||||
OEMCrypto_Generic_Encrypt(
|
OEMCrypto_SUCCESS,
|
||||||
session_.session_id(), clear_buffer_.data(),
|
OEMCrypto_Generic_Encrypt(
|
||||||
clear_buffer_.size(), iv_, OEMCrypto_AES_CBC_128_NO_PADDING,
|
session_.session_id(), clear_buffer_.data(), clear_buffer_.size(),
|
||||||
encrypted.data()));
|
iv_, OEMCrypto_AES_CBC_128_NO_PADDING, encrypted.data()));
|
||||||
ASSERT_EQ(expected_encrypted, encrypted);
|
ASSERT_EQ(expected_encrypted, encrypted);
|
||||||
|
|
||||||
wvcdm::TestSleep::Sleep(kLongSleep + kShortSleep); // Should be expired key.
|
wvcdm::TestSleep::Sleep(kLongSleep + kShortSleep); // Should be expired key.
|
||||||
@@ -4709,11 +4749,10 @@ TEST_P(OEMCryptoGenericCryptoTest, KeyDurationVerify) {
|
|||||||
session_.license().keys[key_index].key_id,
|
session_.license().keys[key_index].key_id,
|
||||||
session_.license().keys[key_index].key_id_length,
|
session_.license().keys[key_index].key_id_length,
|
||||||
OEMCrypto_CipherMode_CTR));
|
OEMCrypto_CipherMode_CTR));
|
||||||
ASSERT_EQ(OEMCrypto_SUCCESS,
|
ASSERT_EQ(OEMCrypto_SUCCESS, OEMCrypto_Generic_Verify(
|
||||||
OEMCrypto_Generic_Verify(
|
session_.session_id(), clear_buffer_.data(),
|
||||||
session_.session_id(), clear_buffer_.data(),
|
clear_buffer_.size(), OEMCrypto_HMAC_SHA256,
|
||||||
clear_buffer_.size(), OEMCrypto_HMAC_SHA256, signature.data(),
|
signature.data(), signature.size()));
|
||||||
signature.size()));
|
|
||||||
|
|
||||||
wvcdm::TestSleep::Sleep(kLongSleep + kShortSleep); // Should be expired key.
|
wvcdm::TestSleep::Sleep(kLongSleep + kShortSleep); // Should be expired key.
|
||||||
OEMCryptoResult status = OEMCrypto_Generic_Verify(
|
OEMCryptoResult status = OEMCrypto_Generic_Verify(
|
||||||
@@ -4827,7 +4866,8 @@ class LicenseWithUsageEntry {
|
|||||||
generic_crypto_(false),
|
generic_crypto_(false),
|
||||||
time_license_received_(0),
|
time_license_received_(0),
|
||||||
time_first_decrypt_(0),
|
time_first_decrypt_(0),
|
||||||
time_last_decrypt_(0) {
|
time_last_decrypt_(0),
|
||||||
|
active_(true) {
|
||||||
license_messages_.set_pst(pst);
|
license_messages_.set_pst(pst);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -4888,6 +4928,15 @@ class LicenseWithUsageEntry {
|
|||||||
if (time_first_decrypt_ == 0) time_first_decrypt_ = time_last_decrypt_;
|
if (time_first_decrypt_ == 0) time_first_decrypt_ = time_last_decrypt_;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void DeactivateUsageEntry() {
|
||||||
|
active_ = false;
|
||||||
|
ASSERT_EQ(
|
||||||
|
OEMCrypto_SUCCESS,
|
||||||
|
OEMCrypto_DeactivateUsageEntry(
|
||||||
|
session_.session_id(),
|
||||||
|
reinterpret_cast<const uint8_t*>(pst().c_str()), pst().length()));
|
||||||
|
}
|
||||||
|
|
||||||
void GenerateVerifyReport(OEMCrypto_Usage_Entry_Status status) {
|
void GenerateVerifyReport(OEMCrypto_Usage_Entry_Status status) {
|
||||||
ASSERT_NO_FATAL_FAILURE(session_.GenerateReport(pst()));
|
ASSERT_NO_FATAL_FAILURE(session_.GenerateReport(pst()));
|
||||||
Test_PST_Report expected(pst(), status);
|
Test_PST_Report expected(pst(), status);
|
||||||
@@ -4897,7 +4946,7 @@ class LicenseWithUsageEntry {
|
|||||||
// The PST report was signed above. Below we verify that the entire message
|
// The PST report was signed above. Below we verify that the entire message
|
||||||
// that is sent to the server will be signed by the right mac keys.
|
// that is sent to the server will be signed by the right mac keys.
|
||||||
RenewalRoundTrip renewal_messages(&license_messages_);
|
RenewalRoundTrip renewal_messages(&license_messages_);
|
||||||
renewal_messages.set_is_release(true);
|
renewal_messages.set_is_release(!active_);
|
||||||
ASSERT_NO_FATAL_FAILURE(renewal_messages.SignAndVerifyRequest());
|
ASSERT_NO_FATAL_FAILURE(renewal_messages.SignAndVerifyRequest());
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -4921,6 +4970,7 @@ class LicenseWithUsageEntry {
|
|||||||
int64_t time_license_received_;
|
int64_t time_license_received_;
|
||||||
int64_t time_first_decrypt_;
|
int64_t time_first_decrypt_;
|
||||||
int64_t time_last_decrypt_;
|
int64_t time_last_decrypt_;
|
||||||
|
bool active_;
|
||||||
};
|
};
|
||||||
|
|
||||||
class OEMCryptoUsageTableTest : public OEMCryptoGenericCryptoTest {
|
class OEMCryptoUsageTableTest : public OEMCryptoGenericCryptoTest {
|
||||||
@@ -4973,7 +5023,7 @@ TEST_P(OEMCryptoUsageTableTest, OnlineLicense) {
|
|||||||
ASSERT_NO_FATAL_FAILURE(s.UpdateUsageEntry(&encrypted_usage_header_));
|
ASSERT_NO_FATAL_FAILURE(s.UpdateUsageEntry(&encrypted_usage_header_));
|
||||||
ASSERT_NO_FATAL_FAILURE(entry.GenerateVerifyReport(kActive));
|
ASSERT_NO_FATAL_FAILURE(entry.GenerateVerifyReport(kActive));
|
||||||
// Flag the entry as inactive.
|
// Flag the entry as inactive.
|
||||||
ASSERT_NO_FATAL_FAILURE(s.DeactivateUsageEntry(entry.pst()));
|
ASSERT_NO_FATAL_FAILURE(entry.DeactivateUsageEntry());
|
||||||
ASSERT_NO_FATAL_FAILURE(s.UpdateUsageEntry(&encrypted_usage_header_));
|
ASSERT_NO_FATAL_FAILURE(s.UpdateUsageEntry(&encrypted_usage_header_));
|
||||||
// It should report as inactive.
|
// It should report as inactive.
|
||||||
ASSERT_NO_FATAL_FAILURE(entry.GenerateVerifyReport(kInactiveUsed));
|
ASSERT_NO_FATAL_FAILURE(entry.GenerateVerifyReport(kInactiveUsed));
|
||||||
@@ -4982,7 +5032,7 @@ TEST_P(OEMCryptoUsageTableTest, OnlineLicense) {
|
|||||||
entry.TestDecryptCTR(false, OEMCrypto_ERROR_UNKNOWN_FAILURE));
|
entry.TestDecryptCTR(false, OEMCrypto_ERROR_UNKNOWN_FAILURE));
|
||||||
// We could call DeactivateUsageEntry multiple times. The state should not
|
// We could call DeactivateUsageEntry multiple times. The state should not
|
||||||
// change.
|
// change.
|
||||||
ASSERT_NO_FATAL_FAILURE(s.DeactivateUsageEntry(entry.pst()));
|
ASSERT_NO_FATAL_FAILURE(entry.DeactivateUsageEntry());
|
||||||
ASSERT_NO_FATAL_FAILURE(s.UpdateUsageEntry(&encrypted_usage_header_));
|
ASSERT_NO_FATAL_FAILURE(s.UpdateUsageEntry(&encrypted_usage_header_));
|
||||||
// It should report as inactive.
|
// It should report as inactive.
|
||||||
ASSERT_NO_FATAL_FAILURE(entry.GenerateVerifyReport(kInactiveUsed));
|
ASSERT_NO_FATAL_FAILURE(entry.GenerateVerifyReport(kInactiveUsed));
|
||||||
@@ -4999,7 +5049,7 @@ TEST_P(OEMCryptoUsageTableTest, OnlineLicenseUnused) {
|
|||||||
// No decrypt. We do not use this license.
|
// No decrypt. We do not use this license.
|
||||||
ASSERT_NO_FATAL_FAILURE(entry.GenerateVerifyReport(kUnused));
|
ASSERT_NO_FATAL_FAILURE(entry.GenerateVerifyReport(kUnused));
|
||||||
// Flag the entry as inactive.
|
// Flag the entry as inactive.
|
||||||
ASSERT_NO_FATAL_FAILURE(s.DeactivateUsageEntry(entry.pst()));
|
ASSERT_NO_FATAL_FAILURE(entry.DeactivateUsageEntry());
|
||||||
ASSERT_NO_FATAL_FAILURE(s.UpdateUsageEntry(&encrypted_usage_header_));
|
ASSERT_NO_FATAL_FAILURE(s.UpdateUsageEntry(&encrypted_usage_header_));
|
||||||
// It should report as inactive.
|
// It should report as inactive.
|
||||||
ASSERT_NO_FATAL_FAILURE(entry.GenerateVerifyReport(kInactiveUnused));
|
ASSERT_NO_FATAL_FAILURE(entry.GenerateVerifyReport(kInactiveUnused));
|
||||||
@@ -5008,7 +5058,7 @@ TEST_P(OEMCryptoUsageTableTest, OnlineLicenseUnused) {
|
|||||||
entry.TestDecryptCTR(false, OEMCrypto_ERROR_UNKNOWN_FAILURE));
|
entry.TestDecryptCTR(false, OEMCrypto_ERROR_UNKNOWN_FAILURE));
|
||||||
// We could call DeactivateUsageEntry multiple times. The state should not
|
// We could call DeactivateUsageEntry multiple times. The state should not
|
||||||
// change.
|
// change.
|
||||||
ASSERT_NO_FATAL_FAILURE(s.DeactivateUsageEntry(entry.pst()));
|
ASSERT_NO_FATAL_FAILURE(entry.DeactivateUsageEntry());
|
||||||
ASSERT_NO_FATAL_FAILURE(s.UpdateUsageEntry(&encrypted_usage_header_));
|
ASSERT_NO_FATAL_FAILURE(s.UpdateUsageEntry(&encrypted_usage_header_));
|
||||||
// It should report as inactive.
|
// It should report as inactive.
|
||||||
ASSERT_NO_FATAL_FAILURE(entry.GenerateVerifyReport(kInactiveUnused));
|
ASSERT_NO_FATAL_FAILURE(entry.GenerateVerifyReport(kInactiveUnused));
|
||||||
@@ -5031,7 +5081,7 @@ TEST_P(OEMCryptoUsageTableTest, ForbidReportWithNoUpdate) {
|
|||||||
// Now it's OK.
|
// Now it's OK.
|
||||||
ASSERT_NO_FATAL_FAILURE(entry.GenerateVerifyReport(kActive));
|
ASSERT_NO_FATAL_FAILURE(entry.GenerateVerifyReport(kActive));
|
||||||
// Flag the entry as inactive.
|
// Flag the entry as inactive.
|
||||||
ASSERT_NO_FATAL_FAILURE(s.DeactivateUsageEntry(entry.pst()));
|
ASSERT_NO_FATAL_FAILURE(entry.DeactivateUsageEntry());
|
||||||
// Cannot generate a report without first updating the file.
|
// Cannot generate a report without first updating the file.
|
||||||
ASSERT_NO_FATAL_FAILURE(
|
ASSERT_NO_FATAL_FAILURE(
|
||||||
s.GenerateReport(entry.pst(), OEMCrypto_ERROR_ENTRY_NEEDS_UPDATE));
|
s.GenerateReport(entry.pst(), OEMCrypto_ERROR_ENTRY_NEEDS_UPDATE));
|
||||||
@@ -5177,6 +5227,32 @@ TEST_P(OEMCryptoUsageTableTest, CreateAndLoadMultipleEntriesAPI16) {
|
|||||||
OEMCrypto_CreateNewUsageEntry(s2.session_id(), &usage_entry_number));
|
OEMCrypto_CreateNewUsageEntry(s2.session_id(), &usage_entry_number));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// An entry can be loaded in only one session at a time.
|
||||||
|
TEST_P(OEMCryptoUsageTableTest, LoadEntryInMultipleSessions) {
|
||||||
|
// Entry Count: we start each test with an empty header.
|
||||||
|
LicenseWithUsageEntry entry;
|
||||||
|
entry.license_messages().set_api_version(license_api_version_);
|
||||||
|
Session& s = entry.session();
|
||||||
|
// Make first entry 0.
|
||||||
|
ASSERT_NO_FATAL_FAILURE(entry.MakeOfflineAndClose(this));
|
||||||
|
const uint32_t usage_entry_number = s.usage_entry_number();
|
||||||
|
EXPECT_EQ(usage_entry_number, 0u); // Should be only entry in this test.
|
||||||
|
|
||||||
|
// Load an entry, then try to create a second.
|
||||||
|
ASSERT_NO_FATAL_FAILURE(s.open());
|
||||||
|
// Reload entry 0.
|
||||||
|
ASSERT_NO_FATAL_FAILURE(s.ReloadUsageEntry());
|
||||||
|
|
||||||
|
// Create an entry, then try to load a second.
|
||||||
|
Session s2;
|
||||||
|
ASSERT_NO_FATAL_FAILURE(s2.open());
|
||||||
|
// Try to load entry 0 into session 2.
|
||||||
|
ASSERT_EQ(OEMCrypto_ERROR_INVALID_SESSION,
|
||||||
|
OEMCrypto_LoadUsageEntry(s2.session_id(), usage_entry_number,
|
||||||
|
s.encrypted_usage_entry().data(),
|
||||||
|
s.encrypted_usage_entry().size()));
|
||||||
|
}
|
||||||
|
|
||||||
// Test generic encrypt when the license uses a PST.
|
// Test generic encrypt when the license uses a PST.
|
||||||
TEST_P(OEMCryptoUsageTableTest, GenericCryptoEncrypt) {
|
TEST_P(OEMCryptoUsageTableTest, GenericCryptoEncrypt) {
|
||||||
LicenseWithUsageEntry entry;
|
LicenseWithUsageEntry entry;
|
||||||
@@ -5201,7 +5277,7 @@ TEST_P(OEMCryptoUsageTableTest, GenericCryptoEncrypt) {
|
|||||||
EXPECT_EQ(expected_encrypted, encrypted);
|
EXPECT_EQ(expected_encrypted, encrypted);
|
||||||
ASSERT_NO_FATAL_FAILURE(s.UpdateUsageEntry(&encrypted_usage_header_));
|
ASSERT_NO_FATAL_FAILURE(s.UpdateUsageEntry(&encrypted_usage_header_));
|
||||||
ASSERT_NO_FATAL_FAILURE(entry.GenerateVerifyReport(kActive));
|
ASSERT_NO_FATAL_FAILURE(entry.GenerateVerifyReport(kActive));
|
||||||
ASSERT_NO_FATAL_FAILURE(s.DeactivateUsageEntry(entry.pst()));
|
ASSERT_NO_FATAL_FAILURE(entry.DeactivateUsageEntry());
|
||||||
ASSERT_NO_FATAL_FAILURE(s.UpdateUsageEntry(&encrypted_usage_header_));
|
ASSERT_NO_FATAL_FAILURE(s.UpdateUsageEntry(&encrypted_usage_header_));
|
||||||
ASSERT_NO_FATAL_FAILURE(entry.GenerateVerifyReport(kInactiveUsed));
|
ASSERT_NO_FATAL_FAILURE(entry.GenerateVerifyReport(kInactiveUsed));
|
||||||
encrypted.assign(clear_buffer_.size(), 0);
|
encrypted.assign(clear_buffer_.size(), 0);
|
||||||
@@ -5236,7 +5312,7 @@ TEST_P(OEMCryptoUsageTableTest, GenericCryptoDecrypt) {
|
|||||||
EXPECT_EQ(clear_buffer_, resultant);
|
EXPECT_EQ(clear_buffer_, resultant);
|
||||||
ASSERT_NO_FATAL_FAILURE(s.UpdateUsageEntry(&encrypted_usage_header_));
|
ASSERT_NO_FATAL_FAILURE(s.UpdateUsageEntry(&encrypted_usage_header_));
|
||||||
ASSERT_NO_FATAL_FAILURE(entry.GenerateVerifyReport(kActive));
|
ASSERT_NO_FATAL_FAILURE(entry.GenerateVerifyReport(kActive));
|
||||||
ASSERT_NO_FATAL_FAILURE(s.DeactivateUsageEntry(entry.pst()));
|
ASSERT_NO_FATAL_FAILURE(entry.DeactivateUsageEntry());
|
||||||
ASSERT_NO_FATAL_FAILURE(s.UpdateUsageEntry(&encrypted_usage_header_));
|
ASSERT_NO_FATAL_FAILURE(s.UpdateUsageEntry(&encrypted_usage_header_));
|
||||||
ASSERT_NO_FATAL_FAILURE(entry.GenerateVerifyReport(kInactiveUsed));
|
ASSERT_NO_FATAL_FAILURE(entry.GenerateVerifyReport(kInactiveUsed));
|
||||||
resultant.assign(encrypted.size(), 0);
|
resultant.assign(encrypted.size(), 0);
|
||||||
@@ -5279,7 +5355,7 @@ TEST_P(OEMCryptoUsageTableTest, GenericCryptoSign) {
|
|||||||
|
|
||||||
ASSERT_NO_FATAL_FAILURE(s.UpdateUsageEntry(&encrypted_usage_header_));
|
ASSERT_NO_FATAL_FAILURE(s.UpdateUsageEntry(&encrypted_usage_header_));
|
||||||
ASSERT_NO_FATAL_FAILURE(entry.GenerateVerifyReport(kActive));
|
ASSERT_NO_FATAL_FAILURE(entry.GenerateVerifyReport(kActive));
|
||||||
ASSERT_NO_FATAL_FAILURE(s.DeactivateUsageEntry(entry.pst()));
|
ASSERT_NO_FATAL_FAILURE(entry.DeactivateUsageEntry());
|
||||||
ASSERT_NO_FATAL_FAILURE(s.UpdateUsageEntry(&encrypted_usage_header_));
|
ASSERT_NO_FATAL_FAILURE(s.UpdateUsageEntry(&encrypted_usage_header_));
|
||||||
ASSERT_NO_FATAL_FAILURE(entry.GenerateVerifyReport(kInactiveUsed));
|
ASSERT_NO_FATAL_FAILURE(entry.GenerateVerifyReport(kInactiveUsed));
|
||||||
signature.assign(SHA256_DIGEST_LENGTH, 0);
|
signature.assign(SHA256_DIGEST_LENGTH, 0);
|
||||||
@@ -5314,7 +5390,7 @@ TEST_P(OEMCryptoUsageTableTest, GenericCryptoVerify) {
|
|||||||
ASSERT_EQ(OEMCrypto_SUCCESS, sts);
|
ASSERT_EQ(OEMCrypto_SUCCESS, sts);
|
||||||
ASSERT_NO_FATAL_FAILURE(s.UpdateUsageEntry(&encrypted_usage_header_));
|
ASSERT_NO_FATAL_FAILURE(s.UpdateUsageEntry(&encrypted_usage_header_));
|
||||||
ASSERT_NO_FATAL_FAILURE(entry.GenerateVerifyReport(kActive));
|
ASSERT_NO_FATAL_FAILURE(entry.GenerateVerifyReport(kActive));
|
||||||
ASSERT_NO_FATAL_FAILURE(s.DeactivateUsageEntry(entry.pst()));
|
ASSERT_NO_FATAL_FAILURE(entry.DeactivateUsageEntry());
|
||||||
ASSERT_NO_FATAL_FAILURE(s.UpdateUsageEntry(&encrypted_usage_header_));
|
ASSERT_NO_FATAL_FAILURE(s.UpdateUsageEntry(&encrypted_usage_header_));
|
||||||
ASSERT_NO_FATAL_FAILURE(entry.GenerateVerifyReport(kInactiveUsed));
|
ASSERT_NO_FATAL_FAILURE(entry.GenerateVerifyReport(kInactiveUsed));
|
||||||
sts = OEMCrypto_Generic_Verify(s.session_id(), clear_buffer_.data(),
|
sts = OEMCrypto_Generic_Verify(s.session_id(), clear_buffer_.data(),
|
||||||
@@ -5505,8 +5581,7 @@ TEST_P(OEMCryptoUsageTableTest, DeactivateOfflineLicense) {
|
|||||||
ASSERT_NO_FATAL_FAILURE(entry.OpenAndReload(this));
|
ASSERT_NO_FATAL_FAILURE(entry.OpenAndReload(this));
|
||||||
ASSERT_NO_FATAL_FAILURE(
|
ASSERT_NO_FATAL_FAILURE(
|
||||||
entry.TestDecryptCTR()); // Should be able to decrypt.
|
entry.TestDecryptCTR()); // Should be able to decrypt.
|
||||||
ASSERT_NO_FATAL_FAILURE(
|
ASSERT_NO_FATAL_FAILURE(entry.DeactivateUsageEntry()); // Then deactivate.
|
||||||
s.DeactivateUsageEntry(entry.pst())); // Then deactivate.
|
|
||||||
// After deactivate, should not be able to decrypt.
|
// After deactivate, should not be able to decrypt.
|
||||||
ASSERT_NO_FATAL_FAILURE(
|
ASSERT_NO_FATAL_FAILURE(
|
||||||
entry.TestDecryptCTR(false, OEMCrypto_ERROR_UNKNOWN_FAILURE));
|
entry.TestDecryptCTR(false, OEMCrypto_ERROR_UNKNOWN_FAILURE));
|
||||||
@@ -5531,7 +5606,7 @@ TEST_P(OEMCryptoUsageTableTest, DeactivateOfflineLicense) {
|
|||||||
ASSERT_NO_FATAL_FAILURE(entry.GenerateVerifyReport(kInactiveUsed));
|
ASSERT_NO_FATAL_FAILURE(entry.GenerateVerifyReport(kInactiveUsed));
|
||||||
// We could call DeactivateUsageEntry multiple times. The state should not
|
// We could call DeactivateUsageEntry multiple times. The state should not
|
||||||
// change.
|
// change.
|
||||||
ASSERT_NO_FATAL_FAILURE(s.DeactivateUsageEntry(entry.pst()));
|
ASSERT_NO_FATAL_FAILURE(entry.DeactivateUsageEntry());
|
||||||
ASSERT_NO_FATAL_FAILURE(s.UpdateUsageEntry(&encrypted_usage_header_));
|
ASSERT_NO_FATAL_FAILURE(s.UpdateUsageEntry(&encrypted_usage_header_));
|
||||||
ASSERT_NO_FATAL_FAILURE(entry.GenerateVerifyReport(kInactiveUsed));
|
ASSERT_NO_FATAL_FAILURE(entry.GenerateVerifyReport(kInactiveUsed));
|
||||||
}
|
}
|
||||||
@@ -5545,8 +5620,7 @@ TEST_P(OEMCryptoUsageTableTest, DeactivateOfflineLicenseUnused) {
|
|||||||
Session& s = entry.session();
|
Session& s = entry.session();
|
||||||
// No Decrypt. This license is unused.
|
// No Decrypt. This license is unused.
|
||||||
ASSERT_NO_FATAL_FAILURE(entry.OpenAndReload(this));
|
ASSERT_NO_FATAL_FAILURE(entry.OpenAndReload(this));
|
||||||
ASSERT_NO_FATAL_FAILURE(
|
ASSERT_NO_FATAL_FAILURE(entry.DeactivateUsageEntry()); // Then deactivate.
|
||||||
s.DeactivateUsageEntry(entry.pst())); // Then deactivate.
|
|
||||||
// After deactivate, should not be able to decrypt.
|
// After deactivate, should not be able to decrypt.
|
||||||
ASSERT_NO_FATAL_FAILURE(
|
ASSERT_NO_FATAL_FAILURE(
|
||||||
entry.TestDecryptCTR(false, OEMCrypto_ERROR_UNKNOWN_FAILURE));
|
entry.TestDecryptCTR(false, OEMCrypto_ERROR_UNKNOWN_FAILURE));
|
||||||
@@ -5571,11 +5645,30 @@ TEST_P(OEMCryptoUsageTableTest, DeactivateOfflineLicenseUnused) {
|
|||||||
ASSERT_NO_FATAL_FAILURE(entry.GenerateVerifyReport(kInactiveUnused));
|
ASSERT_NO_FATAL_FAILURE(entry.GenerateVerifyReport(kInactiveUnused));
|
||||||
// We could call DeactivateUsageEntry multiple times. The state should not
|
// We could call DeactivateUsageEntry multiple times. The state should not
|
||||||
// change.
|
// change.
|
||||||
ASSERT_NO_FATAL_FAILURE(s.DeactivateUsageEntry(entry.pst()));
|
ASSERT_NO_FATAL_FAILURE(entry.DeactivateUsageEntry());
|
||||||
ASSERT_NO_FATAL_FAILURE(s.UpdateUsageEntry(&encrypted_usage_header_));
|
ASSERT_NO_FATAL_FAILURE(s.UpdateUsageEntry(&encrypted_usage_header_));
|
||||||
ASSERT_NO_FATAL_FAILURE(entry.GenerateVerifyReport(kInactiveUnused));
|
ASSERT_NO_FATAL_FAILURE(entry.GenerateVerifyReport(kInactiveUnused));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_P(OEMCryptoUsageTableTest, SecureStop) {
|
||||||
|
LicenseWithUsageEntry entry;
|
||||||
|
entry.license_messages().set_api_version(license_api_version_);
|
||||||
|
entry.MakeAndLoadOnline(this);
|
||||||
|
Session& s = entry.session();
|
||||||
|
ASSERT_NO_FATAL_FAILURE(s.UpdateUsageEntry(&encrypted_usage_header_));
|
||||||
|
ASSERT_NO_FATAL_FAILURE(entry.TestDecryptCTR());
|
||||||
|
ASSERT_NO_FATAL_FAILURE(s.UpdateUsageEntry(&encrypted_usage_header_));
|
||||||
|
ASSERT_NO_FATAL_FAILURE(s.close());
|
||||||
|
// When we generate a secure stop without loading the license first, it
|
||||||
|
// should assume the server does not support core messages.
|
||||||
|
ASSERT_NO_FATAL_FAILURE(s.open());
|
||||||
|
ASSERT_NO_FATAL_FAILURE(entry.ReloadUsageEntry());
|
||||||
|
ASSERT_NO_FATAL_FAILURE(entry.DeactivateUsageEntry());
|
||||||
|
ASSERT_NO_FATAL_FAILURE(s.UpdateUsageEntry(&encrypted_usage_header_));
|
||||||
|
// It should report as inactive.
|
||||||
|
ASSERT_NO_FATAL_FAILURE(entry.GenerateVerifyReport(kInactiveUsed));
|
||||||
|
}
|
||||||
|
|
||||||
// Test update usage table fails when passed a null pointer.
|
// Test update usage table fails when passed a null pointer.
|
||||||
TEST_P(OEMCryptoUsageTableTest, UpdateFailsWithNullPtr) {
|
TEST_P(OEMCryptoUsageTableTest, UpdateFailsWithNullPtr) {
|
||||||
LicenseWithUsageEntry entry;
|
LicenseWithUsageEntry entry;
|
||||||
@@ -5638,8 +5731,7 @@ class OEMCryptoUsageTableDefragTest : public OEMCryptoUsageTableTest {
|
|||||||
ASSERT_LT(0u, header_buffer_length);
|
ASSERT_LT(0u, header_buffer_length);
|
||||||
encrypted_usage_header_.resize(header_buffer_length);
|
encrypted_usage_header_.resize(header_buffer_length);
|
||||||
sts = OEMCrypto_ShrinkUsageTableHeader(
|
sts = OEMCrypto_ShrinkUsageTableHeader(
|
||||||
new_size, encrypted_usage_header_.data(),
|
new_size, encrypted_usage_header_.data(), &header_buffer_length);
|
||||||
&header_buffer_length);
|
|
||||||
ASSERT_EQ(expected_result, sts);
|
ASSERT_EQ(expected_result, sts);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -5885,6 +5977,7 @@ TEST_P(OEMCryptoUsageTableTest, ReloadUsageTableWithSkew) {
|
|||||||
vector<uint8_t> old_usage_header_2_ = encrypted_usage_header_;
|
vector<uint8_t> old_usage_header_2_ = encrypted_usage_header_;
|
||||||
ASSERT_NO_FATAL_FAILURE(s.UpdateUsageEntry(&encrypted_usage_header_));
|
ASSERT_NO_FATAL_FAILURE(s.UpdateUsageEntry(&encrypted_usage_header_));
|
||||||
vector<uint8_t> old_usage_header_1_ = encrypted_usage_header_;
|
vector<uint8_t> old_usage_header_1_ = encrypted_usage_header_;
|
||||||
|
vector<uint8_t> old_usage_entry_1 = s.encrypted_usage_entry();
|
||||||
ASSERT_NO_FATAL_FAILURE(s.UpdateUsageEntry(&encrypted_usage_header_));
|
ASSERT_NO_FATAL_FAILURE(s.UpdateUsageEntry(&encrypted_usage_header_));
|
||||||
ASSERT_NO_FATAL_FAILURE(s.close());
|
ASSERT_NO_FATAL_FAILURE(s.close());
|
||||||
|
|
||||||
@@ -5895,26 +5988,23 @@ TEST_P(OEMCryptoUsageTableTest, ReloadUsageTableWithSkew) {
|
|||||||
nullptr, old_usage_header_2_.size()));
|
nullptr, old_usage_header_2_.size()));
|
||||||
ASSERT_NO_FATAL_FAILURE(s.open());
|
ASSERT_NO_FATAL_FAILURE(s.open());
|
||||||
// Cannot load an entry if header didn't load.
|
// Cannot load an entry if header didn't load.
|
||||||
ASSERT_EQ(
|
ASSERT_EQ(OEMCrypto_ERROR_UNKNOWN_FAILURE,
|
||||||
OEMCrypto_ERROR_UNKNOWN_FAILURE,
|
OEMCrypto_LoadUsageEntry(s.session_id(), s.usage_entry_number(),
|
||||||
OEMCrypto_LoadUsageEntry(s.session_id(), s.usage_entry_number(),
|
s.encrypted_usage_entry().data(),
|
||||||
s.encrypted_usage_entry().data(),
|
s.encrypted_usage_entry().size()));
|
||||||
s.encrypted_usage_entry().size()));
|
|
||||||
ASSERT_NO_FATAL_FAILURE(s.close());
|
ASSERT_NO_FATAL_FAILURE(s.close());
|
||||||
|
|
||||||
// Modified header generates error.
|
// Modified header generates error.
|
||||||
vector<uint8_t> bad_header = encrypted_usage_header_;
|
vector<uint8_t> bad_header = encrypted_usage_header_;
|
||||||
bad_header[3] ^= 42;
|
bad_header[3] ^= 42;
|
||||||
ASSERT_NE(OEMCrypto_SUCCESS,
|
ASSERT_NE(OEMCrypto_SUCCESS, OEMCrypto_LoadUsageTableHeader(
|
||||||
OEMCrypto_LoadUsageTableHeader(bad_header.data(),
|
bad_header.data(), bad_header.size()));
|
||||||
bad_header.size()));
|
|
||||||
ASSERT_NO_FATAL_FAILURE(s.open());
|
ASSERT_NO_FATAL_FAILURE(s.open());
|
||||||
// Cannot load an entry if header didn't load.
|
// Cannot load an entry if header didn't load.
|
||||||
ASSERT_EQ(
|
ASSERT_EQ(OEMCrypto_ERROR_UNKNOWN_FAILURE,
|
||||||
OEMCrypto_ERROR_UNKNOWN_FAILURE,
|
OEMCrypto_LoadUsageEntry(s.session_id(), s.usage_entry_number(),
|
||||||
OEMCrypto_LoadUsageEntry(s.session_id(), s.usage_entry_number(),
|
s.encrypted_usage_entry().data(),
|
||||||
s.encrypted_usage_entry().data(),
|
s.encrypted_usage_entry().size()));
|
||||||
s.encrypted_usage_entry().size()));
|
|
||||||
ASSERT_NO_FATAL_FAILURE(s.close());
|
ASSERT_NO_FATAL_FAILURE(s.close());
|
||||||
|
|
||||||
// Old by 2 generation numbers is error.
|
// Old by 2 generation numbers is error.
|
||||||
@@ -5923,24 +6013,22 @@ TEST_P(OEMCryptoUsageTableTest, ReloadUsageTableWithSkew) {
|
|||||||
old_usage_header_2_.size()));
|
old_usage_header_2_.size()));
|
||||||
ASSERT_NO_FATAL_FAILURE(s.open());
|
ASSERT_NO_FATAL_FAILURE(s.open());
|
||||||
// Cannot load an entry if header didn't load.
|
// Cannot load an entry if header didn't load.
|
||||||
ASSERT_NE(
|
ASSERT_NE(OEMCrypto_SUCCESS,
|
||||||
OEMCrypto_SUCCESS,
|
OEMCrypto_LoadUsageEntry(s.session_id(), s.usage_entry_number(),
|
||||||
OEMCrypto_LoadUsageEntry(s.session_id(), s.usage_entry_number(),
|
s.encrypted_usage_entry().data(),
|
||||||
s.encrypted_usage_entry().data(),
|
s.encrypted_usage_entry().size()));
|
||||||
s.encrypted_usage_entry().size()));
|
|
||||||
ASSERT_NO_FATAL_FAILURE(s.close());
|
ASSERT_NO_FATAL_FAILURE(s.close());
|
||||||
|
|
||||||
// Old by 1 generation numbers is just warning.
|
// Old by 1 generation numbers is just warning.
|
||||||
ASSERT_EQ(OEMCrypto_WARNING_GENERATION_SKEW,
|
ASSERT_EQ(OEMCrypto_WARNING_GENERATION_SKEW,
|
||||||
OEMCrypto_LoadUsageTableHeader(old_usage_header_1_.data(),
|
OEMCrypto_LoadUsageTableHeader(old_usage_header_1_.data(),
|
||||||
old_usage_header_1_.size()));
|
old_usage_header_1_.size()));
|
||||||
// Everything else should still work. Skew by 1 is just a warning.
|
// Everything else should still work. The old entry goes with the old header.
|
||||||
ASSERT_NO_FATAL_FAILURE(s.open());
|
ASSERT_NO_FATAL_FAILURE(s.open());
|
||||||
ASSERT_EQ(
|
ASSERT_EQ(OEMCrypto_SUCCESS,
|
||||||
OEMCrypto_WARNING_GENERATION_SKEW,
|
OEMCrypto_LoadUsageEntry(s.session_id(), s.usage_entry_number(),
|
||||||
OEMCrypto_LoadUsageEntry(s.session_id(), s.usage_entry_number(),
|
old_usage_entry_1.data(),
|
||||||
s.encrypted_usage_entry().data(),
|
old_usage_entry_1.size()));
|
||||||
s.encrypted_usage_entry().size()));
|
|
||||||
ASSERT_NO_FATAL_FAILURE(InstallTestRSAKey(&s));
|
ASSERT_NO_FATAL_FAILURE(InstallTestRSAKey(&s));
|
||||||
ASSERT_EQ(OEMCrypto_SUCCESS, entry.license_messages().LoadResponse());
|
ASSERT_EQ(OEMCrypto_SUCCESS, entry.license_messages().LoadResponse());
|
||||||
}
|
}
|
||||||
@@ -5952,8 +6040,8 @@ TEST_P(OEMCryptoUsageTableTest, GenerateReportWrongPST) {
|
|||||||
entry.MakeAndLoadOnline(this);
|
entry.MakeAndLoadOnline(this);
|
||||||
Session& s = entry.session();
|
Session& s = entry.session();
|
||||||
ASSERT_NO_FATAL_FAILURE(s.UpdateUsageEntry(&encrypted_usage_header_));
|
ASSERT_NO_FATAL_FAILURE(s.UpdateUsageEntry(&encrypted_usage_header_));
|
||||||
ASSERT_NO_FATAL_FAILURE(s.GenerateReport("wrong_pst",
|
ASSERT_NO_FATAL_FAILURE(
|
||||||
OEMCrypto_ERROR_WRONG_PST));
|
s.GenerateReport("wrong_pst", OEMCrypto_ERROR_WRONG_PST));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test usage table timing.
|
// Test usage table timing.
|
||||||
@@ -5992,7 +6080,7 @@ TEST_P(OEMCryptoUsageTableTest, TimingTest) {
|
|||||||
ASSERT_NO_FATAL_FAILURE(entry2.TestDecryptCTR());
|
ASSERT_NO_FATAL_FAILURE(entry2.TestDecryptCTR());
|
||||||
|
|
||||||
wvcdm::TestSleep::Sleep(kLongSleep);
|
wvcdm::TestSleep::Sleep(kLongSleep);
|
||||||
ASSERT_NO_FATAL_FAILURE(s1.DeactivateUsageEntry(entry1.pst()));
|
ASSERT_NO_FATAL_FAILURE(entry1.DeactivateUsageEntry());
|
||||||
ASSERT_NO_FATAL_FAILURE(s1.UpdateUsageEntry(&encrypted_usage_header_));
|
ASSERT_NO_FATAL_FAILURE(s1.UpdateUsageEntry(&encrypted_usage_header_));
|
||||||
ASSERT_NO_FATAL_FAILURE(s2.UpdateUsageEntry(&encrypted_usage_header_));
|
ASSERT_NO_FATAL_FAILURE(s2.UpdateUsageEntry(&encrypted_usage_header_));
|
||||||
ASSERT_NO_FATAL_FAILURE(s1.close());
|
ASSERT_NO_FATAL_FAILURE(s1.close());
|
||||||
@@ -6015,17 +6103,10 @@ TEST_P(OEMCryptoUsageTableTest, TimingTest) {
|
|||||||
ASSERT_NO_FATAL_FAILURE(s2.close());
|
ASSERT_NO_FATAL_FAILURE(s2.close());
|
||||||
|
|
||||||
ASSERT_NO_FATAL_FAILURE(s1.open());
|
ASSERT_NO_FATAL_FAILURE(s1.open());
|
||||||
ASSERT_NO_FATAL_FAILURE(s2.open());
|
|
||||||
ASSERT_NO_FATAL_FAILURE(s3.open());
|
|
||||||
ASSERT_NO_FATAL_FAILURE(entry1.ReloadUsageEntry());
|
ASSERT_NO_FATAL_FAILURE(entry1.ReloadUsageEntry());
|
||||||
ASSERT_NO_FATAL_FAILURE(entry2.ReloadUsageEntry());
|
ASSERT_NO_FATAL_FAILURE(entry2.OpenAndReload(this));
|
||||||
ASSERT_NO_FATAL_FAILURE(entry3.ReloadUsageEntry());
|
ASSERT_NO_FATAL_FAILURE(entry3.OpenAndReload(this));
|
||||||
// Sending a release from an offline license that has been deactivate will
|
|
||||||
// only work if the license server can handle v16 licenses. This is a rare
|
|
||||||
// condition, so it is OK to break it during the transition months.
|
|
||||||
entry1.license_messages().set_api_version(global_features.api_version);
|
|
||||||
entry2.license_messages().set_api_version(global_features.api_version);
|
|
||||||
entry3.license_messages().set_api_version(global_features.api_version);
|
|
||||||
wvcdm::TestSleep::Sleep(kLongSleep);
|
wvcdm::TestSleep::Sleep(kLongSleep);
|
||||||
ASSERT_NO_FATAL_FAILURE(s1.UpdateUsageEntry(&encrypted_usage_header_));
|
ASSERT_NO_FATAL_FAILURE(s1.UpdateUsageEntry(&encrypted_usage_header_));
|
||||||
ASSERT_NO_FATAL_FAILURE(entry1.GenerateVerifyReport(kInactiveUsed));
|
ASSERT_NO_FATAL_FAILURE(entry1.GenerateVerifyReport(kInactiveUsed));
|
||||||
@@ -6105,7 +6186,7 @@ TEST_P(OEMCryptoUsageTableTest, VerifyUsageTimes) {
|
|||||||
// |<------------------------------------| = seconds_since_license_received
|
// |<------------------------------------| = seconds_since_license_received
|
||||||
ASSERT_NO_FATAL_FAILURE(s.UpdateUsageEntry(&encrypted_usage_header_));
|
ASSERT_NO_FATAL_FAILURE(s.UpdateUsageEntry(&encrypted_usage_header_));
|
||||||
ASSERT_NO_FATAL_FAILURE(entry.GenerateVerifyReport(kActive));
|
ASSERT_NO_FATAL_FAILURE(entry.GenerateVerifyReport(kActive));
|
||||||
ASSERT_NO_FATAL_FAILURE(s.DeactivateUsageEntry(entry.pst()));
|
ASSERT_NO_FATAL_FAILURE(entry.DeactivateUsageEntry());
|
||||||
ASSERT_NO_FATAL_FAILURE(s.UpdateUsageEntry(&encrypted_usage_header_));
|
ASSERT_NO_FATAL_FAILURE(s.UpdateUsageEntry(&encrypted_usage_header_));
|
||||||
ASSERT_NO_FATAL_FAILURE(entry.GenerateVerifyReport(kInactiveUsed));
|
ASSERT_NO_FATAL_FAILURE(entry.GenerateVerifyReport(kInactiveUsed));
|
||||||
ASSERT_NO_FATAL_FAILURE(
|
ASSERT_NO_FATAL_FAILURE(
|
||||||
@@ -6195,8 +6276,8 @@ TEST_P(OEMCryptoUsageTableTestWallClock, TimeRollbackPrevention) {
|
|||||||
|
|
||||||
// Rollback the wall clock time.
|
// Rollback the wall clock time.
|
||||||
cout << "Rolling the system time back..." << endl;
|
cout << "Rolling the system time back..." << endl;
|
||||||
ASSERT_NO_FATAL_FAILURE(SetWallTimeDelta(
|
ASSERT_NO_FATAL_FAILURE(
|
||||||
-static_cast<int64_t>(kLongDuration) * 10));
|
SetWallTimeDelta(-static_cast<int64_t>(kLongDuration) * 10));
|
||||||
|
|
||||||
// Try to playback again.
|
// Try to playback again.
|
||||||
ASSERT_NO_FATAL_FAILURE(entry.OpenAndReload(this));
|
ASSERT_NO_FATAL_FAILURE(entry.OpenAndReload(this));
|
||||||
@@ -6210,8 +6291,8 @@ TEST_P(OEMCryptoUsageTableTestWallClock, TimeRollbackPrevention) {
|
|||||||
// not report negative times.
|
// not report negative times.
|
||||||
const auto test_duration = third_decrypt_monotonic - first_decrypt_monotonic;
|
const auto test_duration = third_decrypt_monotonic - first_decrypt_monotonic;
|
||||||
cout << "Rolling the system time forward to the absolute time..." << endl;
|
cout << "Rolling the system time forward to the absolute time..." << endl;
|
||||||
ASSERT_NO_FATAL_FAILURE(SetWallTimeDelta(
|
ASSERT_NO_FATAL_FAILURE(
|
||||||
test_duration / std::chrono::seconds(1)));
|
SetWallTimeDelta(test_duration / std::chrono::seconds(1)));
|
||||||
// Need to update time created since the verification checks the time of PST
|
// Need to update time created since the verification checks the time of PST
|
||||||
// report creation.
|
// report creation.
|
||||||
expected.time_created = UnixTime(wall_clock.now());
|
expected.time_created = UnixTime(wall_clock.now());
|
||||||
@@ -6232,8 +6313,7 @@ TEST_P(OEMCryptoUsageTableTest, PSTLargeBuffer) {
|
|||||||
ASSERT_NO_FATAL_FAILURE(entry.OpenAndReload(this));
|
ASSERT_NO_FATAL_FAILURE(entry.OpenAndReload(this));
|
||||||
ASSERT_NO_FATAL_FAILURE(
|
ASSERT_NO_FATAL_FAILURE(
|
||||||
entry.TestDecryptCTR()); // Should be able to decrypt.
|
entry.TestDecryptCTR()); // Should be able to decrypt.
|
||||||
ASSERT_NO_FATAL_FAILURE(
|
ASSERT_NO_FATAL_FAILURE(entry.DeactivateUsageEntry()); // Then deactivate.
|
||||||
s.DeactivateUsageEntry(entry.pst())); // Then deactivate.
|
|
||||||
// After deactivate, should not be able to decrypt.
|
// After deactivate, should not be able to decrypt.
|
||||||
ASSERT_NO_FATAL_FAILURE(
|
ASSERT_NO_FATAL_FAILURE(
|
||||||
entry.TestDecryptCTR(false, OEMCrypto_ERROR_UNKNOWN_FAILURE));
|
entry.TestDecryptCTR(false, OEMCrypto_ERROR_UNKNOWN_FAILURE));
|
||||||
|
|||||||
46
util/include/advance_iv_ctr.h
Normal file
46
util/include/advance_iv_ctr.h
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
// 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 WVCDM_UTIL_ADVANCE_IV_CTR_H_
|
||||||
|
#define WVCDM_UTIL_ADVANCE_IV_CTR_H_
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#include "string_conversions.h"
|
||||||
|
|
||||||
|
namespace wvcdm {
|
||||||
|
|
||||||
|
// Advance an IV according to ISO-CENC's CTR modes. The lower half of the IV is
|
||||||
|
// split off and treated as an unsigned 64-bit integer, then incremented by the
|
||||||
|
// number of complete crypto blocks decrypted. The resulting value is then
|
||||||
|
// copied back into the IV over the previous lower half.
|
||||||
|
inline void AdvanceIvCtr(uint8_t (*subsample_iv)[16], size_t bytes) {
|
||||||
|
constexpr size_t kAesBlockSize = 16;
|
||||||
|
constexpr size_t kIvSize = kAesBlockSize;
|
||||||
|
constexpr size_t kCounterIndex = kIvSize / 2;
|
||||||
|
constexpr size_t kCounterSize = kIvSize / 2;
|
||||||
|
|
||||||
|
uint64_t counter;
|
||||||
|
|
||||||
|
static_assert(
|
||||||
|
sizeof(*subsample_iv) == kIvSize,
|
||||||
|
"The subsample_iv field is no longer the length of an AES-128 IV.");
|
||||||
|
static_assert(sizeof(counter) == kCounterSize,
|
||||||
|
"A uint64_t failed to be half the size of an AES-128 IV.");
|
||||||
|
|
||||||
|
// Defensive copy because the elements of the array may not be properly
|
||||||
|
// aligned
|
||||||
|
memcpy(&counter, &(*subsample_iv)[kCounterIndex], kCounterSize);
|
||||||
|
|
||||||
|
const size_t increment =
|
||||||
|
bytes / kAesBlockSize; // The truncation here is intentional
|
||||||
|
counter = htonll64(ntohll64(counter) + increment);
|
||||||
|
|
||||||
|
memcpy(&(*subsample_iv)[kCounterIndex], &counter, kCounterSize);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace wvcdm
|
||||||
|
|
||||||
|
#endif // WVCDM_UTIL_ADVANCE_IV_CTR_H_
|
||||||
Reference in New Issue
Block a user