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:
* [in] input_data: An unaligned pointer to this sample from the stream.
* [in] input_data_length: The length of this sample in the stream, in bytes.
* [in] output: A caller-owned descriptor that specifies the handling of the
* decrypted byte stream. See OEMCrypto_DestbufferDesc for details.
* [in] output_descriptor: A caller-owned descriptor that specifies the
* handling of the decrypted byte stream. See OEMCrypto_DestbufferDesc for
* details.
*
* Version:
* This struct changed in API version 16.
@@ -599,7 +600,7 @@ OEMCryptoResult OEMCrypto_Terminate(void);
* This function shall call ODK_InitializeSessionValues to initialize the
* session's clock values, timer values, and nonce values.
* ODK_InitializeSessionValues is described in the document "License Duration
* and Renewal", to initialize the sessions clock values.
* and Renewal", to initialize the session's clock values.
*
* Parameters:
* [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
* will reset the error condition and generate valid nonces again.
*
* The nonce should be stored in the sessions ODK_NonceValue field by calling
* the function ODK_SetNonceValue(&nonce_values, nonce). The ODK functions
* are documented in "Widevine Core Message Serialization".
* The nonce should be stored in the session's ODK_NonceValue field by
* calling the function ODK_SetNonceValue(&nonce_values, nonce). The ODK
* functions are documented in "Widevine Core Message Serialization".
*
* This function shall only be called at most once per open session. It shall
* only be called before signing either a provisioning request or a license
@@ -872,7 +873,7 @@ OEMCryptoResult OEMCrypto_GenerateNonce(OEMCrypto_SESSION session,
* Message Serialization".
*
* The message body is the buffer starting at message + core_message_size,
* and with length message_length-core_message_size. The reason OEMCrypto
* and with length message_length - core_message_size. The reason OEMCrypto
* only signs the message body and not the entire message is to allow a v16
* device to request a license from a v15 license server.
*
@@ -882,12 +883,12 @@ OEMCryptoResult OEMCrypto_GenerateNonce(OEMCrypto_SESSION session,
*
* OEMCrypto shall compute a hash of the core license request. The core
* license request is the buffer starting at message and with length
* core_message_size. The 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.
*
* OEMCrypto shall also call the function ODK_InitializeClockValues,
* 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
* 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.
*
* Parameters:
* [in] session: handle for the session to be used.
* [in/out] message: Pointer to memory for the entire message. Modified by
* OEMCrypto via the ODK library.
* [in] message_length: length of the entire message buffer.
@@ -960,7 +962,7 @@ OEMCryptoResult OEMCrypto_PrepAndSignLicenseRequest(
* If nonce_values.api_major_version is 15, then OEMCrypto shall compute the
* signature of the message body using the session's client renewal mac key.
* The message body is the buffer starting at message+core_message_size with
* length message_length-core_message_size. If the session has not had a
* length message_length - core_message_size. If the session has not had a
* license loaded, it will use the usage entries client mac key to sign the
* message body.
*
@@ -984,6 +986,7 @@ OEMCryptoResult OEMCrypto_PrepAndSignLicenseRequest(
* signature_length to the size needed to receive the output signature.
*
* Parameters:
* [in] session: handle for the session to be used.
* [in/out] message: Pointer to memory for the entire message. Modified by
* OEMCrypto via the ODK library.
* [in] message_length: length of the entire message buffer.
@@ -1029,19 +1032,19 @@ OEMCryptoResult OEMCrypto_PrepAndSignRenewalRequest(
* OEMCrypto_PrepAndSignProvisioningRequest
*
* Description:
* OEMCrypto will use ODK_PrepareCoreRenewalRequest, as described in the
* document "Widevine Core Message Serialization", to prepare the core
* OEMCrypto will use OEMCrypto_PrepAndSignProvisioningRequest, as described
* in the document "Widevine Core Message Serialization", to prepare the core
* message. If it returns an error, the error should be returned by OEMCrypto
* to the CDM layer. If it returns OEMCrypto_SUCCESS, then OEMCrypto shall
* sign compute the signature of the entire message. The entire message is
* the buffer starting at message with length message_length.
*
* For a device that has a keybox, i.e. Provisioning 2.0, OEMCrypto will sign
* the 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.
*
* 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
* OEMCrypto_LoadDRMPrivateKey.
*
@@ -1053,6 +1056,7 @@ OEMCryptoResult OEMCrypto_PrepAndSignRenewalRequest(
* signature_length to the size needed to receive the output signature.
*
* Parameters:
* [in] session: handle for the session to be used.
* [in/out] message: Pointer to memory for the entire message. Modified by
* OEMCrypto via the ODK library.
* [in] message_length: length of the entire message buffer.
@@ -1210,18 +1214,18 @@ OEMCryptoResult OEMCrypto_LoadSRM(const uint8_t* buffer, size_t buffer_length);
* OEMCrypto_ERROR_LICENSE_RELOAD.
* 3. The enc_mac_keys substring must either have zero length, or satisfy
* the range check. I.e. (offset < message_length) && (offset + length
* < message_length) && (offset < offset+length),and offset+length does
* not cause an integer overflow. If it does not have zero length, then
* enc_mac_keys_iv must not have zero length, and must also satisfy the
* range check. If not, return OEMCrypto_ERROR_INVALID_CONTEXT. If the
* length is zero, then OEMCrypto may assume that the offset is also
* < message_length) && (offset < offset + length),and offset + length
* does not cause an integer overflow. If it does not have zero length,
* then enc_mac_keys_iv must not have zero length, and must also satisfy
* the range check. If not, return OEMCrypto_ERROR_INVALID_CONTEXT. If
* the length is zero, then OEMCrypto may assume that the offset is also
* zero.
* 4. The API shall verify that each substring in each KeyObject points to
* a location in the message. I.e. (offset < message_length) &&
* (offset + length < message_length) && (offset < offset+length) and
* offset+length does not cause an integer overflow, for each of key_id,
* key_data_iv, key_data, key_control_iv, key_control. If not, return
* OEMCrypto_ERROR_INVALID_CONTEXT.
* (offset + length < message_length) && (offset < offset + length) and
* offset + length does not cause an integer overflow, for each of
* key_id, key_data_iv, key_data, key_control_iv, key_control. If not,
* return OEMCrypto_ERROR_INVALID_CONTEXT.
* 5. Each key's control block, after decryption, shall have a valid
* verification field. If not, return OEMCrypto_ERROR_INVALID_CONTEXT.
* 6. If any key control block has the Nonce_Enabled bit set, that key's
@@ -1459,18 +1463,18 @@ OEMCryptoResult OEMCrypto_LoadKeys(
* OEMCrypto_ERROR_LICENSE_RELOAD.
* 15. The enc_mac_keys substring must either have zero length, or satisfy
* the range check. I.e. (offset < message_length) && (offset + length
* < message_length) && (offset < offset+length),and offset+length does
* not cause an integer overflow. If it does not have zero length, then
* enc_mac_keys_iv must not have zero length, and must also satisfy the
* range check. If not, return OEMCrypto_ERROR_INVALID_CONTEXT. If the
* length is zero, then OEMCrypto may assume that the offset is also
* < message_length) && (offset < offset + length),and offset + length
* does not cause an integer overflow. If it does not have zero length,
* then enc_mac_keys_iv must not have zero length, and must also satisfy
* the range check. If not, return OEMCrypto_ERROR_INVALID_CONTEXT. If
* the length is zero, then OEMCrypto may assume that the offset is also
* zero.
* 16. The API shall verify that each substring in each KeyObject points to
* a location in the message. I.e. (offset < message_length) &&
* (offset + length < message_length) && (offset < offset+length) and
* offset+length does not cause an integer overflow, for each of key_id,
* key_data_iv, key_data, key_control_iv, key_control. If not, return
* OEMCrypto_ERROR_INVALID_CONTEXT.
* (offset + length < message_length) && (offset < offset + length) and
* offset + length does not cause an integer overflow, for each of
* key_id, key_data_iv, key_data, key_control_iv, key_control. If not,
* return OEMCrypto_ERROR_INVALID_CONTEXT.
* 17. Each key's control block, after decryption, shall have a valid
* verification field. If not, return OEMCrypto_ERROR_INVALID_CONTEXT.
* 18. If any key control block has the Nonce_Enabled bit set, that key's
@@ -2480,8 +2484,9 @@ OEMCryptoResult OEMCrypto_DecryptCENC(
* [in] session: crypto session identifier.
* [in] data_addr: An unaligned pointer to the buffer to be copied.
* [in] data_addr_length: The length of the buffer, in bytes.
* [in] out_buffer: A caller-owned descriptor that specifies the handling of
* the byte stream. See OEMCrypto_DestbufferDesc for details.
* [in] out_buffer_descriptor: A caller-owned descriptor that specifies the
* handling of the byte stream. See OEMCrypto_DestbufferDesc for
* details.
* [in] subsample_flags: bitwise flags indicating if this is the first,
* middle, or last subsample in a chunk of data. 1 = first subsample, 2
* = last subsample, 3 = both first and last subsample, 0 = neither
@@ -2995,17 +3000,17 @@ OEMCryptoResult OEMCrypto_IsKeyboxOrOEMCertValid(void);
* OEMCrypto_GetDeviceID
*
* Description:
* Retrieve DeviceID from the Keybox. For devices that have an OEM
* Certificate instead of a keybox, this function may return
* OEMCrypto_ERROR_NOT_IMPLEMENTED. If the function is implemented on an OEM
* Certificate device, it should set the device ID to a device-unique string,
* such as the device serial number. The ID should be device-unique and it
* should be stable -- i.e. it should not change across a device reboot or a
* system upgrade. This shall match the device id found in the core
* provisioning request message.
* Return a device unique id. For devices with a keybox, retrieve the
* DeviceID from the Keybox. For devices that have an OEM Certificate instead
* of a keybox, it should set the device ID to a device-unique string, such
* as the device serial number. The ID should be device-unique and it should
* be stable -- i.e. it should not change across a device reboot or a system
* upgrade. This shall match the device id found in the core provisioning
* request message. The maximum length of the device id is 64 bytes. The
* device ID field in a keybox is 32 bytes.
*
* 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
* 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.
* - 0x100 = OEMCrypto_Supports_ECC_secp256r1 - Elliptic Curve secp256r1
* - 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:
* 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 |
* |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 |
* |session | | | | |
@@ -3928,12 +3934,12 @@ uint32_t OEMCrypto_ResourceRatingTier(void);
* [in] core_message_length: length of the core submessage, in bytes.
* [in] signature: pointer to memory containing the signature.
* [in] signature_length: length of the signature, in bytes.
* [out] wrapped_rsa_key: pointer to buffer in which encrypted RSA key should
* be stored. May be null on the first call in order to find required
* buffer size.
* [in/out] wrapped_rsa_key_length: (in) length of the encrypted RSA key, in
* bytes.
* (out) actual length of the encrypted RSA key
* [out] wrapped_private_key: pointer to buffer in which encrypted RSA or ECC
* private key should be stored. May be null on the first call in order
* to find required buffer size.
* [in/out] wrapped_private_key_length: (in) length of the encrypted private
* key, in bytes.
* (out) actual length of the encrypted private key
*
* Returns:
* OEMCrypto_SUCCESS success
@@ -4284,7 +4290,7 @@ OEMCryptoResult OEMCrypto_CreateNewUsageEntry(OEMCrypto_SESSION session,
* OEMCrypto_LoadUsageEntry
*
* 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
* 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
@@ -4477,7 +4483,7 @@ OEMCryptoResult OEMCrypto_DeactivateUsageEntry(OEMCrypto_SESSION session,
* buffer_length and return OEMCrypto_ERROR_SHORT_BUFFER.
*
* If an entry was not loaded or created with OEMCrypto_CreateNewUsageEntry
* or OEMCRypto_LoadUsageEntry, or if the pst does not match that in the
* or OEMCrypto_LoadUsageEntry, or if the pst does not match that in the
* entry, return the error OEMCrypto_ERROR_INVALID_CONTEXT.
*
* If the usage entry's flag ForbidReport is set, indicating the entry has
@@ -4489,7 +4495,7 @@ OEMCryptoResult OEMCrypto_DeactivateUsageEntry(OEMCrypto_SESSION session,
*
* 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
* 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")
*
@@ -4857,11 +4863,11 @@ OEMCryptoResult OEMCrypto_GetHashErrorCode(OEMCrypto_SESSION session,
*
* Description:
* 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
* the buffer. OEMCrypto may use the secure_fd to help track the buffer if it
* wishes. The unit tests will pass a pointer to the same destination buffer
* description and the same secure_fd to OEMCrypto_FreeSecureBuffer when the
* buffer is to be freed.
* in output_descriptor. The integer secure_fd may also be set to indicate
* the source of the buffer. OEMCrypto may use the secure_fd to help track
* the buffer if it wishes. The unit tests will pass a pointer to the same
* destination buffer description and the same secure_fd to
* OEMCrypto_FreeSecureBuffer when the buffer is to be freed.
*
* This is especially helpful if the hash functions above are supported. This
* will only be used by the OEMCrypto unit tests, so we recommend returning
@@ -4872,9 +4878,9 @@ OEMCryptoResult OEMCrypto_GetHashErrorCode(OEMCrypto_SESSION session,
* Parameters:
* [in] session: session id for operation.
* [in] buffer_size: the requested buffer size.
* [out] output: the buffer descriptor for the created buffer. This will be
* passed into the OEMCrypto_DecryptCENC function.
* [out] secure_fd: a pointer to platform dependant file or buffer
* [out] output_descriptor: the buffer descriptor for the created buffer.
* This will be passed into the OEMCrypto_DecryptCENC function.
* [out] secure_fd: a pointer to platform dependent file or buffer
* descriptor. This will be passed to OEMCrypto_FreeSecureBuffer.
*
* Returns:
@@ -4907,7 +4913,7 @@ OEMCryptoResult OEMCrypto_AllocateSecureBuffer(
*
* Parameters:
* [in] session: session id for operation.
* [out] output: the buffer descriptor modified by
* [out] output_descriptor: the buffer descriptor modified 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,
uint8_t* signature,
size_t* signature_length);
OEMCryptoResult OEMCrypto_RewrapDeviceRSAKey30(
OEMCrypto_SESSION session, const uint32_t* unaligned_nonce,
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_iv, uint8_t* wrapped_rsa_key,
size_t* wrapped_rsa_key_length);
OEMCryptoResult OEMCrypto_RewrapDeviceRSAKey(
OEMCrypto_SESSION session, const uint8_t* message, size_t message_length,
const uint8_t* signature, size_t signature_length,
const uint32_t* unaligned_nonce, 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);
OEMCryptoResult OEMCrypto_UpdateUsageTable(void);
OEMCryptoResult OEMCrypto_DeleteUsageEntry(OEMCrypto_SESSION, 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_CopyOldUsageEntry(OEMCrypto_SESSION session,
const uint8_t* pst,
size_t pst_length);
OEMCryptoResult OEMCrypto_DeleteOldUsageTable(void);
OEMCryptoResult OEMCrypto_CreateOldUsageEntry(
uint64_t time_since_license_received, uint64_t time_since_first_decrypt,
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,
size_t pst_length);
OEMCryptoResult OEMCrypto_GenerateDerivedKeys_V15(
OEMCrypto_SESSION session, const uint8_t* mac_key_context,
uint32_t mac_key_context_length, const uint8_t* enc_key_context,
uint32_t enc_key_context_length);
typedef struct {
size_t encrypt; // number of 16 byte blocks to decrypt.
size_t skip; // number of 16 byte blocks to leave in clear.
size_t offset; // offset into the pattern in blocks for this call.
} OEMCrypto_CENCEncryptPatternDesc_V15;
OEMCryptoResult OEMCrypto_DecryptCENC_V15(
OEMCrypto_SESSION session, const uint8_t* data_addr, size_t data_length,
bool is_encrypted, const uint8_t* iv,
@@ -4983,9 +5000,11 @@ OEMCryptoResult OEMCrypto_DecryptCENC_V15(
OEMCrypto_DestBufferDesc* out_buffer_descriptor,
const OEMCrypto_CENCEncryptPatternDesc_V15* pattern,
uint8_t subsample_flags);
OEMCryptoResult OEMCrypto_GetOEMPublicCertificate_V15(
OEMCrypto_SESSION session, uint8_t* public_cert,
size_t* public_cert_length);
OEMCryptoResult OEMCrypto_LoadDeviceRSAKey(OEMCrypto_SESSION session,
const uint8_t* wrapped_rsa_key,
size_t wrapped_rsa_key_length);

View File

@@ -81,6 +81,7 @@ cc_test {
srcs: [
"test/odk_test.cpp",
"test/odk_test_helper.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.
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;
}
auto* header = reinterpret_cast<ODK_CoreMessage*>(&response);
auto* header = &response.request.core_message;
header->message_type = message_type;
header->nonce_values.api_major_version = core_request.api_major_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;
const auto& policy = lic.policy();
ODK_TimerLimits& timer_limits = parsed_lic.timer_limits;
// TODO(b/148241181): add field to protobuf.
// timer_limits.soft_enforce_rental_duration =
// policy.soft_enforce_rental_duration();
timer_limits.soft_enforce_rental_duration = true;
timer_limits.soft_enforce_rental_duration =
policy.soft_enforce_rental_duration();
timer_limits.soft_enforce_playback_duration =
policy.soft_enforce_playback_duration();
timer_limits.earliest_playback_start_seconds = 0;

View File

@@ -15,48 +15,63 @@
#include "odk_util.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 */
static OEMCryptoResult ODK_PrepareRequest(uint8_t* buffer, size_t buffer_length,
size_t* core_message_length,
uint32_t message_type,
const ODK_NonceValues* nonce_values,
ODK_CoreMessage* core_message) {
static OEMCryptoResult ODK_PrepareRequest(
uint8_t* message, size_t message_length, size_t* core_message_length,
uint32_t message_type, const ODK_NonceValues* nonce_values,
void* prepared_request_buffer, size_t prepared_request_buffer_length) {
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;
}
Message* msg = NULL;
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){
message_type,
0,
*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) {
case ODK_License_Request_Type: {
core_message->message_length = ODK_LICENSE_REQUEST_SIZE;
if (sizeof(ODK_PreparedLicenseRequest) > prepared_request_buffer_length) {
return ODK_ERROR_CORE_MESSAGE;
}
Pack_ODK_PreparedLicenseRequest(
msg, (ODK_PreparedLicenseRequest*)core_message);
msg, (ODK_PreparedLicenseRequest*)prepared_request_buffer);
break;
}
case ODK_Renewal_Request_Type: {
core_message->message_length = ODK_RENEWAL_REQUEST_SIZE;
if (sizeof(ODK_PreparedRenewalRequest) > prepared_request_buffer_length) {
return ODK_ERROR_CORE_MESSAGE;
}
Pack_ODK_PreparedRenewalRequest(
msg, (ODK_PreparedRenewalRequest*)core_message);
msg, (ODK_PreparedRenewalRequest*)prepared_request_buffer);
break;
}
case ODK_Provisioning_Request_Type: {
core_message->message_length = ODK_PROVISIONING_REQUEST_SIZE;
if (sizeof(ODK_PreparedProvisioningRequest) >
prepared_request_buffer_length) {
return ODK_ERROR_CORE_MESSAGE;
}
Pack_ODK_PreparedProvisioningRequest(
msg, (ODK_PreparedProvisioningRequest*)core_message);
msg, (ODK_PreparedProvisioningRequest*)prepared_request_buffer);
break;
}
default: {
@@ -65,43 +80,59 @@ static OEMCryptoResult ODK_PrepareRequest(uint8_t* buffer, size_t buffer_length,
}
*core_message_length = core_message->message_length;
if (GetStatus(msg) != MESSAGE_STATUS_OK ||
GetSize(msg) != *core_message_length) {
if (GetStatus(msg) != MESSAGE_STATUS_OK) {
/* 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;
}
if (GetSize(msg) != *core_message_length) {
/* This should not happen. Something is wrong. */
return ODK_ERROR_CORE_MESSAGE;
}
return OEMCrypto_SUCCESS;
}
static OEMCryptoResult ODK_ParseResponse(const uint8_t* buf,
size_t message_length,
size_t core_message_length,
uint32_t message_type,
const ODK_NonceValues* nonce_values,
ODK_CoreMessage* const core_message) {
if (core_message_length > message_length) {
static OEMCryptoResult ODK_ParseResponse(
const uint8_t* message, size_t message_length, size_t core_message_length,
uint32_t message_type, const ODK_NonceValues* nonce_values,
void* response_buffer, uint32_t response_buffer_length) {
if (message == NULL || response_buffer == NULL ||
core_message_length > message_length) {
return ODK_ERROR_CORE_MESSAGE;
}
Message* msg = NULL;
AllocateMessage(&msg, message_block);
/* We initialize the message buffer with a size of the entire message
* 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
* shorter length. The core message is the part we are parsing. */
SetSize(msg, core_message_length);
/* Parse message and unpack it into response buffer. */
switch (message_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;
}
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;
}
case ODK_Provisioning_Response_Type: {
Unpack_ODK_ProvisioningResponse(msg,
(ODK_ProvisioningResponse*)core_message);
if (sizeof(ODK_ProvisioningResponse) > response_buffer_length) {
return ODK_ERROR_CORE_MESSAGE;
}
Unpack_ODK_ProvisioningResponse(
msg, (ODK_ProvisioningResponse*)response_buffer);
break;
}
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 ||
message_type != core_message->message_type ||
GetOffset(msg) != core_message->message_length) {
@@ -117,12 +149,7 @@ static OEMCryptoResult ODK_ParseResponse(const uint8_t* buf,
if (nonce_values) {
/* always verify nonce_values for Renewal and Provisioning responses */
if (nonce_values->api_major_version !=
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) {
if (!ODK_NonceValuesEqual(nonce_values, &(core_message->nonce_values))) {
return OEMCrypto_ERROR_INVALID_NONCE;
}
}
@@ -137,12 +164,15 @@ static OEMCryptoResult ODK_ParseResponse(const uint8_t* buf,
OEMCryptoResult ODK_PrepareCoreLicenseRequest(
uint8_t* message, size_t message_length, size_t* core_message_length,
const ODK_NonceValues* nonce_values) {
if (core_message_length == NULL || nonce_values == NULL) {
return ODK_ERROR_CORE_MESSAGE;
}
ODK_PreparedLicenseRequest license_request = {
{0},
};
return ODK_PrepareRequest(message, message_length, core_message_length,
ODK_License_Request_Type, nonce_values,
&license_request.core_message);
return ODK_PrepareRequest(
message, message_length, core_message_length, ODK_License_Request_Type,
nonce_values, &license_request, sizeof(ODK_PreparedLicenseRequest));
}
OEMCryptoResult ODK_PrepareCoreRenewalRequest(uint8_t* message,
@@ -151,8 +181,22 @@ OEMCryptoResult ODK_PrepareCoreRenewalRequest(uint8_t* message,
ODK_NonceValues* nonce_values,
ODK_ClockValues* clock_values,
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;
}
/* 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};
/* First, we compute the time this request was made relative to the playback
* clock. */
@@ -168,19 +212,24 @@ OEMCryptoResult ODK_PrepareCoreRenewalRequest(uint8_t* message,
return ODK_ERROR_CORE_MESSAGE;
}
}
/* 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
* requests will be bad. */
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,
&renewal_request.core_message);
return ODK_PrepareRequest(
message, message_length, core_message_size, ODK_Renewal_Request_Type,
nonce_values, &renewal_request, sizeof(ODK_PreparedRenewalRequest));
}
OEMCryptoResult ODK_PrepareCoreProvisioningRequest(
uint8_t* message, size_t message_length, size_t* core_message_length,
const ODK_NonceValues* nonce_values, const uint8_t* device_id,
size_t device_id_length) {
if (core_message_length == NULL || nonce_values == NULL) {
return ODK_ERROR_CORE_MESSAGE;
}
ODK_PreparedProvisioningRequest provisioning_request = {
{0},
0,
@@ -195,10 +244,11 @@ OEMCryptoResult ODK_PrepareCoreProvisioningRequest(
}
return ODK_PrepareRequest(message, message_length, core_message_length,
ODK_Provisioning_Request_Type, nonce_values,
&provisioning_request.core_message);
&provisioning_request,
sizeof(ODK_PreparedProvisioningRequest));
}
/* @@ parse request functions */
/* @@ parse response functions */
OEMCryptoResult ODK_ParseLicense(
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}};
const OEMCryptoResult err = ODK_ParseResponse(
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) {
return err;
@@ -279,11 +329,11 @@ OEMCryptoResult ODK_ParseRenewal(const uint8_t* message, size_t message_length,
{{0}, 0},
0,
};
OEMCryptoResult err = ODK_ParseResponse(
const OEMCryptoResult err = ODK_ParseResponse(
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;
}
@@ -318,24 +368,25 @@ OEMCryptoResult ODK_ParseProvisioning(
return ODK_ERROR_CORE_MESSAGE;
}
OEMCryptoResult err =
ODK_ParseResponse(message, message_length, core_message_length,
ODK_Provisioning_Response_Type, nonce_values,
&provisioning_response.request.core_message);
const OEMCryptoResult err = ODK_ParseResponse(
message, message_length, core_message_length,
ODK_Provisioning_Response_Type, nonce_values, &provisioning_response,
sizeof(ODK_ProvisioningResponse));
if (err) {
if (err != OEMCrypto_SUCCESS) {
return err;
}
if (memcmp(device_id, provisioning_response.request.device_id,
device_id_length)) {
if (crypto_memcmp(device_id, provisioning_response.request.device_id,
device_id_length) != 0) {
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 */
if (memcmp(zero, provisioning_response.request.device_id + device_id_length,
ODK_DEVICE_ID_LEN_MAX - device_id_length)) {
if (crypto_memcmp(zero,
provisioning_response.request.device_id + device_id_length,
ODK_DEVICE_ID_LEN_MAX - device_id_length) != 0) {
return ODK_ERROR_CORE_MESSAGE;
}

View File

@@ -60,6 +60,15 @@ typedef struct {
ODK_ParsedProvisioning* parsed_provisioning;
} ODK_ProvisioningResponse;
/* These are the sum of sizeof of each individual member of the request structs
*/
/* without any padding added by the compiler. Make sure they get updated when */
/* request structs change. Refer to test suite OdkSizeTest in */
/* ../test/odk_test.cpp for validations of each of the defined request sizes. */
#define ODK_LICENSE_REQUEST_SIZE 20
#define ODK_RENEWAL_REQUEST_SIZE 28
#define ODK_PROVISIONING_REQUEST_SIZE 88
/* These are the possible timer status values. */
#define ODK_CLOCK_TIMER_STATUS_UNDEFINED 0 /* Should not happen. */
/* When the structure has been initialized, but no license is loaded. */

View File

@@ -4,7 +4,6 @@
#include <stdint.h>
#include <string.h>
#include "odk.h"
#include "odk_overflow.h"
#include "odk_structs_priv.h"
@@ -12,8 +11,7 @@
/* Private function. Checks to see if the license is active. Returns
* ODK_TIMER_EXPIRED if the license is valid but inactive. Returns
* OEMCrypto_SUCCESS if the license is active. Returns
* OEMCrypto_ERROR_UNKNOWN_FAILURE on other errors. This also updates the
* timer_status if appropriate. */
* OEMCrypto_ERROR_UNKNOWN_FAILURE on other errors. */
static OEMCryptoResult ODK_LicenseActive(const ODK_TimerLimits* timer_limits,
ODK_ClockValues* clock_values) {
/* Check some basic errors. */
@@ -26,7 +24,6 @@ static OEMCryptoResult ODK_LicenseActive(const ODK_TimerLimits* timer_limits,
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
}
if (clock_values->status > kActive) {
clock_values->timer_status = ODK_CLOCK_TIMER_STATUS_LICENSE_INACTIVE;
return ODK_TIMER_EXPIRED;
}
return OEMCrypto_SUCCESS;
@@ -157,7 +154,9 @@ static OEMCryptoResult ODK_CheckPlaybackWindow(
* have already computed the timer limit. */
static void ODK_UpdateTimerStatusForRenewal(ODK_ClockValues* clock_values,
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) {
/* Signal that the timer is already set. */
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 new_renewal_duration,
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. */
}
/* If this is before the license was signed, something is odd. Return an
* 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;
}
const OEMCryptoResult license_status =
ODK_LicenseActive(timer_limits, clock_values);
/* 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. */
uint64_t new_timer_value = new_renewal_duration;
@@ -193,9 +196,12 @@ OEMCryptoResult ODK_ComputeRenewalDuration(const ODK_TimerLimits* timer_limits,
* new_timer_value. */
const OEMCryptoResult rental_status = ODK_CheckRentalWindow(
timer_limits, clock_values, system_time_seconds, &new_timer_value);
/* 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;
}
/* If playback has already started and it has hard enforcement, then check
* 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
* done. */
if ((playback_status != ODK_DISABLE_TIMER) &&
(playback_status != ODK_SET_TIMER))
(playback_status != ODK_SET_TIMER)) {
return playback_status;
}
}
/* 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);
return ODK_DISABLE_TIMER;
}
/* If the caller gave us a pointer to store the new timer value. Fill it. */
if (timer_value != NULL) {
*timer_value = new_timer_value;
@@ -241,8 +249,9 @@ OEMCryptoResult ODK_InitializeSessionValues(ODK_TimerLimits* timer_limits,
ODK_NonceValues* nonce_values,
uint32_t api_major_version,
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;
}
/* Check that the API version passed in from OEMCrypto matches the version of
* this ODK library. */
if (api_major_version != ODK_MAJOR_VERSION) {
@@ -269,7 +278,9 @@ OEMCryptoResult ODK_InitializeSessionValues(ODK_TimerLimits* timer_limits,
* OEMCrypto_GenerateNonce. */
OEMCryptoResult ODK_SetNonceValues(ODK_NonceValues* nonce_values,
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. */
if (nonce_values->nonce != 0) {
return OEMCrypto_ERROR_INVALID_CONTEXT;
@@ -281,7 +292,9 @@ OEMCryptoResult ODK_SetNonceValues(ODK_NonceValues* nonce_values,
/* This is called when OEMCrypto signs a license. */
OEMCryptoResult ODK_InitializeClockValues(ODK_ClockValues* clock_values,
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_first_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,
enum OEMCrypto_Usage_Entry_Status status,
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_first_decrypt = time_of_first_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,
ODK_ClockValues* clock_values,
uint64_t* timer_value) {
if (clock_values == NULL || timer_limits == NULL)
if (clock_values == NULL || timer_limits == NULL) {
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
}
/* All times are relative to when the license was signed. */
uint64_t rental_time = 0;
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. */
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. */
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. */
status = ODK_CheckRentalWindow(timer_limits, clock_values,
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 (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. */
status = ODK_CheckPlaybackWindow(timer_limits, clock_values,
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. */
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,
ODK_ClockValues* 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) {
case ODK_CLOCK_TIMER_STATUS_UNLIMITED:
break;
@@ -413,7 +437,9 @@ OEMCryptoResult ODK_UpdateLastPlaybackTime(uint64_t system_time_seconds,
/* This is called from OEMCrypto_DeactivateUsageEntry. */
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) {
clock_values->status = kInactiveUnused;
} else if (clock_values->status == kActive) {
@@ -430,8 +456,9 @@ OEMCryptoResult ODK_InitializeV15Values(ODK_TimerLimits* timer_limits,
ODK_NonceValues* nonce_values,
uint32_t key_duration,
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;
}
timer_limits->soft_enforce_playback_duration = false;
timer_limits->soft_enforce_rental_duration = false;
timer_limits->earliest_playback_start_seconds = 0;
@@ -458,12 +485,14 @@ OEMCryptoResult ODK_RefreshV15Values(const ODK_TimerLimits* timer_limits,
uint64_t system_time_seconds,
uint32_t new_key_duration,
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;
if (nonce_values->api_major_version != 15)
}
if (nonce_values->api_major_version != 15) {
return OEMCrypto_ERROR_INVALID_NONCE;
}
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_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;
}
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 <stdint.h>
#include "odk_structs.h"
#ifdef __cplusplus
extern "C" {
#endif
@@ -18,6 +20,8 @@ extern "C" {
* return value when a != b is undefined, other than being non-zero. */
int crypto_memcmp(const void* a, const void* b, size_t len);
bool ODK_NonceValuesEqual(const ODK_NonceValues* a, const ODK_NonceValues* b);
#ifdef __cplusplus
} /* extern "C" */
#endif

View File

@@ -20,8 +20,12 @@ struct _Message {
MessageStatus status;
};
odk_static_assert(SIZE_OF_MESSAGE_STRUCT >= sizeof(Message),
"SIZE_OF_MESSAGE_STRUCT too small");
/* TODO(b/150776214): this can be removed once AllocateMessage gets cleaned up
*/
/*
* odk_static_assert(SIZE_OF_MESSAGE_STRUCT >= sizeof(struct _Message),
* "SIZE_OF_MESSAGE_STRUCT too small");
*/
bool ValidMessage(Message* message) {
if (message == NULL) {
@@ -235,7 +239,8 @@ size_t GetSize(Message* message) {
void SetSize(Message* message, size_t size) {
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
message->size = size;
}

View File

@@ -28,10 +28,20 @@ extern "C" {
*/
#define AllocateMessage(msg, blk) \
uint8_t blk[SIZE_OF_MESSAGE_STRUCT]; \
*(msg) = (Message*)(blk);
*(msg) = (Message*)(blk)
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);
void Pack_enum(Message* message, int value);
@@ -51,16 +61,6 @@ void UnpackArray(Message* message, uint8_t* address,
size_t size); /* copy out */
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
* sizeof(Message) bytes of the buffer. The caller is responsible for ensuring

View File

@@ -9,7 +9,7 @@
#include <cstring>
#include <functional>
#include <memory>
#include <string>
#include <vector>
#include "OEMCryptoCENCCommon.h"
#include "core_message_deserialize.h"
@@ -20,210 +20,307 @@
#include "odk_structs.h"
#include "odk_structs_priv.h"
// TODO(b/147297226): remove this: using namespace std;
// TODO(b/147297226): remove this: using namespace oec_util;
typedef std::function<void(const uint8_t*, uint8_t*, size_t, size_t)>
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
static OEMCryptoResult odk_fun_LicenseRequest(
uint8_t* out, size_t* size, uint32_t api_version, uint32_t nonce,
uint32_t session_id, const ODK_LicenseRequest& /*core_license_request*/) {
return ODK_PrepareCoreLicenseRequest(out, SIZE_MAX, size, api_version, nonce,
session_id);
using oemcrypto_core_message::deserialize::CoreLicenseRequestFromMessage;
using oemcrypto_core_message::deserialize::CoreProvisioningRequestFromMessage;
using oemcrypto_core_message::deserialize::CoreRenewalRequestFromMessage;
using oemcrypto_core_message::serialize::CreateCoreLicenseResponse;
using oemcrypto_core_message::serialize::CreateCoreProvisioningResponse;
using oemcrypto_core_message::serialize::CreateCoreRenewalResponse;
// @ kdo deserialize; odk serialize
static OEMCryptoResult odk_serialize_LicenseRequest(
const void* in, uint8_t* out, size_t* size,
const ODK_LicenseRequest& core_license_request,
const ODK_NonceValues* nonce_values) {
return ODK_PrepareCoreLicenseRequest(out, SIZE_MAX, size, nonce_values);
}
static OEMCryptoResult odk_fun_RenewalRequest(
uint8_t* out, size_t* size, uint32_t api_version, uint32_t nonce,
uint32_t session_id, const ODK_RenewalRequest& core_renewal) {
// todo: fuzz ODK_ClockValues
ODK_ClockValues clock = {};
uint64_t system_time_seconds = core_renewal.playback_time;
return ODK_PrepareCoreRenewalRequest(out, SIZE_MAX, size, api_version, nonce,
session_id, &clock, system_time_seconds);
static OEMCryptoResult odk_serialize_RenewalRequest(
const void* in, uint8_t* out, size_t* size,
const ODK_RenewalRequest& core_renewal, ODK_NonceValues* nonce_values) {
ODK_ClockValues clock{};
memcpy(&clock, in, sizeof(ODK_ClockValues));
uint64_t system_time_seconds = core_renewal.playback_time_seconds;
return ODK_PrepareCoreRenewalRequest(out, SIZE_MAX, size, nonce_values,
&clock, system_time_seconds);
}
static OEMCryptoResult odk_fun_ProvisioningRequest(
uint8_t* out, size_t* size, uint32_t api_version, uint32_t nonce,
uint32_t session_id, const ODK_ProvisioningRequest& core_provisioning) {
static OEMCryptoResult odk_serialize_ProvisioningRequest(
const void* in, uint8_t* out, size_t* size,
const ODK_ProvisioningRequest& core_provisioning,
const ODK_NonceValues* nonce_values) {
const std::string& device_id = core_provisioning.device_id;
return ODK_PrepareCoreProvisioningRequest(
out, SIZE_MAX, size, api_version, nonce, session_id,
out, SIZE_MAX, size, nonce_values,
reinterpret_cast<const uint8_t*>(device_id.data()), device_id.size());
}
/**
* Template arguments:
* T: kdo deserialize output/odk serialize input structure
* F: kdo deserialize function
* G: odk serialize function
*
* raw bytes -> F deserialize -> struct T -> G serialize -> raw bytes
*/
template <typename T, typename F, typename G>
static roundtrip_fun kdo_odk(const F& kdo_fun, const G& odk_fun) {
auto roundtrip = [&](const uint8_t* in, uint8_t* out, size_t size) -> size_t {
std::string input(reinterpret_cast<const char*>(in), size);
auto roundtrip = [&](const uint8_t* in, uint8_t* out, size_t size,
size_t clock_value_size) -> void {
if (size <= clock_value_size) {
return;
}
// Input byte array format: [Clock Values][data to parse]
std::string input(reinterpret_cast<const char*>(in) + clock_value_size,
size - clock_value_size);
T t = {};
if (!kdo_fun(input, &t)) {
return 0;
return;
}
OEMCryptoResult err =
odk_fun(out, &size, t.api_version, t.nonce, t.session_id, t);
return OEMCrypto_SUCCESS == err ? size : 0;
ODK_NonceValues nonce_values = {t.api_minor_version, t.api_major_version,
t.nonce, t.session_id};
OEMCryptoResult err = odk_fun(in, out, &size, t, &nonce_values);
if (OEMCrypto_SUCCESS != err) {
return;
}
assert(0 == memcmp(in + clock_value_size, out, size));
};
return roundtrip;
}
// @ odk deserialize; kdo serialize
namespace {
struct ODK_Common_Args {
uint32_t api_version;
uint32_t nonce;
uint32_t session_id;
};
struct ODK_ParseLicense_Args {
ODK_Common_Args common;
ODK_NonceValues nonce_values;
uint8_t initial_license_load;
uint8_t usage_entry_present;
uint8_t request_hash[32];
ODK_TimerLimits timer_limits;
ODK_ClockValues clock_values;
};
struct ODK_ParseRenewal_Args {
ODK_Common_Args common;
ODK_NonceValues nonce_values;
uint64_t system_time;
ODK_TimerLimits timer_limits;
ODK_ClockValues clock_values;
};
struct ODK_ParseProvisioning_Args {
ODK_Common_Args common;
ODK_NonceValues nonce_values;
size_t device_id_length;
uint8_t device_id[64];
};
} // namespace
static OEMCryptoResult odk_fun_LicenseResponse(
const uint8_t* message, size_t message_length, uint32_t api_version,
uint32_t nonce, uint32_t session_id, const ODK_ParseLicense_Args* a,
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);
bool convert_byte_to_valid_boolean(const bool* in) {
const int value = *reinterpret_cast<const int*>(in);
return value != 0;
}
static bool kdo_fun_LicenseResponse(const ODK_ParseLicense_Args* args,
const ODK_ParsedLicense& parsed_lic,
std::string* oemcrypto_core_message) {
const auto& common = args->common;
ODK_LicenseRequest core_request{common.api_version, common.nonce,
common.session_id};
return CreateCoreLicenseResponse(parsed_lic, core_request,
oemcrypto_core_message);
static OEMCryptoResult odk_deserialize_LicenseResponse(
const uint8_t* message, size_t core_message_length,
ODK_ParseLicense_Args* a, ODK_NonceValues* nonce_values,
ODK_ParsedLicense* parsed_lic) {
return ODK_ParseLicense(message, SIZE_MAX, core_message_length,
static_cast<bool>(a->initial_license_load),
static_cast<bool>(a->usage_entry_present),
a->request_hash, &a->timer_limits, &a->clock_values,
nonce_values, parsed_lic);
}
static OEMCryptoResult odk_fun_RenewalResponse(
const uint8_t* buf, size_t len, uint32_t api_version, uint32_t nonce,
uint32_t session_id, ODK_ParseRenewal_Args* a,
ODK_PreparedRenewalRequest& renewal_msg) {
static bool kdo_serialize_LicenseResponse(const ODK_ParseLicense_Args* args,
const ODK_ParsedLicense& parsed_lic,
std::string* oemcrypto_core_message) {
const auto& nonce_values = args->nonce_values;
ODK_LicenseRequest core_request{nonce_values.api_minor_version,
nonce_values.api_major_version,
nonce_values.nonce, nonce_values.session_id};
std::string core_request_sha_256(
reinterpret_cast<const char*>(args->request_hash), 32);
return CreateCoreLicenseResponse(
parsed_lic, core_request, core_request_sha_256, oemcrypto_core_message);
}
static OEMCryptoResult odk_deserialize_RenewalResponse(
const uint8_t* buf, size_t len, ODK_ParseRenewal_Args* a,
ODK_NonceValues* nonce_values, ODK_PreparedRenewalRequest* renewal_msg) {
/* Address Sanitizer doesn't like values other than 0 OR 1 for boolean
* variables. Input from fuzzer can be parsed and any random bytes can be
* assigned to boolean variables. Using the workaround to mitigate sanitizer
* errors in fuzzer code and converting random bytes to 0 OR 1.
* This has no negative security impact*/
a->timer_limits.soft_enforce_playback_duration =
convert_byte_to_valid_boolean(
&a->timer_limits.soft_enforce_playback_duration);
a->timer_limits.soft_enforce_rental_duration = convert_byte_to_valid_boolean(
&a->timer_limits.soft_enforce_rental_duration);
uint64_t timer_value = 0;
OEMCryptoResult err =
ODK_ParseRenewal(buf, 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);
if (OEMCrypto_SUCCESS == err) {
Message* msg = nullptr;
AllocateMessage(&msg, message_block);
InitMessage(msg, const_cast<uint8_t*>(buf), len);
SetSize(msg, len);
Unpack_ODK_PreparedRenewalRequest(msg, &renewal_msg);
Unpack_ODK_PreparedRenewalRequest(msg, renewal_msg);
assert(ValidMessage(msg));
}
return err;
}
static bool kdo_fun_RenewalResponse(
static bool kdo_serialize_RenewalResponse(
const ODK_ParseRenewal_Args* args,
const ODK_PreparedRenewalRequest& renewal_msg,
std::string* oemcrypto_core_message) {
const auto& common = args->common;
ODK_RenewalRequest core_request{common.api_version, common.nonce,
common.session_id, renewal_msg.playback_time};
return CreateCoreRenewalResponse(core_request, oemcrypto_core_message);
const auto& nonce_values = args->nonce_values;
ODK_RenewalRequest core_request{
nonce_values.api_minor_version, nonce_values.api_major_version,
nonce_values.nonce, nonce_values.session_id, renewal_msg.playback_time};
return CreateCoreRenewalResponse(
core_request, args->timer_limits.initial_renewal_duration_seconds,
oemcrypto_core_message);
}
static OEMCryptoResult odk_fun_ProvisioningResponse(
const uint8_t* buf, size_t len, uint32_t api_version, uint32_t nonce,
uint32_t session_id, ODK_ParseProvisioning_Args* a,
ODK_ParsedProvisioning& parsed_prov) {
return ODK_ParseProvisioning(buf, len, api_version, nonce, session_id,
a->device_id, a->device_id_length, &parsed_prov);
static OEMCryptoResult odk_deserialize_ProvisioningResponse(
const uint8_t* buf, size_t len, ODK_ParseProvisioning_Args* a,
ODK_NonceValues* nonce_values, ODK_ParsedProvisioning* parsed_prov) {
return ODK_ParseProvisioning(buf, SIZE_MAX, len, nonce_values, a->device_id,
a->device_id_length, parsed_prov);
}
static bool kdo_fun_ProvisioningResponse(
static bool kdo_serialize_ProvisioningResponse(
const ODK_ParseProvisioning_Args* args,
const ODK_ParsedProvisioning& parsed_prov,
std::string* oemcrypto_core_message) {
const auto& common = args->common;
assert(args->device_id_length <= sizeof(args->device_id));
const auto& nonce_values = args->nonce_values;
if (args->device_id_length > sizeof(args->device_id)) {
return false;
}
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),
args->device_id_length)};
return CreateCoreProvisioningResponse(parsed_prov, core_request,
oemcrypto_core_message);
}
/**
* Template arguments:
* A: struct holding function arguments
* T: odk deserialize output/kdo serialize input structure
* F: odk deserialize function
* G: kdo serialize function
*
* raw bytes -> F deserialize -> struct T -> G serialize -> raw bytes
*/
template <typename A, typename T, typename F, typename G>
static roundtrip_fun odk_kdo(const F& odk_fun, const G& kdo_fun) {
auto roundtrip = [&](const uint8_t* in, uint8_t* out, size_t size) -> size_t {
if (sizeof(A) > size) {
return 0;
auto roundtrip = [&](const uint8_t* in, uint8_t* out, size_t size,
size_t args_size) -> void {
// Input byte array format: [function arguments][data to parse]
if (args_size > size) {
return;
}
T t = {};
const uint8_t* buf = in + sizeof(A);
size_t len = size - sizeof(A);
const uint8_t* buf = in + args_size;
std::shared_ptr<A> _args(new A());
A* args = _args.get();
memcpy(args, in, sizeof(A));
const auto& common = args->common;
OEMCryptoResult err = odk_fun(buf, len, common.api_version, common.nonce,
common.session_id, args, t);
if (err != OEMCrypto_SUCCESS) {
return 0;
}
memcpy(args, in, args_size);
args->nonce_values.api_major_version = ODK_MAJOR_VERSION;
args->nonce_values.api_minor_version = ODK_MINOR_VERSION;
/*
* Input random bytes from autofuzz are interpreted by this script as
* [function args][data to parse]. Odk deserialize functions
* expect the nonce values in function args to match with those
* in data to parse which is not possible with random bytes.
* We follow two pass approach.
*
* 1st pass - We copy random bytes into struct t and call kdo serialize
* with function args which will create oemcrypto core message using nonce
* from function args. Now we have a valid oemcrypto core message which is
* formed using nonce_values from function args which acts as input bytes
* for 2nd pass
*
* 2nd pass - oemcrypto core message from 1st pass guarantees that
* nonce_values in [function args] and core message match. we call
* odk_deserialize using nonce from function args and oemcrypto core message
* from 1st pass. Then we call kdo function which generates oemcrypto core
* message2, which should be equal to oemcrypto_core_message which was input
* to 2nd pass
*/
// TODO(ellurubharath): Use structure aware fuzzing
// 1st pass
memcpy(&t, buf, sizeof(t));
std::string oemcrypto_core_message;
if (!kdo_fun(args, t, &oemcrypto_core_message)) {
return 0;
return;
}
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;
}
// @ fuzz raw -> parsed -> raw
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);
auto out = _out.data();
size_t n = roundtrip(in, out, size);
assert(!n || (n <= size && 0 == memcmp(in, out, n)));
roundtrip(in, out, size, args_size);
}
// Entry point for fuzzer, data is random bytes program gets from autofuzzer
extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
verify_roundtrip(data, size,
kdo_odk<ODK_LicenseRequest>(CoreLicenseRequestFromMessage,
odk_serialize_LicenseRequest),
0);
verify_roundtrip(data, size,
kdo_odk<ODK_RenewalRequest>(CoreRenewalRequestFromMessage,
odk_serialize_RenewalRequest),
sizeof(ODK_ClockValues));
verify_roundtrip(
data, size,
kdo_odk<ODK_LicenseRequest>(ParseLicenseRequest, odk_fun_LicenseRequest));
kdo_odk<ODK_ProvisioningRequest>(CoreProvisioningRequestFromMessage,
odk_serialize_ProvisioningRequest),
0);
verify_roundtrip(
data, size,
kdo_odk<ODK_RenewalRequest>(ParseRenewalRequest, odk_fun_RenewalRequest));
verify_roundtrip(data, size,
kdo_odk<ODK_ProvisioningRequest>(
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));
odk_kdo<ODK_ParseLicense_Args, ODK_ParsedLicense>(
odk_deserialize_LicenseResponse, kdo_serialize_LicenseResponse),
sizeof(ODK_ParseLicense_Args));
verify_roundtrip(
data, size,
odk_kdo<ODK_ParseProvisioning_Args, ODK_ParsedProvisioning>(
odk_fun_ProvisioningResponse, kdo_fun_ProvisioningResponse));
odk_kdo<ODK_ParseRenewal_Args, ODK_PreparedRenewalRequest>(
odk_deserialize_RenewalResponse, kdo_serialize_RenewalResponse),
sizeof(ODK_ParseRenewal_Args));
verify_roundtrip(data, size,
odk_kdo<ODK_ParseProvisioning_Args, ODK_ParsedProvisioning>(
odk_deserialize_ProvisioningResponse,
kdo_serialize_ProvisioningResponse),
sizeof(ODK_ParseProvisioning_Args));
return 0;
}

File diff suppressed because it is too large Load Diff

View File

@@ -5,6 +5,8 @@
{
'sources': [
'odk_test.cpp',
'odk_test_helper.cpp',
'odk_test_helper.h',
'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',
'ref/oec_ref.gypi',
],
'libraries': [
'-lpthread',
],
'dependencies': [
'<(boringssl_dependency)',
'<(gtest_dependency)',

View File

@@ -95,19 +95,22 @@ SessionContext* CryptoEngine::FindSession(SessionId sid) {
return nullptr;
}
int64_t CryptoEngine::OnlineTime() {
int64_t CryptoEngine::MonotonicTime() {
// Use the monotonic clock for times that don't have to be stable across
// device boots.
int64_t now = wvcdm::Clock().GetCurrentTime();
int64_t now =
wvcdm::Clock().GetCurrentTime() + offline_time_info_.rollback_offset;
static int64_t then = now;
if (now < then) now = then;
if (now < then) {
offline_time_info_.rollback_offset += then - now;
now = then;
}
then = now;
return now;
}
int64_t CryptoEngine::RollbackCorrectedOfflineTime() {
// Add any time offsets in the past to the current time.
int64_t current_time = OnlineTime() + offline_time_info_.rollback_offset;
int64_t CryptoEngine::SystemTime() {
const int64_t current_time = MonotonicTime();
// Write time info to disk if kTimeInfoUpdateWindowInSeconds has elapsed since
// last write.
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.
// For now, the file path is empty.
/*if (!wvcdm::Properties::GetDeviceFilesBasePath(wvcdm::kSecurityLevelL3,
&file_path)) {
LOGE("RollbackCorrectedOfflineTime: Unable to get base path");
}*/
&file_path)) {
LOGE("Unable to get base path");
}*/
return file_path + kStoredUsageTimeFileName;
}
@@ -148,8 +151,7 @@ bool CryptoEngine::LoadOfflineTimeInfo(const std::string& file_path) {
std::unique_ptr<wvcdm::File> file =
file_system->Open(file_path, wvcdm::FileSystem::kReadOnly);
if (!file) {
LOGE("RollbackCorrectedOfflineTime: File open failed: %s",
file_path.c_str());
LOGE("File open failed: %s", file_path.c_str());
return false;
}
// 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.
// 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) {
// Current time is earlier than the previously saved time. Time has been
// 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
// LoadOfflineTimeInfo(). It guarantees that the current time to be saved
// 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.
if (current_time > offline_time_info_.previous_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_path, wvcdm::FileSystem::kCreate | wvcdm::FileSystem::kTruncate);
if (!file) {
LOGE("RollbackCorrectedOfflineTime: File open failed: %s",
file_path.c_str());
LOGE("File open failed: %s", file_path.c_str());
return false;
}
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.
// TODO(b/145836634): Combine RollbackCorrectedOfflineTime with OnlineTime().
int64_t SystemTime() { return RollbackCorrectedOfflineTime(); }
int64_t SystemTime();
// Verify that this nonce does not collide with another nonce in any session.
virtual bool NonceCollision(uint32_t nonce);
@@ -224,11 +223,8 @@ class CryptoEngine {
}
protected:
// System clock, measuring time in seconds.
int64_t OnlineTime();
// System clock with antirollback protection, measuring time in seconds.
int64_t RollbackCorrectedOfflineTime();
// System clock, measuring time in seconds, including anti-rollback offset.
int64_t MonotonicTime();
bool LoadOfflineTimeInfo(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
// key are the same size -- just encrypted with different keys.
// We add 32 bytes for a context, 32 for iv, and 32 bytes for a signature.
// Important: This layout must match OEMCrypto_LoadDeviceRSAKey below.
// Important: This layout must match OEMCrypto_LoadDRMPrivateKey below.
const size_t buffer_size = enc_rsa_key_length + sizeof(WrappedRSAKey);
if (wrapped_rsa_key == nullptr || *wrapped_rsa_key_length < buffer_size) {
@@ -1007,7 +1007,7 @@ static OEMCryptoResult OEMCrypto_RewrapDeviceRSAKey(
// For the reference implementation, the wrapped key and the encrypted
// key are the same size -- just encrypted with different keys.
// We add 32 bytes for a context, 32 for iv, and 32 bytes for a signature.
// Important: This layout must match OEMCrypto_LoadDeviceRSAKey below.
// Important: This layout must match OEMCrypto_LoadDRMPrivateKey below.
const size_t buffer_size = enc_rsa_key_length + sizeof(WrappedRSAKey);
if (wrapped_rsa_key == nullptr || *wrapped_rsa_key_length < buffer_size) {
@@ -1134,7 +1134,7 @@ OEMCRYPTO_API OEMCryptoResult OEMCrypto_LoadProvisioning(
// For the reference implementation, the wrapped key and the encrypted
// key are the same size -- just encrypted with different keys.
// We add 32 bytes for a context, 32 for iv, and 32 bytes for a signature.
// Important: This layout must match OEMCrypto_LoadDeviceRSAKey below.
// Important: This layout must match OEMCrypto_LoadDRMPrivateKey below.
const size_t buffer_size =
parsed_response.enc_private_key.length + sizeof(WrappedRSAKey);
@@ -1170,24 +1170,28 @@ OEMCRYPTO_API OEMCryptoResult OEMCrypto_LoadProvisioning(
}
}
OEMCRYPTO_API OEMCryptoResult OEMCrypto_LoadDeviceRSAKey(
OEMCrypto_SESSION session, const uint8_t* wrapped_rsa_key,
size_t wrapped_rsa_key_length) {
OEMCRYPTO_API OEMCryptoResult OEMCrypto_LoadDRMPrivateKey(
OEMCrypto_SESSION session, OEMCrypto_PrivateKeyType key_type,
const uint8_t* wrapped_rsa_key, size_t wrapped_rsa_key_length) {
if (wrapped_rsa_key == nullptr) {
LOGE("[OEMCrypto_LoadDeviceRSAKey(): OEMCrypto_ERROR_INVALID_CONTEXT]");
LOGE("OEMCrypto_ERROR_INVALID_CONTEXT nullptr");
return OEMCrypto_ERROR_INVALID_CONTEXT;
}
if (crypto_engine == nullptr) {
LOGE("OEMCrypto_LoadDeviceRSAKey: OEMCrypto Not Initialized.");
LOGE("OEMCrypto Not Initialized.");
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 we are using a baked in cert, the "wrapped RSA key" should actually be
// the magic value for baked-in certificates.
if (wrapped_rsa_key_length != sizeof(kBakedInCertificateMagicBytes) ||
memcmp(kBakedInCertificateMagicBytes, wrapped_rsa_key,
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;
} else {
return OEMCrypto_SUCCESS;
@@ -1196,13 +1200,13 @@ OEMCRYPTO_API OEMCryptoResult OEMCrypto_LoadDeviceRSAKey(
const WrappedRSAKey* wrapped =
reinterpret_cast<const WrappedRSAKey*>(wrapped_rsa_key);
if (!crypto_engine->ValidRootOfTrust()) {
LOGE("[OEMCrypto_LoadDeviceRSAKey(): ERROR_KEYBOX_INVALID]");
LOGE("ERROR_KEYBOX_INVALID");
return OEMCrypto_ERROR_KEYBOX_INVALID;
}
SessionContext* session_ctx = crypto_engine->FindSession(session);
if (session_ctx == nullptr || !session_ctx->isValid()) {
LOGE("[OEMCrypto_LoadDeviceRSAKey(): ERROR_INVALID_SESSION]");
LOGE("ERROR_INVALID_SESSION");
return OEMCrypto_ERROR_INVALID_SESSION;
}
const std::vector<uint8_t> context(
@@ -1216,7 +1220,7 @@ OEMCRYPTO_API OEMCryptoResult OEMCrypto_LoadDeviceRSAKey(
if (!session_ctx->ValidateMessage(
wrapped->context, wrapped_rsa_key_length - sizeof(wrapped->signature),
wrapped->signature, sizeof(wrapped->signature))) {
LOGE("[LoadDeviceRSAKey(): Could not verify signature]");
LOGE("Could not verify signature");
return OEMCrypto_ERROR_SIGNATURE_FAILURE;
}
// Decrypt RSA key.

View File

@@ -24,6 +24,7 @@
#include <openssl/sha.h>
#include <openssl/x509.h>
#include "advance_iv_ctr.h"
#include "disallow_copy_and_assign.h"
#include "keys.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 wvoec_ref {
@@ -208,6 +190,7 @@ SessionContext::SessionContext(CryptoEngine* ce, SessionId sid,
id_(sid),
current_content_key_(nullptr),
session_keys_(nullptr),
license_request_hash_(),
rsa_key_(rsa_key),
allowed_schemes_(kSign_RSASSA_PSS),
decrypt_started_(false),
@@ -228,14 +211,6 @@ SessionContext::SessionContext(CryptoEngine* ce, SessionId sid,
}
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
@@ -795,11 +770,11 @@ OEMCryptoResult SessionContext::LoadKeysNoSignature(
if (session_keys_ == nullptr) {
switch (license_type) {
case OEMCrypto_ContentLicense:
session_keys_ = new ContentKeysContext();
session_keys_.reset(new ContentKeysContext());
break;
case OEMCrypto_EntitlementLicense:
session_keys_ = new EntitlementKeysContext();
session_keys_.reset(new EntitlementKeysContext());
break;
default:
@@ -933,7 +908,8 @@ OEMCryptoResult SessionContext::LoadEntitledContentKeys(
if (!key_array) {
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;
}
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 ce_->usage_table().UpdateUsageEntry(
this, usage_entry_, header_buffer, header_buffer_length, entry_buffer,
entry_buffer_length, &clock_values_);
this, usage_entry_.get(), header_buffer, header_buffer_length,
entry_buffer, entry_buffer_length, &clock_values_);
}
OEMCryptoResult SessionContext::DeactivateUsageEntry(
@@ -1532,7 +1508,7 @@ OEMCryptoResult SessionContext::ReportUsage(const std::vector<uint8_t>& pst,
OEMCryptoResult SessionContext::MoveEntry(uint32_t new_index) {
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
@@ -1604,8 +1580,8 @@ OEMCryptoResult SessionContext::DecryptSamples(
advance_dest_buffer(&subsample_dest, subsample_length);
if (subsample.num_bytes_encrypted > 0 &&
current_content_key()->ctr_mode()) {
advance_iv_ctr(&subsample_iv,
subsample.block_offset + subsample.num_bytes_encrypted);
wvcdm::AdvanceIvCtr(&subsample_iv, subsample.block_offset +
subsample.num_bytes_encrypted);
}
} // Subsample loop
} // Sample loop

View File

@@ -267,7 +267,7 @@ class SessionContext {
std::vector<uint8_t> encryption_key_;
std::vector<uint8_t> session_key_;
const Key* current_content_key_;
SessionContextKeys* session_keys_;
std::unique_ptr<SessionContextKeys> session_keys_;
ODK_NonceValues nonce_values_;
uint8_t license_request_hash_[ODK_SHA256_HASH_SIZE];
RSA_shared_ptr rsa_key_;
@@ -275,7 +275,7 @@ class SessionContext {
bool decrypt_started_; // If the license has been used in this session.
ODK_TimerLimits timer_limits_;
ODK_ClockValues clock_values_;
UsageTableEntry* usage_entry_;
std::unique_ptr<UsageTableEntry> usage_entry_;
SRMVersionStatus srm_requirements_status_;
enum UsageEntryStatus {
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_);
}
OEMCryptoResult UsageTable::CreateNewUsageEntry(SessionContext* session,
UsageTableEntry** entry,
uint32_t* usage_entry_number) {
OEMCryptoResult UsageTable::CreateNewUsageEntry(
SessionContext* session, std::unique_ptr<UsageTableEntry>* entry,
uint32_t* usage_entry_number) {
if (!header_loaded_) {
LOGE("CreateNewUsageEntry: Header not loaded.");
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
@@ -367,16 +367,15 @@ OEMCryptoResult UsageTable::CreateNewUsageEntry(SessionContext* session,
generation_numbers_.push_back(master_generation_number_);
sessions_.push_back(session);
master_generation_number_++;
*entry = new_entry;
entry->reset(new_entry);
*usage_entry_number = index;
return OEMCrypto_SUCCESS;
}
OEMCryptoResult UsageTable::LoadUsageEntry(SessionContext* session,
UsageTableEntry** entry,
uint32_t index,
const std::vector<uint8_t>& buffer,
ODK_ClockValues* clock_values) {
OEMCryptoResult UsageTable::LoadUsageEntry(
SessionContext* session, std::unique_ptr<UsageTableEntry>* entry,
uint32_t index, const std::vector<uint8_t>& buffer,
ODK_ClockValues* clock_values) {
if (!header_loaded_) {
LOGE("CreateNewUsageEntry: Header not loaded.");
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
@@ -393,12 +392,11 @@ OEMCryptoResult UsageTable::LoadUsageEntry(SessionContext* session,
LOGE("Too many usage entries: %d/%d", index, max);
return OEMCrypto_ERROR_INSUFFICIENT_RESOURCES;
}
UsageTableEntry* new_entry = MakeEntry(index);
std::unique_ptr<UsageTableEntry> new_entry(MakeEntry(index));
OEMCryptoResult status =
new_entry->LoadData(ce_, index, buffer, clock_values);
if (status != OEMCrypto_SUCCESS) {
delete new_entry;
return status;
}
if (new_entry->generation_number() != generation_numbers_[index]) {
@@ -406,13 +404,12 @@ OEMCryptoResult UsageTable::LoadUsageEntry(SessionContext* session,
generation_numbers_[index]);
if ((new_entry->generation_number() + 1 < generation_numbers_[index]) ||
(new_entry->generation_number() - 1 > generation_numbers_[index])) {
delete new_entry;
return OEMCrypto_ERROR_GENERATION_SKEW;
}
status = OEMCrypto_WARNING_GENERATION_SKEW;
}
sessions_[index] = session;
*entry = new_entry;
*entry = std::move(new_entry);
return status;
}

View File

@@ -88,10 +88,11 @@ class UsageTable {
virtual ~UsageTable();
OEMCryptoResult CreateNewUsageEntry(SessionContext* session,
UsageTableEntry** entry,
std::unique_ptr<UsageTableEntry>* entry,
uint32_t* usage_entry_number);
OEMCryptoResult LoadUsageEntry(SessionContext* session,
UsageTableEntry** entry, uint32_t index,
std::unique_ptr<UsageTableEntry>* entry,
uint32_t index,
const std::vector<uint8_t>& buffer,
ODK_ClockValues* clock_values);
OEMCryptoResult UpdateUsageEntry(

View File

@@ -85,7 +85,7 @@ static std::string GetSSLError() {
static bool DeriveKey(const std::vector<uint8_t>& key,
const std::vector<uint8_t>& context,
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;
return false;
}

View File

@@ -216,7 +216,7 @@ void ProvisioningRoundTrip::PrepareSession(
void ProvisioningRoundTrip::VerifyRequestSignature(
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) {
session()->VerifyRSASignature(data, generated_signature.data(),
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
// out so that the tests compile.
OEMCryptoResult OEMCrypto_RewrapDeviceRSAKey30_V15(
OEMCrypto_SESSION session, const uint32_t* unaligned_nonce,
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_iv, uint8_t* wrapped_rsa_key,
size_t* wrapped_rsa_key_length) {
OEMCrypto_SESSION, const uint32_t*, const uint8_t*, size_t, const uint8_t*,
size_t, const uint8_t*, uint8_t*, size_t*) {
LOGE("Support for v15 functions not included. Define TEST_OEMCRYPTO_V15.");
return OEMCrypto_ERROR_NOT_IMPLEMENTED;
}
OEMCryptoResult OEMCrypto_RewrapDeviceRSAKey_V15(
OEMCrypto_SESSION session, const uint8_t* message, size_t message_length,
const uint8_t* signature, size_t signature_length,
const uint32_t* unaligned_nonce, 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) {
OEMCrypto_SESSION, const uint8_t*, size_t, const uint8_t*, size_t,
const uint32_t*, const uint8_t*, size_t, const uint8_t*, uint8_t*,
size_t*) {
LOGE("Support for v15 functions not included. Define TEST_OEMCRYPTO_V15.");
return OEMCrypto_ERROR_NOT_IMPLEMENTED;
}
@@ -591,9 +586,6 @@ void LicenseRoundTrip::EncryptAndSignResponse() {
memcpy(encrypted_response_.data() + serialized_core_message_.size(),
reinterpret_cast<const uint8_t*>(&encrypted_response_data_),
sizeof(encrypted_response_data_));
if (global_features.provisioning_method == OEMCrypto_OEMCertificate) {
session()->GenerateDerivedKeysFromSessionKey();
}
session()->key_deriver().ServerSignBuffer(encrypted_response_.data(),
encrypted_response_.size(),
&response_signature_);
@@ -831,8 +823,8 @@ void RenewalRoundTrip::VerifyRequestSignature(
void RenewalRoundTrip::FillAndVerifyCoreRequest(
const std::string& core_message_string) {
if (license_messages_->api_version() < kCoreMessagesAPI) {
// For v15, we expect that no core request was created.
if (license_messages_->api_version() < kCoreMessagesAPI || is_release_) {
// For v15 or for a release, we expect that no core request was created.
EXPECT_FALSE(
oemcrypto_core_message::deserialize::CoreRenewalRequestFromMessage(
core_message_string, &core_request_));
@@ -842,18 +834,14 @@ void RenewalRoundTrip::FillAndVerifyCoreRequest(
core_message_string, &core_request_));
EXPECT_EQ(license_messages_->core_request().api_major_version,
core_request_.api_major_version);
if (!is_release_) {
// For a license release, we do not expect the nonce to be correct. That
// is because a release might be sent without loading the license first.
EXPECT_EQ(license_messages_->core_request().nonce, core_request_.nonce);
EXPECT_EQ(license_messages_->core_request().session_id,
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() {
if (license_messages_->api_version() < kCoreMessagesAPI) {
if (license_messages_->api_version() < kCoreMessagesAPI || is_release_) {
uint32_t control = 0;
uint32_t nonce = 0;
// 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;
response_data_.keys[index].key_id_length = 0;
response_data_.keys[index].key_id[0] = '\0';
std::string kcVersion =
"kc" + std::to_string(core_request_.api_major_version);
if (global_features.api_version < kCoreMessagesAPI) {
// For v15 or earlier devices, we use the api of the device.
kcVersion = "kc" + std::to_string(global_features.api_version);
}
const uint32_t renewal_api =
std::max<uint32_t>(core_request_.api_major_version, 15u);
std::string kcVersion = "kc" + std::to_string(renewal_api);
memcpy(response_data_.keys[index].control.verification, kcVersion.c_str(),
4);
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) {
ASSERT_EQ(OEMCrypto_SUCCESS,
OEMCrypto_LoadDeviceRSAKey(session_id(), wrapped_rsa_key.data(),
wrapped_rsa_key.size()));
OEMCrypto_LoadDRMPrivateKey(session_id(), OEMCrypto_RSA_Private_Key,
wrapped_rsa_key.data(),
wrapped_rsa_key.size()));
}
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));
}
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) {
usage_entry_number_ = index;
encrypted_usage_entry_ = buffer;

View File

@@ -177,7 +177,6 @@ class RoundTrip {
CoreRequest& core_request() { return core_request_; }
CoreResponse& core_response() { return core_response_; }
ResponseData& response_data() { return response_data_; }
ResponseData& encrypted_response_data() { return encrypted_response_data_; }
std::vector<uint8_t>& encrypted_response_buffer() {
return encrypted_response_;
}
@@ -284,7 +283,8 @@ class LicenseRoundTrip
update_mac_keys_(true),
api_version_(kCurrentAPI),
expect_request_has_correct_nonce_(true),
license_type_(OEMCrypto_ContentLicense) {}
license_type_(OEMCrypto_ContentLicense),
request_hash_() {}
void CreateDefaultResponse() override;
// 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
@@ -527,8 +527,6 @@ class Session {
void ReloadUsageEntry() { LoadUsageEntry(*this); }
// Update the usage entry and save the header to the specified 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.
uint32_t usage_entry_number() const { return usage_entry_number_; }
void set_usage_entry_number(uint32_t v) { usage_entry_number_ = v; }

View File

@@ -15,9 +15,9 @@
#include <stdint.h>
#ifdef _WIN32
# include <windows.h>
# include <windows.h>
#else
# include <sys/time.h>
# include <sys/time.h>
#endif
#include <gtest/gtest.h>
@@ -46,9 +46,9 @@
using ::testing::Bool;
using ::testing::Combine;
using ::testing::Range;
using ::testing::tuple;
using ::testing::Values;
using ::testing::WithParamInterface;
using ::testing::tuple;
using namespace std;
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
// up the required value for the specified resource for the target OEMCrypto
// library.
template<typename T, size_t N>
template <typename T, size_t N>
T GetResourceValue(T (&resource_values)[N]) {
if (global_features.resource_rating < 1) return resource_values[0];
if (global_features.resource_rating > N) return resource_values[N-1];
return resource_values[global_features.resource_rating-1];
if (global_features.resource_rating > N) return resource_values[N - 1];
return resource_values[global_features.resource_rating - 1];
}
// 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. */
template <typename Duration>
uint64_t UnixTime(const std::chrono::time_point<std::chrono::system_clock,
Duration>& point) {
uint64_t UnixTime(
const std::chrono::time_point<std::chrono::system_clock, Duration>& point) {
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));
uint64_t long_time = static_cast<uint64_t>(file_time.dwLowDateTime) |
(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.dwHighDateTime = long_time >> 32;
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
// tests are failing when the device has the wrong keybox installed.
TEST_F(OEMCryptoClientTest, VersionNumber) {
const std::string log_message =
"OEMCrypto unit tests for API 16.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();
ASSERT_NE(nullptr, level);
ASSERT_EQ('L', level[0]);
@@ -196,13 +206,13 @@ TEST_F(OEMCryptoClientTest, VersionNumber) {
uint32_t version = OEMCrypto_APIVersion();
cout << " OEMCrypto API version is " << version << endl;
if (OEMCrypto_SupportsUsageTable()) {
cout << " OEMCrypto supports usage tables." << endl;
cout << " OEMCrypto supports usage tables" << endl;
} else {
cout << " OEMCrypto does not support usage tables." << endl;
cout << " OEMCrypto does not support usage tables" << endl;
}
if (version >= 15) {
cout << " Resource Rating Tier: "
<< OEMCrypto_ResourceRatingTier() << endl;
const uint32_t tier = OEMCrypto_ResourceRatingTier();
cout << " Resource Rating Tier: " << tier << endl;
const char* build_info = OEMCrypto_BuildInformation();
ASSERT_NE(nullptr, build_info);
ASSERT_TRUE(strnlen(build_info, 256) <= 256)
@@ -344,8 +354,7 @@ TEST_F(OEMCryptoClientTest, MaxSessionsOpenCloseAPI10) {
ASSERT_GE(max_sessions, required_number);
// 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.
size_t max_sessions_with_pad =
max(max_sessions * 19 / 20, required_number);
size_t max_sessions_with_pad = max(max_sessions * 19 / 20, required_number);
vector<OEMCrypto_SESSION> sessions;
// Limit the number of sessions for testing.
const size_t kMaxNumberOfSessionsForTesting = 0x100u;
@@ -631,7 +640,7 @@ TEST_F(OEMCryptoKeyboxTest, GenerateDerivedKeysFromKeyboxLargeBuffer) {
ASSERT_EQ(OEMCrypto_SUCCESS,
OEMCrypto_GenerateDerivedKeys(
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.
@@ -660,7 +669,6 @@ TEST_F(OEMCryptoProv30Test, GetDeviceId) {
ASSERT_EQ(OEMCrypto_SUCCESS, sts);
}
// The OEM certificate must be valid.
TEST_F(OEMCryptoProv30Test, CertValidAPI15) {
ASSERT_EQ(OEMCrypto_SUCCESS, OEMCrypto_IsKeyboxOrOEMCertValid());
@@ -866,6 +874,25 @@ TEST_P(OEMCryptoLicenseTest, LoadKeyNoNonce) {
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.
TEST_P(OEMCryptoLicenseTest, LoadKeyWithNonce) {
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
// keys. This requirement was added in 15.2, so it frequently fails on
// 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(license_messages_.SignAndVerifyRequest());
ASSERT_NO_FATAL_FAILURE(license_messages_.CreateDefaultResponse());
@@ -1319,7 +1348,7 @@ TEST_P(OEMCryptoLicenseTestRangeAPI, LoadKeys) {
INSTANTIATE_TEST_CASE_P(TestAll, OEMCryptoLicenseTestRangeAPI,
Range<uint32_t>(10, kCurrentAPI + 2));
TEST_P(OEMCryptoLicenseTest, LoadKeysBadSignature) {
TEST_P(OEMCryptoLicenseTest, LoadKeysBadSignatureAPI16) {
ASSERT_NO_FATAL_FAILURE(session_.GenerateNonce());
ASSERT_NO_FATAL_FAILURE(license_messages_.SignAndVerifyRequest());
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.
// 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(license_messages_.SignAndVerifyRequest());
ASSERT_NO_FATAL_FAILURE(license_messages_.CreateDefaultResponse());
@@ -1684,22 +1715,28 @@ class OEMCryptoSessionTestDecryptWithHDCP : public OEMCryptoSessionTests,
ASSERT_NO_FATAL_FAILURE(license_messages.EncryptAndSignResponse());
ASSERT_EQ(OEMCrypto_SUCCESS, license_messages.LoadResponse());
if (version > maximum) {
ASSERT_NO_FATAL_FAILURE(
s.TestDecryptCTR(true, OEMCrypto_ERROR_INSUFFICIENT_HDCP));
} else if (version > current) {
if (version > current) {
if (global_features.api_version >= 16) {
// Can provide either OEMCrypto_WARNING_MIXED_OUTPUT_PROTECTION or
// OEMCrypto_ERROR_INSUFFICIENT_HDCP. TestDecryptCTR allows either to be
// reported if OEMCrypto_WARNING_MIXED_OUTPUT_PROTECTION is expected.
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 {
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 {
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());
}
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;
ASSERT_NO_FATAL_FAILURE(s.open());
if (global_features.provisioning_method == OEMCrypto_OEMCertificate) {
@@ -2751,7 +2789,8 @@ TEST_F(OEMCryptoLoadsCertificate, CertificateProvisionBadRange5Prov30_API16) {
// Test that RewrapDeviceRSAKey verifies the message signature.
// TODO(b/144186970): This test should also run on Prov 3.0 devices.
TEST_F(OEMCryptoLoadsCertificate, CertificateProvisionBadSignatureKeyboxTest) {
TEST_F(OEMCryptoLoadsCertificate,
CertificateProvisionBadSignatureKeyboxTestAPI16) {
Session s;
ProvisioningRoundTrip provisioning_messages(&s, encoded_rsa_key_);
provisioning_messages.PrepareSession(keybox_);
@@ -2793,7 +2832,8 @@ TEST_F(OEMCryptoLoadsCertificate, CertificateProvisionBadRSAKey) {
// Test that RewrapDeviceRSAKey verifies the RSA key is valid.
// TODO(b/144186970): This test should also run on Prov 3.0 devices.
TEST_F(OEMCryptoLoadsCertificate, CertificateProvisionBadRSAKeyKeyboxTest) {
TEST_F(OEMCryptoLoadsCertificate,
CertificateProvisionBadRSAKeyKeyboxTestAPI16) {
Session s;
ProvisioningRoundTrip provisioning_messages(&s, encoded_rsa_key_);
provisioning_messages.PrepareSession(keybox_);
@@ -2834,8 +2874,9 @@ TEST_F(OEMCryptoLoadsCertificate, LoadWrappedRSAKey) {
CreateWrappedRSAKey();
Session s;
ASSERT_NO_FATAL_FAILURE(s.open());
sts = OEMCrypto_LoadDeviceRSAKey(s.session_id(), wrapped_rsa_key_.data(),
wrapped_rsa_key_.size());
sts = OEMCrypto_LoadDRMPrivateKey(s.session_id(), OEMCrypto_RSA_Private_Key,
wrapped_rsa_key_.data(),
wrapped_rsa_key_.size());
ASSERT_EQ(OEMCrypto_SUCCESS, sts);
}
@@ -2843,12 +2884,12 @@ TEST_F(OEMCryptoLoadsCertificate, LoadWrappedRSAKey) {
TEST_F(OEMCryptoLoadsCertificate, TestLargeRSAKey3072) {
encoded_rsa_key_.assign(kTestRSAPKCS8PrivateKeyInfo3_3072,
kTestRSAPKCS8PrivateKeyInfo3_3072 +
sizeof(kTestRSAPKCS8PrivateKeyInfo3_3072));
sizeof(kTestRSAPKCS8PrivateKeyInfo3_3072));
CreateWrappedRSAKey();
Session s;
ASSERT_NO_FATAL_FAILURE(s.open());
ASSERT_NO_FATAL_FAILURE(s.PreparePublicKey(encoded_rsa_key_.data(),
encoded_rsa_key_.size()));
ASSERT_NO_FATAL_FAILURE(
s.PreparePublicKey(encoded_rsa_key_.data(), encoded_rsa_key_.size()));
ASSERT_NO_FATAL_FAILURE(s.InstallRSASessionTestKey(wrapped_rsa_key_));
LicenseRoundTrip license_messages(&s);
@@ -2869,8 +2910,8 @@ TEST_F(OEMCryptoLoadsCertificate, TestCarmichaelRSAKey) {
CreateWrappedRSAKey();
Session s;
ASSERT_NO_FATAL_FAILURE(s.open());
ASSERT_NO_FATAL_FAILURE(s.PreparePublicKey(encoded_rsa_key_.data(),
encoded_rsa_key_.size()));
ASSERT_NO_FATAL_FAILURE(
s.PreparePublicKey(encoded_rsa_key_.data(), encoded_rsa_key_.size()));
ASSERT_NO_FATAL_FAILURE(s.InstallRSASessionTestKey(wrapped_rsa_key_));
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
// until after s2 uses its key.
ASSERT_NO_FATAL_FAILURE(s1.open());
ASSERT_NO_FATAL_FAILURE(s1.PreparePublicKey(encoded_rsa_key_.data(),
encoded_rsa_key_.size()));
ASSERT_NO_FATAL_FAILURE(
s1.PreparePublicKey(encoded_rsa_key_.data(), encoded_rsa_key_.size()));
ASSERT_EQ(OEMCrypto_SUCCESS,
OEMCrypto_LoadDeviceRSAKey(s1.session_id(), wrapped_rsa_key_.data(),
wrapped_rsa_key_.size()));
OEMCrypto_LoadDRMPrivateKey(
s1.session_id(), OEMCrypto_RSA_Private_Key,
wrapped_rsa_key_.data(), wrapped_rsa_key_.size()));
Session s2; // Session s2 uses a different rsa key.
encoded_rsa_key_.assign(kTestRSAPKCS8PrivateKeyInfo4_2048,
@@ -2900,8 +2942,8 @@ TEST_F(OEMCryptoLoadsCertificate, TestMultipleRSAKeys) {
sizeof(kTestRSAPKCS8PrivateKeyInfo4_2048));
CreateWrappedRSAKey();
ASSERT_NO_FATAL_FAILURE(s2.open());
ASSERT_NO_FATAL_FAILURE(s2.PreparePublicKey(encoded_rsa_key_.data(),
encoded_rsa_key_.size()));
ASSERT_NO_FATAL_FAILURE(
s2.PreparePublicKey(encoded_rsa_key_.data(), encoded_rsa_key_.size()));
ASSERT_NO_FATAL_FAILURE(s2.InstallRSASessionTestKey(wrapped_rsa_key_));
LicenseRoundTrip license_messages2(&s2);
ASSERT_NO_FATAL_FAILURE(s2.GenerateNonce());
@@ -2975,8 +3017,9 @@ TEST_F(OEMCryptoLoadsCertificate, RSAPerformance) {
while (clock.now() - start_time < kTestDuration) {
Session s;
ASSERT_NO_FATAL_FAILURE(s.open());
sts = OEMCrypto_LoadDeviceRSAKey(s.session_id(), wrapped_rsa_key_.data(),
wrapped_rsa_key_.size());
sts = OEMCrypto_LoadDRMPrivateKey(s.session_id(), OEMCrypto_RSA_Private_Key,
wrapped_rsa_key_.data(),
wrapped_rsa_key_.size());
ASSERT_EQ(OEMCrypto_SUCCESS, sts);
const size_t size = 50;
vector<uint8_t> licenseRequest(size);
@@ -3002,12 +3045,13 @@ TEST_F(OEMCryptoLoadsCertificate, RSAPerformance) {
Session s;
ASSERT_NO_FATAL_FAILURE(s.open());
ASSERT_EQ(OEMCrypto_SUCCESS,
OEMCrypto_LoadDeviceRSAKey(s.session_id(), wrapped_rsa_key_.data(),
wrapped_rsa_key_.size()));
OEMCrypto_LoadDRMPrivateKey(
s.session_id(), OEMCrypto_RSA_Private_Key,
wrapped_rsa_key_.data(), wrapped_rsa_key_.size()));
vector<uint8_t> session_key;
vector<uint8_t> enc_session_key;
ASSERT_NO_FATAL_FAILURE(s.PreparePublicKey(encoded_rsa_key_.data(),
encoded_rsa_key_.size()));
ASSERT_NO_FATAL_FAILURE(
s.PreparePublicKey(encoded_rsa_key_.data(), encoded_rsa_key_.size()));
ASSERT_TRUE(s.GenerateRSASessionKey(&session_key, &enc_session_key));
vector<uint8_t> mac_context;
vector<uint8_t> enc_context;
@@ -3035,10 +3079,9 @@ TEST_F(OEMCryptoLoadsCertificate, RSAPerformance) {
while (clock.now() - start_time < kTestDuration) {
ASSERT_EQ(OEMCrypto_SUCCESS,
OEMCrypto_DeriveKeysFromSessionKey(
s.session_id(),
enc_session_key.data(), enc_session_key.size(),
mac_context.data(), mac_context.size(),
enc_context.data(), enc_context.size()));
s.session_id(), enc_session_key.data(),
enc_session_key.size(), mac_context.data(),
mac_context.size(), enc_context.data(), enc_context.size()));
count++;
}
delta_time = clock.now() - start_time;
@@ -3068,9 +3111,8 @@ TEST_F(OEMCryptoUsesCertificate, GenerateDerivedKeysLargeBuffer) {
}
ASSERT_EQ(OEMCrypto_SUCCESS,
OEMCrypto_DeriveKeysFromSessionKey(
session_.session_id(),
enc_session_key.data(), enc_session_key.size(),
mac_context.data(), mac_context.size(),
session_.session_id(), enc_session_key.data(),
enc_session_key.size(), mac_context.data(), mac_context.size(),
enc_context.data(), enc_context.size()));
}
@@ -3081,8 +3123,9 @@ class OEMCryptoLoadsCertificateAlternates : public OEMCryptoLoadsCertificate {
OEMCryptoResult sts;
Session s;
ASSERT_NO_FATAL_FAILURE(s.open());
sts = OEMCrypto_LoadDeviceRSAKey(s.session_id(), wrapped_rsa_key_.data(),
wrapped_rsa_key_.size());
sts = OEMCrypto_LoadDRMPrivateKey(s.session_id(), OEMCrypto_RSA_Private_Key,
wrapped_rsa_key_.data(),
wrapped_rsa_key_.size());
ASSERT_EQ(OEMCrypto_SUCCESS, sts);
// Sign a Message
@@ -3090,19 +3133,16 @@ class OEMCryptoLoadsCertificateAlternates : public OEMCryptoLoadsCertificate {
GetRandBytes(licenseRequest.data(), licenseRequest.size());
size_t signature_length = 256;
vector<uint8_t> signature(signature_length);
sts = OEMCrypto_GenerateRSASignature(s.session_id(), licenseRequest.data(),
licenseRequest.size(),
signature.data(), &signature_length,
scheme);
sts = OEMCrypto_GenerateRSASignature(
s.session_id(), licenseRequest.data(), licenseRequest.size(),
signature.data(), &signature_length, scheme);
// Allow OEMCrypto to request a full buffer.
if (sts == OEMCrypto_ERROR_SHORT_BUFFER) {
ASSERT_NE(static_cast<size_t>(0), signature_length);
signature.assign(signature_length, 0);
sts = OEMCrypto_GenerateRSASignature(s.session_id(),
licenseRequest.data(),
licenseRequest.size(),
signature.data(), &signature_length,
scheme);
sts = OEMCrypto_GenerateRSASignature(
s.session_id(), licenseRequest.data(), licenseRequest.size(),
signature.data(), &signature_length, scheme);
}
EXPECT_NE(OEMCrypto_SUCCESS, sts)
@@ -3116,8 +3156,9 @@ class OEMCryptoLoadsCertificateAlternates : public OEMCryptoLoadsCertificate {
OEMCryptoResult sts;
Session s;
ASSERT_NO_FATAL_FAILURE(s.open());
sts = OEMCrypto_LoadDeviceRSAKey(s.session_id(), wrapped_rsa_key_.data(),
wrapped_rsa_key_.size());
sts = OEMCrypto_LoadDRMPrivateKey(s.session_id(), OEMCrypto_RSA_Private_Key,
wrapped_rsa_key_.data(),
wrapped_rsa_key_.size());
ASSERT_EQ(OEMCrypto_SUCCESS, sts);
vector<uint8_t> licenseRequest(size);
@@ -3137,8 +3178,8 @@ class OEMCryptoLoadsCertificateAlternates : public OEMCryptoLoadsCertificate {
ASSERT_EQ(OEMCrypto_SUCCESS, sts)
<< "Failed to sign with padding scheme=" << (int)scheme
<< ", size=" << (int)size;
ASSERT_NO_FATAL_FAILURE(s.PreparePublicKey(encoded_rsa_key_.data(),
encoded_rsa_key_.size()));
ASSERT_NO_FATAL_FAILURE(
s.PreparePublicKey(encoded_rsa_key_.data(), encoded_rsa_key_.size()));
ASSERT_NO_FATAL_FAILURE(s.VerifyRSASignature(licenseRequest, signature,
signature_length, scheme));
delete[] signature;
@@ -3148,14 +3189,15 @@ class OEMCryptoLoadsCertificateAlternates : public OEMCryptoLoadsCertificate {
OEMCryptoResult sts;
Session s;
ASSERT_NO_FATAL_FAILURE(s.open());
sts = OEMCrypto_LoadDeviceRSAKey(s.session_id(), wrapped_rsa_key_.data(),
wrapped_rsa_key_.size());
sts = OEMCrypto_LoadDRMPrivateKey(s.session_id(), OEMCrypto_RSA_Private_Key,
wrapped_rsa_key_.data(),
wrapped_rsa_key_.size());
ASSERT_EQ(OEMCrypto_SUCCESS, sts);
s.GenerateNonce();
vector<uint8_t> session_key;
vector<uint8_t> enc_session_key;
ASSERT_NO_FATAL_FAILURE(s.PreparePublicKey(encoded_rsa_key_.data(),
encoded_rsa_key_.size()));
ASSERT_NO_FATAL_FAILURE(
s.PreparePublicKey(encoded_rsa_key_.data(), encoded_rsa_key_.size()));
ASSERT_TRUE(s.GenerateRSASessionKey(&session_key, &enc_session_key));
vector<uint8_t> mac_context;
vector<uint8_t> enc_context;
@@ -3359,7 +3401,7 @@ class OEMCryptoCastReceiverTest : public OEMCryptoLoadsCertificateAlternates {
"01" // 1
"05" // null object. (field=parameter?)
"00" // size of null object
);
);
vector<uint8_t> pkey = wvcdm::a2b_hex("020100"); // integer, version = 0.
pkey = concat(pkey, field_n);
@@ -3383,8 +3425,9 @@ class OEMCryptoCastReceiverTest : public OEMCryptoLoadsCertificateAlternates {
OEMCryptoResult sts;
Session s;
ASSERT_NO_FATAL_FAILURE(s.open());
sts = OEMCrypto_LoadDeviceRSAKey(s.session_id(), wrapped_rsa_key_.data(),
wrapped_rsa_key_.size());
sts = OEMCrypto_LoadDRMPrivateKey(s.session_id(), OEMCrypto_RSA_Private_Key,
wrapped_rsa_key_.data(),
wrapped_rsa_key_.size());
ASSERT_EQ(OEMCrypto_SUCCESS, sts);
// 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)
<< "Failed to sign with padding scheme=" << (int)scheme
<< ", size=" << (int)message.size();
ASSERT_NO_FATAL_FAILURE(s.PreparePublicKey(encoded_rsa_key_.data(),
encoded_rsa_key_.size()));
ASSERT_NO_FATAL_FAILURE(
s.PreparePublicKey(encoded_rsa_key_.data(), encoded_rsa_key_.size()));
// Verify that the signature matches the official test vector.
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
// to test OEMCrypto, but it does verify that this test is valid.
ASSERT_NO_FATAL_FAILURE(s.VerifyRSASignature(
digest, signature.data(), signature_length, scheme));
ASSERT_NO_FATAL_FAILURE(s.VerifyRSASignature(digest, signature.data(),
signature_length, scheme));
ASSERT_NO_FATAL_FAILURE(s.VerifyRSASignature(
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.
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
@@ -4215,10 +4259,9 @@ class OEMCryptoGenericCryptoTest : public OEMCryptoRefreshTest {
OEMCrypto_CipherMode_CTR);
ASSERT_EQ(OEMCrypto_SUCCESS, sts);
vector<uint8_t> encrypted(buffer_length);
sts =
OEMCrypto_Generic_Encrypt(session_.session_id(), clear_buffer_.data(),
buffer_length, iv_, algorithm,
encrypted.data());
sts = OEMCrypto_Generic_Encrypt(session_.session_id(), clear_buffer_.data(),
buffer_length, iv_, algorithm,
encrypted.data());
EXPECT_NE(OEMCrypto_SUCCESS, sts);
expected_encrypted.resize(buffer_length);
EXPECT_NE(encrypted, expected_encrypted);
@@ -4237,10 +4280,9 @@ class OEMCryptoGenericCryptoTest : public OEMCryptoRefreshTest {
OEMCrypto_CipherMode_CTR);
ASSERT_EQ(OEMCrypto_SUCCESS, sts);
vector<uint8_t> resultant(encrypted.size());
sts =
OEMCrypto_Generic_Decrypt(session_.session_id(), encrypted.data(),
buffer_length, iv_, algorithm,
resultant.data());
sts = OEMCrypto_Generic_Decrypt(session_.session_id(), encrypted.data(),
buffer_length, iv_, algorithm,
resultant.data());
EXPECT_NE(OEMCrypto_SUCCESS, sts);
EXPECT_NE(clear_buffer_, resultant);
}
@@ -4310,11 +4352,11 @@ TEST_P(OEMCryptoGenericCryptoTest, GenericKeyEncrypt) {
session_.license().keys[key_index].key_id_length,
OEMCrypto_CipherMode_CTR));
vector<uint8_t> encrypted(clear_buffer_.size());
ASSERT_EQ(OEMCrypto_SUCCESS,
OEMCrypto_Generic_Encrypt(
session_.session_id(), clear_buffer_.data(),
clear_buffer_.size(), iv_, OEMCrypto_AES_CBC_128_NO_PADDING,
encrypted.data()));
ASSERT_EQ(
OEMCrypto_SUCCESS,
OEMCrypto_Generic_Encrypt(
session_.session_id(), clear_buffer_.data(), clear_buffer_.size(),
iv_, OEMCrypto_AES_CBC_128_NO_PADDING, encrypted.data()));
ASSERT_EQ(expected_encrypted, encrypted);
}
@@ -4346,8 +4388,8 @@ TEST_P(OEMCryptoGenericCryptoTest, GenericKeyEncryptSameBufferAPI12) {
vector<uint8_t> buffer = clear_buffer_;
ASSERT_EQ(OEMCrypto_SUCCESS,
OEMCrypto_Generic_Encrypt(
session_.session_id(), buffer.data(), buffer.size(),
iv_, OEMCrypto_AES_CBC_128_NO_PADDING, buffer.data()));
session_.session_id(), buffer.data(), buffer.size(), iv_,
OEMCrypto_AES_CBC_128_NO_PADDING, buffer.data()));
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_length,
OEMCrypto_CipherMode_CTR));
ASSERT_EQ(OEMCrypto_SUCCESS,
OEMCrypto_Generic_Verify(
session_.session_id(), clear_buffer_.data(),
clear_buffer_.size(), OEMCrypto_HMAC_SHA256, signature.data(),
signature.size()));
ASSERT_EQ(OEMCrypto_SUCCESS, OEMCrypto_Generic_Verify(
session_.session_id(), clear_buffer_.data(),
clear_buffer_.size(), OEMCrypto_HMAC_SHA256,
signature.data(), signature.size()));
}
// 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,
OEMCrypto_CipherMode_CTR));
vector<uint8_t> encrypted(clear_buffer_.size());
ASSERT_EQ(OEMCrypto_SUCCESS,
OEMCrypto_Generic_Encrypt(
session_.session_id(), clear_buffer_.data(),
clear_buffer_.size(), iv_, OEMCrypto_AES_CBC_128_NO_PADDING,
encrypted.data()));
ASSERT_EQ(
OEMCrypto_SUCCESS,
OEMCrypto_Generic_Encrypt(
session_.session_id(), clear_buffer_.data(), clear_buffer_.size(),
iv_, OEMCrypto_AES_CBC_128_NO_PADDING, encrypted.data()));
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_length,
OEMCrypto_CipherMode_CTR));
ASSERT_EQ(OEMCrypto_SUCCESS,
OEMCrypto_Generic_Verify(
session_.session_id(), clear_buffer_.data(),
clear_buffer_.size(), OEMCrypto_HMAC_SHA256, signature.data(),
signature.size()));
ASSERT_EQ(OEMCrypto_SUCCESS, OEMCrypto_Generic_Verify(
session_.session_id(), clear_buffer_.data(),
clear_buffer_.size(), OEMCrypto_HMAC_SHA256,
signature.data(), signature.size()));
}
// 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_length,
OEMCrypto_CipherMode_CTR));
ASSERT_EQ(OEMCrypto_SUCCESS,
OEMCrypto_Generic_Encrypt(
session_.session_id(), clear_buffer_.data(),
clear_buffer_.size(), iv_, OEMCrypto_AES_CBC_128_NO_PADDING,
encrypted.data()));
ASSERT_EQ(
OEMCrypto_SUCCESS,
OEMCrypto_Generic_Encrypt(
session_.session_id(), clear_buffer_.data(), clear_buffer_.size(),
iv_, OEMCrypto_AES_CBC_128_NO_PADDING, encrypted.data()));
ASSERT_EQ(expected_encrypted, encrypted);
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_length,
OEMCrypto_CipherMode_CTR));
ASSERT_EQ(OEMCrypto_SUCCESS,
OEMCrypto_Generic_Verify(
session_.session_id(), clear_buffer_.data(),
clear_buffer_.size(), OEMCrypto_HMAC_SHA256, signature.data(),
signature.size()));
ASSERT_EQ(OEMCrypto_SUCCESS, OEMCrypto_Generic_Verify(
session_.session_id(), clear_buffer_.data(),
clear_buffer_.size(), OEMCrypto_HMAC_SHA256,
signature.data(), signature.size()));
wvcdm::TestSleep::Sleep(kLongSleep + kShortSleep); // Should be expired key.
OEMCryptoResult status = OEMCrypto_Generic_Verify(
@@ -4827,7 +4866,8 @@ class LicenseWithUsageEntry {
generic_crypto_(false),
time_license_received_(0),
time_first_decrypt_(0),
time_last_decrypt_(0) {
time_last_decrypt_(0),
active_(true) {
license_messages_.set_pst(pst);
}
@@ -4888,6 +4928,15 @@ class LicenseWithUsageEntry {
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) {
ASSERT_NO_FATAL_FAILURE(session_.GenerateReport(pst()));
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
// that is sent to the server will be signed by the right mac keys.
RenewalRoundTrip renewal_messages(&license_messages_);
renewal_messages.set_is_release(true);
renewal_messages.set_is_release(!active_);
ASSERT_NO_FATAL_FAILURE(renewal_messages.SignAndVerifyRequest());
}
@@ -4921,6 +4970,7 @@ class LicenseWithUsageEntry {
int64_t time_license_received_;
int64_t time_first_decrypt_;
int64_t time_last_decrypt_;
bool active_;
};
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(entry.GenerateVerifyReport(kActive));
// 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_));
// It should report as inactive.
ASSERT_NO_FATAL_FAILURE(entry.GenerateVerifyReport(kInactiveUsed));
@@ -4982,7 +5032,7 @@ TEST_P(OEMCryptoUsageTableTest, OnlineLicense) {
entry.TestDecryptCTR(false, OEMCrypto_ERROR_UNKNOWN_FAILURE));
// We could call DeactivateUsageEntry multiple times. The state should not
// change.
ASSERT_NO_FATAL_FAILURE(s.DeactivateUsageEntry(entry.pst()));
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));
@@ -4999,7 +5049,7 @@ TEST_P(OEMCryptoUsageTableTest, OnlineLicenseUnused) {
// No decrypt. We do not use this license.
ASSERT_NO_FATAL_FAILURE(entry.GenerateVerifyReport(kUnused));
// 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_));
// It should report as inactive.
ASSERT_NO_FATAL_FAILURE(entry.GenerateVerifyReport(kInactiveUnused));
@@ -5008,7 +5058,7 @@ TEST_P(OEMCryptoUsageTableTest, OnlineLicenseUnused) {
entry.TestDecryptCTR(false, OEMCrypto_ERROR_UNKNOWN_FAILURE));
// We could call DeactivateUsageEntry multiple times. The state should not
// change.
ASSERT_NO_FATAL_FAILURE(s.DeactivateUsageEntry(entry.pst()));
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(kInactiveUnused));
@@ -5031,7 +5081,7 @@ TEST_P(OEMCryptoUsageTableTest, ForbidReportWithNoUpdate) {
// Now it's OK.
ASSERT_NO_FATAL_FAILURE(entry.GenerateVerifyReport(kActive));
// 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.
ASSERT_NO_FATAL_FAILURE(
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));
}
// 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_P(OEMCryptoUsageTableTest, GenericCryptoEncrypt) {
LicenseWithUsageEntry entry;
@@ -5201,7 +5277,7 @@ TEST_P(OEMCryptoUsageTableTest, GenericCryptoEncrypt) {
EXPECT_EQ(expected_encrypted, encrypted);
ASSERT_NO_FATAL_FAILURE(s.UpdateUsageEntry(&encrypted_usage_header_));
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(entry.GenerateVerifyReport(kInactiveUsed));
encrypted.assign(clear_buffer_.size(), 0);
@@ -5236,7 +5312,7 @@ TEST_P(OEMCryptoUsageTableTest, GenericCryptoDecrypt) {
EXPECT_EQ(clear_buffer_, resultant);
ASSERT_NO_FATAL_FAILURE(s.UpdateUsageEntry(&encrypted_usage_header_));
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(entry.GenerateVerifyReport(kInactiveUsed));
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(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(entry.GenerateVerifyReport(kInactiveUsed));
signature.assign(SHA256_DIGEST_LENGTH, 0);
@@ -5314,7 +5390,7 @@ TEST_P(OEMCryptoUsageTableTest, GenericCryptoVerify) {
ASSERT_EQ(OEMCrypto_SUCCESS, sts);
ASSERT_NO_FATAL_FAILURE(s.UpdateUsageEntry(&encrypted_usage_header_));
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(entry.GenerateVerifyReport(kInactiveUsed));
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.TestDecryptCTR()); // Should be able to decrypt.
ASSERT_NO_FATAL_FAILURE(
s.DeactivateUsageEntry(entry.pst())); // Then deactivate.
ASSERT_NO_FATAL_FAILURE(entry.DeactivateUsageEntry()); // Then deactivate.
// After deactivate, should not be able to decrypt.
ASSERT_NO_FATAL_FAILURE(
entry.TestDecryptCTR(false, OEMCrypto_ERROR_UNKNOWN_FAILURE));
@@ -5531,7 +5606,7 @@ TEST_P(OEMCryptoUsageTableTest, DeactivateOfflineLicense) {
ASSERT_NO_FATAL_FAILURE(entry.GenerateVerifyReport(kInactiveUsed));
// We could call DeactivateUsageEntry multiple times. The state should not
// 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(entry.GenerateVerifyReport(kInactiveUsed));
}
@@ -5545,8 +5620,7 @@ TEST_P(OEMCryptoUsageTableTest, DeactivateOfflineLicenseUnused) {
Session& s = entry.session();
// No Decrypt. This license is unused.
ASSERT_NO_FATAL_FAILURE(entry.OpenAndReload(this));
ASSERT_NO_FATAL_FAILURE(
s.DeactivateUsageEntry(entry.pst())); // Then deactivate.
ASSERT_NO_FATAL_FAILURE(entry.DeactivateUsageEntry()); // Then deactivate.
// After deactivate, should not be able to decrypt.
ASSERT_NO_FATAL_FAILURE(
entry.TestDecryptCTR(false, OEMCrypto_ERROR_UNKNOWN_FAILURE));
@@ -5571,11 +5645,30 @@ TEST_P(OEMCryptoUsageTableTest, DeactivateOfflineLicenseUnused) {
ASSERT_NO_FATAL_FAILURE(entry.GenerateVerifyReport(kInactiveUnused));
// We could call DeactivateUsageEntry multiple times. The state should not
// 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(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_P(OEMCryptoUsageTableTest, UpdateFailsWithNullPtr) {
LicenseWithUsageEntry entry;
@@ -5638,8 +5731,7 @@ class OEMCryptoUsageTableDefragTest : public OEMCryptoUsageTableTest {
ASSERT_LT(0u, header_buffer_length);
encrypted_usage_header_.resize(header_buffer_length);
sts = OEMCrypto_ShrinkUsageTableHeader(
new_size, encrypted_usage_header_.data(),
&header_buffer_length);
new_size, encrypted_usage_header_.data(), &header_buffer_length);
ASSERT_EQ(expected_result, sts);
}
};
@@ -5885,6 +5977,7 @@ TEST_P(OEMCryptoUsageTableTest, ReloadUsageTableWithSkew) {
vector<uint8_t> old_usage_header_2_ = 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_entry_1 = s.encrypted_usage_entry();
ASSERT_NO_FATAL_FAILURE(s.UpdateUsageEntry(&encrypted_usage_header_));
ASSERT_NO_FATAL_FAILURE(s.close());
@@ -5895,26 +5988,23 @@ TEST_P(OEMCryptoUsageTableTest, ReloadUsageTableWithSkew) {
nullptr, old_usage_header_2_.size()));
ASSERT_NO_FATAL_FAILURE(s.open());
// Cannot load an entry if header didn't load.
ASSERT_EQ(
OEMCrypto_ERROR_UNKNOWN_FAILURE,
OEMCrypto_LoadUsageEntry(s.session_id(), s.usage_entry_number(),
s.encrypted_usage_entry().data(),
s.encrypted_usage_entry().size()));
ASSERT_EQ(OEMCrypto_ERROR_UNKNOWN_FAILURE,
OEMCrypto_LoadUsageEntry(s.session_id(), s.usage_entry_number(),
s.encrypted_usage_entry().data(),
s.encrypted_usage_entry().size()));
ASSERT_NO_FATAL_FAILURE(s.close());
// Modified header generates error.
vector<uint8_t> bad_header = encrypted_usage_header_;
bad_header[3] ^= 42;
ASSERT_NE(OEMCrypto_SUCCESS,
OEMCrypto_LoadUsageTableHeader(bad_header.data(),
bad_header.size()));
ASSERT_NE(OEMCrypto_SUCCESS, OEMCrypto_LoadUsageTableHeader(
bad_header.data(), bad_header.size()));
ASSERT_NO_FATAL_FAILURE(s.open());
// Cannot load an entry if header didn't load.
ASSERT_EQ(
OEMCrypto_ERROR_UNKNOWN_FAILURE,
OEMCrypto_LoadUsageEntry(s.session_id(), s.usage_entry_number(),
s.encrypted_usage_entry().data(),
s.encrypted_usage_entry().size()));
ASSERT_EQ(OEMCrypto_ERROR_UNKNOWN_FAILURE,
OEMCrypto_LoadUsageEntry(s.session_id(), s.usage_entry_number(),
s.encrypted_usage_entry().data(),
s.encrypted_usage_entry().size()));
ASSERT_NO_FATAL_FAILURE(s.close());
// Old by 2 generation numbers is error.
@@ -5923,24 +6013,22 @@ TEST_P(OEMCryptoUsageTableTest, ReloadUsageTableWithSkew) {
old_usage_header_2_.size()));
ASSERT_NO_FATAL_FAILURE(s.open());
// Cannot load an entry if header didn't load.
ASSERT_NE(
OEMCrypto_SUCCESS,
OEMCrypto_LoadUsageEntry(s.session_id(), s.usage_entry_number(),
s.encrypted_usage_entry().data(),
s.encrypted_usage_entry().size()));
ASSERT_NE(OEMCrypto_SUCCESS,
OEMCrypto_LoadUsageEntry(s.session_id(), s.usage_entry_number(),
s.encrypted_usage_entry().data(),
s.encrypted_usage_entry().size()));
ASSERT_NO_FATAL_FAILURE(s.close());
// Old by 1 generation numbers is just warning.
ASSERT_EQ(OEMCrypto_WARNING_GENERATION_SKEW,
OEMCrypto_LoadUsageTableHeader(old_usage_header_1_.data(),
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_EQ(
OEMCrypto_WARNING_GENERATION_SKEW,
OEMCrypto_LoadUsageEntry(s.session_id(), s.usage_entry_number(),
s.encrypted_usage_entry().data(),
s.encrypted_usage_entry().size()));
ASSERT_EQ(OEMCrypto_SUCCESS,
OEMCrypto_LoadUsageEntry(s.session_id(), s.usage_entry_number(),
old_usage_entry_1.data(),
old_usage_entry_1.size()));
ASSERT_NO_FATAL_FAILURE(InstallTestRSAKey(&s));
ASSERT_EQ(OEMCrypto_SUCCESS, entry.license_messages().LoadResponse());
}
@@ -5952,8 +6040,8 @@ TEST_P(OEMCryptoUsageTableTest, GenerateReportWrongPST) {
entry.MakeAndLoadOnline(this);
Session& s = entry.session();
ASSERT_NO_FATAL_FAILURE(s.UpdateUsageEntry(&encrypted_usage_header_));
ASSERT_NO_FATAL_FAILURE(s.GenerateReport("wrong_pst",
OEMCrypto_ERROR_WRONG_PST));
ASSERT_NO_FATAL_FAILURE(
s.GenerateReport("wrong_pst", OEMCrypto_ERROR_WRONG_PST));
}
// Test usage table timing.
@@ -5992,7 +6080,7 @@ TEST_P(OEMCryptoUsageTableTest, TimingTest) {
ASSERT_NO_FATAL_FAILURE(entry2.TestDecryptCTR());
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(s2.UpdateUsageEntry(&encrypted_usage_header_));
ASSERT_NO_FATAL_FAILURE(s1.close());
@@ -6015,17 +6103,10 @@ TEST_P(OEMCryptoUsageTableTest, TimingTest) {
ASSERT_NO_FATAL_FAILURE(s2.close());
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(entry2.ReloadUsageEntry());
ASSERT_NO_FATAL_FAILURE(entry3.ReloadUsageEntry());
// 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);
ASSERT_NO_FATAL_FAILURE(entry2.OpenAndReload(this));
ASSERT_NO_FATAL_FAILURE(entry3.OpenAndReload(this));
wvcdm::TestSleep::Sleep(kLongSleep);
ASSERT_NO_FATAL_FAILURE(s1.UpdateUsageEntry(&encrypted_usage_header_));
ASSERT_NO_FATAL_FAILURE(entry1.GenerateVerifyReport(kInactiveUsed));
@@ -6105,7 +6186,7 @@ TEST_P(OEMCryptoUsageTableTest, VerifyUsageTimes) {
// |<------------------------------------| = seconds_since_license_received
ASSERT_NO_FATAL_FAILURE(s.UpdateUsageEntry(&encrypted_usage_header_));
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(entry.GenerateVerifyReport(kInactiveUsed));
ASSERT_NO_FATAL_FAILURE(
@@ -6195,8 +6276,8 @@ TEST_P(OEMCryptoUsageTableTestWallClock, TimeRollbackPrevention) {
// Rollback the wall clock time.
cout << "Rolling the system time back..." << endl;
ASSERT_NO_FATAL_FAILURE(SetWallTimeDelta(
-static_cast<int64_t>(kLongDuration) * 10));
ASSERT_NO_FATAL_FAILURE(
SetWallTimeDelta(-static_cast<int64_t>(kLongDuration) * 10));
// Try to playback again.
ASSERT_NO_FATAL_FAILURE(entry.OpenAndReload(this));
@@ -6210,8 +6291,8 @@ TEST_P(OEMCryptoUsageTableTestWallClock, TimeRollbackPrevention) {
// not report negative times.
const auto test_duration = third_decrypt_monotonic - first_decrypt_monotonic;
cout << "Rolling the system time forward to the absolute time..." << endl;
ASSERT_NO_FATAL_FAILURE(SetWallTimeDelta(
test_duration / std::chrono::seconds(1)));
ASSERT_NO_FATAL_FAILURE(
SetWallTimeDelta(test_duration / std::chrono::seconds(1)));
// Need to update time created since the verification checks the time of PST
// report creation.
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.TestDecryptCTR()); // Should be able to decrypt.
ASSERT_NO_FATAL_FAILURE(
s.DeactivateUsageEntry(entry.pst())); // Then deactivate.
ASSERT_NO_FATAL_FAILURE(entry.DeactivateUsageEntry()); // Then deactivate.
// After deactivate, should not be able to decrypt.
ASSERT_NO_FATAL_FAILURE(
entry.TestDecryptCTR(false, OEMCrypto_ERROR_UNKNOWN_FAILURE));