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:
Fred Gylys-Colwell
2020-03-30 18:48:40 -07:00
parent 1a9765171b
commit 016c2970ac
31 changed files with 1902 additions and 1126 deletions

View File

@@ -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);

View File

@@ -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",
], ],

View File

@@ -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

View File

@@ -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;

View File

@@ -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;

View File

@@ -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;
} }

View File

@@ -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. */

View File

@@ -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,

View File

@@ -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);
}

View File

@@ -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

View File

@@ -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;
} }

View File

@@ -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

View File

@@ -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

View File

@@ -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',
], ],
} }

View 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

View 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_ */

View File

@@ -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)',

View File

@@ -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));

View File

@@ -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);

View File

@@ -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.

View File

@@ -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

View File

@@ -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.

View File

@@ -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;
} }

View File

@@ -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(

View File

@@ -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;
} }

View File

@@ -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;

View File

@@ -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; }

View File

@@ -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));

View 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_