Initial LoadRenewal unit tests.

This commit is contained in:
Fred Gylys-Colwell
2019-12-13 11:15:50 -08:00
parent a18b487a6e
commit ba75fe85da
11 changed files with 1307 additions and 1030 deletions

View File

@@ -23,84 +23,14 @@
#include <stddef.h>
#include <stdint.h>
#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,

View File

@@ -0,0 +1 @@
../../oemcrypto/odk/include/OEMCryptoCENCCommon.h

View File

@@ -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<wvcdm::FileSystem>&& file_system);
virtual SessionContext* MakeSession(SessionId sid);
virtual UsageTable* MakeUsageTable();

View File

@@ -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<uint8_t> key_id;
std::vector<uint8_t> key_control;
std::vector<uint8_t> 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;
}

View File

@@ -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<uint32_t>((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<uint8_t>& key_control,
const std::vector<uint8_t>& 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);
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<uint8_t> content_key_value = content_key->value();
// Decrypt encrypted key control block
std::vector<uint8_t> 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);
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;
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,9 +1416,11 @@ 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_) {
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<uint8_t>(
@@ -1469,8 +1429,7 @@ OEMCryptoResult SessionContext::LoadUsageEntry(
mac_key_client_ = std::vector<uint8_t>(
usage_entry_->mac_key_client(),
usage_entry_->mac_key_client() + wvoec::MAC_KEY_SIZE);
}
return result;
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<uint8_t>& 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<uint8_t>& pst,

View File

@@ -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<uint8_t>& key_control,
const std::vector<uint8_t>& 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_;

View File

@@ -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<uint8_t>& 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<uint8_t>& 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<uint8_t>& 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<uint8_t>& 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<uint64_t>(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<SignedEntryBlock*>(&clear_buffer[0]);
SignedEntryBlock* encrypted =
reinterpret_cast<SignedEntryBlock*>(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<uint8_t>& buffer) {
const std::vector<uint8_t>& 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<uint8_t>& buffer) {
const std::vector<uint8_t>& 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;

View File

@@ -13,8 +13,9 @@
#include <vector>
#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<uint8_t>& server,
const std::vector<uint8_t>& client);
bool SetMacKeys(const std::vector<uint8_t>& server,
const std::vector<uint8_t>& 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<uint8_t>& pst);
virtual OEMCryptoResult ReportUsage(const std::vector<uint8_t>& 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<uint8_t>& buffer);
const std::vector<uint8_t>& 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<uint8_t>& 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<uint8_t>& 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);

View File

@@ -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);
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<uint8_t> 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<OEMCrypto_LicenseType>(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<const uint8_t*>(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<const uint8_t*>(ptr) -
reinterpret_cast<const uint8_t*>(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<const uint8_t*>(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<uint8_t*>(&block),
&size);
if (sts != OEMCrypto_ERROR_NOT_IMPLEMENTED) {
ASSERT_EQ(OEMCrypto_SUCCESS, sts);
ASSERT_EQ(sizeof(block), size);
// control duration and bits stored in network byte order. For printing
// we change to host byte order.
ASSERT_EQ((htonl_fnc(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<uint8_t>& data, const vector<uint8_t>& generated_signature,
size_t core_message_length) {
ASSERT_EQ(HMAC_SHA256_SIGNATURE_SIZE, generated_signature.size());
std::vector<uint8_t> expected_signature;
if (license_messages_->api_version() < kCoreMessagesAPI) {
// For v15 or earlier, we only sign the message body. Ignore the core
// message.
std::vector<uint8_t> 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<uint32_t>(
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<const uint8_t*>(&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(

View File

@@ -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<oec_util::ODK_ProvisioningRequest,
: public RoundTrip</* CoreRequest */ oec_util::ODK_ProvisioningRequest,
OEMCrypto_PrepAndSignProvisioningRequest,
ODK_ParsedProvisioning, RSAPrivateKeyMessage> {
/* CoreResponse */ ODK_ParsedProvisioning,
/* ResponseData */ RSAPrivateKeyMessage> {
public:
ProvisioningRoundTrip(Session* session,
const std::vector<uint8_t>& encoded_rsa_key)
@@ -257,20 +262,21 @@ class ProvisioningRoundTrip
std::vector<uint8_t> wrapped_rsa_key_;
};
class LicenseRoundTrip : public RoundTrip<oec_util::ODK_LicenseRequest,
class LicenseRoundTrip
: public RoundTrip</* CoreRequest */ oec_util::ODK_LicenseRequest,
OEMCrypto_PrepAndSignLicenseRequest,
ODK_ParsedLicense, MessageData> {
/* CoreResponse */ ODK_ParsedLicense,
/* ResponseData */ MessageData> {
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<oec_util::ODK_LicenseRequest,
void EncryptAndSignResponse() override;
OEMCryptoResult LoadResponse() override;
void VerifyTestKeys();
const OEMCrypto_KeyObject* key_array() { return key_array_; }
// Set the default key control block for all keys. This is used in
// CreateDefaultResponse. The key control block determines the restrictions
// that OEMCrypto should place on a key's use. For example, it specifies the
@@ -286,24 +291,34 @@ class LicenseRoundTrip : public RoundTrip<oec_util::ODK_LicenseRequest,
// video path. See the section "Key Control Block" in the document "Widevine
// Modular DRM Security Integration Guide for CENC".
void set_control(uint32_t control) { control_ = control; }
uint32_t control() const { return control_; }
// Set the number of keys to use in the license.
void set_num_keys(uint32_t num_keys) { num_keys_ = num_keys; }
uint32_t num_keys() { return num_keys_; }
uint32_t num_keys() const { return num_keys_; }
// Set the pst for the license.
void set_pst(const std::string& pst) { pst_ = pst; }
// Set the minimum SRM version for the license.
void set_minimum_srm_version(uint32_t minimum_srm_version) {
minimum_srm_version_ = minimum_srm_version;
}
// Change the hash of the core request. This should cause the response to be
// rejected.
void BreakRequestHash() { core_response_.request_hash[3] ^= 42; }
// Set the API version for the license itself. This will be used in
// CreateDefaultResponse.
void set_api_version(uint32_t api_version) { api_version_ = api_version; }
uint32_t api_version() const { return api_version_; }
void set_update_mac_keys(bool update_mac_keys) {
update_mac_keys_ = update_mac_keys;
}
void set_license_type(OEMCrypto_LicenseType license_type) {
license_type_ = license_type;
}
// Skip the nonce check when verifying the license request.
void skip_nonce_check() { expect_request_has_correct_nonce_ = false; }
// This sets the key id of the specified key to the specified string.
// This is used to test with different key id lengths.
void SetKeyId(size_t index, const string& key_id);
protected:
void VerifyRequestSignature(const vector<uint8_t>& data,
@@ -313,10 +328,6 @@ class LicenseRoundTrip : public RoundTrip<oec_util::ODK_LicenseRequest,
virtual void FillAndVerifyCoreRequest(
const std::string& core_message_string) override;
// Array of key objects for this license.
OEMCrypto_KeyObject key_array_[kMaxNumKeys];
// The server's computed hash of the core license request.
uint8_t license_request_hash_[ODK_SHA256_HASH_SIZE];
// The default key control bits used with CreateDefaultResponse.
uint32_t control_;
// The number of keys in the license response.
@@ -331,19 +342,29 @@ class LicenseRoundTrip : public RoundTrip<oec_util::ODK_LicenseRequest,
// is signed, it will be set to the same as OEMCrypto's API version. It may
// be set to a lower value in order to test backwards compatibility.
uint32_t api_version_;
// If true, then we expect the nonce in the core request to match that in
// session. This is usually true, but when we are testing how OEMCrypto
// handles a bad nonce, we don't want to.
bool expect_request_has_correct_nonce_;
// Whether this is a content license or an entitlement license. Used in
// CreateDefaultResponse.
OEMCrypto_LicenseType license_type_;
};
class RenewalRoundTrip
: public RoundTrip<oec_util::ODK_RenewalRequest,
class RenewalRoundTrip : public RoundTrip<
/* CoreRequest */ oec_util::ODK_RenewalRequest,
OEMCrypto_PrepAndSignRenewalRequest,
oec_util::ODK_RenewalRequest, // Response has same
// information as request.
MessageData> {
// 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<uint8_t>& 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_;

File diff suppressed because it is too large Load Diff