From ba75fe85da741f5c2f00f1f7a5383712d1d7d2d3 Mon Sep 17 00:00:00 2001 From: Fred Gylys-Colwell Date: Fri, 13 Dec 2019 11:15:50 -0800 Subject: [PATCH] Initial LoadRenewal unit tests. --- oemcrypto/include/OEMCryptoCENC.h | 376 +++--- oemcrypto/include/OEMCryptoCENCCommon.h | 1 + oemcrypto/ref/src/oemcrypto_engine_ref.h | 14 +- oemcrypto/ref/src/oemcrypto_ref.cpp | 60 +- oemcrypto/ref/src/oemcrypto_session.cpp | 183 +-- oemcrypto/ref/src/oemcrypto_session.h | 9 +- .../ref/src/oemcrypto_usage_table_ref.cpp | 78 +- oemcrypto/ref/src/oemcrypto_usage_table_ref.h | 36 +- oemcrypto/test/oec_session_util.cpp | 286 +++- oemcrypto/test/oec_session_util.h | 101 +- oemcrypto/test/oemcrypto_test.cpp | 1193 ++++++++--------- 11 files changed, 1307 insertions(+), 1030 deletions(-) create mode 120000 oemcrypto/include/OEMCryptoCENCCommon.h diff --git a/oemcrypto/include/OEMCryptoCENC.h b/oemcrypto/include/OEMCryptoCENC.h index c62903b..ddee0a1 100644 --- a/oemcrypto/include/OEMCryptoCENC.h +++ b/oemcrypto/include/OEMCryptoCENC.h @@ -23,84 +23,14 @@ #include #include +#include "OEMCryptoCENCCommon.h" + #ifdef __cplusplus extern "C" { #endif typedef uint32_t OEMCrypto_SESSION; -// clang-format off -typedef enum OEMCryptoResult { - OEMCrypto_SUCCESS = 0, - OEMCrypto_ERROR_INIT_FAILED = 1, - OEMCrypto_ERROR_TERMINATE_FAILED = 2, - OEMCrypto_ERROR_OPEN_FAILURE = 3, - OEMCrypto_ERROR_CLOSE_FAILURE = 4, - OEMCrypto_ERROR_ENTER_SECURE_PLAYBACK_FAILED = 5, // deprecated - OEMCrypto_ERROR_EXIT_SECURE_PLAYBACK_FAILED = 6, // deprecated - OEMCrypto_ERROR_SHORT_BUFFER = 7, - OEMCrypto_ERROR_NO_DEVICE_KEY = 8, // no keybox device key. - OEMCrypto_ERROR_NO_ASSET_KEY = 9, - OEMCrypto_ERROR_KEYBOX_INVALID = 10, - OEMCrypto_ERROR_NO_KEYDATA = 11, - OEMCrypto_ERROR_NO_CW = 12, - OEMCrypto_ERROR_DECRYPT_FAILED = 13, - OEMCrypto_ERROR_WRITE_KEYBOX = 14, - OEMCrypto_ERROR_WRAP_KEYBOX = 15, - OEMCrypto_ERROR_BAD_MAGIC = 16, - OEMCrypto_ERROR_BAD_CRC = 17, - OEMCrypto_ERROR_NO_DEVICEID = 18, - OEMCrypto_ERROR_RNG_FAILED = 19, - OEMCrypto_ERROR_RNG_NOT_SUPPORTED = 20, - OEMCrypto_ERROR_SETUP = 21, - OEMCrypto_ERROR_OPEN_SESSION_FAILED = 22, - OEMCrypto_ERROR_CLOSE_SESSION_FAILED = 23, - OEMCrypto_ERROR_INVALID_SESSION = 24, - OEMCrypto_ERROR_NOT_IMPLEMENTED = 25, - OEMCrypto_ERROR_NO_CONTENT_KEY = 26, - OEMCrypto_ERROR_CONTROL_INVALID = 27, - OEMCrypto_ERROR_UNKNOWN_FAILURE = 28, - OEMCrypto_ERROR_INVALID_CONTEXT = 29, - OEMCrypto_ERROR_SIGNATURE_FAILURE = 30, - OEMCrypto_ERROR_TOO_MANY_SESSIONS = 31, - OEMCrypto_ERROR_INVALID_NONCE = 32, - OEMCrypto_ERROR_TOO_MANY_KEYS = 33, - OEMCrypto_ERROR_DEVICE_NOT_RSA_PROVISIONED = 34, - OEMCrypto_ERROR_INVALID_RSA_KEY = 35, - OEMCrypto_ERROR_KEY_EXPIRED = 36, - OEMCrypto_ERROR_INSUFFICIENT_RESOURCES = 37, - OEMCrypto_ERROR_INSUFFICIENT_HDCP = 38, - OEMCrypto_ERROR_BUFFER_TOO_LARGE = 39, - OEMCrypto_WARNING_GENERATION_SKEW = 40, // Warning, not an error. - OEMCrypto_ERROR_GENERATION_SKEW = 41, - OEMCrypto_LOCAL_DISPLAY_ONLY = 42, // Info, not an error. - OEMCrypto_ERROR_ANALOG_OUTPUT = 43, - OEMCrypto_ERROR_WRONG_PST = 44, - OEMCrypto_ERROR_WRONG_KEYS = 45, - OEMCrypto_ERROR_MISSING_MASTER = 46, - OEMCrypto_ERROR_LICENSE_INACTIVE = 47, - OEMCrypto_ERROR_ENTRY_NEEDS_UPDATE = 48, - OEMCrypto_ERROR_ENTRY_IN_USE = 49, - OEMCrypto_ERROR_USAGE_TABLE_UNRECOVERABLE = 50, // Reserved. Do not use. - OEMCrypto_KEY_NOT_LOADED = 51, // obsolete. use error 26. - OEMCrypto_KEY_NOT_ENTITLED = 52, - OEMCrypto_ERROR_BAD_HASH = 53, - OEMCrypto_ERROR_OUTPUT_TOO_LARGE = 54, - OEMCrypto_ERROR_SESSION_LOST_STATE = 55, - OEMCrypto_ERROR_SYSTEM_INVALIDATED = 56, - OEMCrypto_ERROR_LICENSE_RELOAD = 57, - OEMCrypto_ERROR_MULTIPLE_USAGE_ENTRIES = 58, - OEMCrypto_WARNING_MIXED_OUTPUT_PROTECTION = 59, - /* ODK return values */ - ODK_ERROR_BASE = 1000, - ODK_ERROR_CORE_MESSAGE = ODK_ERROR_BASE, - ODK_SET_TIMER = ODK_ERROR_BASE + 1, - ODK_DISABLE_TIMER = ODK_ERROR_BASE + 2, - ODK_TIMER_EXPIRED = ODK_ERROR_BASE + 3, - ODK_UNSUPPORTED_API = ODK_ERROR_BASE + 4, -} OEMCryptoResult; -// clang-format on - /* * The memory referenced by OEMCrypto_SharedMemory* is safe to be placed in * shared memory. The only data that should be placed into shared @@ -208,7 +138,7 @@ typedef struct { * This struct changed in API version 16. */ typedef struct { - OEMCrypto_SharedMemory* input_data; // source for encrypted data. + const OEMCrypto_SharedMemory* input_data; // source for encrypted data. size_t input_data_length; // length of encrypted data. OEMCrypto_DestBufferDesc output_descriptor; // destination for clear data. } OEMCrypto_InputOutputPair; @@ -266,12 +196,12 @@ typedef struct { * subsamples parameter. * * Version: - * This method changed in API version 16. + * This struct changed in API version 16. */ typedef struct { OEMCrypto_InputOutputPair buffers; // The source and destination buffers. uint8_t iv[16]; // The IV for the initial subsample. - OEMCrypto_SubSampleDescription* subsamples; // subsamples array. + const OEMCrypto_SubSampleDescription* subsamples; // subsamples array. size_t subsamples_length; // the number of subsamples in the sample. } OEMCrypto_SampleDescription; @@ -309,48 +239,6 @@ typedef enum OEMCrypto_LicenseType { OEMCrypto_EntitlementLicense = 1 } OEMCrypto_LicenseType; -/* - * OEMCrypto_Substring - * - * Used to indicate a substring of a signed message in OEMCrypto_LoadKeys and - * other functions which must verify that a parameter is contained within a - * signed message. - */ -typedef struct { - size_t offset; - size_t length; -} OEMCrypto_Substring; - -/* - * OEMCrypto_KeyObject - * Points to the relevant fields for a content key. The fields are extracted - * from the License Response message offered to OEMCrypto_LoadKeys(). Each - * field points to one of the components of the key. Key data, key control, - * and both IV fields are 128 bits (16 bytes): - * key_id - the unique id of this key. - * key_id_length - the size of key_id. OEMCrypto may assume this is at - * most 16. However, OEMCrypto shall correctly handle key id lengths - * from 1 to 16 bytes. - * key_data_iv - the IV for performing AES-128-CBC decryption of the - * key_data field. - * key_data - the key data. It is encrypted (AES-128-CBC) with the - * session's derived encrypt key and the key_data_iv. - * key_control_iv - the IV for performing AES-128-CBC decryption of the - * key_control field. - * key_control - the key control block. It is encrypted (AES-128-CBC) with - * the content key from the key_data field. - * - * The memory for the OEMCrypto_KeyObject fields is allocated and freed - * by the caller of OEMCrypto_LoadKeys(). - */ -typedef struct { - OEMCrypto_Substring key_id; - OEMCrypto_Substring key_data_iv; - OEMCrypto_Substring key_data; - OEMCrypto_Substring key_control_iv; - OEMCrypto_Substring key_control; -} OEMCrypto_KeyObject; - /* * OEMCrypto_EntitledContentKeyObject * Contains encrypted content key data for loading into the sessions keytable. @@ -371,6 +259,32 @@ typedef struct { OEMCrypto_Substring content_key_data; } OEMCrypto_EntitledContentKeyObject; +/* + * OEMCrypto_KeyRefreshObject + * This structure is being deprecated. It is only used for legacy licenses. + * Points to the relevant fields for renewing a content key. The fields are + * extracted from the License Renewal Response message offered to + * OEMCrypto_RefreshKeys(). Each field points to one of the components of + * the key. + * key_id - the unique id of this key. + * key_control_iv - the IV for performing AES-128-CBC decryption of the + * key_control field. 16 bytes. + * key_control - the key control block. It is encrypted (AES-128-CBC) with + * the content key from the key_data field. 16 bytes. + * + * The key_data is unchanged from the original OEMCrypto_LoadKeys() call. Some + * Key Control Block fields, especially those related to key lifetime, may + * change. + * + * The memory for the OEMCrypto_KeyRefreshObject fields is allocated and freed + * by the caller of OEMCrypto_RefreshKeys(). + */ +typedef struct { + OEMCrypto_Substring key_id; + OEMCrypto_Substring key_control_iv; + OEMCrypto_Substring key_control; +} OEMCrypto_KeyRefreshObject; + /* * OEMCrypto_Algorithm * This is a list of valid algorithms for OEMCrypto_Generic_* functions. @@ -381,18 +295,6 @@ typedef enum OEMCrypto_Algorithm { OEMCrypto_HMAC_SHA256 = 1, } OEMCrypto_Algorithm; -/* - * OEMCrypto_Usage_Entry_Status. - * Valid values for status in the usage table. - */ -typedef enum OEMCrypto_Usage_Entry_Status { - kUnused = 0, - kActive = 1, - kInactive = 2, // Deprecated. Used kInactiveUsed or kInactiveUnused. - kInactiveUsed = 3, - kInactiveUnused = 4, -} OEMCrypto_Usage_Entry_Status; - /* * OEMCrypto_PST_Report is used to report an entry from the Usage Table. * @@ -456,7 +358,7 @@ typedef enum OEMCrypto_ProvisioningMethod { OEMCrypto_OEMCertificate = 3 // Device has factory installed OEM certificate. } OEMCrypto_ProvisioningMethod; -/* Private key type used in OEMCrypto_LoadDevicePrivateKey. */ +/* Private key type used in OEMCrypto_LoadDRMPrivateKey. */ typedef enum OEMCrypto_PrivateKeyType { OEMCrypto_RSA_Private_Key, OEMCrypto_ECC_Private_Key, @@ -596,7 +498,7 @@ typedef enum OEMCrypto_PrivateKeyType { #define OEMCrypto_LoadOEMPrivateKey _oecc103 #define OEMCrypto_GetOEMPublicCertificate _oecc104 #define OEMCrypto_DecryptCENC _oecc105 -#define OEMCrypto_LoadDevicePrivateKey _oecc107 +#define OEMCrypto_LoadDRMPrivateKey _oecc107 // clang-format on /* @@ -801,8 +703,8 @@ OEMCryptoResult OEMCrypto_CloseSession(OEMCrypto_SESSION session); * OEMCrypto_ERROR_SYSTEM_INVALIDATED * * Buffer Sizes: - * OEMCrypto shall support mac_key_context and enc_key_context sizes of at - * least 8 KiB. + * OEMCrypto shall support mac_key_context and enc_key_context sizes as + * described in the section OEMCrypto_ResourceRatingTier. * OEMCrypto shall return OEMCrypto_ERROR_BUFFER_TOO_LARGE if the buffers are * too large. * @@ -845,7 +747,7 @@ OEMCryptoResult OEMCrypto_GenerateDerivedKeys( * Elliptic Curve Support" for details. * * Once the enc_key and mac_keys have been generated, all calls to LoadKeys - * and RefreshKeys proceed in the same manner for license requests using RSA + * or LoadLicense proceed in the same manner for license requests using RSA * or using a Widevine keybox token. * * Verification: @@ -887,8 +789,8 @@ OEMCryptoResult OEMCrypto_GenerateDerivedKeys( * OEMCrypto_ERROR_SYSTEM_INVALIDATED * * Buffer Sizes: - * OEMCrypto shall support mac_key_context and enc_key_context sizes of at - * least 8 KiB. + * OEMCrypto shall support mac_key_context and enc_key_context sizes as + * described in the section OEMCrypto_ResourceRatingTier. * OEMCrypto shall return OEMCrypto_ERROR_BUFFER_TOO_LARGE if the buffers are * too large. * @@ -919,12 +821,12 @@ OEMCryptoResult OEMCrypto_DeriveKeysFromSessionKey( * * Because the nonce will be used to prevent replay attacks, it is desirable * that a rogue application cannot rapidly call this function until a - * repeated nonce is created randomly. With this in mind, if more than 20 - * nonces are requested within one second, OEMCrypto will return an error - * after the 20th and not generate any more nonces for the rest of the - * second. After an error, if the application waits at least one second - * before requesting more nonces, then OEMCrypto will reset the error - * condition and generate valid nonces again. + * repeated nonce is created randomly. This is called a nonce flood. With + * this in mind, if more than 200 nonces are requested within one second, + * OEMCrypto will return an error after the 200th and not generate any more + * nonces for the rest of the second. After an error, if the application + * waits at least one second before requesting more nonces, then OEMCrypto + * will reset the error condition and generate valid nonces again. * * The nonce should be stored in the sessions ODK_NonceValue field by calling * the function ODK_SetNonceValue(&nonce_values, nonce). The ODK functions @@ -1017,7 +919,8 @@ OEMCryptoResult OEMCrypto_GenerateNonce(OEMCrypto_SESSION session, * OEMCrypto_ERROR_SYSTEM_INVALIDATED * * Buffer Sizes: - * OEMCrypto shall support message sizes of at least 8 KiB. + * OEMCrypto shall support message sizes as described in the section + * OEMCrypto_ResourceRatingTier. * OEMCrypto shall return OEMCrypto_ERROR_BUFFER_TOO_LARGE if the buffer is * larger than the supported size. * @@ -1055,8 +958,8 @@ OEMCryptoResult OEMCrypto_PrepAndSignLicenseRequest( * * If nonce_values.api_level is 15, then OEMCrypto shall compute the * signature of the message body using the session's client renewal mac key. - * The entire message is the buffer starting at message+core_message_size - * with length message_length-core_message_size. + * The message body is the buffer starting at message+core_message_size with + * length message_length-core_message_size. * * This function generates a HMAC-SHA256 signature using the mac_key[client] * for license request signing under the license server protocol for CENC. @@ -1100,7 +1003,8 @@ OEMCryptoResult OEMCrypto_PrepAndSignLicenseRequest( * OEMCrypto_ERROR_SYSTEM_INVALIDATED * * Buffer Sizes: - * OEMCrypto shall support message sizes of at least 8 KiB. + * OEMCrypto shall support message sizes as described in the section + * OEMCrypto_ResourceRatingTier. * OEMCrypto shall return OEMCrypto_ERROR_BUFFER_TOO_LARGE if the buffer is * larger than the supported size. * @@ -1136,7 +1040,7 @@ OEMCryptoResult OEMCrypto_PrepAndSignRenewalRequest( * 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 * Certificate. The key shall have been loaded by a previous call to - * OEMCrypto_LoadPrivateDRMKey. + * OEMCrypto_LoadDRMPrivateKey. * * Refer to the Signing Messages Sent to a Server section above for more * details. @@ -1168,7 +1072,8 @@ OEMCryptoResult OEMCrypto_PrepAndSignRenewalRequest( * OEMCrypto_ERROR_SYSTEM_INVALIDATED * * Buffer Sizes: - * OEMCrypto shall support message sizes of at least 8 KiB. + * OEMCrypto shall support message sizes as described in the section + * OEMCrypto_ResourceRatingTier. * OEMCrypto shall return OEMCrypto_ERROR_BUFFER_TOO_LARGE if the buffer is * larger than the supported size. * @@ -1251,9 +1156,7 @@ OEMCryptoResult OEMCrypto_LoadSRM(const uint8_t* buffer, size_t buffer_length); * The new mac_keys replaces the current mac_keys for future calls to * OEMCrypto_RefreshKeys(). The first 256 bits of the mac_keys become the * mac_key[server] and the following 256 bits of the mac_keys become the - * mac_key[client]. If enc_mac_keys is null, then there will not be a call to - * OEMCrypto_RefreshKeys for this session and the current mac_keys should - * remain unchanged. + * mac_key[client]. * * The mac_key and encrypt_key were generated and stored by the previous call * to OEMCrypto_GenerateDerivedKeys() or @@ -1453,7 +1356,8 @@ OEMCryptoResult OEMCrypto_LoadSRM(const uint8_t* buffer, size_t buffer_length); * OEMCrypto_ERROR_LICENSE_RELOAD * * Buffer Sizes: - * OEMCrypto shall support message sizes of at least 8 KiB. + * OEMCrypto shall support message sizes as described in the section + * OEMCrypto_ResourceRatingTier. * OEMCrypto shall return OEMCrypto_ERROR_BUFFER_TOO_LARGE if the buffer is * larger than the supported size. * @@ -1704,7 +1608,8 @@ OEMCryptoResult OEMCrypto_LoadKeys( * OEMCrypto_ERROR_LICENSE_RELOAD * * Buffer Sizes: - * OEMCrypto shall support message sizes of at least 8 KiB. + * OEMCrypto shall support message sizes as described in the section + * OEMCrypto_ResourceRatingTier. * OEMCrypto shall return OEMCrypto_ERROR_BUFFER_TOO_LARGE if the buffer is * larger than the supported size. * @@ -1791,6 +1696,84 @@ OEMCryptoResult OEMCrypto_LoadEntitledContentKeys( size_t key_array_length, const OEMCrypto_EntitledContentKeyObject* key_array); +/* + * OEMCrypto_RefreshKeys + * + * Description: + * Updates the license clock values to allow playback to continue. This + * function is being deprecated and is only used for version v15 licenses -- + * i.e. offline license saved before an update or licenses from a server that + * has not update to the v16 license server SDK. + * + * OEMCrypto shall compute the signature of the message using + * mac_key[server], and shall verify the computed signature matches the + * signature passed in. If not, return OEMCrypto_ERROR_SIGNATURE_FAILURE. The + * signature verification shall use a constant-time algorithm (a signature + * mismatch will always take the same time as a successful comparison). + * + * The function ODK_RefreshV15Values shall be called to update the clock + * values. See the document "Widevine Core Message Serialization" for the + * documentation of the ODK library functions. + * + * If ODK_RefreshV15Values returns + * + * - ODK_SET_TIMER: Success. The timer should be reset to the specified + * timer value. + * - ODK_DISABLE_TIMER: Success, but disable timer. Unlimited playback is + * allowed. + * - ODK_TIMER_EXPIRED: Set timer as diabled. Playback is not allowed. + * - ODK_STALE_RENEWAL: This renewal is not the most recently signed. It is + * rejected. Return this error + * - Any other error - OEMCrypto shall pass any other error up to the + * caller of OEMCrypto_RefreshKeys. + * + * NOTE: OEMCrypto_LoadKeys() must be called first to load the keys into the + * session. + * + * Parameters: + * [in] session: handle for the session to be used. + * [in] message: pointer to memory containing message to be verified. + * [in] message_length: length of the message, in bytes. + * [in] signature: pointer to memory containing the signature. + * [in] signature_length: length of the signature, in bytes. + * [in] num_keys: number of keys present. + * [in] key_array: set of key updates. + * + * Returns: + * OEMCrypto_SUCCESS success + * OEMCrypto_ERROR_NO_DEVICE_KEY + * OEMCrypto_ERROR_INVALID_SESSION + * OEMCrypto_ERROR_INVALID_CONTEXT + * OEMCrypto_ERROR_SIGNATURE_FAILURE + * OEMCrypto_ERROR_INVALID_NONCE + * OEMCrypto_ERROR_INSUFFICIENT_RESOURCES + * OEMCrypto_ERROR_UNKNOWN_FAILURE + * OEMCrypto_ERROR_BUFFER_TOO_LARGE + * OEMCrypto_ERROR_NO_CONTENT_KEY + * OEMCrypto_ERROR_SESSION_LOST_STATE + * OEMCrypto_ERROR_SYSTEM_INVALIDATED + * + * Buffer Sizes: + * OEMCrypto shall support message sizes as described in the section + * OEMCrypto_ResourceRatingTier. + * OEMCrypto shall return OEMCrypto_ERROR_BUFFER_TOO_LARGE if the buffer is + * larger than the supported size. + * + * Threading: + * This is a "Session Function" and may be called simultaneously with session + * functions for other sessions but not simultaneously with other functions + * for this session. It will not be called simultaneously with initialization + * or usage table functions. It is as if the CDM holds a write lock for this + * session, and a read lock on the OEMCrypto system. + * + * Version: + * This method changed in API version 16. + */ +OEMCryptoResult OEMCrypto_RefreshKeys( + OEMCrypto_SESSION session, const uint8_t* message, size_t message_length, + const uint8_t* signature, size_t signature_length, size_t num_keys, + const OEMCrypto_KeyRefreshObject* key_array); + /* * OEMCrypto_LoadRenewal * @@ -1822,8 +1805,8 @@ OEMCryptoResult OEMCrypto_LoadEntitledContentKeys( * success. These values shall be handled by OEMCrypto, as discussed in the * document "License Duration and Renewal". * - * NOTE: OEMCrypto_LoadKeys() or OEMCrypto_LoadLicense() must be called first - * to load the keys into the session. + * NOTE: OEMCrypto_LoadLicense() must be called first to load the keys into + * the session. * * Verification: * The following checks should be performed. If any check fails, an error is @@ -1868,9 +1851,11 @@ OEMCryptoResult OEMCrypto_LoadEntitledContentKeys( * OEMCrypto_ERROR_NO_CONTENT_KEY * OEMCrypto_ERROR_SESSION_LOST_STATE * OEMCrypto_ERROR_SYSTEM_INVALIDATED + * ODK_STALE_RENEWAL * * Buffer Sizes: - * OEMCrypto shall support message sizes of at least 8 KiB. + * OEMCrypto shall support message sizes as described in the section + * OEMCrypto_ResourceRatingTier. * OEMCrypto shall return OEMCrypto_ERROR_BUFFER_TOO_LARGE if the buffer is * larger than the supported size. * @@ -1958,7 +1943,8 @@ OEMCryptoResult OEMCrypto_QueryKeyControl(OEMCrypto_SESSION session, * Select a content key and install it in the hardware key ladder for * subsequent decryption operations (OEMCrypto_DecryptCENC()) for this * session. The specified key must have been previously "installed" via - * OEMCrypto_LoadKeys() or OEMCrypto_RefreshKeys(). + * OEMCrypto_LoadKeys(), OEMCrypto_LoadLicense, or + * OEMCrypto_LoadEntitledContentKeys(). * * A key control block is associated with the key and the session, and is * used to configure the session context. The Key Control data is documented @@ -1983,24 +1969,18 @@ OEMCryptoResult OEMCrypto_QueryKeyControl(OEMCrypto_SESSION session, * 1. If the key id is not found in the keytable for this session, then the * key state is not changed and OEMCrypto shall return * OEMCrypto_ERROR_NO_CONTENT_KEY. - * 2. If the current key's control block has a nonzero Duration field, then - * the API shall verify that the duration is greater than the session's - * elapsed time clock before the key is used. OEMCrypto may return - * OEMCrypto_ERROR_KEY_EXPIRED from OEMCrypto_SelectKey, or SelectKey - * may return success from select key and the decrypt or generic crypto - * call will return OEMCrypto_ERROR_KEY_EXPIRED. - * 3. If the key control block has the bit Disable_Analog_Output set, then + * 2. If the key control block has the bit Disable_Analog_Output set, then * the device should disable analog video output. If the device has * analog video output that cannot be disabled, then the key is not * selected, and OEMCrypto_ERROR_ANALOG_OUTPUT is returned. This step is * optional -- SelectKey may return OEMCrypto_SUCCESS and delay the * error until a call to OEMCrypto_DecryptCENC. - * 4. If the key control block has HDCP required, and the device cannot + * 3. If the key control block has HDCP required, and the device cannot * enforce HDCP, then the key is not selected, and * OEMCrypto_ERROR_INSUFFICIENT_HDCP is returned. This step is optional * -- SelectKey may return OEMCrypto_SUCCESS and delay the error until a * call to OEMCrypto_DecryptCENC. - * 5. If the key control block has a nonzero value for HDCP_Version, and + * 4. If the key control block has a nonzero value for HDCP_Version, and * the device cannot enforce at least that version of HDCP, then the key * is not selected, and OEMCrypto_ERROR_INSUFFICIENT_HDCP is returned. * @@ -2015,7 +1995,7 @@ OEMCryptoResult OEMCrypto_QueryKeyControl(OEMCrypto_SESSION session, * * Returns: * OEMCrypto_SUCCESS success - * OEMCrypto_ERROR_KEY_EXPIRED - if the key's timer has expired + * OEMCrypto_ERROR_KEY_EXPIRED - if the session's timer has expired * OEMCrypto_ERROR_INVALID_SESSION crypto session ID invalid or not open * OEMCrypto_ERROR_NO_DEVICE_KEY failed to decrypt device key * OEMCrypto_ERROR_NO_CONTENT_KEY failed to decrypt content key @@ -2551,7 +2531,7 @@ OEMCryptoResult OEMCrypto_CopyBuffer( * the time_of_last_decrypt. If the status of the entry is "unused", then * change the status to "active" and set the time_of_first_decrypt. * - * OEMCrypto should be able to handle buffers at least 100 KiB long. + * OEMCrypto shall be able to handle buffers at least 100 KiB long. * * Verification: * The following checks should be performed. If any check fails, an error is @@ -3775,11 +3755,23 @@ uint32_t OEMCrypto_GetAnalogOutputFlags(void); * Each of these pictures would correspond to a separate playback session * with active decryption. * - * The total number of keys for all sessions indicates that the device does - * may share key memory over multiple sessions. For example, on a Tier 3 - * device, the device must support four sessions with 20 keys each (80 - * total), or 20 sessions with 4 keys each (80 total), but it does not need - * to support 20 sessions with 20 keys each. + * The total number of keys for all sessions indicates that the device may + * share key memory over multiple sessions. For example, on a Tier 3 device, + * the device must support four sessions with 20 keys each (80 total), or 20 + * sessions with 4 keys each (80 total), but it does not need to support 20 + * sessions with 20 keys each. + * + * Living room devices refer to devices such as TVs, set top boxes, game + * consoles, blu-ray players etc. These devices tend to have reduced UI and + * frequently are dedicated to playing video. For these devices, we expect + * there to be more memory dedicated to video playback and video + * applications. The number of sessions required for living room devices is + * larger than for mobile devices. + * + * The message size that is needed for a license with a large number of keys + * is larger than in previous versions. The message size limit applies to all + * functions that sign or verify messages. It also applies to the size of + * context buffers in the derive key functions. * * Decrypted frames per second -- strictly speaking, OEMCrypto only controls * the decryption part of playback and cannot control the decoding and @@ -3808,6 +3800,10 @@ uint32_t OEMCrypto_GetAnalogOutputFlags(void); * |size | | | | | * +--------------------------------+---------+----------+---------+---------+ * |Minimum number of open sessions |10 |20 |20 |30 | + * |(mobile devices) | | | | | + * +--------------------------------+---------+----------+---------+---------+ + * |Minimum number of open sessions |10 |100 |100 |100 | + * |(living room devices) | | | | | * +--------------------------------+---------+----------+---------+---------+ * |Minimum number of keys per |4 |20 |20 |30 | * |session | | | | | @@ -3815,6 +3811,8 @@ uint32_t OEMCrypto_GetAnalogOutputFlags(void); * |Minimum total number of keys |16 |40 |80 |90 | * | (all sessions) | | | | | * +--------------------------------+---------+----------+---------+---------+ + * |Minimum Message Size |8 KiB |8 KiB |16 KiB |32 KiB | + * +--------------------------------+---------+----------+---------+---------+ * |Decrypted Frames per Second |30 fps SD|30 fps HD |60 fps HD|60 fps 8k| * +--------------------------------+---------+----------+---------+---------+ * @@ -3953,7 +3951,8 @@ uint32_t OEMCrypto_ResourceRatingTier(void); * OEMCrypto_ERROR_SYSTEM_INVALIDATED * * Buffer Sizes: - * OEMCrypto shall support message sizes of at least 8 KiB. + * OEMCrypto shall support message sizes as described in the section + * OEMCrypto_ResourceRatingTier. * OEMCrypto shall return OEMCrypto_ERROR_BUFFER_TOO_LARGE if the buffer is * larger than the supported size. * @@ -3974,7 +3973,7 @@ OEMCryptoResult OEMCrypto_LoadProvisioning( size_t* wrapped_private_key_length); /* - * OEMCrypto_LoadDevicePrivateKey + * OEMCrypto_LoadDRMPrivateKey * * Description: * Loads a wrapped RSA or ECC private key to secure memory for use by this @@ -4028,9 +4027,10 @@ OEMCryptoResult OEMCrypto_LoadProvisioning( * Version: * This method changed in API version 16. */ -OEMCryptoResult OEMCrypto_LoadDevicePrivateKey( - OEMCrypto_SESSION session, OEMCrypto_PrivateKeyType key_type, - const uint8_t* wrapped_rsa_key, size_t wrapped_rsa_key_length); +OEMCryptoResult OEMCrypto_LoadDRMPrivateKey(OEMCrypto_SESSION session, + OEMCrypto_PrivateKeyType key_type, + const uint8_t* wrapped_rsa_key, + size_t wrapped_rsa_key_length); /* * OEMCrypto_LoadTestRSAKey @@ -4076,7 +4076,7 @@ OEMCryptoResult OEMCrypto_LoadTestRSAKey(void); * Description: * The OEMCrypto_GenerateRSASignature method is only used for devices that * are CAST receivers. This function is called after - * OEMCrypto_LoadDevicePrivateKey for the same session. + * OEMCrypto_LoadDRMPrivateKey for the same session. * * The parameter padding_scheme has two possible legacy values: * @@ -4129,7 +4129,8 @@ OEMCryptoResult OEMCrypto_LoadTestRSAKey(void); * OEMCrypto_ERROR_SYSTEM_INVALIDATED * * Buffer Sizes: - * OEMCrypto shall support message sizes of at least 8 KiB. + * OEMCrypto shall support message sizes as described in the section + * OEMCrypto_ResourceRatingTier. * OEMCrypto shall return OEMCrypto_ERROR_BUFFER_TOO_LARGE if the buffer is * larger than the supported size. * @@ -4738,14 +4739,15 @@ uint32_t OEMCrypto_SupportsDecryptHash(void); * Description: * Set the hash value for the next frame to be decrypted. This function is * called before the first subsample is passed to OEMCrypto_DecryptCENC, when - * the subsample_flag has the bit OEMCrytpo_FirstSubsample set. The hash is + * the subsample_flag has the bit OEMCrypto_FirstSubsample set. The hash is * over all of the frame or sample: encrypted and clear subsamples * concatenated together, up to, and including the subsample with the * subsample_flag having the bit OEMCrypto_LastSubsample set. If hashing the * output is not supported, then this will return * OEMCrypto_ERROR_NOT_IMPLEMENTED. If the hash is ill formed or there are * other error conditions, this returns OEMCrypto_ERROR_UNKNOWN_FAILURE. The - * length of the hash will be at most 128 bytes. + * length of the hash will be at most 128 bytes, and will be 4 bytes (32 + * bits) for the default CRC32 hash. * * This may be called before the first call to SelectKey. In that case, this * function cannot verify that the key control block allows hash @@ -4931,16 +4933,6 @@ OEMCryptoResult OEMCrypto_GenerateSignature(OEMCrypto_SESSION session, size_t message_length, uint8_t* signature, size_t* signature_length); -typedef struct { - OEMCrypto_Substring key_id; - OEMCrypto_Substring key_control_iv; - OEMCrypto_Substring key_control; -} OEMCrypto_KeyRefreshObject; - -OEMCryptoResult OEMCrypto_RefreshKeys( - OEMCrypto_SESSION session, const uint8_t* message, size_t message_length, - const uint8_t* signature, size_t signature_length, size_t num_keys, - const OEMCrypto_KeyRefreshObject* key_array); OEMCryptoResult OEMCrypto_RewrapDeviceRSAKey30( OEMCrypto_SESSION session, const uint32_t* unaligned_nonce, const uint8_t* encrypted_message_key, size_t encrypted_message_key_length, diff --git a/oemcrypto/include/OEMCryptoCENCCommon.h b/oemcrypto/include/OEMCryptoCENCCommon.h new file mode 120000 index 0000000..dd92964 --- /dev/null +++ b/oemcrypto/include/OEMCryptoCENCCommon.h @@ -0,0 +1 @@ +../../oemcrypto/odk/include/OEMCryptoCENCCommon.h \ No newline at end of file diff --git a/oemcrypto/ref/src/oemcrypto_engine_ref.h b/oemcrypto/ref/src/oemcrypto_engine_ref.h index 868a4a4..f1e1b6b 100644 --- a/oemcrypto/ref/src/oemcrypto_engine_ref.h +++ b/oemcrypto/ref/src/oemcrypto_engine_ref.h @@ -89,11 +89,9 @@ class CryptoEngine { return kMaxSupportedOEMCryptoSessions; } - // System clock, measuring time in seconds. - int64_t OnlineTime(); - - // System clock with antirollback protection, measuring time in seconds. - int64_t RollbackCorrectedOfflineTime(); + // The OEMCrypto system time. Prevents time rollback. + // TODO(b/145836634): Combine RollbackCorrectedOfflineTime with OnlineTime(). + int64_t SystemTime() { return RollbackCorrectedOfflineTime(); } // Verify that this nonce does not collide with another nonce in any session. virtual bool NonceCollision(uint32_t nonce); @@ -212,6 +210,12 @@ class CryptoEngine { } protected: + // System clock, measuring time in seconds. + int64_t OnlineTime(); + + // System clock with antirollback protection, measuring time in seconds. + int64_t RollbackCorrectedOfflineTime(); + explicit CryptoEngine(std::unique_ptr&& file_system); virtual SessionContext* MakeSession(SessionId sid); virtual UsageTable* MakeUsageTable(); diff --git a/oemcrypto/ref/src/oemcrypto_ref.cpp b/oemcrypto/ref/src/oemcrypto_ref.cpp index 14d4930..15a849e 100644 --- a/oemcrypto/ref/src/oemcrypto_ref.cpp +++ b/oemcrypto/ref/src/oemcrypto_ref.cpp @@ -407,38 +407,69 @@ OEMCRYPTO_API OEMCryptoResult OEMCrypto_LoadEntitledContentKeys( key_array_length, key_array); } -OEMCRYPTO_API OEMCryptoResult OEMCrypto_RefreshKeys( - OEMCrypto_SESSION session, const uint8_t* message, size_t message_length, - const uint8_t* signature, size_t signature_length, size_t num_keys, - const OEMCrypto_KeyRefreshObject* key_array) { +OEMCRYPTO_API OEMCryptoResult OEMCrypto_LoadRenewal(OEMCrypto_SESSION session, + const uint8_t* message, + size_t message_length, + size_t core_message_length, + const uint8_t* signature, + size_t signature_length) { if (crypto_engine == nullptr) { - LOGE("OEMCrypto_RefreshKeys: OEMCrypto Not Initialized."); + LOGE("OEMCrypto Not Initialized."); return OEMCrypto_ERROR_UNKNOWN_FAILURE; } if (!crypto_engine->ValidRootOfTrust()) { - LOGE("[OEMCrypto_RefreshKeys(): 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_RefreshKeys(): ERROR_INVALID_SESSION]"); + LOGE("ERROR_INVALID_SESSION"); + return OEMCrypto_ERROR_INVALID_SESSION; + } + + if (message == nullptr || message_length == 0 || signature == nullptr || + signature_length == 0) { + LOGE("ERROR_INVALID_CONTEXT"); + return OEMCrypto_ERROR_INVALID_CONTEXT; + } + return session_ctx->LoadRenewal(message, message_length, core_message_length, + signature, signature_length); +} + +OEMCRYPTO_API OEMCryptoResult OEMCrypto_RefreshKeys( + OEMCrypto_SESSION session, const uint8_t* message, size_t message_length, + const uint8_t* signature, size_t signature_length, size_t num_keys, + const OEMCrypto_KeyRefreshObject* key_array) { + if (crypto_engine == nullptr) { + LOGE("OEMCrypto Not Initialized."); + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + + if (!crypto_engine->ValidRootOfTrust()) { + LOGE("ERROR_KEYBOX_INVALID"); + return OEMCrypto_ERROR_KEYBOX_INVALID; + } + + SessionContext* session_ctx = crypto_engine->FindSession(session); + if (session_ctx == nullptr || !session_ctx->isValid()) { + LOGE("ERROR_INVALID_SESSION"); return OEMCrypto_ERROR_INVALID_SESSION; } if (message == nullptr || message_length == 0 || signature == nullptr || signature_length == 0 || num_keys == 0) { - LOGE("[OEMCrypto_RefreshKeys(): OEMCrypto_ERROR_INVALID_CONTEXT]"); + LOGE("ERROR_INVALID_CONTEXT"); return OEMCrypto_ERROR_INVALID_CONTEXT; } // Range check - for (unsigned int i = 0; i < num_keys; i++) { + for (size_t i = 0; i < num_keys; i++) { if (!RangeCheck(message_length, key_array[i].key_id, true) || !RangeCheck(message_length, key_array[i].key_control, false) || !RangeCheck(message_length, key_array[i].key_control_iv, true)) { - LOGE("[OEMCrypto_RefreshKeys(): Range Check %d]", i); + LOGE("Range Check %zu", i); return OEMCrypto_ERROR_INVALID_CONTEXT; } } @@ -446,7 +477,7 @@ OEMCRYPTO_API OEMCryptoResult OEMCrypto_RefreshKeys( // Validate message signature if (!session_ctx->ValidateMessage(message, message_length, signature, signature_length)) { - LOGE("[OEMCrypto_RefreshKeys(): signature was invalid]"); + LOGE("Signature was invalid"); return OEMCrypto_ERROR_SIGNATURE_FAILURE; } @@ -455,7 +486,7 @@ OEMCRYPTO_API OEMCryptoResult OEMCrypto_RefreshKeys( std::vector key_id; std::vector key_control; std::vector key_control_iv; - for (unsigned int i = 0; i < num_keys; i++) { + for (size_t i = 0; i < num_keys; i++) { if (key_array[i].key_id.length != 0) { key_id.assign( message + key_array[i].key_id.offset, @@ -482,16 +513,13 @@ OEMCRYPTO_API OEMCryptoResult OEMCrypto_RefreshKeys( status = session_ctx->RefreshKey(key_id, key_control, key_control_iv); if (status != OEMCrypto_SUCCESS) { - LOGE("[OEMCrypto_RefreshKeys(): error %u in key %i]", status, i); + LOGE("error %d in key %zu", status, i); break; } } - if (status != OEMCrypto_SUCCESS) { return status; } - - session_ctx->StartTimer(); return OEMCrypto_SUCCESS; } diff --git a/oemcrypto/ref/src/oemcrypto_session.cpp b/oemcrypto/ref/src/oemcrypto_session.cpp index 549e0c4..6a19199 100644 --- a/oemcrypto/ref/src/oemcrypto_session.cpp +++ b/oemcrypto/ref/src/oemcrypto_session.cpp @@ -172,6 +172,7 @@ SessionContext::SessionContext(CryptoEngine* ce, SessionId sid, session_keys_(nullptr), rsa_key_(rsa_key), allowed_schemes_(kSign_RSASSA_PSS), + decrypt_started_(false), timer_limits_(), clock_values_(), usage_entry_(nullptr), @@ -353,7 +354,7 @@ OEMCryptoResult SessionContext::PrepAndSignLicenseRequest( result = GenerateCertSignature(message_body, message_body_length, signature, signature_length); if (result == OEMCrypto_SUCCESS) state_request_signed_ = true; - ODK_InitializeClockValues(&clock_values_, ce_->OnlineTime()); + ODK_InitializeClockValues(&clock_values_, ce_->SystemTime()); return result; } @@ -371,7 +372,7 @@ OEMCryptoResult SessionContext::PrepAndSignRenewalRequest( return OEMCrypto_ERROR_UNKNOWN_FAILURE; } const size_t required_signature_size = SHA256_DIGEST_LENGTH; - const uint64_t now = CurrentTimer(); + const uint64_t now = ce_->SystemTime(); const OEMCryptoResult result = ODK_PrepareCoreRenewalRequest( message, message_length, core_message_length, &nonce_values_, &clock_values_, now); @@ -679,13 +680,6 @@ OEMCryptoResult SessionContext::CheckNonceOrEntry( return OEMCrypto_SUCCESS; } -void SessionContext::StartTimer() { timer_start_ = ce_->OnlineTime(); } - -uint32_t SessionContext::CurrentTimer() { - int64_t now = ce_->OnlineTime(); - return static_cast((now >= timer_start_) ? now - timer_start_ : 0); -} - OEMCryptoResult SessionContext::LoadLicense(const uint8_t* message, size_t message_length, size_t core_message_length, @@ -697,8 +691,7 @@ OEMCryptoResult SessionContext::LoadLicense(const uint8_t* message, return OEMCrypto_ERROR_LICENSE_RELOAD; } ODK_ParsedLicense parsed_response; - // TODO(b/140765227): handle license reload. - constexpr bool initial_license_load = true; + const bool initial_license_load = (usage_entry_status_ != kUsageEntryLoaded); const OEMCryptoResult result = ODK_ParseLicense( message, message_length, core_message_length, initial_license_load, usage_entry_present(), license_request_hash_, &timer_limits_, @@ -741,11 +734,10 @@ OEMCryptoResult SessionContext::LoadKeys( message, message_length, enc_mac_keys_iv, enc_mac_keys, num_keys, key_array, pst, srm_restriction_data, license_type); if (result != OEMCrypto_SUCCESS) return result; - if (num_keys < 1) return OEMCrypto_ERROR_INVALID_CONTEXT; Key* key = session_keys_->FirstKey(); uint32_t duration = key ? key->control().duration() : 0; result = ODK_InitializeV15Values(&timer_limits_, &clock_values_, - &nonce_values_, duration, ce_->OnlineTime()); + &nonce_values_, duration, ce_->SystemTime()); // TODO(b/140765227): clear session on errors return result; } @@ -760,6 +752,7 @@ OEMCryptoResult SessionContext::LoadKeysNoSignature( return OEMCrypto_ERROR_LICENSE_RELOAD; } state_response_loaded_ = true; + if (num_keys < 1) return OEMCrypto_ERROR_INVALID_CONTEXT; if (session_keys_ == nullptr) { switch (license_type) { @@ -779,9 +772,6 @@ OEMCryptoResult SessionContext::LoadKeysNoSignature( return OEMCrypto_ERROR_INVALID_CONTEXT; } } - - StartTimer(); - if (srm_restriction_data.length != 0) { const std::string kSRMVerificationString = "HDCPDATA"; if (memcmp(message + srm_restriction_data.offset, @@ -1041,64 +1031,45 @@ bool SessionContext::InstallRSAEncryptedKey( return true; } +OEMCryptoResult SessionContext::LoadRenewal(const uint8_t* message, + size_t message_length, + size_t core_message_length, + const uint8_t* signature, + size_t signature_length) { + if (session_keys_ == nullptr) { + return OEMCrypto_ERROR_INVALID_CONTEXT; + } + if (!ValidateMessage(message, message_length, signature, signature_length)) { + LOGE("signature was invalid"); + return OEMCrypto_ERROR_SIGNATURE_FAILURE; + } + + // The reference implementation does not use a hardware timer. + uint64_t* timer_value = nullptr; + const OEMCryptoResult result = ODK_ParseRenewal( + message, message_length, core_message_length, &nonce_values_, + ce_->SystemTime(), &timer_limits_, &clock_values_, timer_value); + if (result == ODK_SET_TIMER || result == ODK_DISABLE_TIMER) + return OEMCrypto_SUCCESS; + if (result == ODK_TIMER_EXPIRED) return OEMCrypto_ERROR_KEY_EXPIRED; + // All other errors are returned to the caller. + return result; +} + OEMCryptoResult SessionContext::RefreshKey( const KeyId& key_id, const std::vector& key_control, const std::vector& key_control_iv) { if (session_keys_ == nullptr) { return OEMCrypto_ERROR_INVALID_CONTEXT; } - if (key_id.empty()) { - // Key control is not encrypted if key id is nullptr - KeyControlBlock key_control_block(key_control); - if (!key_control_block.valid()) { - LOGE("Parse key control error"); - return OEMCrypto_ERROR_INVALID_CONTEXT; - } - if ((key_control_block.control_bits() & wvoec::kControlNonceEnabled) && - (!CheckNonce(key_control_block.nonce()))) { - LOGE("KCB: BAD Nonce"); - return OEMCrypto_ERROR_INVALID_NONCE; - } - // Apply duration to all keys in this session - session_keys_->UpdateDuration(key_control_block); + uint64_t* timer_value = nullptr; + const OEMCryptoResult result = + ODK_RefreshV15Values(&timer_limits_, &clock_values_, &nonce_values_, + ce_->SystemTime(), timer_value); + if (result == ODK_SET_TIMER || result == ODK_DISABLE_TIMER) return OEMCrypto_SUCCESS; - } - - Key* content_key = session_keys_->Find(key_id); - - if (content_key == nullptr) { - LOGE("Key ID not found"); - return OEMCrypto_ERROR_NO_CONTENT_KEY; - } - - if (key_control.empty()) { - return OEMCrypto_ERROR_UNKNOWN_FAILURE; - } - - const std::vector content_key_value = content_key->value(); - - // Decrypt encrypted key control block - std::vector control; - if (key_control_iv.empty()) { - control = key_control; - } else { - if (!DecryptMessage(content_key_value, key_control_iv, key_control, - &control, 128 /* key size */)) { - return OEMCrypto_ERROR_UNKNOWN_FAILURE; - } - } - - KeyControlBlock key_control_block(control); - if (!key_control_block.valid()) { - LOGE("Error parsing key control"); - return OEMCrypto_ERROR_INVALID_CONTEXT; - } - if ((key_control_block.control_bits() & wvoec::kControlNonceEnabled) && - (!CheckNonce(key_control_block.nonce()))) { - return OEMCrypto_ERROR_INVALID_NONCE; - } - content_key->UpdateDuration(key_control_block); - return OEMCrypto_SUCCESS; + if (result == ODK_TIMER_EXPIRED) return OEMCrypto_ERROR_KEY_EXPIRED; + return result; } bool SessionContext::DecryptRSAKey(const uint8_t* enc_rsa_key, @@ -1163,19 +1134,19 @@ OEMCryptoResult SessionContext::CheckKeyUse(const std::string& log_string, return OEMCrypto_ERROR_DECRYPT_FAILED; } } - if (control.control_bits() & wvoec::kControlReplayMask) { - if (!CheckUsageEntry()) { - LOGE("[%s(): usage entry not valid]", log_string.c_str()); - return OEMCrypto_ERROR_UNKNOWN_FAILURE; - } - } - if (control.duration() > 0) { - if (control.duration() < CurrentTimer()) { - LOGE("%s: key expired. duration=%d, timer=%d, now=%ld", - log_string.c_str(), control.duration(), CurrentTimer(), - ce_->OnlineTime()); - return OEMCrypto_ERROR_KEY_EXPIRED; - } + if (!decrypt_started_) { + // The reference implementation does not have a hardware timer. + uint64_t* timer_expiration = nullptr; + const OEMCryptoResult result = ODK_AttemptFirstPlayback( + ce_->SystemTime(), &timer_limits_, &clock_values_, timer_expiration); + if (result == ODK_TIMER_EXPIRED) return OEMCrypto_ERROR_KEY_EXPIRED; + if (usage_entry_ != nullptr) usage_entry_->ForbidReport(); + } else { + // Continued playback. + const OEMCryptoResult result = ODK_UpdateLastPlaybackTime( + ce_->SystemTime(), &timer_limits_, &clock_values_); + if (result == ODK_TIMER_EXPIRED) return OEMCrypto_ERROR_KEY_EXPIRED; + if (usage_entry_ != nullptr) usage_entry_->set_recent_decrypt(true); } if (!ce_->config_local_display_only()) { // Only look at HDCP restrictions if the display can be non-local. @@ -1215,6 +1186,7 @@ OEMCryptoResult SessionContext::CheckKeyUse(const std::string& log_string, return OEMCrypto_ERROR_ANALOG_OUTPUT; } } + decrypt_started_ = true; // First playback for session. return OEMCrypto_SUCCESS; } @@ -1421,23 +1393,9 @@ OEMCryptoResult SessionContext::SelectContentKey( } content_key->set_ctr_mode(cipher_mode == OEMCrypto_CipherMode_CTR); current_content_key_ = content_key; - const KeyControlBlock& control = current_content_key()->control(); - - if (control.duration() > 0) { - if (control.duration() < CurrentTimer()) { - LOGE("KEY_EXPIRED duration=%d, timer=%d, now=%ld", control.duration(), - CurrentTimer(), ce_->OnlineTime()); - return OEMCrypto_ERROR_KEY_EXPIRED; - } - } return OEMCrypto_SUCCESS; } -bool SessionContext::CheckUsageEntry() { - if (!usage_entry_) return false; - return usage_entry_->CheckForUse(); -} - OEMCryptoResult SessionContext::CreateNewUsageEntry( uint32_t* usage_entry_number) { if (usage_entry_) { @@ -1458,19 +1416,20 @@ OEMCryptoResult SessionContext::LoadUsageEntry( // Can only load one entry per session. return OEMCrypto_ERROR_MULTIPLE_USAGE_ENTRIES; } - OEMCryptoResult result = - ce_->usage_table().LoadUsageEntry(this, &usage_entry_, index, buffer); - if (usage_entry_) { - usage_entry_status_ = kUsageEntryLoaded; - // Copy the mac keys to the current session. - mac_key_server_ = std::vector( - usage_entry_->mac_key_server(), - usage_entry_->mac_key_server() + wvoec::MAC_KEY_SIZE); - mac_key_client_ = std::vector( - usage_entry_->mac_key_client(), - usage_entry_->mac_key_client() + wvoec::MAC_KEY_SIZE); - } - return result; + const OEMCryptoResult result = ce_->usage_table().LoadUsageEntry( + this, &usage_entry_, index, buffer, &clock_values_); + if (result != OEMCrypto_SUCCESS) return result; + if (!usage_entry_) return OEMCrypto_ERROR_UNKNOWN_FAILURE; + + usage_entry_status_ = kUsageEntryLoaded; + // Copy the mac keys to the current session. + mac_key_server_ = std::vector( + usage_entry_->mac_key_server(), + usage_entry_->mac_key_server() + wvoec::MAC_KEY_SIZE); + mac_key_client_ = std::vector( + usage_entry_->mac_key_client(), + usage_entry_->mac_key_client() + wvoec::MAC_KEY_SIZE); + return OEMCrypto_SUCCESS; } OEMCryptoResult SessionContext::UpdateUsageEntry(uint8_t* header_buffer, @@ -1481,16 +1440,16 @@ OEMCryptoResult SessionContext::UpdateUsageEntry(uint8_t* header_buffer, LOGE("UpdateUsageEntry: Session has no entry"); return OEMCrypto_ERROR_INVALID_CONTEXT; } - return ce_->usage_table().UpdateUsageEntry(this, usage_entry_, header_buffer, - header_buffer_length, entry_buffer, - entry_buffer_length); + return ce_->usage_table().UpdateUsageEntry( + this, usage_entry_, header_buffer, header_buffer_length, entry_buffer, + entry_buffer_length, &clock_values_); } OEMCryptoResult SessionContext::DeactivateUsageEntry( const std::vector& pst) { if (!usage_entry_) return OEMCrypto_ERROR_INVALID_CONTEXT; - usage_entry_->Deactivate(pst); - return OEMCrypto_SUCCESS; + usage_entry_->ForbidReport(); + return ODK_DeactivateUsageEntry(&clock_values_); } OEMCryptoResult SessionContext::ReportUsage(const std::vector& pst, diff --git a/oemcrypto/ref/src/oemcrypto_session.h b/oemcrypto/ref/src/oemcrypto_session.h index 5843f50..e10b8c7 100644 --- a/oemcrypto/ref/src/oemcrypto_session.h +++ b/oemcrypto/ref/src/oemcrypto_session.h @@ -118,8 +118,6 @@ class SessionContext { OEMCrypto_Algorithm algorithm, const uint8_t* signature, size_t signature_length); - void StartTimer(); - uint32_t CurrentTimer(); // (seconds). virtual OEMCryptoResult LoadLicense(const uint8_t* message, size_t message_length, size_t core_message_length, @@ -153,6 +151,11 @@ class SessionContext { bool EncryptRSAKey(const uint8_t* pkcs8_rsa_key, size_t enc_rsa_key_length, const uint8_t* enc_rsa_key_iv, uint8_t* enc_rsa_key); bool LoadRSAKey(const uint8_t* pkcs8_rsa_key, size_t rsa_key_length); + virtual OEMCryptoResult LoadRenewal(const uint8_t* message, + size_t message_length, + size_t core_message_length, + const uint8_t* signature, + size_t signature_length); virtual OEMCryptoResult RefreshKey( const KeyId& key_id, const std::vector& key_control, const std::vector& key_control_iv); @@ -271,7 +274,7 @@ class SessionContext { uint8_t license_request_hash_[ODK_SHA256_HASH_SIZE]; RSA_shared_ptr rsa_key_; uint32_t allowed_schemes_; // for RSA signatures. - int64_t timer_start_; // TODO(b/140764222): delete. + bool decrypt_started_; // If the license has been used in this session. ODK_TimerLimits timer_limits_; ODK_ClockValues clock_values_; UsageTableEntry* usage_entry_; diff --git a/oemcrypto/ref/src/oemcrypto_usage_table_ref.cpp b/oemcrypto/ref/src/oemcrypto_usage_table_ref.cpp index a08d06c..1c68c48 100644 --- a/oemcrypto/ref/src/oemcrypto_usage_table_ref.cpp +++ b/oemcrypto/ref/src/oemcrypto_usage_table_ref.cpp @@ -19,6 +19,7 @@ #include "file_store.h" #include "log.h" +#include "odk.h" #include "oemcrypto_engine_ref.h" // TODO(fredgc): Setting the device files base bath is currently broken as // wvcdm::Properties is no longer used by the reference code. @@ -70,8 +71,7 @@ OEMCryptoResult UsageTableEntry::SetPST(const uint8_t* pst, size_t pst_length) { data_.pst_length = pst_length; if (!pst || !pst_length) return OEMCrypto_ERROR_INVALID_CONTEXT; memcpy(data_.pst, pst, pst_length); - data_.time_of_license_received = - usage_table_->ce_->RollbackCorrectedOfflineTime(); + data_.time_of_license_received = usage_table_->ce_->SystemTime(); return OEMCrypto_SUCCESS; } @@ -100,25 +100,7 @@ bool UsageTableEntry::SetMacKeys(const std::vector& server, return true; } -bool UsageTableEntry::CheckForUse() { - if (Inactive()) return false; - recent_decrypt_ = true; - if (data_.status == kUnused) { - data_.status = kActive; - data_.time_of_first_decrypt = - usage_table_->ce_->RollbackCorrectedOfflineTime(); - data_.generation_number++; - usage_table_->IncrementGeneration(); - } - return true; -} - -void UsageTableEntry::Deactivate(const std::vector& pst) { - if (data_.status == kUnused) { - data_.status = kInactiveUnused; - } else if (data_.status == kActive) { - data_.status = kInactiveUsed; - } +void UsageTableEntry::ForbidReport() { forbid_report_ = true; data_.generation_number++; usage_table_->IncrementGeneration(); @@ -150,7 +132,7 @@ OEMCryptoResult UsageTableEntry::ReportUsage(const std::vector& pst, return OEMCrypto_ERROR_INVALID_CONTEXT; } wvcdm::Unpacked_PST_Report pst_report(buffer); - int64_t now = usage_table_->ce_->RollbackCorrectedOfflineTime(); + int64_t now = usage_table_->ce_->SystemTime(); pst_report.set_seconds_since_license_received(now - data_.time_of_license_received); pst_report.set_seconds_since_first_decrypt(now - data_.time_of_first_decrypt); @@ -169,12 +151,29 @@ OEMCryptoResult UsageTableEntry::ReportUsage(const std::vector& pst, return OEMCrypto_SUCCESS; } -void UsageTableEntry::UpdateAndIncrement() { +void UsageTableEntry::UpdateAndIncrement(ODK_ClockValues* clock_values) { if (recent_decrypt_) { - data_.time_of_last_decrypt = - usage_table_->ce_->RollbackCorrectedOfflineTime(); + data_.time_of_last_decrypt = usage_table_->ce_->SystemTime(); recent_decrypt_ = false; } + data_.time_of_license_received = clock_values->time_of_license_signed; + data_.time_of_first_decrypt = clock_values->time_of_first_decrypt; + // Use the most recent time_of_last_decrypt. + if (static_cast(data_.time_of_last_decrypt) < + clock_values->time_of_last_decrypt) { + // For the reference implementation, we update the clock_values on every + // decrypt. + data_.time_of_last_decrypt = clock_values->time_of_last_decrypt; + } else { + // For this reference implementation of OEMCrypto, we regularly update + // clock_values->time_of_last_decrypt and we could just update + // data_.time_of_last_decrypt here. However, I'm including the line below to + // make it clear that you could do it the other way around. When this + // function is called, the two values should be synced so that the usage + // entry can be saved with the correct value. + clock_values->time_of_last_decrypt = data_.time_of_last_decrypt; + } + data_.status = clock_values->status; data_.generation_number++; usage_table_->IncrementGeneration(); forbid_report_ = false; @@ -193,7 +192,7 @@ OEMCryptoResult UsageTableEntry::SaveData(CryptoEngine* ce, reinterpret_cast(&clear_buffer[0]); SignedEntryBlock* encrypted = reinterpret_cast(signed_buffer); - clear->data = this->data_; // Copy the current data. + clear->data = data_; // Copy the current data. memcpy(clear->verification, kEntryVerification, kMagicLength); // This should be encrypted and signed with a device specific key. @@ -227,7 +226,8 @@ OEMCryptoResult UsageTableEntry::SaveData(CryptoEngine* ce, } OEMCryptoResult UsageTableEntry::LoadData(CryptoEngine* ce, uint32_t index, - const std::vector& buffer) { + const std::vector& buffer, + ODK_ClockValues* clock_values) { if (buffer.size() < SignedEntrySize()) return OEMCrypto_ERROR_SHORT_BUFFER; if (buffer.size() > SignedEntrySize()) LOGW("LoadUsageTableEntry: buffer is large. %d > %d", buffer.size(), @@ -294,8 +294,10 @@ OEMCryptoResult UsageTableEntry::LoadData(CryptoEngine* ce, uint32_t index, LOGE("LoadUsageEntry: entry has bad status %d", clear->data.status); return OEMCrypto_ERROR_UNKNOWN_FAILURE; } - this->data_ = clear->data; - return OEMCrypto_SUCCESS; + data_ = clear->data; + return ODK_ReloadClockValues( + clock_values, data_.time_of_license_received, data_.time_of_first_decrypt, + data_.time_of_last_decrypt, data_.status, ce->SystemTime()); } size_t UsageTableEntry::SignedEntrySize() { @@ -314,12 +316,10 @@ size_t UsageTable::SignedHeaderSize(size_t count) { return blocks * wvoec::KEY_IV_SIZE; } -OEMCryptoResult UsageTable::UpdateUsageEntry(SessionContext* session, - UsageTableEntry* entry, - uint8_t* header_buffer, - size_t* header_buffer_length, - uint8_t* entry_buffer, - size_t* entry_buffer_length) { +OEMCryptoResult UsageTable::UpdateUsageEntry( + SessionContext* session, UsageTableEntry* entry, uint8_t* header_buffer, + size_t* header_buffer_length, uint8_t* entry_buffer, + size_t* entry_buffer_length, ODK_ClockValues* clock_values) { size_t signed_header_size = SignedHeaderSize(generation_numbers_.size()); if (*entry_buffer_length < UsageTableEntry::SignedEntrySize() || *header_buffer_length < signed_header_size) { @@ -331,7 +331,7 @@ OEMCryptoResult UsageTable::UpdateUsageEntry(SessionContext* session, *header_buffer_length = signed_header_size; if ((!header_buffer) || (!entry_buffer)) return OEMCrypto_ERROR_UNKNOWN_FAILURE; - entry->UpdateAndIncrement(); + entry->UpdateAndIncrement(clock_values); generation_numbers_[entry->index()] = entry->generation_number(); OEMCryptoResult result = entry->SaveData(ce_, session, entry_buffer, *entry_buffer_length); @@ -371,7 +371,8 @@ OEMCryptoResult UsageTable::CreateNewUsageEntry(SessionContext* session, OEMCryptoResult UsageTable::LoadUsageEntry(SessionContext* session, UsageTableEntry** entry, uint32_t index, - const std::vector& buffer) { + const std::vector& buffer, + ODK_ClockValues* clock_values) { if (!header_loaded_) { LOGE("CreateNewUsageEntry: Header not loaded."); return OEMCrypto_ERROR_UNKNOWN_FAILURE; @@ -390,7 +391,8 @@ OEMCryptoResult UsageTable::LoadUsageEntry(SessionContext* session, } UsageTableEntry* new_entry = MakeEntry(index); - OEMCryptoResult status = new_entry->LoadData(ce_, index, buffer); + OEMCryptoResult status = + new_entry->LoadData(ce_, index, buffer, clock_values); if (status != OEMCrypto_SUCCESS) { delete new_entry; return status; diff --git a/oemcrypto/ref/src/oemcrypto_usage_table_ref.h b/oemcrypto/ref/src/oemcrypto_usage_table_ref.h index 31913f7..661ba3a 100644 --- a/oemcrypto/ref/src/oemcrypto_usage_table_ref.h +++ b/oemcrypto/ref/src/oemcrypto_usage_table_ref.h @@ -13,8 +13,9 @@ #include #include "OEMCryptoCENC.h" -#include "openssl/sha.h" +#include "odk_structs.h" #include "oemcrypto_types.h" +#include "openssl/sha.h" namespace wvoec_ref { @@ -42,28 +43,34 @@ class UsageTableEntry { UsageTableEntry(UsageTable* table, uint32_t index, int64_t generation); virtual ~UsageTableEntry(); // Free memory, remove reference in header. bool Inactive() { return data_.status >= kInactive; } + // Mark this entry as modified and forbid a usage report until the data has + // been saved. This is done on important events like first decrypt and + // deactivation. + void ForbidReport(); OEMCryptoResult SetPST(const uint8_t* pst, size_t pst_length); bool VerifyPST(const uint8_t* pst, size_t pst_length); bool VerifyMacKeys(const std::vector& server, const std::vector& client); bool SetMacKeys(const std::vector& server, const std::vector& client); - // Returns false if the entry is inactive. Otherwise, returns true. - // If the status was unused, it is updated, and decrypt times are flaged - // for update. - bool CheckForUse(); - void Deactivate(const std::vector& pst); virtual OEMCryptoResult ReportUsage(const std::vector& pst, uint8_t* buffer, size_t* buffer_length); - virtual void UpdateAndIncrement(); + virtual void UpdateAndIncrement(ODK_ClockValues* clock_values); + // Save all data to the give buffer. This should be called after updating the + // data. OEMCryptoResult SaveData(CryptoEngine* ce, SessionContext* session, uint8_t* signed_buffer, size_t buffer_size); + // Load all data from the buffer, and then update clock_values. OEMCryptoResult LoadData(CryptoEngine* ce, uint32_t index, - const std::vector& buffer); + const std::vector& buffer, + ODK_ClockValues* clock_values); int64_t generation_number() { return data_.generation_number; } void set_generation_number(int64_t value) { data_.generation_number = value; } void set_index(int32_t index) { data_.index = index; } uint32_t index() { return data_.index; } + void set_recent_decrypt(bool recent_decrypt) { + recent_decrypt_ = recent_decrypt; + } static size_t SignedEntrySize(); const uint8_t* mac_key_server() { return data_.mac_key_server; } const uint8_t* mac_key_client() { return data_.mac_key_client; } @@ -85,13 +92,12 @@ class UsageTable { uint32_t* usage_entry_number); OEMCryptoResult LoadUsageEntry(SessionContext* session, UsageTableEntry** entry, uint32_t index, - const std::vector& buffer); - OEMCryptoResult UpdateUsageEntry(SessionContext* session, - UsageTableEntry* entry, - uint8_t* header_buffer, - size_t* header_buffer_length, - uint8_t* entry_buffer, - size_t* entry_buffer_length); + const std::vector& buffer, + ODK_ClockValues* clock_values); + OEMCryptoResult UpdateUsageEntry( + SessionContext* session, UsageTableEntry* entry, uint8_t* header_buffer, + size_t* header_buffer_length, uint8_t* entry_buffer, + size_t* entry_buffer_length, ODK_ClockValues* clock_values); OEMCryptoResult MoveEntry(UsageTableEntry* entry, uint32_t new_index); OEMCryptoResult CreateUsageTableHeader(uint8_t* header_buffer, size_t* header_buffer_length); diff --git a/oemcrypto/test/oec_session_util.cpp b/oemcrypto/test/oec_session_util.cpp index 3ed8190..3a4f440 100644 --- a/oemcrypto/test/oec_session_util.cpp +++ b/oemcrypto/test/oec_session_util.cpp @@ -319,7 +319,9 @@ void LicenseRoundTrip::FillAndVerifyCoreRequest( EXPECT_TRUE( oec_util::ParseLicenseRequest(core_message_string, &core_request_)); EXPECT_EQ(global_features.api_version, core_request_.api_version); - EXPECT_EQ(session()->nonce(), core_request_.nonce); + if (expect_request_has_correct_nonce_) { + EXPECT_EQ(session()->nonce(), core_request_.nonce); + } EXPECT_EQ(session()->session_id(), core_request_.session_id); if (api_version_ == 0) api_version_ = core_request_.api_version; } @@ -442,6 +444,7 @@ void LicenseRoundTrip::EncryptAndSignResponse() { for (size_t i = 0; i < encrypted_response_.size(); i++) { encrypted_response_[i] = i % 0x100; } + ASSERT_GE(kMaxCoreMessage, serialized_core_message_.size()); ASSERT_GE(encrypted_response_.size(), serialized_core_message_.size()); memcpy(encrypted_response_.data(), serialized_core_message_.data(), serialized_core_message_.size()); @@ -459,10 +462,20 @@ void LicenseRoundTrip::EncryptAndSignResponse() { } OEMCryptoResult LicenseRoundTrip::LoadResponse() { + // Some tests adjust the offset to be beyond the length of the message. Here, + // we create a duplicate of the message buffer so that these offsets do not + // point to garbage data. The goal is to make sure OEMCrypto is verifying that + // the offset points outside of the message -- we don't want OEMCrypto to look + // at what offset points to and return an error if the data is garbage. Since + // the memory after the message buffer is an exact copy of the message, we can + // increment the offset by the message size and get valid data. + std::vector double_message = encrypted_response_; + double_message.insert(double_message.end(), encrypted_response_.begin(), + encrypted_response_.end()); OEMCryptoResult result; if (api_version_ < kCoreMessagesAPI) { result = OEMCrypto_LoadKeys( - session_->session_id(), encrypted_response_.data(), + session_->session_id(), double_message.data(), encrypted_response_.size(), response_signature_.data(), response_signature_.size(), core_response_.enc_mac_keys_iv, core_response_.enc_mac_keys, core_response_.key_array_length, @@ -471,7 +484,7 @@ OEMCryptoResult LicenseRoundTrip::LoadResponse() { static_cast(core_response_.license_type)); } else { result = OEMCrypto_LoadLicense( - session_->session_id(), encrypted_response_.data(), + session_->session_id(), double_message.data(), encrypted_response_.size(), serialized_core_message_.size(), response_signature_.data(), response_signature_.size()); } @@ -480,15 +493,22 @@ OEMCryptoResult LicenseRoundTrip::LoadResponse() { // call SelectKey, use key control information, and so that it has key data // to verify decrypt operations. session_->set_license(response_data_); - VerifyTestKeys(); + // Also, if the license has new mac keys, then install them now. + if (core_response_.enc_mac_keys.length > 0) { + session_->set_mac_keys(response_data_.mac_keys); + } + + // Note: we verify content licenses here. For entitlement license, we verify + // the key control blocks after loading entitled content keys. + if (license_type_ == OEMCrypto_ContentLicense) VerifyTestKeys(); } return result; } // This function verifies that the key control block reported by OEMCrypto agree // with the truth key control block. Failures in this function probably -// indicate the OEMCrypto_LoadKeys did not correctly process the key control -// block. +// indicate the OEMCrypto_LoadLicense/LoadKeys did not correctly process the key +// control block. void LicenseRoundTrip::VerifyTestKeys() { for (unsigned int i = 0; i < num_keys_; i++) { KeyControlBlock block; @@ -512,6 +532,253 @@ void LicenseRoundTrip::VerifyTestKeys() { } } +void LicenseRoundTrip::SetKeyId(size_t index, const string& key_id) { + ASSERT_LT(index, num_keys_); + MessageKeyData& key = response_data_.keys[index]; + key.key_id_length = key_id.length(); + ASSERT_LE(key.key_id_length, kTestKeyIdMaxLength); + memcpy(key.key_id, key_id.data(), key.key_id_length); +} + +void EntitledMessage::FillKeyArray() { + for (size_t i = 0; i < license_messages_->num_keys(); ++i) { + MakeOneKey(i); + } +} + +void EntitledMessage::MakeOneKey(size_t entitlement_key_index) { + ASSERT_LT(entitlement_key_index, kMaxNumKeys); + ASSERT_LT(num_keys_, kMaxNumKeys); + EntitledContentKeyData* key_data = &entitled_key_data_[num_keys_]; + MessageKeyData* entitlement_key = + &license_messages_->response_data().keys[entitlement_key_index]; + OEMCrypto_EntitledContentKeyObject* offsets = &entitled_key_array_[num_keys_]; + num_keys_++; + + key_data->key_index = entitlement_key_index; + ASSERT_LE(entitlement_key->key_id_length, kTestKeyIdMaxLength); + memcpy(key_data->entitlement_key_id, entitlement_key->key_id, + entitlement_key->key_id_length); + key_data->entitlement_key_id_length = entitlement_key->key_id_length; + offsets->entitlement_key_id = FindSubstring(key_data->entitlement_key_id, + entitlement_key->key_id_length); + + key_data->content_key_id_length = kDefaultKeyIdLength; + // Fill the key ID as CnCnCnCn... so it's easy to see in debug logs. + memset(key_data->content_key_id, 0xC0 + num_keys_, + key_data->content_key_id_length); + offsets->content_key_id = + FindSubstring(key_data->content_key_id, key_data->content_key_id_length); + + EXPECT_EQ(1, GetRandBytes(key_data->content_key_data, + sizeof(key_data->content_key_data))); + // Note: we give the encrypted content key to OEMCrypto, not the clear + // content key. + offsets->content_key_data = + FindSubstring(key_data->encrypted_content_key_data, + sizeof(key_data->encrypted_content_key_data)); + + EXPECT_EQ(1, GetRandBytes(key_data->content_key_data_iv, + sizeof(key_data->content_key_data_iv))); + offsets->content_key_data_iv = FindSubstring( + key_data->content_key_data_iv, sizeof(key_data->content_key_data_iv)); +} + +void EntitledMessage::SetEntitlementKeyId(unsigned int index, + const std::string& key_id) { + ASSERT_LT(index, num_keys_); + ASSERT_LE(key_id.size(), kTestKeyIdMaxLength); + entitled_key_data_[index].entitlement_key_id_length = key_id.size(); + memcpy(entitled_key_data_[index].entitlement_key_id, + reinterpret_cast(key_id.c_str()), key_id.length()); + entitled_key_array_[index].entitlement_key_id = FindSubstring( + entitled_key_data_[index].entitlement_key_id, key_id.length()); +} + +OEMCrypto_Substring EntitledMessage::FindSubstring(const void* ptr, + size_t size) { + OEMCrypto_Substring substring{0, 0}; + if (ptr != nullptr) { + substring.offset = reinterpret_cast(ptr) - + reinterpret_cast(entitled_key_data_); + substring.length = size; + } + return substring; +} + +void EntitledMessage::LoadKeys(OEMCryptoResult expected_sts) { + for (size_t i = 0; i < num_keys_; ++i) { + EntitledContentKeyData* key_data = &entitled_key_data_[i]; + const size_t entitlement_key_index = key_data->key_index; + MessageKeyData* entitlement_key = + &license_messages_->response_data().keys[entitlement_key_index]; + + // Load the entitlement key from |key_array_|. + AES_KEY aes_key; + AES_set_encrypt_key(entitlement_key->key_data, 256, &aes_key); + + // Encrypt the content key with the entitlement key. + uint8_t iv[16]; + memcpy(&iv[0], key_data->content_key_data_iv, KEY_IV_SIZE); + AES_cbc_encrypt(key_data->content_key_data, + key_data->encrypted_content_key_data, KEY_SIZE, &aes_key, + iv, AES_ENCRYPT); + } + ASSERT_EQ(expected_sts, + OEMCrypto_LoadEntitledContentKeys( + license_messages_->session()->session_id(), + reinterpret_cast(entitled_key_data_), + sizeof(entitled_key_data_), num_keys_, entitled_key_array_)); + if (expected_sts != OEMCrypto_SUCCESS) { + return; + } + VerifyEntitlementTestKeys(); +} + +// This function verifies that the key control block reported by OEMCrypto agree +// with the truth key control block. Failures in this function probably +// indicate the OEMCrypto_LoadEntitledKeys did not correctly process the key +// control block. +void EntitledMessage::VerifyEntitlementTestKeys() { + for (unsigned int i = 0; i < num_keys_; i++) { + EntitledContentKeyData* key_data = &entitled_key_data_[i]; + const size_t entitlement_key_index = key_data->key_index; + MessageKeyData* entitlement_key = + &license_messages_->response_data().keys[entitlement_key_index]; + KeyControlBlock block; + size_t size = sizeof(block); + OEMCryptoResult sts = OEMCrypto_QueryKeyControl( + license_messages_->session()->session_id(), key_data->content_key_id, + key_data->content_key_id_length, reinterpret_cast(&block), + &size); + if (sts != OEMCrypto_ERROR_NOT_IMPLEMENTED) { + ASSERT_EQ(OEMCrypto_SUCCESS, sts); + ASSERT_EQ(sizeof(block), size); + // control duration and bits stored in network byte order. For printing + // we change to host byte order. + ASSERT_EQ((htonl_fnc(entitlement_key->control.duration)), + (htonl_fnc(block.duration))) + << "For key " << i; + ASSERT_EQ(htonl_fnc(entitlement_key->control.control_bits), + htonl_fnc(block.control_bits)) + << "For key " << i; + } + } +} + +void RenewalRoundTrip::VerifyRequestSignature( + const vector& data, const vector& generated_signature, + size_t core_message_length) { + ASSERT_EQ(HMAC_SHA256_SIGNATURE_SIZE, generated_signature.size()); + std::vector expected_signature; + if (license_messages_->api_version() < kCoreMessagesAPI) { + // For v15 or earlier, we only sign the message body. Ignore the core + // message. + std::vector subdata(data.begin() + core_message_length, + data.end()); + session()->key_deriver().ClientSignBuffer(subdata, &expected_signature); + } else { + session()->key_deriver().ClientSignBuffer(data, &expected_signature); + } + ASSERT_EQ(expected_signature, generated_signature); +} + +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. + EXPECT_FALSE( + oec_util::ParseRenewalRequest(core_message_string, &core_request_)); + } else { + EXPECT_TRUE( + oec_util::ParseRenewalRequest(core_message_string, &core_request_)); + EXPECT_EQ(license_messages_->core_request().api_version, + core_request_.api_version); + 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) { + uint32_t control = 0; + uint32_t nonce = 0; + // If this is a v15 device, and a v15 license, and the license used a nonce, + // then the response should require a new nonce, too. + if (global_features.api_version < kCoreMessagesAPI && + (license_messages_->control() & wvoec::kControlNonceEnabled)) { + control = wvoec::kControlNonceEnabled; + session_->GenerateNonce(); + nonce = session_->nonce(); + } + // A single key object with no key id should update all keys. + 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_version); + memcpy(response_data_.keys[index].control.verification, kcVersion.c_str(), + 4); + const uint32_t duration = static_cast( + license_messages_->core_response() + .timer_limits.renewal_playback_duration_seconds); + response_data_.keys[index].control.duration = htonl(duration); + response_data_.keys[index].control.nonce = htonl(nonce); + response_data_.keys[index].control.control_bits = htonl(control); + } +} + +void RenewalRoundTrip::EncryptAndSignResponse() { + // Renewal messages are not encrypted. + encrypted_response_data_ = response_data_; + + // Stripe the encrypted message. + encrypted_response_.resize(message_size_); + for (size_t i = 0; i < encrypted_response_.size(); i++) { + encrypted_response_[i] = i % 0x100; + } + // Either create a KeyRefreshObject for a call to RefreshKeys or a core + // response for a call to LoadRenewal. + if (license_messages_->api_version() < kCoreMessagesAPI) { + refresh_object_.key_id = FindSubstring(nullptr, 0); + refresh_object_.key_control_iv = FindSubstring(nullptr, 0); + refresh_object_.key_control = + FindSubstring(&response_data_.keys[0].control, + sizeof(response_data_.keys[0].control)); + serialized_core_message_.resize(0); + } else { + ASSERT_TRUE( + CreateCoreRenewalResponse(core_request_, &serialized_core_message_)); + } + // Concatenate the core message and the response. + ASSERT_GE(kMaxCoreMessage, serialized_core_message_.size()); + ASSERT_GE(encrypted_response_.size(), serialized_core_message_.size()); + memcpy(encrypted_response_.data(), serialized_core_message_.data(), + serialized_core_message_.size()); + ASSERT_GE(encrypted_response_.size(), + serialized_core_message_.size() + sizeof(encrypted_response_data_)); + memcpy(encrypted_response_.data() + serialized_core_message_.size(), + reinterpret_cast(&encrypted_response_data_), + sizeof(encrypted_response_data_)); + session()->key_deriver().ServerSignBuffer(encrypted_response_.data(), + encrypted_response_.size(), + &response_signature_); +} + +OEMCryptoResult RenewalRoundTrip::LoadResponse() { + if (license_messages_->api_version() < kCoreMessagesAPI) { + return OEMCrypto_RefreshKeys( + session_->session_id(), encrypted_response_.data(), + encrypted_response_.size(), response_signature_.data(), + response_signature_.size(), 1, &refresh_object_); + } else { + return OEMCrypto_LoadRenewal( + session_->session_id(), encrypted_response_.data(), + encrypted_response_.size(), serialized_core_message_.size(), + response_signature_.data(), response_signature_.size()); + } +} + Session::Session() : open_(false), forced_session_id_(false), @@ -859,13 +1126,6 @@ void Session::RefreshTestKeys(const size_t key_count, uint32_t control_bits, } } -void Session::SetKeyId(int index, const string& key_id) { - MessageKeyData& key = license_.keys[index]; - key.key_id_length = key_id.length(); - ASSERT_LE(key.key_id_length, kTestKeyIdMaxLength); - memcpy(key.key_id, key_id.data(), key.key_id_length); -} - void Session::FillSimpleMessage(uint32_t duration, uint32_t control, uint32_t nonce, const std::string& pst) { EXPECT_EQ( diff --git a/oemcrypto/test/oec_session_util.h b/oemcrypto/test/oec_session_util.h index 8a7a5dd..23c0bcd 100644 --- a/oemcrypto/test/oec_session_util.h +++ b/oemcrypto/test/oec_session_util.h @@ -104,10 +104,14 @@ struct Test_PST_Report { }; struct EntitledContentKeyData { - uint8_t entitlement_key_id[KEY_SIZE]; - uint8_t content_key_id[KEY_SIZE]; - uint8_t content_key_data_iv[KEY_SIZE]; + uint8_t entitlement_key_id[kTestKeyIdMaxLength]; + size_t entitlement_key_id_length; + uint8_t content_key_id[kTestKeyIdMaxLength]; + size_t content_key_id_length; + uint8_t content_key_data_iv[KEY_IV_SIZE]; uint8_t content_key_data[KEY_SIZE]; + uint8_t encrypted_content_key_data[KEY_SIZE]; + size_t key_index; // Index into the license's key array. Only for testing. }; // returns 1 on success, -1 if not supported, or 0 if other failure. @@ -219,9 +223,10 @@ class RoundTrip { }; class ProvisioningRoundTrip - : public RoundTrip { + /* CoreResponse */ ODK_ParsedProvisioning, + /* ResponseData */ RSAPrivateKeyMessage> { public: ProvisioningRoundTrip(Session* session, const std::vector& encoded_rsa_key) @@ -257,20 +262,21 @@ class ProvisioningRoundTrip std::vector wrapped_rsa_key_; }; -class LicenseRoundTrip : public RoundTrip { +class LicenseRoundTrip + : public RoundTrip { public: LicenseRoundTrip(Session* session) : RoundTrip(session), - key_array_(), - license_request_hash_(), control_(wvoec::kControlNonceEnabled), num_keys_(4), pst_(""), minimum_srm_version_(0), update_mac_keys_(true), api_version_(0), + expect_request_has_correct_nonce_(true), license_type_(OEMCrypto_ContentLicense) {} void CreateDefaultResponse() override; // Fill the |core_response| substrings. @@ -278,7 +284,6 @@ class LicenseRoundTrip : public RoundTrip& data, @@ -313,10 +328,6 @@ class LicenseRoundTrip : public RoundTrip { +class RenewalRoundTrip : public RoundTrip< + /* CoreRequest */ oec_util::ODK_RenewalRequest, + OEMCrypto_PrepAndSignRenewalRequest, + // Renewal response info is same as request: + /* CoreResponse */ oec_util::ODK_RenewalRequest, + /* ResponseData */ MessageData> { public: - RenewalRoundTrip(Session* session) : RoundTrip(session) {} + RenewalRoundTrip(LicenseRoundTrip* license_messages) + : RoundTrip(license_messages->session()), + license_messages_(license_messages), + refresh_object_() {} + void CreateDefaultResponse() override; + void EncryptAndSignResponse() override; + OEMCryptoResult LoadResponse() override; protected: void VerifyRequestSignature(const vector& data, @@ -352,6 +373,34 @@ class RenewalRoundTrip // Verify the values of the core response. virtual void FillAndVerifyCoreRequest( const std::string& core_message_string) override; + LicenseRoundTrip* license_messages_; + OEMCrypto_KeyRefreshObject refresh_object_; +}; + +class EntitledMessage { + public: + EntitledMessage(LicenseRoundTrip* license_messages) + : license_messages_(license_messages), num_keys_() {} + void FillKeyArray(); + void MakeOneKey(size_t entitlement_key_index); + void LoadKeys(OEMCryptoResult expected_sts); + void set_num_keys(uint32_t num_keys) { num_keys_ = num_keys; } + uint32_t num_keys() const { return num_keys_; } + void SetEntitlementKeyId(unsigned int index, const std::string& key_id); + // Verify that key control blocks of the loaded keys. + void VerifyEntitlementTestKeys(); + + private: + // Find the offset of the give pointer, relative to |entitled_key_data_|. + OEMCrypto_Substring FindSubstring(const void* ptr, size_t size); + + LicenseRoundTrip* license_messages_; + uint32_t num_keys_; + // Clear Entitlement key data. This is the backing data for + // |entitled_key_array_|. + EntitledContentKeyData entitled_key_data_[kMaxNumKeys]; + // Entitled key object. Pointers are backed by |entitled_key_data_|. + OEMCrypto_EntitledContentKeyObject entitled_key_array_[kMaxNumKeys]; }; class Session { @@ -415,9 +464,6 @@ class Session { // server's mac key, and calls OEMCrypto_RefreshKeys. void RefreshTestKeys(const size_t key_count, uint32_t control_bits, uint32_t nonce, OEMCryptoResult expected_result); - // This sets the key id in the current message data to the specified string. - // This is used to test with different key id lengths. - void SetKeyId(int index, const string& key_id); // This fills the data structure license_ with key information. This data // can be modified, and then should be encrypted and signed in EncryptAndSign // before being loaded in LoadTestKeys. @@ -622,6 +668,9 @@ class Session { // Pointer to buffer holding |encrypted_entitled_message_| const uint8_t* encrypted_entitled_message_ptr(); const KeyDeriver& key_deriver() const { return key_deriver_; } + void set_mac_keys(const uint8_t* mac_keys) { + key_deriver_.set_mac_keys(mac_keys); + } private: bool open_; diff --git a/oemcrypto/test/oemcrypto_test.cpp b/oemcrypto/test/oemcrypto_test.cpp index 0e8f467..b29da34 100644 --- a/oemcrypto/test/oemcrypto_test.cpp +++ b/oemcrypto/test/oemcrypto_test.cpp @@ -711,9 +711,12 @@ TEST_F(OEMCryptoProv30Test, GetCertOnlyAPI16) { // loaded. ASSERT_NO_FATAL_FAILURE(s.GenerateDerivedKeysFromSessionKey()); // Now fill a message and try to load it. - ASSERT_NO_FATAL_FAILURE(s.FillSimpleMessage(kDuration, 0, 0)); - ASSERT_NO_FATAL_FAILURE(s.EncryptAndSign()); - ASSERT_NO_FATAL_FAILURE(s.LoadTestKeys()); + LicenseRoundTrip license_messages(&s); + license_messages.set_control(0); + ASSERT_NO_FATAL_FAILURE(license_messages.SignAndVerifyRequest()); + ASSERT_NO_FATAL_FAILURE(license_messages.CreateDefaultResponse()); + ASSERT_NO_FATAL_FAILURE(license_messages.EncryptAndSignResponse()); + ASSERT_EQ(OEMCrypto_SUCCESS, license_messages.LoadResponse()); } // @@ -790,6 +793,7 @@ class OEMCryptoLicenseTestAPI16 : public OEMCryptoSessionTests { // current or one version old. class OEMCryptoLicenseTest : public OEMCryptoLicenseTestAPI16, public WithParamInterface { + protected: void SetUp() override { // The only difference between this class and it's parent is that we use a // different license api: @@ -799,6 +803,17 @@ class OEMCryptoLicenseTest : public OEMCryptoLicenseTestAPI16, } }; +// This class is used to test a license that is only for v15 license. +class OEMCryptoLicenseTestAPI15 : public OEMCryptoLicenseTestAPI16 { + void SetUp() override { + // The only difference between this class and it's parent is that we use a + // different license api: + license_api_version_ = 15; + license_messages_.set_api_version(license_api_version_); + OEMCryptoLicenseTestAPI16::SetUp(); + } +}; + // This class is used to test each key control block verification string in the // range kc09-kc1?. This test is parameterized by the API number in the key // control lock. @@ -861,48 +876,51 @@ TEST_P(OEMCryptoLicenseTest, LoadKeyWithNonceTwice) { } // This verifies that entitlement keys and entitled content keys can be loaded. -TEST_F(OEMCryptoSessionTests, LoadEntitlementKeysAPI14) { - Session s; - ASSERT_NO_FATAL_FAILURE(s.open()); - ASSERT_NO_FATAL_FAILURE(InstallTestRSAKey(&s)); - ASSERT_NO_FATAL_FAILURE(s.FillSimpleEntitlementMessage(0, 0, 0)); - ASSERT_NO_FATAL_FAILURE(s.EncryptAndSign()); - ASSERT_NO_FATAL_FAILURE(s.LoadEntitlementTestKeys()); - s.FillEntitledKeyArray(); - ASSERT_NO_FATAL_FAILURE(s.LoadEntitledContentKeys()); - s.FillEntitledKeyArray(); - ASSERT_NO_FATAL_FAILURE(s.LoadEntitledContentKeys()); +TEST_P(OEMCryptoLicenseTest, LoadEntitlementKeysAPI14) { + ASSERT_NO_FATAL_FAILURE(session_.GenerateNonce()); + license_messages_.set_license_type(OEMCrypto_EntitlementLicense); + ASSERT_NO_FATAL_FAILURE(license_messages_.SignAndVerifyRequest()); + ASSERT_NO_FATAL_FAILURE(license_messages_.CreateDefaultResponse()); + ASSERT_NO_FATAL_FAILURE(license_messages_.EncryptAndSignResponse()); + ASSERT_EQ(OEMCrypto_SUCCESS, license_messages_.LoadResponse()); + EntitledMessage entitled_message_1(&license_messages_); + entitled_message_1.FillKeyArray(); + ASSERT_NO_FATAL_FAILURE(entitled_message_1.LoadKeys(OEMCrypto_SUCCESS)); + EntitledMessage entitled_message_2(&license_messages_); + entitled_message_2.FillKeyArray(); + ASSERT_NO_FATAL_FAILURE(entitled_message_2.LoadKeys(OEMCrypto_SUCCESS)); } // This verifies that entitled content keys cannot be loaded if we have not yet // loaded the entitlement keys. -TEST_F(OEMCryptoSessionTests, LoadEntitlementKeysNoEntitlementKeysAPI14) { - Session s; - ASSERT_NO_FATAL_FAILURE(s.open()); - ASSERT_NO_FATAL_FAILURE(InstallTestRSAKey(&s)); - ASSERT_NO_FATAL_FAILURE(s.FillSimpleEntitlementMessage(0, 0, 0)); - ASSERT_NO_FATAL_FAILURE(s.EncryptAndSign()); - // We do NOT call LoadEntitlementTestKeys. - s.FillEntitledKeyArray(); - s.LoadEntitledContentKeys(OEMCrypto_ERROR_INVALID_CONTEXT); +TEST_P(OEMCryptoLicenseTest, LoadEntitlementKeysNoEntitlementKeysAPI14) { + ASSERT_NO_FATAL_FAILURE(session_.GenerateNonce()); + license_messages_.set_license_type(OEMCrypto_EntitlementLicense); + ASSERT_NO_FATAL_FAILURE(license_messages_.SignAndVerifyRequest()); + ASSERT_NO_FATAL_FAILURE(license_messages_.CreateDefaultResponse()); + ASSERT_NO_FATAL_FAILURE(license_messages_.EncryptAndSignResponse()); + EntitledMessage entitled_message_1(&license_messages_); + entitled_message_1.FillKeyArray(); + ASSERT_NO_FATAL_FAILURE( + entitled_message_1.LoadKeys(OEMCrypto_ERROR_INVALID_CONTEXT)); } // This verifies that entitled content keys cannot be loaded if we have loaded // the wrong entitlement keys. -TEST_F(OEMCryptoSessionTests, LoadEntitlementKeysWrongEntitlementKeysAPI14) { - Session s; - ASSERT_NO_FATAL_FAILURE(s.open()); - ASSERT_NO_FATAL_FAILURE(InstallTestRSAKey(&s)); - ASSERT_NO_FATAL_FAILURE(s.FillSimpleEntitlementMessage(0, 0, 0)); - ASSERT_NO_FATAL_FAILURE(s.EncryptAndSign()); - ASSERT_NO_FATAL_FAILURE(s.LoadEntitlementTestKeys()); - s.FillEntitledKeyArray(); +TEST_P(OEMCryptoLicenseTest, LoadEntitlementKeysWrongEntitlementKeysAPI14) { + ASSERT_NO_FATAL_FAILURE(session_.GenerateNonce()); + license_messages_.set_license_type(OEMCrypto_EntitlementLicense); + ASSERT_NO_FATAL_FAILURE(license_messages_.SignAndVerifyRequest()); + ASSERT_NO_FATAL_FAILURE(license_messages_.CreateDefaultResponse()); + ASSERT_NO_FATAL_FAILURE(license_messages_.EncryptAndSignResponse()); + ASSERT_EQ(OEMCrypto_SUCCESS, license_messages_.LoadResponse()); + EntitledMessage entitled_message_1(&license_messages_); + entitled_message_1.FillKeyArray(); const std::string key_id = "no_key"; - memcpy(const_cast(s.encrypted_entitled_message_ptr()) + - s.entitled_key_array()[0].entitlement_key_id.offset, - reinterpret_cast(key_id.c_str()), key_id.length()); - s.entitled_key_array()[0].entitlement_key_id.length = key_id.length(); - s.LoadEntitledContentKeys(OEMCrypto_KEY_NOT_ENTITLED); + entitled_message_1.SetEntitlementKeyId(0, key_id); + + ASSERT_NO_FATAL_FAILURE( + entitled_message_1.LoadKeys(OEMCrypto_KEY_NOT_ENTITLED)); } // This tests load license with an 8k license response. @@ -915,6 +933,21 @@ TEST_P(OEMCryptoLicenseTest, LoadKeyLargeBuffer) { ASSERT_EQ(OEMCrypto_SUCCESS, license_messages_.LoadResponse()); } +// Verify that you can't use LoadKeys on a v16 license. +TEST_F(OEMCryptoLicenseTestAPI16, UseWrongLoadAPI16) { + ASSERT_NO_FATAL_FAILURE(session_.GenerateNonce()); + ASSERT_NO_FATAL_FAILURE(license_messages_.SignAndVerifyRequest()); + ASSERT_NO_FATAL_FAILURE(license_messages_.CreateDefaultResponse()); + ASSERT_NO_FATAL_FAILURE(license_messages_.EncryptAndSignResponse()); + // After the license round trip was create for v16, we now tell it to use v15 + // so it call LoadKeys instead of LoadLicense. This means the license request + // was made from a v16 device, and the response was created and signed by a + // v16 server. So OEMCrypto should only accept it if we load it using + // LoadLicense. A call to LoadKeys should fail. + license_messages_.set_api_version(kCoreMessagesAPI - 1); + ASSERT_NE(OEMCrypto_SUCCESS, license_messages_.LoadResponse()); +} + // Returns a string containing two times the original message in continuous // memory. Used as part of the BadRange tests. std::string DuplicateMessage(MessageData& message) { @@ -924,384 +957,307 @@ std::string DuplicateMessage(MessageData& message) { return double_message; } -// The Bad Range tests verify that OEMCrypto_LoadKeys checks the range -// of all the pointers. It should reject a message if the pointer does -// not point into the message buffer. -TEST_F(OEMCryptoSessionTests, LoadKeyWithBadRange1) { - Session s; - ASSERT_NO_FATAL_FAILURE(s.open()); - ASSERT_NO_FATAL_FAILURE(InstallTestRSAKey(&s)); - ASSERT_NO_FATAL_FAILURE(s.FillSimpleMessage(0, 0, 0)); - ASSERT_NO_FATAL_FAILURE(s.EncryptAndSign()); - std::string double_message = DuplicateMessage(s.encrypted_license()); - OEMCrypto_Substring wrong_mac_keys = s.enc_mac_keys_substr(); - wrong_mac_keys.offset += s.message_size(); - - OEMCryptoResult sts = OEMCrypto_LoadKeys( - s.session_id(), reinterpret_cast(double_message.data()), - s.message_size(), s.signature().data(), s.signature().size(), - s.enc_mac_keys_iv_substr(), - wrong_mac_keys, // Not within range of one message. - s.num_keys(), s.key_array(), GetSubstring(), GetSubstring(), - OEMCrypto_ContentLicense); - ASSERT_NE(OEMCrypto_SUCCESS, sts); +//---------------------------------------------------------------------------// +//---------------------------------------------------------------------------// +// Each of the following LoadKeyWithBadRange_* tests is similar. They verify +// that OEMCrypto_LoadLicense checks the range of all the pointers. It should +// reject a message if the pointer does not point into the message buffer. +//---------------------------------------------------------------------------// +//---------------------------------------------------------------------------// +TEST_P(OEMCryptoLicenseTest, LoadKeyWithBadRange_enc_mac_keys) { + ASSERT_NO_FATAL_FAILURE(session_.GenerateNonce()); + ASSERT_NO_FATAL_FAILURE(license_messages_.SignAndVerifyRequest()); + ASSERT_NO_FATAL_FAILURE(license_messages_.CreateDefaultResponse()); + // See the comment in LicenseRoundTrip::LoadResponse for why we increment by + // the message size. + license_messages_.core_response().enc_mac_keys.offset += + license_messages_.message_size(); + ASSERT_NO_FATAL_FAILURE(license_messages_.EncryptAndSignResponse()); + ASSERT_NE(OEMCrypto_SUCCESS, license_messages_.LoadResponse()); } -// The Bad Range tests verify that OEMCrypto_LoadKeys checks the range -// of all the pointers. It should reject a message if the pointer does -// not point into the message buffer. -TEST_F(OEMCryptoSessionTests, LoadKeyWithBadRange2) { - Session s; - ASSERT_NO_FATAL_FAILURE(s.open()); - ASSERT_NO_FATAL_FAILURE(InstallTestRSAKey(&s)); - ASSERT_NO_FATAL_FAILURE(s.FillSimpleMessage(0, 0, 0)); - ASSERT_NO_FATAL_FAILURE(s.EncryptAndSign()); - std::string double_message = DuplicateMessage(s.encrypted_license()); - OEMCrypto_Substring wrong_mac_keys_iv = s.enc_mac_keys_iv_substr(); - wrong_mac_keys_iv.offset += s.message_size(); - - OEMCryptoResult sts = OEMCrypto_LoadKeys( - s.session_id(), reinterpret_cast(double_message.data()), - s.message_size(), s.signature().data(), s.signature().size(), - wrong_mac_keys_iv, // bad. - s.enc_mac_keys_substr(), s.num_keys(), s.key_array(), GetSubstring(), - GetSubstring(), OEMCrypto_ContentLicense); - ASSERT_NE(OEMCrypto_SUCCESS, sts); +TEST_P(OEMCryptoLicenseTest, LoadKeyWithBadRange_enc_mac_keys_iv) { + ASSERT_NO_FATAL_FAILURE(session_.GenerateNonce()); + ASSERT_NO_FATAL_FAILURE(license_messages_.SignAndVerifyRequest()); + ASSERT_NO_FATAL_FAILURE(license_messages_.CreateDefaultResponse()); + // See the comment in LicenseRoundTrip::LoadResponse for why we increment by + // the message size. + license_messages_.core_response().enc_mac_keys_iv.offset += + license_messages_.message_size(); + ASSERT_NO_FATAL_FAILURE(license_messages_.EncryptAndSignResponse()); + ASSERT_NE(OEMCrypto_SUCCESS, license_messages_.LoadResponse()); } -// The Bad Range tests verify that OEMCrypto_LoadKeys checks the range -// of all the pointers. It should reject a message if the pointer does -// not point into the message buffer. -TEST_F(OEMCryptoSessionTests, LoadKeyWithBadRange3) { - Session s; - ASSERT_NO_FATAL_FAILURE(s.open()); - ASSERT_NO_FATAL_FAILURE(InstallTestRSAKey(&s)); - ASSERT_NO_FATAL_FAILURE(s.FillSimpleMessage(0, 0, 0)); - ASSERT_NO_FATAL_FAILURE(s.EncryptAndSign()); - std::string double_message = DuplicateMessage(s.encrypted_license()); - s.key_array()[0].key_id.offset += s.message_size(); - - OEMCryptoResult sts = OEMCrypto_LoadKeys( - s.session_id(), reinterpret_cast(double_message.data()), - s.message_size(), s.signature().data(), s.signature().size(), - s.enc_mac_keys_iv_substr(), s.enc_mac_keys_substr(), s.num_keys(), - s.key_array(), GetSubstring(), GetSubstring(), OEMCrypto_ContentLicense); - ASSERT_NE(OEMCrypto_SUCCESS, sts); +TEST_P(OEMCryptoLicenseTest, LoadKeyWithBadRange_key_id) { + ASSERT_NO_FATAL_FAILURE(session_.GenerateNonce()); + ASSERT_NO_FATAL_FAILURE(license_messages_.SignAndVerifyRequest()); + ASSERT_NO_FATAL_FAILURE(license_messages_.CreateDefaultResponse()); + // See the comment in LicenseRoundTrip::LoadResponse for why we increment by + // the message size. + license_messages_.core_response().key_array[0].key_id.offset += + license_messages_.message_size(); + ASSERT_NO_FATAL_FAILURE(license_messages_.EncryptAndSignResponse()); + ASSERT_NE(OEMCrypto_SUCCESS, license_messages_.LoadResponse()); } -// The Bad Range tests verify that OEMCrypto_LoadKeys checks the range -// of all the pointers. It should reject a message if the pointer does -// not point into the message buffer. -TEST_F(OEMCryptoSessionTests, LoadKeyWithBadRange4) { - Session s; - ASSERT_NO_FATAL_FAILURE(s.open()); - ASSERT_NO_FATAL_FAILURE(InstallTestRSAKey(&s)); - ASSERT_NO_FATAL_FAILURE(s.FillSimpleMessage(0, 0, 0)); - ASSERT_NO_FATAL_FAILURE(s.EncryptAndSign()); - - std::string double_message = DuplicateMessage(s.encrypted_license()); - s.key_array()[1].key_data.offset += s.message_size(); - - OEMCryptoResult sts = OEMCrypto_LoadKeys( - s.session_id(), reinterpret_cast(double_message.data()), - s.message_size(), s.signature().data(), s.signature().size(), - s.enc_mac_keys_iv_substr(), s.enc_mac_keys_substr(), s.num_keys(), - s.key_array(), GetSubstring(), GetSubstring(), OEMCrypto_ContentLicense); - ASSERT_NE(OEMCrypto_SUCCESS, sts); +TEST_P(OEMCryptoLicenseTest, LoadKeyWithBadRange_key_data) { + ASSERT_NO_FATAL_FAILURE(session_.GenerateNonce()); + ASSERT_NO_FATAL_FAILURE(license_messages_.SignAndVerifyRequest()); + ASSERT_NO_FATAL_FAILURE(license_messages_.CreateDefaultResponse()); + // See the comment in LicenseRoundTrip::LoadResponse for why we increment by + // the message size. + license_messages_.core_response().key_array[1].key_data.offset += + license_messages_.message_size(); + ASSERT_NO_FATAL_FAILURE(license_messages_.EncryptAndSignResponse()); + ASSERT_NE(OEMCrypto_SUCCESS, license_messages_.LoadResponse()); } -// The Bad Range tests verify that OEMCrypto_LoadKeys checks the range -// of all the pointers. It should reject a message if the pointer does -// not point into the message buffer. -TEST_F(OEMCryptoSessionTests, LoadKeyWithBadRange5) { - Session s; - ASSERT_NO_FATAL_FAILURE(s.open()); - ASSERT_NO_FATAL_FAILURE(InstallTestRSAKey(&s)); - ASSERT_NO_FATAL_FAILURE(s.FillSimpleMessage(0, 0, 0)); - ASSERT_NO_FATAL_FAILURE(s.EncryptAndSign()); - std::string double_message = DuplicateMessage(s.encrypted_license()); - s.key_array()[1].key_data_iv.offset += s.message_size(); - OEMCryptoResult sts = OEMCrypto_LoadKeys( - s.session_id(), reinterpret_cast(double_message.data()), - s.message_size(), s.signature().data(), s.signature().size(), - s.enc_mac_keys_iv_substr(), s.enc_mac_keys_substr(), s.num_keys(), - s.key_array(), GetSubstring(), GetSubstring(), OEMCrypto_ContentLicense); - ASSERT_NE(OEMCrypto_SUCCESS, sts); +TEST_P(OEMCryptoLicenseTest, LoadKeyWithBadRange_key_data_iv) { + ASSERT_NO_FATAL_FAILURE(session_.GenerateNonce()); + ASSERT_NO_FATAL_FAILURE(license_messages_.SignAndVerifyRequest()); + ASSERT_NO_FATAL_FAILURE(license_messages_.CreateDefaultResponse()); + // See the comment in LicenseRoundTrip::LoadResponse for why we increment by + // the message size. + license_messages_.core_response().key_array[1].key_data_iv.offset += + license_messages_.message_size(); + ASSERT_NO_FATAL_FAILURE(license_messages_.EncryptAndSignResponse()); + ASSERT_NE(OEMCrypto_SUCCESS, license_messages_.LoadResponse()); } -// The Bad Range tests verify that OEMCrypto_LoadKeys checks the range -// of all the pointers. It should reject a message if the pointer does -// not point into the message buffer. -TEST_F(OEMCryptoSessionTests, LoadKeyWithBadRange6) { - Session s; - ASSERT_NO_FATAL_FAILURE(s.open()); - ASSERT_NO_FATAL_FAILURE(InstallTestRSAKey(&s)); - ASSERT_NO_FATAL_FAILURE(s.FillSimpleMessage(0, 0, 0)); - ASSERT_NO_FATAL_FAILURE(s.EncryptAndSign()); - - std::string double_message = DuplicateMessage(s.encrypted_license()); - s.key_array()[2].key_control.offset += s.message_size(); - - OEMCryptoResult sts = OEMCrypto_LoadKeys( - s.session_id(), reinterpret_cast(double_message.data()), - s.message_size(), s.signature().data(), s.signature().size(), - s.enc_mac_keys_iv_substr(), s.enc_mac_keys_substr(), s.num_keys(), - s.key_array(), GetSubstring(), GetSubstring(), OEMCrypto_ContentLicense); - ASSERT_NE(OEMCrypto_SUCCESS, sts); +TEST_P(OEMCryptoLicenseTest, LoadKeyWithBadRange_key_control) { + ASSERT_NO_FATAL_FAILURE(session_.GenerateNonce()); + ASSERT_NO_FATAL_FAILURE(license_messages_.SignAndVerifyRequest()); + ASSERT_NO_FATAL_FAILURE(license_messages_.CreateDefaultResponse()); + // See the comment in LicenseRoundTrip::LoadResponse for why we increment by + // the message size. + license_messages_.core_response().key_array[2].key_control.offset += + license_messages_.message_size(); + ASSERT_NO_FATAL_FAILURE(license_messages_.EncryptAndSignResponse()); + ASSERT_NE(OEMCrypto_SUCCESS, license_messages_.LoadResponse()); } -// The Bad Range tests verify that OEMCrypto_LoadKeys checks the range -// of all the pointers. It should reject a message if the pointer does -// not point into the message buffer. -TEST_F(OEMCryptoSessionTests, LoadKeyWithBadRange7) { - Session s; - ASSERT_NO_FATAL_FAILURE(s.open()); - ASSERT_NO_FATAL_FAILURE(InstallTestRSAKey(&s)); - ASSERT_NO_FATAL_FAILURE(s.FillSimpleMessage(0, 0, 0)); - ASSERT_NO_FATAL_FAILURE(s.EncryptAndSign()); - std::string double_message = DuplicateMessage(s.encrypted_license()); - s.key_array()[2].key_control_iv.offset += s.message_size(); - - OEMCryptoResult sts = OEMCrypto_LoadKeys( - s.session_id(), reinterpret_cast(double_message.data()), - s.message_size(), s.signature().data(), s.signature().size(), - s.enc_mac_keys_iv_substr(), s.enc_mac_keys_substr(), s.num_keys(), - s.key_array(), GetSubstring(), GetSubstring(), OEMCrypto_ContentLicense); - ASSERT_NE(OEMCrypto_SUCCESS, sts); +TEST_P(OEMCryptoLicenseTest, LoadKeyWithBadRange_key_control_iv) { + ASSERT_NO_FATAL_FAILURE(session_.GenerateNonce()); + ASSERT_NO_FATAL_FAILURE(license_messages_.SignAndVerifyRequest()); + ASSERT_NO_FATAL_FAILURE(license_messages_.CreateDefaultResponse()); + // See the comment in LicenseRoundTrip::LoadResponse for why we increment by + // the message size. + license_messages_.core_response().key_array[2].key_control_iv.offset += + license_messages_.message_size(); + ASSERT_NO_FATAL_FAILURE(license_messages_.EncryptAndSignResponse()); + ASSERT_NE(OEMCrypto_SUCCESS, license_messages_.LoadResponse()); } +//---------------------------------------------------------------------------// +//---------------------------------------------------------------------------// // The IV should not be identical to the data right before the encrypted mac // keys. -TEST_F(OEMCryptoSessionTests, LoadKeyWithSuspiciousIV) { - Session s; - ASSERT_NO_FATAL_FAILURE(s.open()); - ASSERT_NO_FATAL_FAILURE(InstallTestRSAKey(&s)); - ASSERT_NO_FATAL_FAILURE(s.FillSimpleMessage(0, 0, 0)); +TEST_F(OEMCryptoLicenseTestAPI15, LoadKeyWithSuspiciousIV) { + ASSERT_NO_FATAL_FAILURE(session_.GenerateNonce()); + ASSERT_NO_FATAL_FAILURE(license_messages_.SignAndVerifyRequest()); + ASSERT_NO_FATAL_FAILURE(license_messages_.CreateDefaultResponse()); + // This is suspicious: the data right before the mac keys is identical to the // iv. - memcpy(s.license().padding, s.license().mac_key_iv, - sizeof(s.license().padding)); - ASSERT_NO_FATAL_FAILURE(s.EncryptAndSign()); + memcpy(license_messages_.response_data().padding, + license_messages_.response_data().mac_key_iv, + sizeof(license_messages_.response_data().padding)); - OEMCryptoResult sts = OEMCrypto_LoadKeys( - s.session_id(), s.message_ptr(), s.message_size(), s.signature().data(), - s.signature().size(), s.enc_mac_keys_iv_substr(), s.enc_mac_keys_substr(), - s.num_keys(), s.key_array(), GetSubstring(), GetSubstring(), - OEMCrypto_ContentLicense); - ASSERT_NE(OEMCrypto_SUCCESS, sts); + ASSERT_NO_FATAL_FAILURE(license_messages_.EncryptAndSignResponse()); + ASSERT_NE(OEMCrypto_SUCCESS, license_messages_.LoadResponse()); } // Test that LoadKeys fails when a key is loaded with no key control block. -TEST_F(OEMCryptoSessionTests, LoadKeyWithNullKeyControl) { - Session s; - ASSERT_NO_FATAL_FAILURE(s.open()); - ASSERT_NO_FATAL_FAILURE(InstallTestRSAKey(&s)); - ASSERT_NO_FATAL_FAILURE(s.FillSimpleMessage(0, 0, 0)); - ASSERT_NO_FATAL_FAILURE(s.EncryptAndSign()); - s.key_array()[2].key_control.offset = 0; - s.key_array()[2].key_control.length = 0; - - OEMCryptoResult sts = OEMCrypto_LoadKeys( - s.session_id(), s.message_ptr(), s.message_size(), s.signature().data(), - s.signature().size(), s.enc_mac_keys_iv_substr(), s.enc_mac_keys_substr(), - s.num_keys(), s.key_array(), GetSubstring(), GetSubstring(), - OEMCrypto_ContentLicense); - ASSERT_NE(OEMCrypto_SUCCESS, sts); +TEST_P(OEMCryptoLicenseTest, LoadKeyWithNullKeyControl) { + ASSERT_NO_FATAL_FAILURE(session_.GenerateNonce()); + ASSERT_NO_FATAL_FAILURE(license_messages_.SignAndVerifyRequest()); + ASSERT_NO_FATAL_FAILURE(license_messages_.CreateDefaultResponse()); + license_messages_.core_response().key_array[2].key_control.offset = 0; + license_messages_.core_response().key_array[2].key_control.length = 0; + ASSERT_NO_FATAL_FAILURE(license_messages_.EncryptAndSignResponse()); + ASSERT_NE(OEMCrypto_SUCCESS, license_messages_.LoadResponse()); } // Test that LoadKeys fails when the key control block encryption has a null IV. -TEST_F(OEMCryptoSessionTests, LoadKeyWithNullKeyControlIv) { - Session s; - ASSERT_NO_FATAL_FAILURE(s.open()); - ASSERT_NO_FATAL_FAILURE(InstallTestRSAKey(&s)); - ASSERT_NO_FATAL_FAILURE(s.FillSimpleMessage(0, 0, 0)); - ASSERT_NO_FATAL_FAILURE(s.EncryptAndSign()); - s.key_array()[2].key_control_iv.offset = 0; - s.key_array()[2].key_control_iv.length = 0; - - OEMCryptoResult sts = OEMCrypto_LoadKeys( - s.session_id(), s.message_ptr(), s.message_size(), s.signature().data(), - s.signature().size(), s.enc_mac_keys_iv_substr(), s.enc_mac_keys_substr(), - s.num_keys(), s.key_array(), GetSubstring(), GetSubstring(), - OEMCrypto_ContentLicense); - ASSERT_NE(OEMCrypto_SUCCESS, sts); +TEST_P(OEMCryptoLicenseTest, LoadKeyWithNullKeyControlIv) { + ASSERT_NO_FATAL_FAILURE(session_.GenerateNonce()); + ASSERT_NO_FATAL_FAILURE(license_messages_.SignAndVerifyRequest()); + ASSERT_NO_FATAL_FAILURE(license_messages_.CreateDefaultResponse()); + license_messages_.core_response().key_array[2].key_control_iv.offset = 0; + license_messages_.core_response().key_array[2].key_control_iv.length = 0; + ASSERT_NO_FATAL_FAILURE(license_messages_.EncryptAndSignResponse()); + ASSERT_NE(OEMCrypto_SUCCESS, license_messages_.LoadResponse()); } -// Verify that LoadKeys fails when a key's nonce is not in the session. -TEST_F(OEMCryptoSessionTests, LoadKeyWithBadNonce) { - Session s; - ASSERT_NO_FATAL_FAILURE(s.open()); - ASSERT_NO_FATAL_FAILURE(InstallTestRSAKey(&s)); - ASSERT_NO_FATAL_FAILURE(s.FillSimpleMessage(0, wvoec::kControlNonceEnabled, - 42)); // bad nonce. - ASSERT_NO_FATAL_FAILURE(s.EncryptAndSign()); - OEMCryptoResult sts = OEMCrypto_LoadKeys( - s.session_id(), s.message_ptr(), s.message_size(), s.signature().data(), - s.signature().size(), s.enc_mac_keys_iv_substr(), s.enc_mac_keys_substr(), - s.num_keys(), s.key_array(), GetSubstring(), GetSubstring(), - OEMCrypto_ContentLicense); +// Verify that LoadKeys fails when a key's nonce is wrong. +TEST_P(OEMCryptoLicenseTest, LoadKeyWithBadNonce) { + ASSERT_NO_FATAL_FAILURE(session_.GenerateNonce()); + ASSERT_NO_FATAL_FAILURE(license_messages_.SignAndVerifyRequest()); + ASSERT_NO_FATAL_FAILURE(license_messages_.CreateDefaultResponse()); + for (unsigned int i = 0; i < license_messages_.num_keys(); i++) + license_messages_.response_data().keys[i].control.nonce ^= 42; + ASSERT_NO_FATAL_FAILURE(license_messages_.EncryptAndSignResponse()); + ASSERT_EQ(OEMCrypto_ERROR_INVALID_NONCE, license_messages_.LoadResponse()); +} - ASSERT_NE(OEMCrypto_SUCCESS, sts); +// Verify that LoadKeys fails when the core message's nonce is wrong. +TEST_F(OEMCryptoLicenseTestAPI16, LoadKeyWithBadNonce2) { + ASSERT_NO_FATAL_FAILURE(session_.GenerateNonce()); + ASSERT_NO_FATAL_FAILURE(license_messages_.SignAndVerifyRequest()); + ASSERT_NO_FATAL_FAILURE(license_messages_.CreateDefaultResponse()); + license_messages_.core_request().nonce ^= 42; + ASSERT_NO_FATAL_FAILURE(license_messages_.EncryptAndSignResponse()); + ASSERT_EQ(OEMCrypto_ERROR_INVALID_NONCE, license_messages_.LoadResponse()); +} + +// Verify that LoadKeys fails when the core message's session is wrong. +TEST_F(OEMCryptoLicenseTestAPI16, LoadKeyWithBadNonce3) { + ASSERT_NO_FATAL_FAILURE(session_.GenerateNonce()); + ASSERT_NO_FATAL_FAILURE(license_messages_.SignAndVerifyRequest()); + ASSERT_NO_FATAL_FAILURE(license_messages_.CreateDefaultResponse()); + license_messages_.core_request().session_id++; + ASSERT_NO_FATAL_FAILURE(license_messages_.EncryptAndSignResponse()); + ASSERT_EQ(OEMCrypto_ERROR_INVALID_NONCE, license_messages_.LoadResponse()); } // Verify that LoadKeys fails when an attempt is made to use a nonce twice. -TEST_F(OEMCryptoSessionTests, LoadKeyWithRepeatNonce) { - Session s; - ASSERT_NO_FATAL_FAILURE(s.open()); - ASSERT_NO_FATAL_FAILURE(InstallTestRSAKey(&s)); - ASSERT_NO_FATAL_FAILURE(s.GenerateNonce()); - uint32_t nonce = s.nonce(); - ASSERT_NO_FATAL_FAILURE( - s.FillSimpleMessage(0, wvoec::kControlNonceEnabled, nonce)); - ASSERT_NO_FATAL_FAILURE(s.EncryptAndSign()); +TEST_P(OEMCryptoLicenseTest, LoadKeyWithRepeatNonce) { + ASSERT_NO_FATAL_FAILURE(session_.GenerateNonce()); + const uint32_t nonce = session_.nonce(); + ASSERT_NO_FATAL_FAILURE(license_messages_.SignAndVerifyRequest()); + ASSERT_NO_FATAL_FAILURE(license_messages_.CreateDefaultResponse()); + ASSERT_NO_FATAL_FAILURE(license_messages_.EncryptAndSignResponse()); // This is the first attempt. It should succeed. - ASSERT_NO_FATAL_FAILURE(s.LoadTestKeys()); - ASSERT_NO_FATAL_FAILURE(s.close()); + ASSERT_EQ(OEMCrypto_SUCCESS, license_messages_.LoadResponse()); - ASSERT_NO_FATAL_FAILURE(s.open()); - ASSERT_NO_FATAL_FAILURE(InstallTestRSAKey(&s)); - ASSERT_NO_FATAL_FAILURE(s.FillSimpleMessage(0, wvoec::kControlNonceEnabled, - nonce)); // same old nonce. - ASSERT_NO_FATAL_FAILURE(s.EncryptAndSign()); - // This is the second attempt to load the keys with a repeated nonce. It - // should fail. - OEMCryptoResult sts = OEMCrypto_LoadKeys( - s.session_id(), s.message_ptr(), s.message_size(), s.signature().data(), - s.signature().size(), s.enc_mac_keys_iv_substr(), s.enc_mac_keys_substr(), - s.num_keys(), s.key_array(), GetSubstring(), GetSubstring(), - OEMCrypto_ContentLicense); - - ASSERT_NE(OEMCrypto_SUCCESS, sts); + // Now, open a new session and try to load a license with the same nonce. + session_.close(); + ASSERT_NO_FATAL_FAILURE(session_.open()); + ASSERT_NO_FATAL_FAILURE(InstallTestRSAKey(&session_)); + license_messages_.skip_nonce_check(); + ASSERT_NO_FATAL_FAILURE(license_messages_.SignAndVerifyRequest()); + ASSERT_NO_FATAL_FAILURE(license_messages_.CreateDefaultResponse()); + // Repeat the nonce. + license_messages_.core_request().nonce = nonce; + for (unsigned int i = 0; i < license_messages_.num_keys(); i++) + license_messages_.response_data().keys[i].control.nonce = htonl(nonce); + ASSERT_NO_FATAL_FAILURE(license_messages_.EncryptAndSignResponse()); + ASSERT_EQ(OEMCrypto_ERROR_INVALID_NONCE, license_messages_.LoadResponse()); } // This tests that a nonce cannot be used in new session. This is similar to // the previous test, but does not use the nonce in the first session. The nonce // should be tied to a session, so generating a nonce in the first session and // then using it in the second session should fail. -TEST_F(OEMCryptoSessionTests, LoadKeyNonceReopenSession) { - Session s; - ASSERT_NO_FATAL_FAILURE(s.open()); - ASSERT_NO_FATAL_FAILURE(InstallTestRSAKey(&s)); - ASSERT_NO_FATAL_FAILURE(s.GenerateNonce()); - uint32_t nonce = s.nonce(); +TEST_P(OEMCryptoLicenseTest, LoadKeyNonceReopenSession) { + ASSERT_NO_FATAL_FAILURE(session_.GenerateNonce()); + uint32_t nonce = session_.nonce(); // Do not use the nonce now. Close session and use it after re-opening. - ASSERT_NO_FATAL_FAILURE(s.close()); + ASSERT_NO_FATAL_FAILURE(session_.close()); // Actually, this isn't the same session. OEMCrypto opens a new session, but // we are guarding against the possiblity that it re-uses the session data // and might not clear out the nonce correctly. - ASSERT_NO_FATAL_FAILURE(s.open()); - ASSERT_NO_FATAL_FAILURE(InstallTestRSAKey(&s)); - ASSERT_NO_FATAL_FAILURE(s.FillSimpleMessage(0, wvoec::kControlNonceEnabled, - nonce)); // same old nonce - ASSERT_NO_FATAL_FAILURE(s.EncryptAndSign()); - OEMCryptoResult sts = OEMCrypto_LoadKeys( - s.session_id(), s.message_ptr(), s.message_size(), s.signature().data(), - s.signature().size(), s.enc_mac_keys_iv_substr(), s.enc_mac_keys_substr(), - s.num_keys(), s.key_array(), GetSubstring(), GetSubstring(), - OEMCrypto_ContentLicense); - - ASSERT_NE(OEMCrypto_SUCCESS, sts); + ASSERT_NO_FATAL_FAILURE(session_.open()); + ASSERT_NO_FATAL_FAILURE(InstallTestRSAKey(&session_)); + license_messages_.skip_nonce_check(); + ASSERT_NO_FATAL_FAILURE(license_messages_.SignAndVerifyRequest()); + ASSERT_NO_FATAL_FAILURE(license_messages_.CreateDefaultResponse()); + license_messages_.core_request().nonce = nonce; + for (unsigned int i = 0; i < license_messages_.num_keys(); i++) + license_messages_.response_data().keys[i].control.nonce = htonl(nonce); + ASSERT_NO_FATAL_FAILURE(license_messages_.EncryptAndSignResponse()); + ASSERT_EQ(OEMCrypto_ERROR_INVALID_NONCE, license_messages_.LoadResponse()); } // This tests that a nonce cannot be used in wrong session. This is similar to // the previous test, except we do not close session 1 before we open session 2. -TEST_F(OEMCryptoSessionTests, LoadKeyNonceWrongSession) { - Session s1; - ASSERT_NO_FATAL_FAILURE(s1.open()); - ASSERT_NO_FATAL_FAILURE(InstallTestRSAKey(&s1)); - ASSERT_NO_FATAL_FAILURE(s1.GenerateNonce()); - uint32_t nonce = s1.nonce(); - // Do not use the nonce. Also, leave the session open. We want to make sure - // that s and s1 do NOT share a nonce. This is different from the - // LoadKeyNonceReopenSession in that we do not close s1. - +TEST_P(OEMCryptoLicenseTest, LoadKeyNonceWrongSession) { + // First, open a session and generate a nonce. We don't use the nonce in this + // session. Session s2; ASSERT_NO_FATAL_FAILURE(s2.open()); ASSERT_NO_FATAL_FAILURE(InstallTestRSAKey(&s2)); ASSERT_NO_FATAL_FAILURE(s2.GenerateNonce()); - ASSERT_NO_FATAL_FAILURE(s2.FillSimpleMessage(0, wvoec::kControlNonceEnabled, - nonce)); // nonce from session s1 - ASSERT_NO_FATAL_FAILURE(s2.EncryptAndSign()); - OEMCryptoResult sts = OEMCrypto_LoadKeys( - s2.session_id(), s2.message_ptr(), s2.message_size(), - s2.signature().data(), s2.signature().size(), s2.enc_mac_keys_iv_substr(), - s2.enc_mac_keys_substr(), s2.num_keys(), s2.key_array(), GetSubstring(), - GetSubstring(), OEMCrypto_ContentLicense); + uint32_t nonce = s2.nonce(); - ASSERT_NE(OEMCrypto_SUCCESS, sts); + // Do not use the nonce. Also, leave the session open. We want to make sure + // that session_ and s2 do NOT share a nonce. This is different from + // the LoadKeyNonceReopenSession in that we do not close s1. + + ASSERT_NO_FATAL_FAILURE(license_messages_.SignAndVerifyRequest()); + ASSERT_NO_FATAL_FAILURE(license_messages_.CreateDefaultResponse()); + license_messages_.core_request().nonce = nonce; + for (unsigned int i = 0; i < license_messages_.num_keys(); i++) + license_messages_.response_data().keys[i].control.nonce = htonl(nonce); + ASSERT_NO_FATAL_FAILURE(license_messages_.EncryptAndSignResponse()); + ASSERT_EQ(OEMCrypto_ERROR_INVALID_NONCE, license_messages_.LoadResponse()); } // LoadKeys should fail if the key control block as a bad verification string. -TEST_F(OEMCryptoSessionTests, LoadKeyWithBadVerification) { - Session s; - ASSERT_NO_FATAL_FAILURE(s.open()); - ASSERT_NO_FATAL_FAILURE(InstallTestRSAKey(&s)); - ASSERT_NO_FATAL_FAILURE(s.FillSimpleMessage(0, 0, 0)); - s.license().keys[1].control.verification[2] = 'Z'; - ASSERT_NO_FATAL_FAILURE(s.EncryptAndSign()); - OEMCryptoResult sts = OEMCrypto_LoadKeys( - s.session_id(), s.message_ptr(), s.message_size(), s.signature().data(), - s.signature().size(), s.enc_mac_keys_iv_substr(), s.enc_mac_keys_substr(), - s.num_keys(), s.key_array(), GetSubstring(), GetSubstring(), - OEMCrypto_ContentLicense); - - ASSERT_NE(OEMCrypto_SUCCESS, sts); +TEST_P(OEMCryptoLicenseTest, LoadKeyWithBadVerification) { + ASSERT_NO_FATAL_FAILURE(session_.GenerateNonce()); + ASSERT_NO_FATAL_FAILURE(license_messages_.SignAndVerifyRequest()); + ASSERT_NO_FATAL_FAILURE(license_messages_.CreateDefaultResponse()); + license_messages_.response_data().keys[1].control.verification[2] = 'Z'; + ASSERT_NO_FATAL_FAILURE(license_messages_.EncryptAndSignResponse()); + ASSERT_NE(OEMCrypto_SUCCESS, license_messages_.LoadResponse()); } // This test verifies that LoadKeys still works when the message is not aligned // in memory on a word (2 or 4 byte) boundary. -TEST_F(OEMCryptoSessionTests, LoadKeyUnalignedMessage) { - Session s; - ASSERT_NO_FATAL_FAILURE(s.open()); - ASSERT_NO_FATAL_FAILURE(InstallTestRSAKey(&s)); - ASSERT_NO_FATAL_FAILURE(s.GenerateNonce()); - ASSERT_NO_FATAL_FAILURE( - s.FillSimpleMessage(kDuration, wvoec::kControlNonceEnabled, s.nonce())); - ASSERT_NO_FATAL_FAILURE(s.EncryptAndSign()); +TEST_P(OEMCryptoLicenseTest, LoadKeyUnalignedMessageAPI16) { + ASSERT_NO_FATAL_FAILURE(session_.GenerateNonce()); + ASSERT_NO_FATAL_FAILURE(license_messages_.SignAndVerifyRequest()); + ASSERT_NO_FATAL_FAILURE(license_messages_.CreateDefaultResponse()); + ASSERT_NO_FATAL_FAILURE(license_messages_.EncryptAndSignResponse()); + std::vector buffer(1, '0'); // A string of 1 byte long. size_t offset = buffer.size(); ASSERT_EQ(1u, offset); // We assume that vectors are allocated on as a small chunk of data that is // aligned on a word boundary. I.e. we assume buffer is word aligned. Next, // we append the message to buffer after the single padding byte. - buffer.insert(buffer.end(), s.message_ptr(), - s.message_ptr() + s.message_size()); + buffer.insert(buffer.end(), + license_messages_.encrypted_response_buffer().begin(), + license_messages_.encrypted_response_buffer().end()); // Thus, buffer[offset] is NOT word aligned. const uint8_t* unaligned_message = &buffer[offset]; - OEMCryptoResult sts = OEMCrypto_LoadKeys( - s.session_id(), unaligned_message, s.message_size(), s.signature().data(), - s.signature().size(), s.enc_mac_keys_iv_substr(), s.enc_mac_keys_substr(), - s.num_keys(), s.key_array(), GetSubstring(), GetSubstring(), - OEMCrypto_ContentLicense); - ASSERT_EQ(OEMCrypto_SUCCESS, sts); + if (license_api_version_ < kCoreMessagesAPI) { + ASSERT_EQ(OEMCrypto_SUCCESS, + OEMCrypto_LoadKeys( + session_.session_id(), unaligned_message, + license_messages_.encrypted_response_buffer().size(), + license_messages_.response_signature().data(), + license_messages_.response_signature().size(), + license_messages_.core_response().enc_mac_keys_iv, + license_messages_.core_response().enc_mac_keys, + license_messages_.core_response().key_array_length, + license_messages_.core_response().key_array, + license_messages_.core_response().pst, + license_messages_.core_response().srm_restriction_data, + static_cast( + license_messages_.core_response().license_type))); + } else { + ASSERT_EQ(OEMCrypto_SUCCESS, + OEMCrypto_LoadLicense( + session_.session_id(), unaligned_message, + license_messages_.message_size(), + license_messages_.serialized_core_message().size(), + license_messages_.response_signature().data(), + license_messages_.response_signature().size())); + } } // Verifies that a session can't reload a license without being closed and // reopened. -TEST_F(OEMCryptoSessionTests, LoadLicenseAgainFailureAPI16) { - Session s; - ASSERT_NO_FATAL_FAILURE(s.open()); - ASSERT_NO_FATAL_FAILURE(InstallTestRSAKey(&s)); - ASSERT_NO_FATAL_FAILURE(s.FillSimpleMessage(0, 0, 0)); - ASSERT_NO_FATAL_FAILURE(s.EncryptAndSign()); - ASSERT_EQ( - OEMCrypto_SUCCESS, - OEMCrypto_LoadKeys(s.session_id(), s.message_ptr(), s.message_size(), - s.signature().data(), s.signature().size(), - s.enc_mac_keys_iv_substr(), s.enc_mac_keys_substr(), - s.num_keys(), s.key_array(), GetSubstring(), - GetSubstring(), OEMCrypto_ContentLicense)); - ASSERT_NO_FATAL_FAILURE(InstallTestRSAKey(&s)); - ASSERT_NO_FATAL_FAILURE(s.FillSimpleMessage(0, 0, 0)); - ASSERT_NO_FATAL_FAILURE(s.EncryptAndSign()); - ASSERT_EQ( - OEMCrypto_ERROR_LICENSE_RELOAD, - OEMCrypto_LoadKeys(s.session_id(), s.message_ptr(), s.message_size(), - s.signature().data(), s.signature().size(), - s.enc_mac_keys_iv_substr(), s.enc_mac_keys_substr(), - s.num_keys(), s.key_array(), GetSubstring(), - GetSubstring(), OEMCrypto_ContentLicense)); +TEST_P(OEMCryptoLicenseTest, LoadLicenseAgainFailureAPI16) { + ASSERT_NO_FATAL_FAILURE(session_.GenerateNonce()); + ASSERT_NO_FATAL_FAILURE(license_messages_.SignAndVerifyRequest()); + ASSERT_NO_FATAL_FAILURE(license_messages_.CreateDefaultResponse()); + ASSERT_NO_FATAL_FAILURE(license_messages_.EncryptAndSignResponse()); + ASSERT_EQ(OEMCrypto_SUCCESS, license_messages_.LoadResponse()); + ASSERT_EQ(OEMCrypto_ERROR_LICENSE_RELOAD, license_messages_.LoadResponse()); } TEST_P(OEMCryptoLicenseTestRangeAPI, LoadKeys) { @@ -1328,70 +1284,58 @@ TEST_P(OEMCryptoLicenseTestRangeAPI, LoadKeys) { INSTANTIATE_TEST_CASE_P(TestAll, OEMCryptoLicenseTestRangeAPI, Range(10, kCurrentAPI + 2)); -TEST_F(OEMCryptoSessionTests, LoadKeysBadSignature) { - Session s; - ASSERT_NO_FATAL_FAILURE(s.open()); - ASSERT_NO_FATAL_FAILURE(InstallTestRSAKey(&s)); - ASSERT_NO_FATAL_FAILURE(s.FillSimpleMessage(0, 0, 0)); - ASSERT_NO_FATAL_FAILURE(s.EncryptAndSign()); - s.signature()[0] ^= 42; // Bad signature. - OEMCryptoResult sts = OEMCrypto_LoadKeys( - s.session_id(), s.message_ptr(), s.message_size(), s.signature().data(), - s.signature().size(), s.enc_mac_keys_iv_substr(), s.enc_mac_keys_substr(), - s.num_keys(), s.key_array(), GetSubstring(), GetSubstring(), - OEMCrypto_ContentLicense); - ASSERT_NE(OEMCrypto_SUCCESS, sts); +TEST_P(OEMCryptoLicenseTest, LoadKeysBadSignature) { + ASSERT_NO_FATAL_FAILURE(session_.GenerateNonce()); + ASSERT_NO_FATAL_FAILURE(license_messages_.SignAndVerifyRequest()); + ASSERT_NO_FATAL_FAILURE(license_messages_.CreateDefaultResponse()); + ASSERT_NO_FATAL_FAILURE(license_messages_.EncryptAndSignResponse()); + license_messages_.response_signature()[0] ^= 42; + ASSERT_EQ(OEMCrypto_ERROR_SIGNATURE_FAILURE, + license_messages_.LoadResponse()); +} + +TEST_F(OEMCryptoLicenseTestAPI16, BadCoreHashAPI16) { + ASSERT_NO_FATAL_FAILURE(session_.GenerateNonce()); + ASSERT_NO_FATAL_FAILURE(license_messages_.SignAndVerifyRequest()); + ASSERT_NO_FATAL_FAILURE(license_messages_.CreateDefaultResponse()); + license_messages_.BreakRequestHash(); + ASSERT_NO_FATAL_FAILURE(license_messages_.EncryptAndSignResponse()); + ASSERT_NE(OEMCrypto_SUCCESS, license_messages_.LoadResponse()); } // LoadKeys should fail if we try to load keys with no keys. -TEST_F(OEMCryptoSessionTests, LoadKeyNoKeys) { - Session s; - ASSERT_NO_FATAL_FAILURE(s.open()); - ASSERT_NO_FATAL_FAILURE(InstallTestRSAKey(&s)); - ASSERT_NO_FATAL_FAILURE(s.FillSimpleMessage(kDuration, 0, 0)); - ASSERT_NO_FATAL_FAILURE(s.EncryptAndSign()); - int kNoKeys = 0; - ASSERT_NE(OEMCrypto_SUCCESS, - OEMCrypto_LoadKeys( - s.session_id(), s.message_ptr(), s.message_size(), - s.signature().data(), s.signature().size(), GetSubstring(), - GetSubstring(), kNoKeys, s.key_array(), GetSubstring(), - GetSubstring(), OEMCrypto_ContentLicense)); +TEST_P(OEMCryptoLicenseTest, LoadKeyNoKeys) { + ASSERT_NO_FATAL_FAILURE(license_messages_.SignAndVerifyRequest()); + license_messages_.set_control(0); + license_messages_.set_num_keys(0); + ASSERT_NO_FATAL_FAILURE(license_messages_.CreateDefaultResponse()); + ASSERT_NO_FATAL_FAILURE(license_messages_.EncryptAndSignResponse()); + ASSERT_NE(OEMCrypto_SUCCESS, license_messages_.LoadResponse()); } // Like the previous test, except we ask for a nonce first. -TEST_F(OEMCryptoSessionTests, LoadKeyNoKeyWithNonce) { - Session s; - ASSERT_NO_FATAL_FAILURE(s.open()); - ASSERT_NO_FATAL_FAILURE(InstallTestRSAKey(&s)); - ASSERT_NO_FATAL_FAILURE(s.GenerateNonce()); - ASSERT_NO_FATAL_FAILURE( - s.FillSimpleMessage(0, wvoec::kControlNonceEnabled, s.nonce())); - ASSERT_NO_FATAL_FAILURE(s.EncryptAndSign()); - int kNoKeys = 0; - ASSERT_NE(OEMCrypto_SUCCESS, - OEMCrypto_LoadKeys( - s.session_id(), s.message_ptr(), s.message_size(), - s.signature().data(), s.signature().size(), GetSubstring(), - GetSubstring(), kNoKeys, s.key_array(), GetSubstring(), - GetSubstring(), OEMCrypto_ContentLicense)); +TEST_P(OEMCryptoLicenseTest, LoadKeyNoKeyWithNonce) { + ASSERT_NO_FATAL_FAILURE(session_.GenerateNonce()); + ASSERT_NO_FATAL_FAILURE(license_messages_.SignAndVerifyRequest()); + license_messages_.set_num_keys(0); + ASSERT_NO_FATAL_FAILURE(license_messages_.CreateDefaultResponse()); + ASSERT_NO_FATAL_FAILURE(license_messages_.EncryptAndSignResponse()); + ASSERT_NE(OEMCrypto_SUCCESS, license_messages_.LoadResponse()); } // SelectKey should fail if we attempt to select a key that has not been loaded. // Also, the error should be NO_CONTENT_KEY. -TEST_F(OEMCryptoSessionTests, SelectKeyNotThereAPI15) { - Session s; - ASSERT_NO_FATAL_FAILURE(s.open()); - ASSERT_NO_FATAL_FAILURE(InstallTestRSAKey(&s)); - ASSERT_NO_FATAL_FAILURE(s.GenerateNonce()); - ASSERT_NO_FATAL_FAILURE( - s.FillSimpleMessage(0, wvoec::kControlNonceEnabled, s.nonce())); - ASSERT_NO_FATAL_FAILURE(s.EncryptAndSign()); - ASSERT_NO_FATAL_FAILURE(s.LoadTestKeys()); +TEST_P(OEMCryptoLicenseTest, SelectKeyNotThereAPI15) { + ASSERT_NO_FATAL_FAILURE(session_.GenerateNonce()); + ASSERT_NO_FATAL_FAILURE(license_messages_.SignAndVerifyRequest()); + ASSERT_NO_FATAL_FAILURE(license_messages_.CreateDefaultResponse()); + ASSERT_NO_FATAL_FAILURE(license_messages_.EncryptAndSignResponse()); + ASSERT_EQ(OEMCrypto_SUCCESS, license_messages_.LoadResponse()); + const char* key_id = "no_key"; OEMCryptoResult sts = OEMCrypto_SelectKey( - s.session_id(), reinterpret_cast(key_id), strlen(key_id), - OEMCrypto_CipherMode_CTR); + session_.session_id(), reinterpret_cast(key_id), + strlen(key_id), OEMCrypto_CipherMode_CTR); if (sts != OEMCrypto_SUCCESS) { EXPECT_EQ(OEMCrypto_ERROR_NO_CONTENT_KEY, sts); } else { @@ -1425,22 +1369,20 @@ TEST_F(OEMCryptoSessionTests, SelectKeyNotThereAPI15) { // After loading keys, we should be able to query the key control block. If we // attempt to query a key that has not been loaded, the error should be // NO_CONTENT_KEY. -TEST_F(OEMCryptoSessionTests, QueryKeyControl) { - Session s; - ASSERT_NO_FATAL_FAILURE(s.open()); - ASSERT_NO_FATAL_FAILURE(InstallTestRSAKey(&s)); - ASSERT_NO_FATAL_FAILURE(s.GenerateNonce()); - ASSERT_NO_FATAL_FAILURE( - s.FillSimpleMessage(0, wvoec::kControlNonceEnabled, s.nonce())); - ASSERT_NO_FATAL_FAILURE(s.EncryptAndSign()); - ASSERT_NO_FATAL_FAILURE(s.LoadTestKeys()); +TEST_P(OEMCryptoLicenseTest, QueryKeyControl) { + ASSERT_NO_FATAL_FAILURE(session_.GenerateNonce()); + ASSERT_NO_FATAL_FAILURE(license_messages_.SignAndVerifyRequest()); + ASSERT_NO_FATAL_FAILURE(license_messages_.CreateDefaultResponse()); + ASSERT_NO_FATAL_FAILURE(license_messages_.EncryptAndSignResponse()); + ASSERT_EQ(OEMCrypto_SUCCESS, license_messages_.LoadResponse()); + // Note: successful cases are tested in VerifyTestKeys. KeyControlBlock block; size_t size = sizeof(block) - 1; - OEMCryptoResult sts = - OEMCrypto_QueryKeyControl(s.session_id(), s.license().keys[0].key_id, - s.license().keys[0].key_id_length, - reinterpret_cast(&block), &size); + OEMCryptoResult sts = OEMCrypto_QueryKeyControl( + session_.session_id(), license_messages_.response_data().keys[0].key_id, + license_messages_.response_data().keys[0].key_id_length, + reinterpret_cast(&block), &size); if (sts == OEMCrypto_ERROR_NOT_IMPLEMENTED) { return; } @@ -1449,25 +1391,20 @@ TEST_F(OEMCryptoSessionTests, QueryKeyControl) { size = sizeof(block); ASSERT_EQ(OEMCrypto_ERROR_NO_CONTENT_KEY, OEMCrypto_QueryKeyControl( - s.session_id(), reinterpret_cast(key_id), + session_.session_id(), reinterpret_cast(key_id), strlen(key_id), reinterpret_cast(&block), &size)); } // If the device says it supports anti-rollback in the hardware, then it should -// accept a key control block the anti-rollback hardware bit set. Otherwise, it -// should reject that key control block. -TEST_F(OEMCryptoSessionTests, AntiRollbackHardwareRequired) { - Session s; - ASSERT_NO_FATAL_FAILURE(s.open()); - ASSERT_NO_FATAL_FAILURE(InstallTestRSAKey(&s)); - ASSERT_NO_FATAL_FAILURE(s.FillSimpleMessage( - 0, wvoec::kControlRequireAntiRollbackHardware, 0)); - ASSERT_NO_FATAL_FAILURE(s.EncryptAndSign()); - OEMCryptoResult sts = OEMCrypto_LoadKeys( - s.session_id(), s.message_ptr(), s.message_size(), s.signature().data(), - s.signature().size(), s.enc_mac_keys_iv_substr(), s.enc_mac_keys_substr(), - s.num_keys(), s.key_array(), GetSubstring(), GetSubstring(), - OEMCrypto_ContentLicense); +// accept a key control block with the anti-rollback hardware bit set. +// Otherwise, it should reject that key control block. +TEST_P(OEMCryptoLicenseTest, AntiRollbackHardwareRequired) { + ASSERT_NO_FATAL_FAILURE(session_.GenerateNonce()); + ASSERT_NO_FATAL_FAILURE(license_messages_.SignAndVerifyRequest()); + license_messages_.set_control(wvoec::kControlRequireAntiRollbackHardware); + ASSERT_NO_FATAL_FAILURE(license_messages_.CreateDefaultResponse()); + ASSERT_NO_FATAL_FAILURE(license_messages_.EncryptAndSignResponse()); + OEMCryptoResult sts = license_messages_.LoadResponse(); if (OEMCrypto_IsAntiRollbackHwPresent()) { ASSERT_EQ(OEMCrypto_SUCCESS, sts); } else { @@ -1475,6 +1412,25 @@ TEST_F(OEMCryptoSessionTests, AntiRollbackHardwareRequired) { } } +// This test verifies that OEMCrypto can load the number of keys required for +// the reported resource level. +TEST_P(OEMCryptoLicenseTest, MinimumKeysAPI12) { + const size_t num_keys = GetResourceValue(kMaxKeysPerSession); + ASSERT_LE(num_keys, kMaxNumKeys) << "Test constants need updating."; + license_messages_.set_num_keys(num_keys); + ASSERT_NO_FATAL_FAILURE(session_.GenerateNonce()); + ASSERT_NO_FATAL_FAILURE(license_messages_.SignAndVerifyRequest()); + ASSERT_NO_FATAL_FAILURE(license_messages_.CreateDefaultResponse()); + ASSERT_NO_FATAL_FAILURE(license_messages_.EncryptAndSignResponse()); + ASSERT_EQ(OEMCrypto_SUCCESS, license_messages_.LoadResponse()); + + for (size_t key_index = 0; key_index < num_keys; key_index++) { + constexpr bool kSelectKeyFirst = true; + ASSERT_NO_FATAL_FAILURE( + session_.TestDecryptCTR(kSelectKeyFirst, OEMCrypto_SUCCESS, key_index)); + } +} + // This test verifies that the minimum patch level can be required. The device // should accept a key control block with the current patch level, and it should // reject any key control blocks with a future patch level. @@ -1485,67 +1441,39 @@ TEST_F(OEMCryptoSessionTests, CheckMinimumPatchLevel) { Session s; ASSERT_NO_FATAL_FAILURE(s.open()); ASSERT_NO_FATAL_FAILURE(InstallTestRSAKey(&s)); - ASSERT_NO_FATAL_FAILURE(s.FillSimpleMessage( - 0, patch_level << wvoec::kControlSecurityPatchLevelShift, 0)); - ASSERT_NO_FATAL_FAILURE(s.EncryptAndSign()); - ASSERT_EQ( - OEMCrypto_SUCCESS, - OEMCrypto_LoadKeys(s.session_id(), s.message_ptr(), s.message_size(), - s.signature().data(), s.signature().size(), - s.enc_mac_keys_iv_substr(), s.enc_mac_keys_substr(), - s.num_keys(), s.key_array(), GetSubstring(), - GetSubstring(), OEMCrypto_ContentLicense)); + LicenseRoundTrip license_messages(&s); + license_messages.set_control(patch_level + << wvoec::kControlSecurityPatchLevelShift); + ASSERT_NO_FATAL_FAILURE(license_messages.SignAndVerifyRequest()); + ASSERT_NO_FATAL_FAILURE(license_messages.CreateDefaultResponse()); + ASSERT_NO_FATAL_FAILURE(license_messages.EncryptAndSignResponse()); + ASSERT_EQ(OEMCrypto_SUCCESS, license_messages.LoadResponse()); } // Reject any future patch levels. if (patch_level < 0x3F) { Session s; ASSERT_NO_FATAL_FAILURE(s.open()); ASSERT_NO_FATAL_FAILURE(InstallTestRSAKey(&s)); - ASSERT_NO_FATAL_FAILURE(s.FillSimpleMessage( - 0, (patch_level + 1) << wvoec::kControlSecurityPatchLevelShift, 0)); - ASSERT_NO_FATAL_FAILURE(s.EncryptAndSign()); - ASSERT_EQ( - OEMCrypto_ERROR_UNKNOWN_FAILURE, - OEMCrypto_LoadKeys(s.session_id(), s.message_ptr(), s.message_size(), - s.signature().data(), s.signature().size(), - s.enc_mac_keys_iv_substr(), s.enc_mac_keys_substr(), - s.num_keys(), s.key_array(), GetSubstring(), - GetSubstring(), OEMCrypto_ContentLicense)); + LicenseRoundTrip license_messages(&s); + license_messages.set_control((patch_level + 1) + << wvoec::kControlSecurityPatchLevelShift); + ASSERT_NO_FATAL_FAILURE(license_messages.SignAndVerifyRequest()); + ASSERT_NO_FATAL_FAILURE(license_messages.CreateDefaultResponse()); + ASSERT_NO_FATAL_FAILURE(license_messages.EncryptAndSignResponse()); + ASSERT_EQ(OEMCrypto_ERROR_UNKNOWN_FAILURE, license_messages.LoadResponse()); } // Accept an old patch level. if (patch_level > 0) { Session s; ASSERT_NO_FATAL_FAILURE(s.open()); ASSERT_NO_FATAL_FAILURE(InstallTestRSAKey(&s)); - ASSERT_NO_FATAL_FAILURE(s.FillSimpleMessage( - 0, (patch_level - 1) << wvoec::kControlSecurityPatchLevelShift, 0)); - ASSERT_NO_FATAL_FAILURE(s.EncryptAndSign()); - ASSERT_EQ( - OEMCrypto_SUCCESS, - OEMCrypto_LoadKeys(s.session_id(), s.message_ptr(), s.message_size(), - s.signature().data(), s.signature().size(), - s.enc_mac_keys_iv_substr(), s.enc_mac_keys_substr(), - s.num_keys(), s.key_array(), GetSubstring(), - GetSubstring(), OEMCrypto_ContentLicense)); - } -} - -// This test verifies that OEMCrypto can load the number of keys required for -// the reported resource level. -TEST_F(OEMCryptoSessionTests, MinimumKeysAPI12) { - Session s; - ASSERT_NO_FATAL_FAILURE(s.open()); - ASSERT_NO_FATAL_FAILURE(InstallTestRSAKey(&s)); - size_t num_keys = GetResourceValue(kMaxKeysPerSession); - ASSERT_LE(num_keys, kMaxNumKeys) << "Test constants need updating."; - s.set_num_keys(num_keys); - ASSERT_NO_FATAL_FAILURE(s.FillSimpleMessage(0, 0, 0)); - ASSERT_NO_FATAL_FAILURE(s.EncryptAndSign()); - ASSERT_NO_FATAL_FAILURE(s.LoadTestKeys()); - for (size_t key_index = 0; key_index < num_keys; key_index++) { - bool kSelectKeyFirst = true; - ASSERT_NO_FATAL_FAILURE( - s.TestDecryptCTR(kSelectKeyFirst, OEMCrypto_SUCCESS, key_index)); + LicenseRoundTrip license_messages(&s); + license_messages.set_control((patch_level - 1) + << wvoec::kControlSecurityPatchLevelShift); + ASSERT_NO_FATAL_FAILURE(license_messages.SignAndVerifyRequest()); + ASSERT_NO_FATAL_FAILURE(license_messages.CreateDefaultResponse()); + ASSERT_NO_FATAL_FAILURE(license_messages.EncryptAndSignResponse()); + ASSERT_EQ(OEMCrypto_SUCCESS, license_messages.LoadResponse()); } } @@ -1553,7 +1481,7 @@ TEST_F(OEMCryptoSessionTests, MinimumKeysAPI12) { // required HDCP version in the key control block. class OEMCryptoSessionTestDecryptWithHDCP : public OEMCryptoSessionTests, public WithParamInterface { - public: + protected: void DecryptWithHDCP(OEMCrypto_HDCP_Capability version) { OEMCryptoResult sts; OEMCrypto_HDCP_Capability current, maximum; @@ -1562,13 +1490,14 @@ class OEMCryptoSessionTestDecryptWithHDCP : public OEMCryptoSessionTests, Session s; ASSERT_NO_FATAL_FAILURE(s.open()); ASSERT_NO_FATAL_FAILURE(InstallTestRSAKey(&s)); - ASSERT_NO_FATAL_FAILURE(s.FillSimpleMessage( - 0, - (version << wvoec::kControlHDCPVersionShift) | - wvoec::kControlObserveHDCP | wvoec::kControlHDCPRequired, - 0)); - ASSERT_NO_FATAL_FAILURE(s.EncryptAndSign()); - ASSERT_NO_FATAL_FAILURE(s.LoadTestKeys()); + LicenseRoundTrip license_messages(&s); + license_messages.set_control((version << wvoec::kControlHDCPVersionShift) | + wvoec::kControlObserveHDCP | + wvoec::kControlHDCPRequired); + ASSERT_NO_FATAL_FAILURE(license_messages.SignAndVerifyRequest()); + ASSERT_NO_FATAL_FAILURE(license_messages.CreateDefaultResponse()); + ASSERT_NO_FATAL_FAILURE(license_messages.EncryptAndSignResponse()); + ASSERT_EQ(OEMCrypto_SUCCESS, license_messages.LoadResponse()); if (version > maximum) { ASSERT_NO_FATAL_FAILURE( @@ -1600,57 +1529,80 @@ INSTANTIATE_TEST_CASE_P(TestHDCP, OEMCryptoSessionTestDecryptWithHDCP, // // Load, Refresh Keys Test // -// This test is parameterized by two parameters: -// 1. A boolean that determines if the license sets a new pair of mac keys in -// the license. -// 2. The number of keys refreshed in the refresh method. If the number of keys -// is zero, then all of the keys should be refreshed. -class OEMCryptoSessionTestRefreshKeyTestAPI16 - : public OEMCryptoSessionTests, - public WithParamInterface > { - public: +class OEMCryptoRefreshTest : public OEMCryptoLicenseTest { + protected: void SetUp() override { - OEMCryptoSessionTests::SetUp(); - new_mac_keys_ = - GetParam().first; // Whether to put new mac keys in LoadKeys. - num_keys_ = static_cast(GetParam().second); // # keys in refresh. + OEMCryptoLicenseTest::SetUp(); + // These values allow us to run a few simple duration tests or just start + // playback right away. All times are in seconds since the license was + // signed. + // Soft expiry false means timers are strictly enforce. + timer_limits_.soft_expiry = false; + // Playback may begin immediately. + timer_limits_.earliest_playback_start_seconds = 0; + // First playback may be within the first two seconds. + timer_limits_.latest_playback_start_seconds = kDuration; + // Once started, playback may last two seconds without a renewal. + timer_limits_.initial_playback_duration_seconds = kDuration; + // Playback may continue for four seconds after a renewal is loaded. + timer_limits_.renewal_playback_duration_seconds = 2 * kDuration; + // Total playback is not limited. + timer_limits_.license_duration_seconds = 0; } - protected: - bool new_mac_keys_; - size_t num_keys_; // Number of keys to refresh. + void LoadLicense() { + // If we require a nonce, then generate one. + if (license_messages_.control() & wvoec::kControlNonceEnabled) { + ASSERT_NO_FATAL_FAILURE(session_.GenerateNonce()); + } + license_messages_.core_response().timer_limits = timer_limits_; + ASSERT_NO_FATAL_FAILURE(license_messages_.SignAndVerifyRequest()); + ASSERT_NO_FATAL_FAILURE(license_messages_.CreateDefaultResponse()); + ASSERT_NO_FATAL_FAILURE(license_messages_.EncryptAndSignResponse()); + ASSERT_EQ(OEMCrypto_SUCCESS, license_messages_.LoadResponse()); + } + + void MakeRenewalRequest(RenewalRoundTrip* renewal_messages) { + ASSERT_NO_FATAL_FAILURE(renewal_messages->SignAndVerifyRequest()); + ASSERT_NO_FATAL_FAILURE(renewal_messages->CreateDefaultResponse()); + } + + void LoadRenewal(RenewalRoundTrip* renewal_messages, + OEMCryptoResult expected_result) { + ASSERT_NO_FATAL_FAILURE(renewal_messages->EncryptAndSignResponse()); + ASSERT_EQ(expected_result, renewal_messages->LoadResponse()); + } + + ODK_TimerLimits timer_limits_; }; // Refresh keys should work if the license uses a nonce. -TEST_P(OEMCryptoSessionTestRefreshKeyTestAPI16, RefreshWithNonce) { - Session s; - ASSERT_NO_FATAL_FAILURE(s.open()); - ASSERT_NO_FATAL_FAILURE(InstallTestRSAKey(&s)); - ASSERT_NO_FATAL_FAILURE(s.GenerateNonce()); - ASSERT_NO_FATAL_FAILURE( - s.FillSimpleMessage(0, wvoec::kControlNonceEnabled, s.nonce())); - ASSERT_NO_FATAL_FAILURE(s.EncryptAndSign()); - ASSERT_NO_FATAL_FAILURE(s.LoadTestKeys("", new_mac_keys_)); - // License renewal message is signed by client and verified by the server. - ASSERT_NO_FATAL_FAILURE(s.VerifyRenewalRequestSignature()); - ASSERT_NO_FATAL_FAILURE(s.RefreshTestKeys( - num_keys_, wvoec::kControlNonceEnabled, s.nonce(), OEMCrypto_SUCCESS)); +TEST_P(OEMCryptoRefreshTest, RefreshWithNonce) { + LoadLicense(); + RenewalRoundTrip renewal_messages(&license_messages_); + MakeRenewalRequest(&renewal_messages); + LoadRenewal(&renewal_messages, OEMCrypto_SUCCESS); } // Refresh keys should work if the license does not use a nonce. -TEST_P(OEMCryptoSessionTestRefreshKeyTestAPI16, RefreshNoNonce) { - Session s; - ASSERT_NO_FATAL_FAILURE(s.open()); - ASSERT_NO_FATAL_FAILURE(InstallTestRSAKey(&s)); - ASSERT_NO_FATAL_FAILURE(s.FillSimpleMessage(kDuration, 0, 0)); - ASSERT_NO_FATAL_FAILURE(s.EncryptAndSign()); - ASSERT_NO_FATAL_FAILURE(s.LoadTestKeys("", new_mac_keys_)); - // License renewal message is signed by client and verified by the server. - ASSERT_NO_FATAL_FAILURE(s.VerifyRenewalRequestSignature()); - ASSERT_NO_FATAL_FAILURE(s.RefreshTestKeys( - num_keys_, wvoec::kControlNonceEnabled, 0, OEMCrypto_SUCCESS)); +TEST_P(OEMCryptoRefreshTest, RefreshNoNonce) { + license_messages_.set_control(0); + LoadLicense(); + RenewalRoundTrip renewal_messages(&license_messages_); + MakeRenewalRequest(&renewal_messages); + LoadRenewal(&renewal_messages, OEMCrypto_SUCCESS); } +// Refresh keys should NOT work if a license has not been loaded. +TEST_P(OEMCryptoRefreshTest, RefreshNoLicense) { + // We do not call LoadLicense(); + RenewalRoundTrip renewal_messages(&license_messages_); + MakeRenewalRequest(&renewal_messages); + LoadRenewal(&renewal_messages, OEMCrypto_ERROR_UNKNOWN_FAILURE); +} + +#if 0 // These tests will be updated in a future CL. + // TODO(b/136281032): Nonce should be same as one in license. // Refresh keys should fail if the nonce is not in the session. TEST_P(OEMCryptoSessionTestRefreshKeyTestAPI16, RefreshBadNonceAPI11) { @@ -1732,42 +1684,35 @@ TEST_P(OEMCryptoSessionTestRefreshKeyTestAPI16, RefreshWithNoSelectKey) { wvcdm::TestSleep::Sleep(kShortSleep + kLongSleep); ASSERT_NO_FATAL_FAILURE(s.TestDecryptCTR(false)); } +#endif // XXX These tests will be updated in a future CL. -// If only one key control block in the refesh, we update all the keys. -INSTANTIATE_TEST_CASE_P(TestRefreshAllKeys, - OEMCryptoSessionTestRefreshKeyTestAPI16, - Values(std::make_pair(true, 1), - std::make_pair(false, 1))); - -// If multiple key control blocks, we update each key separately. -INSTANTIATE_TEST_CASE_P(TestRefreshEachKeys, - OEMCryptoSessionTestRefreshKeyTestAPI16, - Values(std::make_pair(true, 4), - std::make_pair(false, 4))); +INSTANTIATE_TEST_CASE_P(TestAll, OEMCryptoRefreshTest, + Range(kCurrentAPI - 1, kCurrentAPI + 1)); // If the license does not allow a hash, then we should not compute one. -TEST_F(OEMCryptoSessionTests, HashForbiddenAPI15) { +TEST_P(OEMCryptoLicenseTest, HashForbiddenAPI15) { uint32_t hash_type = OEMCrypto_SupportsDecryptHash(); // If hash is not supported, or is vendor defined, don't try to test it. if (hash_type != OEMCrypto_CRC_Clear_Buffer) return; - Session s; - ASSERT_NO_FATAL_FAILURE(s.open()); - ASSERT_NO_FATAL_FAILURE(InstallTestRSAKey(&s)); - ASSERT_NO_FATAL_FAILURE(s.FillSimpleMessage(kDuration, 0, 0)); - ASSERT_NO_FATAL_FAILURE(s.EncryptAndSign()); - ASSERT_NO_FATAL_FAILURE(s.LoadTestKeys()); + + ASSERT_NO_FATAL_FAILURE(session_.GenerateNonce()); + ASSERT_NO_FATAL_FAILURE(license_messages_.SignAndVerifyRequest()); + ASSERT_NO_FATAL_FAILURE(license_messages_.CreateDefaultResponse()); + ASSERT_NO_FATAL_FAILURE(license_messages_.EncryptAndSignResponse()); + ASSERT_EQ(OEMCrypto_SUCCESS, license_messages_.LoadResponse()); + uint32_t frame_number = 1; uint32_t hash = 42; // It is OK to set the hash before loading the keys ASSERT_EQ(OEMCrypto_SUCCESS, - OEMCrypto_SetDecryptHash(s.session_id(), frame_number, + OEMCrypto_SetDecryptHash(session_.session_id(), frame_number, reinterpret_cast(&hash), sizeof(hash))); // It is OK to select the key and decrypt. - ASSERT_NO_FATAL_FAILURE(s.TestDecryptCTR()); + ASSERT_NO_FATAL_FAILURE(session_.TestDecryptCTR()); // But the error code should be bad. ASSERT_EQ(OEMCrypto_ERROR_UNKNOWN_FAILURE, - OEMCrypto_GetHashErrorCode(s.session_id(), &frame_number)); + OEMCrypto_GetHashErrorCode(session_.session_id(), &frame_number)); } // @@ -2389,30 +2334,28 @@ INSTANTIATE_TEST_CASE_P( // A request to decrypt data to a clear buffer when the key control block // requires a secure data path. -TEST_F(OEMCryptoSessionTests, DecryptSecureToClear) { - Session s; - ASSERT_NO_FATAL_FAILURE(s.open()); - ASSERT_NO_FATAL_FAILURE(InstallTestRSAKey(&s)); - ASSERT_NO_FATAL_FAILURE(s.FillSimpleMessage( - kDuration, wvoec::kControlObserveDataPath | wvoec::kControlDataPathSecure, - 0)); - ASSERT_NO_FATAL_FAILURE(s.EncryptAndSign()); - ASSERT_NO_FATAL_FAILURE(s.LoadTestKeys()); +TEST_P(OEMCryptoLicenseTest, DecryptSecureToClear) { + ASSERT_NO_FATAL_FAILURE(session_.GenerateNonce()); + ASSERT_NO_FATAL_FAILURE(license_messages_.SignAndVerifyRequest()); + license_messages_.set_control(wvoec::kControlObserveDataPath | + wvoec::kControlDataPathSecure); + ASSERT_NO_FATAL_FAILURE(license_messages_.CreateDefaultResponse()); + ASSERT_NO_FATAL_FAILURE(license_messages_.EncryptAndSignResponse()); + ASSERT_EQ(OEMCrypto_SUCCESS, license_messages_.LoadResponse()); ASSERT_NO_FATAL_FAILURE( - s.TestDecryptCTR(true, OEMCrypto_ERROR_UNKNOWN_FAILURE)); + session_.TestDecryptCTR(true, OEMCrypto_ERROR_UNKNOWN_FAILURE)); } // If analog is forbidden, then decrypt to a clear buffer should be forbidden. -TEST_F(OEMCryptoSessionTests, DecryptNoAnalogToClearAPI13) { - Session s; - ASSERT_NO_FATAL_FAILURE(s.open()); - ASSERT_NO_FATAL_FAILURE(InstallTestRSAKey(&s)); +TEST_P(OEMCryptoLicenseTest, DecryptNoAnalogToClearAPI13) { + ASSERT_NO_FATAL_FAILURE(session_.GenerateNonce()); + ASSERT_NO_FATAL_FAILURE(license_messages_.SignAndVerifyRequest()); + license_messages_.set_control(wvoec::kControlDisableAnalogOutput); + ASSERT_NO_FATAL_FAILURE(license_messages_.CreateDefaultResponse()); + ASSERT_NO_FATAL_FAILURE(license_messages_.EncryptAndSignResponse()); + ASSERT_EQ(OEMCrypto_SUCCESS, license_messages_.LoadResponse()); ASSERT_NO_FATAL_FAILURE( - s.FillSimpleMessage(kDuration, wvoec::kControlDisableAnalogOutput, 0)); - ASSERT_NO_FATAL_FAILURE(s.EncryptAndSign()); - ASSERT_NO_FATAL_FAILURE(s.LoadTestKeys()); - ASSERT_NO_FATAL_FAILURE( - s.TestDecryptCTR(true, OEMCrypto_ERROR_ANALOG_OUTPUT)); + session_.TestDecryptCTR(true, OEMCrypto_ERROR_ANALOG_OUTPUT)); } // Test that key duration is honored. @@ -2630,6 +2573,8 @@ TEST_F(OEMCryptoLoadsCertificate, CertificateProvisionBadRSAKeyKeyboxTest) { ASSERT_NO_FATAL_FAILURE(provisioning_messages.EncryptAndSignResponse()); size_t rsa_offset = provisioning_messages.core_response().enc_private_key.offset; + // Offsets are relative to the message body, after the core message. + rsa_offset += provisioning_messages.serialized_core_message().size(); rsa_offset += 4; // Change the middle of the key. provisioning_messages.encrypted_response_buffer()[rsa_offset] ^= 42; ASSERT_EQ(OEMCrypto_ERROR_SIGNATURE_FAILURE, @@ -2675,9 +2620,13 @@ TEST_F(OEMCryptoLoadsCertificate, TestLargeRSAKey3072) { ASSERT_NO_FATAL_FAILURE(s.PreparePublicKey(encoded_rsa_key_.data(), encoded_rsa_key_.size())); ASSERT_NO_FATAL_FAILURE(s.InstallRSASessionTestKey(wrapped_rsa_key_)); - ASSERT_NO_FATAL_FAILURE(s.FillSimpleMessage(kDuration, 0, 0)); - ASSERT_NO_FATAL_FAILURE(s.EncryptAndSign()); - ASSERT_NO_FATAL_FAILURE(s.LoadTestKeys()); + + LicenseRoundTrip license_messages(&s); + ASSERT_NO_FATAL_FAILURE(s.GenerateNonce()); + ASSERT_NO_FATAL_FAILURE(license_messages.SignAndVerifyRequest()); + ASSERT_NO_FATAL_FAILURE(license_messages.CreateDefaultResponse()); + ASSERT_NO_FATAL_FAILURE(license_messages.EncryptAndSignResponse()); + ASSERT_EQ(OEMCrypto_SUCCESS, license_messages.LoadResponse()); ASSERT_NO_FATAL_FAILURE(s.TestDecryptCTR()); } @@ -2693,9 +2642,13 @@ TEST_F(OEMCryptoLoadsCertificate, TestCarmichaelRSAKey) { ASSERT_NO_FATAL_FAILURE(s.PreparePublicKey(encoded_rsa_key_.data(), encoded_rsa_key_.size())); ASSERT_NO_FATAL_FAILURE(s.InstallRSASessionTestKey(wrapped_rsa_key_)); - ASSERT_NO_FATAL_FAILURE(s.FillSimpleMessage(kDuration, 0, 0)); - ASSERT_NO_FATAL_FAILURE(s.EncryptAndSign()); - ASSERT_NO_FATAL_FAILURE(s.LoadTestKeys()); + + LicenseRoundTrip license_messages(&s); + ASSERT_NO_FATAL_FAILURE(s.GenerateNonce()); + ASSERT_NO_FATAL_FAILURE(license_messages.SignAndVerifyRequest()); + ASSERT_NO_FATAL_FAILURE(license_messages.CreateDefaultResponse()); + ASSERT_NO_FATAL_FAILURE(license_messages.EncryptAndSignResponse()); + ASSERT_EQ(OEMCrypto_SUCCESS, license_messages.LoadResponse()); ASSERT_NO_FATAL_FAILURE(s.TestDecryptCTR()); } @@ -2720,17 +2673,22 @@ TEST_F(OEMCryptoLoadsCertificate, TestMultipleRSAKeys) { ASSERT_NO_FATAL_FAILURE(s2.PreparePublicKey(encoded_rsa_key_.data(), encoded_rsa_key_.size())); ASSERT_NO_FATAL_FAILURE(s2.InstallRSASessionTestKey(wrapped_rsa_key_)); - ASSERT_NO_FATAL_FAILURE(s2.FillSimpleMessage(kDuration, 0, 0)); - ASSERT_NO_FATAL_FAILURE(s2.EncryptAndSign()); - ASSERT_NO_FATAL_FAILURE(s2.LoadTestKeys()); + LicenseRoundTrip license_messages2(&s2); + ASSERT_NO_FATAL_FAILURE(s2.GenerateNonce()); + ASSERT_NO_FATAL_FAILURE(license_messages2.SignAndVerifyRequest()); + ASSERT_NO_FATAL_FAILURE(license_messages2.CreateDefaultResponse()); + ASSERT_NO_FATAL_FAILURE(license_messages2.EncryptAndSignResponse()); + ASSERT_EQ(OEMCrypto_SUCCESS, license_messages2.LoadResponse()); ASSERT_NO_FATAL_FAILURE(s2.TestDecryptCTR()); s2.close(); // After s2 has loaded its rsa key, we continue using s1's key. - ASSERT_NO_FATAL_FAILURE(s1.GenerateDerivedKeysFromSessionKey()); - ASSERT_NO_FATAL_FAILURE(s1.FillSimpleMessage(kDuration, 0, 0)); - ASSERT_NO_FATAL_FAILURE(s1.EncryptAndSign()); - ASSERT_NO_FATAL_FAILURE(s1.LoadTestKeys()); + LicenseRoundTrip license_messages1(&s1); + ASSERT_NO_FATAL_FAILURE(s1.GenerateNonce()); + ASSERT_NO_FATAL_FAILURE(license_messages1.SignAndVerifyRequest()); + ASSERT_NO_FATAL_FAILURE(license_messages1.CreateDefaultResponse()); + ASSERT_NO_FATAL_FAILURE(license_messages1.EncryptAndSignResponse()); + ASSERT_EQ(OEMCrypto_SUCCESS, license_messages1.LoadResponse()); ASSERT_NO_FATAL_FAILURE(s1.TestDecryptCTR()); } @@ -2741,6 +2699,9 @@ TEST_F(OEMCryptoLoadsCertificate, SupportsCertificatesAPI13) { << "Supported certificates is only " << OEMCrypto_SupportedCertificates(); } +// These tests are run by all L1 devices that load and use certificates. It is +// also run by a few L3 devices that use a baked in certificate, but cannot load +// a certificate. class OEMCryptoUsesCertificate : public OEMCryptoLoadsCertificate { protected: void SetUp() override { @@ -3944,17 +3905,19 @@ class OEMCryptoGenericCryptoTest : public OEMCryptoSessionTests { protected: // buffer_size_ must be a multiple of encryption block size, 16. We'll use a // reasonable number of blocks for most of the tests. - OEMCryptoGenericCryptoTest() : buffer_size_(160) {} + OEMCryptoGenericCryptoTest() + : buffer_size_(160), license_messages_(&session_) {} void SetUp() override { OEMCryptoSessionTests::SetUp(); ASSERT_NO_FATAL_FAILURE(session_.open()); ASSERT_NO_FATAL_FAILURE(InstallTestRSAKey(&session_)); + ASSERT_NO_FATAL_FAILURE(license_messages_.SignAndVerifyRequest()); ASSERT_NO_FATAL_FAILURE(MakeFourKeys()); } void TearDown() override { - session_.close(); + ASSERT_NO_FATAL_FAILURE(session_.close()); OEMCryptoSessionTests::TearDown(); } @@ -3962,20 +3925,23 @@ class OEMCryptoGenericCryptoTest : public OEMCryptoSessionTests { // to test. void MakeFourKeys(uint32_t duration = kDuration, uint32_t control = 0, uint32_t nonce = 0, const std::string& pst = "") { - ASSERT_NO_FATAL_FAILURE( - session_.FillSimpleMessage(duration, control, nonce, pst)); - session_.license().keys[0].control.control_bits |= + license_messages_.set_control(control); + license_messages_.set_pst(pst); + license_messages_.core_response().timer_limits.license_duration_seconds = + kDuration; + ASSERT_NO_FATAL_FAILURE(license_messages_.CreateDefaultResponse()); + license_messages_.response_data().keys[0].control.control_bits |= htonl(wvoec::kControlAllowEncrypt); - session_.license().keys[1].control.control_bits |= + license_messages_.response_data().keys[1].control.control_bits |= htonl(wvoec::kControlAllowDecrypt); - session_.license().keys[2].control.control_bits |= + license_messages_.response_data().keys[2].control.control_bits |= htonl(wvoec::kControlAllowSign); - session_.license().keys[3].control.control_bits |= + license_messages_.response_data().keys[3].control.control_bits |= htonl(wvoec::kControlAllowVerify); - - session_.license().keys[2].key_data_length = wvoec::MAC_KEY_SIZE; - session_.license().keys[3].key_data_length = wvoec::MAC_KEY_SIZE; - + license_messages_.response_data().keys[2].key_data_length = + wvoec::MAC_KEY_SIZE; + license_messages_.response_data().keys[3].key_data_length = + wvoec::MAC_KEY_SIZE; clear_buffer_.assign(buffer_size_, 0); for (size_t i = 0; i < clear_buffer_.size(); i++) { clear_buffer_[i] = 1 + i % 250; @@ -3983,11 +3949,12 @@ class OEMCryptoGenericCryptoTest : public OEMCryptoSessionTests { for (size_t i = 0; i < wvoec::KEY_IV_SIZE; i++) { iv_[i] = i; } + ASSERT_NO_FATAL_FAILURE(license_messages_.FillCoreResponseSubstrings()); } void EncryptAndLoadKeys() { - ASSERT_NO_FATAL_FAILURE(session_.EncryptAndSign()); - session_.LoadTestKeys(); + ASSERT_NO_FATAL_FAILURE(license_messages_.EncryptAndSignResponse()); + ASSERT_EQ(OEMCrypto_SUCCESS, license_messages_.LoadResponse()); } // Encrypt the buffer with the specified key made in MakeFourKeys. @@ -4108,6 +4075,7 @@ class OEMCryptoGenericCryptoTest : public OEMCryptoSessionTests { vector encrypted_buffer_; uint8_t iv_[wvoec::KEY_IV_SIZE]; Session session_; + LicenseRoundTrip license_messages_; }; TEST_F(OEMCryptoGenericCryptoTest, GenericKeyLoad) { EncryptAndLoadKeys(); } @@ -4210,8 +4178,8 @@ TEST_F(OEMCryptoGenericCryptoTest, GenericKeyDecryptSameBufferAPI12) { // Test that Generic_Decrypt fails to decrypt to an insecure buffer if the key // requires a secure data path. TEST_F(OEMCryptoGenericCryptoTest, GenericSecureToClear) { - session_.license().keys[1].control.control_bits |= htonl( - wvoec::kControlObserveDataPath | wvoec::kControlDataPathSecure); + MakeFourKeys(0, + wvoec::kControlObserveDataPath | wvoec::kControlDataPathSecure); EncryptAndLoadKeys(); unsigned int key_index = 1; vector encrypted; @@ -4548,31 +4516,36 @@ class OEMCryptoGenericCryptoKeyIdLengthTest protected: void SetUp() override { OEMCryptoGenericCryptoTest::SetUp(); - const uint32_t kNoNonce = 0; - session_.set_num_keys(5); - ASSERT_NO_FATAL_FAILURE(session_.FillSimpleMessage( - kDuration, wvoec::kControlAllowDecrypt, kNoNonce)); + license_messages_.set_num_keys(5); + license_messages_.set_control(wvoec::kControlAllowDecrypt); + license_messages_.core_response().timer_limits.license_duration_seconds = + kDuration; + ASSERT_NO_FATAL_FAILURE(license_messages_.CreateDefaultResponse()); SetUniformKeyIdLength(16); // Start with all key ids being 16 bytes. // But, we are testing that the key ids do not have to have the same length. - session_.SetKeyId(0, "123456789012"); // 12 bytes (common key id length). - session_.SetKeyId(1, "12345"); // short key id. - session_.SetKeyId(2, "1234567890123456"); // 16 byte key id. (default) - session_.SetKeyId(3, "12345678901234"); // 14 byte. (uncommon) - session_.SetKeyId(4, "1"); // very short key id. + // 12 bytes (common key id length). + license_messages_.SetKeyId(0, "123456789012"); + license_messages_.SetKeyId(1, "12345"); // short key id. + // 16 byte key id. (default) + license_messages_.SetKeyId(2, "1234567890123456"); + license_messages_.SetKeyId(3, "12345678901234"); // 14 byte. (uncommon) + license_messages_.SetKeyId(4, "1"); // very short key id. ASSERT_EQ(2u, kLongKeyId); + ASSERT_NO_FATAL_FAILURE(license_messages_.FillCoreResponseSubstrings()); } // Make all four keys have the same length. void SetUniformKeyIdLength(size_t key_id_length) { - for (unsigned int i = 0; i < session_.num_keys(); i++) { + for (size_t i = 0; i < license_messages_.num_keys(); i++) { string key_id; key_id.resize(key_id_length, i + 'a'); - session_.SetKeyId(i, key_id); + license_messages_.SetKeyId(i, key_id); } + ASSERT_NO_FATAL_FAILURE(license_messages_.FillCoreResponseSubstrings()); } void TestWithKey(unsigned int key_index) { - ASSERT_LT(key_index, session_.num_keys()); + ASSERT_LT(key_index, license_messages_.num_keys()); EncryptAndLoadKeys(); vector encrypted; // To make sure OEMCrypto is not expecting the key_id to be zero padded, we @@ -4626,7 +4599,7 @@ TEST_F(OEMCryptoGenericCryptoKeyIdLengthTest, UniformLongKeyId) { // Test usage table functionality. class OEMCryptoUsageTableTest : public OEMCryptoGenericCryptoTest { - public: + protected: void SetUp() override { OEMCryptoGenericCryptoTest::SetUp(); new_mac_keys_ = true; @@ -4720,7 +4693,7 @@ class OEMCryptoUsageTableTest : public OEMCryptoGenericCryptoTest { // installs new mac keys in LoadKeys. class OEMCryptoUsageTableTestWithMAC : public OEMCryptoUsageTableTest, public WithParamInterface { - public: + protected: void SetUp() override { OEMCryptoUsageTableTest::SetUp(); new_mac_keys_ = GetParam(); @@ -4978,9 +4951,9 @@ TEST_P(OEMCryptoUsageTableTestWithMAC, GenericCryptoEncrypt) { uint32_t nonce = session_.nonce(); MakeFourKeys(0, wvoec::kControlNonceEnabled | wvoec::kControlNonceRequired, nonce, pst); - ASSERT_NO_FATAL_FAILURE(session_.EncryptAndSign()); + ASSERT_NO_FATAL_FAILURE(license_messages_.EncryptAndSignResponse()); ASSERT_NO_FATAL_FAILURE(session_.CreateNewUsageEntry()); - ASSERT_NO_FATAL_FAILURE(session_.LoadTestKeys(pst, new_mac_keys_)); + ASSERT_EQ(OEMCrypto_SUCCESS, license_messages_.LoadResponse()); OEMCryptoResult sts; unsigned int key_index = 0; vector expected_encrypted; @@ -5017,9 +4990,9 @@ TEST_P(OEMCryptoUsageTableTestWithMAC, GenericCryptoDecrypt) { MakeFourKeys( 0, wvoec::kControlNonceEnabled | wvoec::kControlNonceRequired, nonce, pst); - ASSERT_NO_FATAL_FAILURE(session_.EncryptAndSign()); + ASSERT_NO_FATAL_FAILURE(license_messages_.EncryptAndSignResponse()); ASSERT_NO_FATAL_FAILURE(session_.CreateNewUsageEntry()); - ASSERT_NO_FATAL_FAILURE(session_.LoadTestKeys(pst, new_mac_keys_)); + ASSERT_EQ(OEMCrypto_SUCCESS, license_messages_.LoadResponse()); OEMCryptoResult sts; unsigned int key_index = 1; vector encrypted; @@ -5056,9 +5029,9 @@ TEST_P(OEMCryptoUsageTableTestWithMAC, GenericCryptoSign) { MakeFourKeys( 0, wvoec::kControlNonceEnabled | wvoec::kControlNonceRequired, nonce, pst); - ASSERT_NO_FATAL_FAILURE(session_.EncryptAndSign()); + ASSERT_NO_FATAL_FAILURE(license_messages_.EncryptAndSignResponse()); ASSERT_NO_FATAL_FAILURE(session_.CreateNewUsageEntry()); - ASSERT_NO_FATAL_FAILURE(session_.LoadTestKeys(pst, new_mac_keys_)); + ASSERT_EQ(OEMCrypto_SUCCESS, license_messages_.LoadResponse()); OEMCryptoResult sts; unsigned int key_index = 2; @@ -5106,9 +5079,9 @@ TEST_P(OEMCryptoUsageTableTestWithMAC, GenericCryptoVerify) { MakeFourKeys( 0, wvoec::kControlNonceEnabled | wvoec::kControlNonceRequired, nonce, pst); - ASSERT_NO_FATAL_FAILURE(session_.EncryptAndSign()); + ASSERT_NO_FATAL_FAILURE(license_messages_.EncryptAndSignResponse()); ASSERT_NO_FATAL_FAILURE(session_.CreateNewUsageEntry()); - ASSERT_NO_FATAL_FAILURE(session_.LoadTestKeys(pst, new_mac_keys_)); + ASSERT_EQ(OEMCrypto_SUCCESS, license_messages_.LoadResponse()); OEMCryptoResult sts; unsigned int key_index = 3;