diff --git a/docs/License_Duration_and_Renewal.pdf b/docs/License_Duration_and_Renewal.pdf index 1800d98..c8ac6ad 100644 Binary files a/docs/License_Duration_and_Renewal.pdf and b/docs/License_Duration_and_Renewal.pdf differ diff --git a/docs/OEMCrypto_State_Diagrams.pdf b/docs/OEMCrypto_State_Diagrams.pdf new file mode 100644 index 0000000..5425839 Binary files /dev/null and b/docs/OEMCrypto_State_Diagrams.pdf differ diff --git a/docs/WidevineModularDRMSecurityIntegrationGuideforCENC_v16.pdf b/docs/WidevineModularDRMSecurityIntegrationGuideforCENC_v16.pdf new file mode 100644 index 0000000..2ec854a Binary files /dev/null and b/docs/WidevineModularDRMSecurityIntegrationGuideforCENC_v16.pdf differ diff --git a/docs/Widevine_Core_Message_Serialization.pdf b/docs/Widevine_Core_Message_Serialization.pdf index 082d167..a4240a8 100644 Binary files a/docs/Widevine_Core_Message_Serialization.pdf and b/docs/Widevine_Core_Message_Serialization.pdf differ diff --git a/docs/Widevine_Modular_DRM_Version_16_Delta.pdf b/docs/Widevine_Modular_DRM_Version_16_Delta.pdf index 4803689..34d33e3 100644 Binary files a/docs/Widevine_Modular_DRM_Version_16_Delta.pdf and b/docs/Widevine_Modular_DRM_Version_16_Delta.pdf differ diff --git a/oemcrypto/include/OEMCryptoCENC.h b/oemcrypto/include/OEMCryptoCENC.h index 5a84c47..8d5f0cd 100644 --- a/oemcrypto/include/OEMCryptoCENC.h +++ b/oemcrypto/include/OEMCryptoCENC.h @@ -88,53 +88,85 @@ typedef enum OEMCryptoResult { 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 /* - * OEMCrypto_DestBufferDesc - * Describes the type and access information for the memory to receive - * decrypted data. + * The memory referenced by OEMCrypto_SharedMemory* is safe to be placed in + * shared memory. The only data that should be placed into shared + * memory is the contents of input/output buffers, i.e. data that will + * not introduce security vulnerabilities if it is subject to + * modification while being accessed. + */ +typedef uint8_t OEMCrypto_SharedMemory; + +/* + * OEMCrypto_DestBufferDesc Structure * - * The OEMCrypto API supports a range of client device architectures. - * Different architectures have different methods for acquiring and securing - * buffers that will hold portions of the audio or video stream after - * decryption. Three basic strategies are recognized for handling decrypted - * stream data: - * 1. Return the decrypted data in the clear into normal user memory - * (ClearBuffer). The caller uses normal memory allocation methods to - * acquire a buffer, and supplies the memory address of the buffer in the - * descriptor. - * 2. Place the decrypted data into protected memory (SecureBuffer). The - * caller uses a platform-specific method to acquire the protected buffer - * and a user-memory handle that references it. The handle is supplied - * to the decrypt call in the descriptor. If the buffer is filled with - * several OEMCrypto calls, the same handle will be used, and the offset - * will be incremented to indicate where the next write should take place. - * 3. Place the decrypted data directly into the audio or video decoder fifo - * (Direct). The caller will use platform-specific methods to initialize - * the fifo and the decoders. The decrypted stream data is not accessible - * to the caller. + * Description: + * This structure is used as parameters in the OEMCrypto_DecryptCENC and + * OEMCrypto_CopyBuffer functions. This describes the type and access + * information for the memory to receive decrypted data. * - * Specific fields are as follows: + * The OEMCrypto API supports a range of client device architectures. + * Different architectures have different methods for acquiring and securing + * buffers that will hold portions of the audio or video stream after + * decryption. Three basic strategies are recognized for handling decrypted + * stream data: * - * (type == OEMCrypto_BufferType_Clear) - * address - Address of start of user memory buffer. - * max_length - Size of user memory buffer. - * (type == OEMCrypto_BufferType_Secure) - * buffer - handle to a platform-specific secure buffer. - * max_length - Size of platform-specific secure buffer. - * offset - offset from beginning of buffer to which OEMCrypto should write. - * (type == OEMCrypto_BufferType_Direct) - * is_video - If true, decrypted bytes are routed to the video - * decoder. If false, decrypted bytes are routed to the - * audio decoder. + * 1. Return the decrypted data in the clear into normal user memory + * (ClearBuffer). The caller uses normal memory allocation methods to + * acquire a buffer, and supplies the memory address of the buffer in + * the descriptor. + * 2. Place the decrypted data into protected memory (SecureBuffer). The + * caller uses a platform-specific method to acquire the protected + * buffer and a user-memory handle that references it. The handle is + * supplied to the decrypt call in the descriptor. If the buffer is + * filled with several OEMCrypto calls, the same handle will be used, + * and the offset will be incremented to indicate where the next write + * should take place. + * 3. Place the decrypted data directly into the audio or video decoder + * fifo (Direct). The caller will use platform-specific methods to + * initialize the fifo and the decoders. The decrypted stream data is + * not accessible to the caller. This is used on some platforms only. + * + * Fields: + * [in] type: A tag that indicates which variant of the union is valid for + * this instance of the structure. + * [variant] clear: This variant is valid when the type is + * OEMCrypto_BufferType_Clear. This OEMCrypto_DestBufferDesc indicates output + * should be written to a clear buffer. + * [in] address: A pointer to the address in memory to begin writing output. + * [in] address_length: The length of the buffer that is available to contain + * output. + * [variant] secure: This variant is valid when the type is + * OEMCrypto_BufferType_Secure. This OEMCrypto_DestBufferDesc indicates + * output should be written to a secure buffer. The decrypted output must + * never leave the secure area until it is output from the device. + * [in] handle: An opaque handle to a secure buffer. The meaning of this + * handle is platform-specific. + * [in] handle_length: The length of the data contained in the secure buffer. + * [in] offset: An offset indicating where in the secure buffer to start + * writing data. + * [variant] direct: This variant is valid when the type is + * OEMCrypto_BufferType_Direct. This OEMCrypto_DestBufferDesc indicates + * output should be written directly to the decoder. + * [in] is_video: A flag indicating if the data is video and should be sent + * to the video decoder. If this is false, the data can be assumed to be + * audio and sent to the audio decoder. + * + * Version: + * This struct changed in API version 16. */ typedef enum OEMCryptoBufferType { OEMCrypto_BufferType_Clear, @@ -146,12 +178,12 @@ typedef struct { OEMCryptoBufferType type; union { struct { // type == OEMCrypto_BufferType_Clear - uint8_t* address; - size_t max_length; + OEMCrypto_SharedMemory* address; + size_t address_length; } clear; struct { // type == OEMCrypto_BufferType_Secure void* handle; - size_t max_length; + size_t handle_length; size_t offset; } secure; struct { // type == OEMCrypto_BufferType_Direct @@ -160,6 +192,107 @@ typedef struct { } buffer; } OEMCrypto_DestBufferDesc; +/* + * OEMCrypto_InputOutputPair Structure + * + * Description: + * This structure is used as parameters in the OEMCrypto_DecryptCENC function. + * + * Fields: + * [in] input_data: An unaligned pointer to this sample from the stream. + * [in] input_data_length: The length of this sample in the stream, in bytes. + * [in] output: A caller-owned descriptor that specifies the handling of the + * decrypted byte stream. See OEMCrypto_DestbufferDesc for details. + * + * Version: + * This struct changed in API version 16. + */ +typedef struct { + 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; + +/* + * OEMCrypto_SubSampleDescription Structure + * + * Description: + * This structure is used as parameters in the OEMCrypto_DecryptCENC function. + * + * Fields: + * [in] num_bytes_clear: The number of clear bytes in this subsample. The + * clear bytes come before the encrypted bytes. + * [in] num_bytes_encrypted: The number of encrypted bytes in this subsample. + * The encrypted bytes come after the clear bytes. + * [in] subsample_flags: bitwise flags indicating if this is the first, + * middle, or last subsample in a sample. 1 = first subsample, 2 = last + * subsample, 3 = both first and last subsample, 0 = neither first nor last + * subsample. + * [in] block_offset: This will only be non-zero for the 'cenc' scheme. If it + * is non-zero, the decryption block boundary is different from the start of + * the data. block_offset should be subtracted from data to compute the + * starting address of the first decrypted block. The bytes between the + * decryption block start address and data are discarded after decryption. It + * does not adjust the beginning of the source or destination data. This + * parameter satisfies 0 <= block_offset < 16. + * + * Version: + * This struct changed in API version 16. + */ +typedef struct { + size_t num_bytes_clear; + size_t num_bytes_encrypted; + uint8_t subsample_flags; // is this the first/last subsample in a sample? + size_t block_offset; // used for CTR "cenc" mode only. +} OEMCrypto_SubSampleDescription; + +#define OEMCrypto_FirstSubsample 1 +#define OEMCrypto_LastSubsample 2 + +/* + * OEMCrypto_SampleDescription Structure + * + * Description: + * This structure is used as parameters in the OEMCrypto_DecryptCENC function. + * + * Fields: + * [in] buffers: A structure containing information about the input and + * output buffers. + * [in] iv: A 16-byte array containing the IV for the initial subsample of + * the sample. + * [in] subsamples: A caller-owned array of OEMCrypto_SubSampleDescription + * structures. Each entry in this array describes one subsample in the sample. + * [in] subsamples_length: The length of the array pointed to by the + * subsamples parameter. + * + * Version: + * This method changed in API version 16. + */ +typedef struct { + OEMCrypto_InputOutputPair buffers; // The source and destination buffers. + const uint8_t iv[16]; // The IV for the initial subsample. + const OEMCrypto_SubSampleDescription* subsamples; // subsamples array. + size_t subsamples_length; // the number of subsamples in the sample. +} OEMCrypto_SampleDescription; + +/* + * OEMCrypto_CENCEncryptPatternDesc Structure + * + * Description: + * This structure is used as parameters in the OEMCrypto_DecryptCENC function. + * + * Fields: + * [in] encrypt: The number of 16-byte crypto blocks to encrypt. + * [in] skip: The number of 16-byte crypto blocks to leave in the clear. + * + * Version: + * This struct changed in API version 16. + */ +typedef struct { + size_t encrypt; // number of 16 byte blocks to decrypt. + size_t skip; // number of 16 byte blocks to leave in clear. +} OEMCrypto_CENCEncryptPatternDesc; + /** OEMCryptoCipherMode is used in SelectKey to prepare a key for either CTR * decryption or CBC decryption. */ @@ -218,16 +351,6 @@ typedef struct { OEMCrypto_Substring key_control; } OEMCrypto_KeyObject; -/* - * SRM_Restriction_Data - * - * Structure passed into LoadKeys to specify required SRM version. - */ -typedef struct { - uint8_t verification[8]; // must be "HDCPDATA" - uint32_t minimum_srm_version; // version number in network byte order. -} SRM_Restriction_Data; - /* * OEMCrypto_EntitledContentKeyObject * Contains encrypted content key data for loading into the sessions keytable. @@ -248,31 +371,6 @@ typedef struct { OEMCrypto_Substring content_key_data; } OEMCrypto_EntitledContentKeyObject; -/* - * OEMCrypto_KeyRefreshObject - * 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. @@ -283,22 +381,6 @@ typedef enum OEMCrypto_Algorithm { OEMCrypto_HMAC_SHA256 = 1, } OEMCrypto_Algorithm; -/* - * Flags indicating data endpoints in OEMCrypto_DecryptCENC. - */ -#define OEMCrypto_FirstSubsample 1 -#define OEMCrypto_LastSubsample 2 - -/* OEMCrypto_CENCEncryptPatternDesc - * This is used in OEMCrypto_DecryptCENC to indicate the encrypt/skip pattern - * used, as specified in the CENC standard. - */ -typedef struct { - size_t encrypt; // number of 16 byte blocks to decrypt. - size_t skip; // number of 16 byte blocks to leave in clear. - size_t offset; // offset into the pattern in blocks for this call. -} OEMCrypto_CENCEncryptPatternDesc; - /* * OEMCrypto_Usage_Entry_Status. * Valid values for status in the usage table. @@ -374,12 +456,21 @@ typedef enum OEMCrypto_ProvisioningMethod { OEMCrypto_OEMCertificate = 3 // Device has factory installed OEM certificate. } OEMCrypto_ProvisioningMethod; +/* Private key type used in OEMCrypto_LoadDevicePrivateKey. */ +typedef enum OEMCrypto_PrivateKeyType { + OEMCrypto_RSA_Private_Key, + OEMCrypto_ECC_Private_Key, +} OEMCrypto_PrivateKeyType; + /* - * Flags indicating RSA keys supported. + * Flags indicating public/private key types supported. */ #define OEMCrypto_Supports_RSA_2048bit 0x1 #define OEMCrypto_Supports_RSA_3072bit 0x2 #define OEMCrypto_Supports_RSA_CAST 0x10 +#define OEMCrypto_Supports_ECC_secp256r1 0x100 +#define OEMCrypto_Supports_ECC_secp384r1 0x200 +#define OEMCrypto_Supports_ECC_secp521r1 0x400 /* * Flags indicating full decrypt path hash supported. @@ -458,7 +549,7 @@ typedef enum OEMCrypto_ProvisioningMethod { #define OEMCrypto_LoadTestRSAKey _oecc45 #define OEMCrypto_Security_Patch_Level _oecc46 #define OEMCrypto_LoadKeys_V11_or_V12 _oecc47 -#define OEMCrypto_DecryptCENC _oecc48 +#define OEMCrypto_DecryptCENC_V15 _oecc48 #define OEMCrypto_GetProvisioningMethod _oecc49 #define OEMCrypto_GetOEMPublicCertificate_V15 _oecc50 #define OEMCrypto_RewrapDeviceRSAKey30 _oecc51 @@ -492,21 +583,20 @@ typedef enum OEMCrypto_ProvisioningMethod { #define OEMCrypto_GetHashErrorCode _oecc89 #define OEMCrypto_BuildInformation _oecc90 #define OEMCrypto_RefreshKeys _oecc91 -#define OEMCrypto_LoadEntitledContentKeys_V15 _oecc92 +#define OEMCrypto_LoadEntitledContentKeys _oecc92 #define OEMCrypto_CopyBuffer _oecc93 #define OEMCrypto_MaximumUsageTableHeaderSize _oecc94 #define OEMCrypto_GenerateDerivedKeys _oecc95 -#define OEMCrypto_SignLicenseRequest _oecc96 -#define OEMCrypto_SignRenewalRequest _oecc97 -#define OEMCrypto_SignProvisioningRequest _oecc98 +#define OEMCrypto_PrepAndSignLicenseRequest _oecc96 +#define OEMCrypto_PrepAndSignRenewalRequest _oecc97 +#define OEMCrypto_PrepAndSignProvisioningRequest _oecc98 #define OEMCrypto_LoadLicense _oecc99 -#define OEMCrypto_ReloadLicense _oecc100 #define OEMCrypto_LoadRenewal _oecc101 #define OEMCrypto_LoadProvisioning _oecc102 #define OEMCrypto_LoadOEMPrivateKey _oecc103 #define OEMCrypto_GetOEMPublicCertificate _oecc104 -#define OEMCrypto_DecryptCENC_TODO _oecc105 -#define OEMCrypto_LoadEntitledContentKeys _oecc106 +#define OEMCrypto_DecryptCENC _oecc105 +#define OEMCrypto_LoadDevicePrivateKey _oecc107 // clang-format on /* @@ -533,7 +623,7 @@ typedef enum OEMCrypto_ProvisioningMethod { * * Parameters: * [in] sandbox_id: a short string unique to the current sandbox. - * [in] sandobx_id_length: length of sandbox_id. + * [in] sandbox_id_length: length of sandbox_id. * * Returns: * OEMCrypto_SUCCESS success @@ -608,6 +698,11 @@ OEMCryptoResult OEMCrypto_Terminate(void); * session, and return a session handle that identifies that session in * future calls. * + * This function shall call ODK_InitializeSessionValues to initialize the + * session's clock values, timer values, and nonce values. + * ODK_InitializeSessionValues is described in the document "License Duration + * and Renewal", to initialize the sessions clock values. + * * Parameters: * [out] session: an opaque handle that the crypto firmware uses to identify * the session. @@ -625,7 +720,7 @@ OEMCryptoResult OEMCrypto_Terminate(void); * on the OEMCrypto system. * * Version: - * This method changed in API version 5. + * This method changed in API version 16. */ OEMCryptoResult OEMCrypto_OpenSession(OEMCrypto_SESSION* session); @@ -721,11 +816,11 @@ OEMCryptoResult OEMCrypto_CloseSession(OEMCrypto_SESSION session); * Version: * This method changed in API version 12. */ -OEMCryptoResult OEMCrypto_GenerateDerivedKeys(OEMCrypto_SESSION session, - const uint8_t* mac_key_context, - size_t mac_key_context_length, - const uint8_t* enc_key_context, - size_t enc_key_context_length); +OEMCryptoResult OEMCrypto_GenerateDerivedKeys( + OEMCrypto_SESSION session, const OEMCrypto_SharedMemory* mac_key_context, + size_t mac_key_context_length, + const OEMCrypto_SharedMemory* enc_key_context, + size_t enc_key_context_length); /* * OEMCrypto_DeriveKeysFromSessionKey @@ -738,9 +833,16 @@ OEMCryptoResult OEMCrypto_GenerateDerivedKeys(OEMCrypto_SESSION session, * This function is similar to OEMCrypto_GenerateDerivedKeys, except that it * uses a session key to generate the secondary keys instead of the Widevine * Keybox device key. These three keys will be stored in secure memory until - * the next call to LoadKeys. The session key is passed in encrypted by the - * device RSA public key, and must be decrypted with the RSA private key - * before use. + * the next call to LoadLicense or LoadProvisioning. + * + * If the session's private key is an RSA key, then the session key is passed + * in encrypted by the device RSA public key as the derivation_key, and must + * be decrypted with the RSA private key before use. + * + * If the sesion's private key is an ECC key, then the session key is the + * SHA256 of the shared secret key calculated by ECDH between the device's + * ECC private key and the derivation_key. See the document "OEMCrypto + * 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 @@ -754,9 +856,9 @@ OEMCryptoResult OEMCrypto_GenerateDerivedKeys(OEMCrypto_SESSION session, * * Parameters: * [in] session: handle for the session to be used. - * [in] enc_session_key: session key, encrypted with the public RSA key (from + * [in] derivation_key: session key, encrypted with the public RSA key (from * the DRM certifcate) using RSA-OAEP. - * n_key_l[in] enc_sessioength: length of session_key, in bytes. + * [in] derivation_key_length: length of derivation_key, in bytes. * [in] mac_key_context: pointer to memory containing context data for * computing the HMAC generation key. * [in] mac_key_context_length: length of the HMAC key context data, in bytes. @@ -798,12 +900,13 @@ OEMCryptoResult OEMCrypto_GenerateDerivedKeys(OEMCrypto_SESSION session, * session, and a read lock on the OEMCrypto system. * * Version: - * This method changed in API version 12. + * This method changed in API version 16. */ OEMCryptoResult OEMCrypto_DeriveKeysFromSessionKey( - OEMCrypto_SESSION session, const uint8_t* enc_session_key, - size_t enc_session_key_length, const uint8_t* mac_key_context, - size_t mac_key_context_length, const uint8_t* enc_key_context, + OEMCrypto_SESSION session, const uint8_t* derivation_key, + size_t derivation_key_length, const OEMCrypto_SharedMemory* mac_key_context, + size_t mac_key_context_length, + const OEMCrypto_SharedMemory* enc_key_context, size_t enc_key_context_length); /* @@ -811,8 +914,8 @@ OEMCryptoResult OEMCrypto_DeriveKeysFromSessionKey( * * Description: * Generates a 32-bit nonce to detect possible replay attack on the key - * control block. The nonce is stored in secure memory and will be used for - * the next call to LoadKeys. + * control block. The nonce is stored in secure memory and will be used in + * the license or provisioning request. * * Because the nonce will be used to prevent replay attacks, it is desirable * that a rogue application cannot rapidly call this function until a @@ -823,9 +926,9 @@ OEMCryptoResult OEMCrypto_DeriveKeysFromSessionKey( * before requesting more nonces, then OEMCrypto will reset the error * condition and generate valid nonces again. * - * To prevent Birthday Paradox attacks, OEMCrypto shall verify that the value - * generated is not in this session's nonce table, and that it is not in the - * nonce table of any other session. + * The nonce should be stored in the sessions ODK_NonceValue field by calling + * the function ODK_SetNonceValue(&nonce_values, nonce). The ODK functions + * are documented in "Widevine Core Message Serialization". * * Parameters: * [in] session: handle for the session to be used. @@ -849,26 +952,119 @@ OEMCryptoResult OEMCrypto_DeriveKeysFromSessionKey( * on the OEMCrypto system. * * Version: - * This method changed in API version 5. + * This method changed in API version 16. */ OEMCryptoResult OEMCrypto_GenerateNonce(OEMCrypto_SESSION session, uint32_t* nonce); /* - * OEMCrypto_GenerateSignature + * OEMCrypto_PrepAndSignLicenseRequest * * Description: - * Generates a HMAC-SHA256 signature using the mac_key[client] for license - * request signing under the license server protocol for CENC. + * OEMCrypto will use ODK_PrepareCoreLicenseRequest to prepare the core + * message. If it returns OEMCrypto_SUCCESS, then OEMCrypto shall sign the + * the message body using the DRM certificate's private key. If it returns an + * error, the error should be returned by OEMCrypto to the CDM layer. + * ODK_PrepareCoreLicenseRequest is described in the document "Widevine Core + * Message Serialization". + * + * The message body is the buffer starting at message + core_message_size, + * and with length message_length-core_message_size. The reason OEMCrypto + * only signs the message body and not the entire message is to allow a v16 + * device to request a license from a v15 license server. + * + * If the session's private RSA key has an "allowed_schemes" bit field, then + * it must be 0x1 (RSASSA-PSS with SHA1). If not, then an error of + * OEMCrypto_ERROR_SIGNATURE_FAILURE shall be returned. + * + * OEMCrypto shall compute a hash of the core license request. The core + * license request is the buffer starting at message and with length + * core_message_size. The has will be saved with the session and verified + * that it matches a hash in the license response. + * + * OEMCrypto shall also call the function ODK_InitializeClockValues, + * described in the document "License Duration and Renewal", to initialize + * the sessions clock values. + * + * Refer to the Signing Messages Sent to a Server section above for more + * details about the signature algorithm. + * + * NOTE: if signature pointer is null and/or input signature_length is zero, + * this function returns OEMCrypto_ERROR_SHORT_BUFFER and sets output + * signature_length to the size needed to receive the output signature. + * + * Parameters: + * [in/out] message: Pointer to memory for the entire message. Modified by + * OEMCrypto via the ODK library. + * [in] message_length: length of the entire message buffer. + * [in/out] core_message_size: length of the core message at the beginning of + * the message. (in) size of buffer reserved for the core message, in + * bytes. (out) actual length of the core message, in bytes. + * [out] signature: pointer to memory to receive the computed signature. + * [in/out] signature_length: (in) length of the signature buffer, in bytes. + * (out) actual length of the signature, in bytes. + * + * Returns: + * OEMCrypto_SUCCESS success + * OEMCrypto_ERROR_INVALID_SESSION + * OEMCrypto_ERROR_SHORT_BUFFER if signature buffer is not large enough to + * hold the signature. + * OEMCrypto_ERROR_INSUFFICIENT_RESOURCES + * OEMCrypto_ERROR_UNKNOWN_FAILURE + * OEMCrypto_ERROR_SIGNATURE_FAILURE + * OEMCrypto_ERROR_BUFFER_TOO_LARGE + * OEMCrypto_ERROR_SESSION_LOST_STATE + * OEMCrypto_ERROR_SYSTEM_INVALIDATED + * + * Buffer Sizes: + * OEMCrypto shall support message sizes of at least 8 KiB. + * 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_PrepAndSignLicenseRequest( + OEMCrypto_SESSION session, uint8_t* message, size_t message_length, + size_t* core_message_size, uint8_t* signature, size_t* signature_length); + +/* + * OEMCrypto_PrepAndSignRenewalRequest + * + * Description: + * OEMCrypto will use ODK_PrepareCoreRenewalRequest, as described in the + * document "Widevine Core Message Serialization", to prepare the core + * message. + * + * If it returns an error, the error should be returned by OEMCrypto to the + * CDM layer. If it returns OEMCrypto_SUCCESS, then OEMCrypto computes the + * signature using the renewal mac key which was delivered in the license via + * LoadLicense. + * + * If nonce_values.api_level is 16, then OEMCrypto shall compute the + * signature of the entire message using the session's client renewal mac + * key. The entire message is the buffer starting at message with length + * message_length. + * + * 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. + * + * This function generates a HMAC-SHA256 signature using the mac_key[client] + * for license request signing under the license server protocol for CENC. * * The key used for signing should be the mac_key[client] that was generated - * for this session or loaded for this session by the most recent successful - * call to any one of + * for this session or loaded for this session by OEMCrypto_LoadKeys, + * OEMCrypto_LoadLicense, or OEMCrypto_LoadUsageEntry. * - * - OEMCrypto_GenerateDerivedKeys, - * - OEMCrypto_DeriveKeysFromSessionKey, - * - OEMCrypto_LoadKeys, or - * - OEMCrypto_LoadUsageEntry. * Refer to the Signing Messages Sent to a Server section above for more * details. * @@ -877,18 +1073,20 @@ OEMCryptoResult OEMCrypto_GenerateNonce(OEMCrypto_SESSION session, * entry may be different. In this case, the mac keys specified in the usage * entry should be used. * - * NOTE: if signature pointer is null and/or input signature_length set to - * zero, this function returns OEMCrypto_ERROR_SHORT_BUFFER and sets output + * NOTE: if signature pointer is null and/or input signature_length is zero, + * this function returns OEMCrypto_ERROR_SHORT_BUFFER and sets output * signature_length to the size needed to receive the output signature. * * Parameters: - * [in] session: crypto session identifier. - * [in] message: pointer to memory containing message to be signed. - * [in] message_length: length of the message, in bytes. - * [out] signature: pointer to memory to received the computed signature. May - * be null (see note above). + * [in/out] message: Pointer to memory for the entire message. Modified by + * OEMCrypto via the ODK library. + * [in] message_length: length of the entire message buffer. + * [in/out] core_message_size: length of the core message at the beginning of + * the message. (in) size of buffer reserved for the core message, in + * bytes. (out) actual length of the core message, in bytes. + * [out] signature: pointer to memory to receive the computed signature. * [in/out] signature_length: (in) length of the signature buffer, in bytes. - * (out) actual length of the signature, in bytes. + * (out) actual length of the signature, in bytes. * * Returns: * OEMCrypto_SUCCESS success @@ -914,8 +1112,79 @@ OEMCryptoResult OEMCrypto_GenerateNonce(OEMCrypto_SESSION session, * session, and a read lock on the OEMCrypto system. * * Version: - * This method changed in API version 12. + * This method changed in API version 16. */ +OEMCryptoResult OEMCrypto_PrepAndSignRenewalRequest( + OEMCrypto_SESSION session, uint8_t* message, size_t message_length, + size_t* core_message_size, uint8_t* signature, size_t* signature_length); + +/* + * OEMCrypto_PrepAndSignProvisioningRequest + * + * Description: + * OEMCrypto will use ODK_PrepareCoreRenewalRequest, as described in the + * document "Widevine Core Message Serialization", to prepare the core + * message. If it returns an error, the error should be returned by OEMCrypto + * to the CDM layer. If it returns OEMCrypto_SUCCESS, then OEMCrypto shall + * sign compute the signature of the entire message. The entire message is + * the buffer starting at message with length message_length. + * + * For a device that has a keybox, i.e. Provisioning 2.0, OEMCrypto will sign + * the response with the session's derived client mac key from the previous + * call to OEMCrypto_GenerateDerivedKeys. + * + * For a device that has an OEM Certificate, i.e. Provisioning 3.0, OEMCrypto + * will sign the response with the private key associated with the OEM + * Certificate. The key shall have been loaded by a previous call to + * OEMCrypto_LoadPrivateDRMKey. + * + * Refer to the Signing Messages Sent to a Server section above for more + * details. + * + * NOTE: if signature pointer is null and/or input signature_length is zero, + * this function returns OEMCrypto_ERROR_SHORT_BUFFER and sets output + * signature_length to the size needed to receive the output signature. + * + * Parameters: + * [in/out] message: Pointer to memory for the entire message. Modified by + * OEMCrypto via the ODK library. + * [in] message_length: length of the entire message buffer. + * [in/out] core_message_size: length of the core message at the beginning of + * the message. (in) size of buffer reserved for the core message, in + * bytes. (out) actual length of the core message, in bytes. + * [out] signature: pointer to memory to receive the computed signature. + * [in/out] signature_length: (in) length of the signature buffer, in bytes. + * (out) actual length of the signature, in bytes. + * + * Returns: + * OEMCrypto_SUCCESS success + * OEMCrypto_ERROR_INVALID_SESSION + * OEMCrypto_ERROR_SHORT_BUFFER if signature buffer is not large enough to + * hold the signature. + * OEMCrypto_ERROR_INSUFFICIENT_RESOURCES + * OEMCrypto_ERROR_UNKNOWN_FAILURE + * OEMCrypto_ERROR_BUFFER_TOO_LARGE + * OEMCrypto_ERROR_SESSION_LOST_STATE + * OEMCrypto_ERROR_SYSTEM_INVALIDATED + * + * Buffer Sizes: + * OEMCrypto shall support message sizes of at least 8 KiB. + * 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_PrepAndSignProvisioningRequest( + OEMCrypto_SESSION session, uint8_t* message, size_t message_length, + size_t* core_message_size, uint8_t* signature, size_t* signature_length); /* * OEMCrypto_LoadSRM @@ -960,7 +1229,9 @@ OEMCryptoResult OEMCrypto_LoadSRM(const uint8_t* buffer, size_t buffer_length); * OEMCrypto_LoadKeys * * Description: - * Installs a set of keys for performing decryption in the current session. + * Install a set of keys for performing decryption in the current session. + * This function will be deprecated and will only be used for legacy license + * from a license server that does not yet support the v16 interface. * * The relevant fields have been extracted from the License Response protocol * message, but the entire message and associated signature are provided so @@ -993,9 +1264,7 @@ OEMCryptoResult OEMCrypto_LoadSRM(const uint8_t* buffer, size_t buffer_length); * in OEMCrypto_DecryptCENC(). * * NOTE: The calling software must have previously established the mac_keys - * and encrypt_key with a call to OEMCrypto_GenerateDerivedKeys(), - * OEMCrypto_DeriveKeysFromSessionKey(), or a previous call to - * OEMCrypto_LoadKeys(). + * and encrypt_key with a call to OEMCrypto_DeriveKeysFromSessionKey(). * * Refer to the Verification of Messages from a Server section above for more * details. @@ -1019,16 +1288,20 @@ OEMCryptoResult OEMCrypto_LoadSRM(const uint8_t* buffer, size_t buffer_length); * current interval, next interval) times 4 content keys (audio, SD, HD, UHD) * plus up to 8 keys for watermarks. * + * After a call to OEMCrypto_LoadKeys, oemcrypto should clear the encrypt_key + * for the session. + * * Verification: * The following checks should be performed. If any check fails, an error is * returned, and none of the keys are loaded. - * * 1. The signature of the message shall be computed, and the API 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). - * 2. The enc_mac_keys substring must either have zero length, or satisfy + * 2. If there already is a license loaded into this session, return + * OEMCrypto_ERROR_LICENSE_RELOAD. + * 3. The enc_mac_keys substring must either have zero length, or satisfy * the range check. I.e. (offset < message_length) && (offset + length * < message_length) && (offset < offset+length),and offset+length does * not cause an integer overflow. If it does not have zero length, then @@ -1036,48 +1309,40 @@ OEMCryptoResult OEMCrypto_LoadSRM(const uint8_t* buffer, size_t buffer_length); * range check. If not, return OEMCrypto_ERROR_INVALID_CONTEXT. If the * length is zero, then OEMCrypto may assume that the offset is also * zero. - * 3. The API shall verify that each substring in each KeyObject points to + * 4. The API shall verify that each substring in each KeyObject points to * a location in the message. I.e. (offset < message_length) && * (offset + length < message_length) && (offset < offset+length) and * offset+length does not cause an integer overflow, for each of key_id, * key_data_iv, key_data, key_control_iv, key_control. If not, return * OEMCrypto_ERROR_INVALID_CONTEXT. - * 4. Each key's control block, after decryption, shall have a valid + * 5. Each key's control block, after decryption, shall have a valid * verification field. If not, return OEMCrypto_ERROR_INVALID_CONTEXT. - * 5. If any key control block has the Nonce_Enabled bit set, that key's + * 6. If any key control block has the Nonce_Enabled bit set, that key's * Nonce field shall match a nonce in the cache. If not, return * OEMCrypto_ERROR_INVALID_NONCE. If there is a match, remove that * nonce from the cache. Note that all the key control blocks in a * particular call shall have the same nonce value. - * 6. If any key control block has the Require_AntiRollback_Hardware bit + * 7. If any key control block has the Require_AntiRollback_Hardware bit * set, and the device does not protect the usage table from rollback, * then do not load the keys and return OEMCrypto_ERROR_UNKNOWN_FAILURE. - * 7. If the key control block has a nonzero Replay_Control, then the + * 8. If the key control block has a nonzero Replay_Control, then the * verification described below is also performed. - * 8. If the key control block has the bit SRMVersionRequired is set, then + * 9. If the key control block has the bit SRMVersionRequired is set, then * the verification described below is also performed. If the SRM * requirement is not met, then the key control block's HDCP_Version * will be changed to 0xF - local display only. - * 9. If num_keys == 0, then return OEMCrypto_ERROR_INVALID_CONTEXT. - * 10. If any key control block has the Shared_License bit set, and this - * call to LoadKeys is not replacing keys loaded from a previous call to - * LoadKeys, then the keys are not loaded, and the error - * OEMCrypto_ERROR_MISSING_MASTER is returned. This feature is obsolete, - * and no longer used by production license servers. OEMCrypto unit - * tests for this feature have been removed. + * 10. If key_array_length == 0, then return + * OEMCrypto_ERROR_INVALID_CONTEXT. * 11. If this session is associated with a usage table entry, and that * entry is marked as "inactive" (either kInactiveUsed or * kInactiveUnused), then the keys are not loaded, and the error * OEMCrypto_ERROR_LICENSE_INACTIVE is returned. * 12. The data in enc_mac_keys_iv is not identical to the 16 bytes before * enc_mac_keys. If it is, return OEMCrypto_ERROR_INVALID_CONTEXT. - * * Usage Table and Provider Session Token (pst) - * * If a key control block has a nonzero value for Replay_Control, then all * keys in this license will have the same value for Replay_Control. In this * case, the following additional checks are performed. - * * - The substring pst must have nonzero length and must satisfy the range * check described above. If not, return * OEMCrypto_ERROR_INVALID_CONTEXT. @@ -1127,6 +1392,11 @@ OEMCryptoResult OEMCrypto_LoadSRM(const uint8_t* buffer, size_t buffer_length); * Devices that do not support the Usage Table will return * OEMCrypto_ERROR_INVALID_CONTEXT if the Replay_Control is nonzero. * + * Timer Update + * After verification, the session's clock and timer values are updated by + * calling the function ODK_InitializeV15Values as described in the document + * "Widevine Core Message Serialization". + * * SRM Restriction Data * * If any key control block has the flag SRMVersionRequired set, then the @@ -1156,10 +1426,10 @@ OEMCryptoResult OEMCrypto_LoadSRM(const uint8_t* buffer, size_t buffer_length); * [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] enc_mac_key_iv: IV for decrypting new mac_key. Size is 128 bits. + * [in] enc_mac_keys_iv: IV for decrypting new mac_key. Size is 128 bits. * [in] enc_mac_keys: encrypted mac_keys for generating new mac_keys. Size is * 512 bits. - * [in] num_keys: number of keys present. + * [in] key_array_length: number of keys present. * [in] key_array: set of keys to be installed. * [in] pst: the Provider Session Token. * [in] srm_restriction_data: optional data specifying the minimum SRM @@ -1180,6 +1450,7 @@ OEMCryptoResult OEMCrypto_LoadSRM(const uint8_t* buffer, size_t buffer_length); * OEMCrypto_ERROR_BUFFER_TOO_LARGE * OEMCrypto_ERROR_SESSION_LOST_STATE * OEMCrypto_ERROR_SYSTEM_INVALIDATED + * OEMCrypto_ERROR_LICENSE_RELOAD * * Buffer Sizes: * OEMCrypto shall support message sizes of at least 8 KiB. @@ -1194,8 +1465,265 @@ OEMCryptoResult OEMCrypto_LoadSRM(const uint8_t* buffer, size_t buffer_length); * session, and a read lock on the OEMCrypto system. * * Version: - * This method changed in API version 14. + * This method changed in API version 16. */ +OEMCryptoResult OEMCrypto_LoadKeys( + OEMCrypto_SESSION session, const uint8_t* message, size_t message_length, + const uint8_t* signature, size_t signature_length, + OEMCrypto_Substring enc_mac_keys_iv, OEMCrypto_Substring enc_mac_keys, + size_t key_array_length, const OEMCrypto_KeyObject* key_array, + OEMCrypto_Substring pst, OEMCrypto_Substring srm_restriction_data, + OEMCrypto_LicenseType license_type); + +/* + * OEMCrypto_LoadLicense + * + * Description: + * Install a set of keys for performing decryption in the current session. + * + * First, OEMCrypto shall verify the signature of the message using + * HMAC-SHA256 with the derived mac_key[server]. The signature verification + * shall use a constant-time algorithm (a signature mismatch will always take + * the same time as a successful comparison). The signature is over the + * entire message buffer starting at message with length message_length. If + * the signature verification fails, ignore all other arguments and return + * OEMCrypto_ERROR_SIGNATURE_FAILURE. Otherwise, add the keys to the session + * context. + * + * NOTE: The calling software must have previously established the mac_keys + * and encrypt_key with a call to OEMCrypto_DeriveKeysFromSessionKey(). + * + * Refer to the Verification of Messages from a Server section above for more + * details. + * + * The function ODK_ParseLicense is called to parse the message. If it + * returns an error, OEMCrypto shall return that error to the CDM layer. The + * function ODK_ParseLicense is described in the document "Widevine Core + * Message Serialization". + * + * Below, all fields are found in the struct ODK_ParsedLicense parsed_license + * returend by ODK_ParseLicense. + * + * The keys will be decrypted using the current encrypt_key (AES-128-CBC) and + * the IV given in the KeyObject. Each key control block will be decrypted + * using the first 128 bits of the corresponding content key (AES-128-CBC) + * and the IV given in the KeyObject. + * + * If its length is not zero, enc_mac_keys will be used to create new + * mac_keys. After all keys have been decrypted and validated, the new + * mac_keys are decrypted with the current encrypt_key and the offered IV. + * The new mac_keys replaces the current mac_keys for future signing renewal + * requests and loading renewal responses. 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_LoadRenewal for this session and the current + * mac_keys may be deleted. + * + * If the field license_type is OEMCrypto_ContentLicense, then the fields + * key_id and key_data in an OEMCrypto_KeyObject are loaded in to the + * content_key_id and content_key_data fields of the key table entry. In this + * case, entitlement key ids and entitlement key data is left blank. + * + * If the field license_type is OEMCrypto_EntitlementLicense, then the + * fields key_id and key_data in an OEMCrypto_KeyObject are loaded in to the + * entitlement_key_id and entitlement_key_data fields of the key table entry. + * In this case, content key ids and content key data will be loaded later + * with a call to OEMCrypto_LoadEntitledContentKeys(). + * + * OEMCrypto may assume that the key_id_length is at most 16. However, + * OEMCrypto shall correctly handle key id lengths from 1 to 16 bytes. + * + * OEMCrypto shall handle multiple keys, as described in the section on + * Resource Rating Tiers in this document. + * + * After a call to OEMCrypto_LoadLicense, oemcrypto should clear the + * encrypt_key for the session. + * + * Verification: + * The following checks should be performed. If any check fails, an error is + * returned, and none of the keys are loaded. + * 13. The signature of the message shall be computed, and the API 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). + * 14. If there already is a license loaded into this session, return + * OEMCrypto_ERROR_LICENSE_RELOAD. + * 15. The enc_mac_keys substring must either have zero length, or satisfy + * the range check. I.e. (offset < message_length) && (offset + length + * < message_length) && (offset < offset+length),and offset+length does + * not cause an integer overflow. If it does not have zero length, then + * enc_mac_keys_iv must not have zero length, and must also satisfy the + * range check. If not, return OEMCrypto_ERROR_INVALID_CONTEXT. If the + * length is zero, then OEMCrypto may assume that the offset is also + * zero. + * 16. The API shall verify that each substring in each KeyObject points to + * a location in the message. I.e. (offset < message_length) && + * (offset + length < message_length) && (offset < offset+length) and + * offset+length does not cause an integer overflow, for each of key_id, + * key_data_iv, key_data, key_control_iv, key_control. If not, return + * OEMCrypto_ERROR_INVALID_CONTEXT. + * 17. Each key's control block, after decryption, shall have a valid + * verification field. If not, return OEMCrypto_ERROR_INVALID_CONTEXT. + * 18. If any key control block has the Nonce_Enabled bit set, that key's + * Nonce field shall match a nonce in the cache. If not, return + * OEMCrypto_ERROR_INVALID_NONCE. If there is a match, remove that + * nonce from the cache. Note that all the key control blocks in a + * particular call shall have the same nonce value. + * 19. If any key control block has the Require_AntiRollback_Hardware bit + * set, and the device does not protect the usage table from rollback, + * then do not load the keys and return OEMCrypto_ERROR_UNKNOWN_FAILURE. + * 20. If the key control block has a nonzero Replay_Control, then the + * verification described below is also performed. + * 21. If the key control block has the bit SRMVersionRequired is set, then + * the verification described below is also performed. If the SRM + * requirement is not met, then the key control block's HDCP_Version + * will be changed to 0xF - local display only. + * 22. If key_array_length == 0, then return + * OEMCrypto_ERROR_INVALID_CONTEXT. + * 23. If this session is associated with a usage table entry, and that + * entry is marked as "inactive" (either kInactiveUsed or + * kInactiveUnused), then the keys are not loaded, and the error + * OEMCrypto_ERROR_LICENSE_INACTIVE is returned. + * 24. The data in enc_mac_keys_iv is not identical to the 16 bytes before + * enc_mac_keys. If it is, return OEMCrypto_ERROR_INVALID_CONTEXT. + * Usage Table and Provider Session Token (pst) + * The parameter usage_entry_present shall be set to true if a usage entry + * was created or loaded for this session. This parameter is passed into + * ODK_ParseLicense and used for usage entry verification. + * The parameter initial_license_load shall be false if the usage entry was + * loaded. If there is no usage entry or if the usage entry was created with + * OEMCrypto_CreateNewUsageEntry, then initial_license_load shall be true. + * If a usage entry is present, then it shall be verified after the call to + * ODK_ParseLicense. + * If initial_license_load is true: + * 1. OEMCrypto shall copy the PST from the parsed license to the usage + * entry. + * 2. OEMCrypto shall verify that the server and client mac keys were + * updated by the license. The server and client mac keys shall be + * copied to the usage entry. + * If initial_license_load is false: + * 1. OEMCrypto shall verify the PST from the parsed license matches that + * in the usage entry. If not, then an error OEMCrypto_ERROR_WRONG_PST + * is returned. + * 2. OEMCrypto shall verify that the server and client mac keys were + * updated by the license. OEMCrypto shall verify that the server and + * client mac keys match those in the usage entry. If not the error + * OEMCrypto_ERROR_WRONG_KEYS is returned. + * If a key control block has a nonzero value for Replay_Control, then all + * keys in this license will have the same value for Replay_Control. In this + * case, the following additional checks are performed. + * - The substring pst must have nonzero length and must satisfy the range + * check described above. If not, return + * OEMCrypto_ERROR_INVALID_CONTEXT. + * - The session must be associated with a usage table entry, either + * created via OEMCrypto_CreateNewUsageEntry or loaded via + * OEMCrypto_LoadUsageEntry. + * - If Replay_Control is 1 = Nonce_Required, then OEMCrypto will perform a + * nonce check as described above. OEMCrypto will verify that the + * usage entry is newly created with OEMCrypto_CreateNewUsageEntry. If + * an existing entry was reloaded, an error + * OEMCrypto_ERROR_INVALID_CONTEXT is returned and no keys are loaded. + * OEMCrypto will then copy the pst and the mac keys to the usage entry, + * and set the status to Unused. This Replay_Control prevents the + * license from being loaded more than once, and will be used for online + * streaming. + * - If Replay_Control is 2 = "Require existing Session Usage table entry + * or Nonce", then OEMCrypto will behave slightly differently on the + * first call to LoadKeys for this license. + * * If the usage entry was created with OEMCrypto_CreateNewUsageEntry + * for this session, then OEMCrypto will verify the nonce for each + * key. OEMCrypto will copy the pst and mac keys to the usage + * entry. The license received time of the entry will be updated + * to the current time, and the status will be set to Unused. + * * If the usage entry was loaded with OEMCrypto_LoadUsageEntry for + * this session, then OEMCrypto will NOT verify the nonce for each + * key. Instead, it will verify that the pst passed in matches + * that in the entry. Also, the entry's mac keys will be verified + * against the current session's mac keys. This allows an offline + * license to be reloaded but maintain continuity of the playback + * times from one session to the next. + * * If the nonce is not valid and a usage entry was not loaded, the + * return error is OEMCrypto_ERROR_INVALID_NONCE. + * * If the loaded usage entry has a pst that does not match, + * OEMCrypto returns the error OEMCrypto_ERROR_WRONG_PST. + * * If the loaded usage entry has mac keys that do not match the + * license, OEMCrypto returns the error OEMCrypto_ERROR_WRONG_KEYS. + * Note: If LoadKeys updates the mac keys, then the new updated mac keys will + * be used with the Usage Entry -- i.e. the new keys are stored in the + * usage table when creating a new entry, or the new keys are verified + * against those in the usage table if there is an existing entry. If + * LoadKeys does not update the mac keys, the existing session mac keys are + * used. + * Sessions that are associated with an entry will need to be able to update + * and verify the status of the entry, and the time stamps in the entry. + * Devices that do not support the Usage Table will return + * OEMCrypto_ERROR_INVALID_CONTEXT if the Replay_Control is nonzero. + * SRM Restriction Data + * If any key control block has the flag SRMVersionRequired set, then the + * following verification is also performed. + * 4. The substring srm_restriction_data must have nonzero length and must + * satisfy the range check described above. If not, return + * OEMCrypto_ERROR_INVALID_CONTEXT. + * 5. The first 8 bytes of srm_restriction_data must match the string + * "HDCPDATA". If not, return OEMCrypto_ERROR_INVALID_CONTEXT. + * 6. The next 4 bytes of srm_restriction_data will be converted from + * network byte order. If the current SRM installed on the device has a + * version number less than this, then the SRM requirement is not met. + * If the device does not support SRM files, or OEMCrypto cannot + * determine the current SRM version number, then the SRM requirement is + * not met. + * Note: if the current SRM version requirement is not met, LoadKeys will + * still succeed and the keys will be loaded. However, those keys with the + * SRMVersionRequired bit set will have their HDCP_Version increased to 0xF - + * local display only. Any future call to SelectKey for these keys while + * there is an external display will return OEMCrypto_ERROR_INSUFFICIENT_HDCP + * at that time. + * + * Parameters: + * [in] session: crypto session identifier. + * [in] message: pointer to memory containing data. + * [in] message_length: length of the message, in bytes. + * [in] core_message_length: length of the core submessage, in bytes. + * [in] signature: pointer to memory containing the signature. + * [in] signature_length: length of the signature, in bytes. + * + * Returns: + * OEMCrypto_SUCCESS success + * OEMCrypto_ERROR_NO_DEVICE_KEY + * OEMCrypto_ERROR_INVALID_SESSION + * OEMCrypto_ERROR_UNKNOWN_FAILURE + * OEMCrypto_ERROR_INVALID_CONTEXT + * OEMCrypto_ERROR_SIGNATURE_FAILURE + * OEMCrypto_ERROR_INVALID_NONCE + * OEMCrypto_ERROR_TOO_MANY_KEYS + * OEMCrypto_ERROR_NOT_IMPLEMENTED + * OEMCrypto_ERROR_BUFFER_TOO_LARGE + * OEMCrypto_ERROR_SESSION_LOST_STATE + * OEMCrypto_ERROR_SYSTEM_INVALIDATED + * OEMCrypto_ERROR_LICENSE_RELOAD + * + * Buffer Sizes: + * OEMCrypto shall support message sizes of at least 8 KiB. + * 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_LoadLicense(OEMCrypto_SESSION session, + const uint8_t* message, + size_t message_length, + size_t core_message_length, + const uint8_t* signature, + size_t signature_length); /* * OEMCrypto_LoadEntitledContentKeys @@ -1233,7 +1761,9 @@ OEMCryptoResult OEMCrypto_LoadSRM(const uint8_t* buffer, size_t buffer_length); * * Parameters: * [in] session: handle for the session to be used. - * [in] num_keys: number of keys present. + * [in] message: pointer to memory containing message to be verified. + * [in] message_length: length of the message, in bytes. + * [in] key_array_length: number of keys present. * [in] key_array: set of key updates. * * Returns: @@ -1258,60 +1788,42 @@ OEMCryptoResult OEMCrypto_LoadSRM(const uint8_t* buffer, size_t buffer_length); */ OEMCryptoResult OEMCrypto_LoadEntitledContentKeys( OEMCrypto_SESSION session, const uint8_t* message, size_t message_length, - const OEMCrypto_EntitledContentKeyObject* key_array, - size_t key_array_length); + size_t key_array_length, + const OEMCrypto_EntitledContentKeyObject* key_array); /* - * OEMCrypto_RefreshKeys + * OEMCrypto_LoadRenewal * * Description: - * Updates an existing set of keys for continuing decryption in the current + * Updates the clock values and resets the renewal timer for the current * session. * - * The relevant fields have been extracted from the Renewal Response protocol - * message, but the entire message and associated signature are provided so - * the message can be verified (using HMAC-SHA256 with the current - * mac_key[server]). If any verification step fails, an error is returned. - * Otherwise, the key table in trusted memory is updated using the - * key_control block. When updating an entry in the table, only the duration, - * nonce, and nonce_enabled fields are used. All other key control bits are - * not modified. + * OEMCrypto shall verify the signature of the message using the session's + * renewal mac key for the server. If the signature does not match, OEMCrypto + * returns OEMCrypto_ERROR_SIGNATURE_FAILURE. * - * NOTE: OEMCrypto_LoadKeys() must be called first to load the keys into the - * session. + * If nonce_values.api_level is 16, then OEMCrypto shall verify the signature + * of the entire message using the session's server renewal mac key. The + * entire message is the buffer starting at message with length + * message_length. * - * This session's elapsed time clock is reset to 0 when this function is - * called. The elapsed time clock is used in OEMCrypto_DecryptCENC() and the - * other Decryption API functions to determine if the key has expired. + * If nonce_values.api_level is 15, then OEMCrypto shall compute the + * signature of the message body using the session's server renewal mac key. + * The entire message is the buffer starting at message+core_message_size + * with length message_length-core_message_size. * - * This function does not add keys to the key table. It is only used to - * update a key control block license duration. This function is used to - * update the duration of a key, only. It is not used to update key control - * bits. + * If the signature passes, OEMCrypto shall use the function + * ODK_ParseRenewal, as described in the document "Widevine Core Message + * Serialization" to parse and verify the message. If ODK_ParseRenewal + * returns an error OEMCrypto returns the error to the CDM layer. * - * If the KeyRefreshObject's key_control_iv has zero length, then the - * key_control is not encrypted. If the key_control_iv is specified, then - * key_control is encrypted with the first 128 bits of the corresponding - * content key. + * The function ODK_ParseRenewal updates the clock values for the session, + * and may return ODK_SET_TIMER, ODK_DISABLE_TIMER or ODK_TIMER_EXPIRED on + * success. These values shall be handled by OEMCrypto, as discussed in the + * document "License Duration and Renewal". * - * If the KeyRefreshObject's key_id has zero length, then this refresh object - * should be used to update the duration of all keys for the current session. - * In this case, key_control_iv will also have zero length and the control - * block will not be encrypted. - * - * If the session's license_type is OEMCrypto_ContentLicense, and the - * KeyRefreshObject's key_id is not null, then the entry in the keytable with - * the matching content_key_id is updated. - * - * If the session's license_type is OEMCrypto_EntitlementLicense, and the - * KeyRefreshObject's key_id is not null, then the entry in the keytable with - * the matching entitlment_key_id is updated. - * - * If the key_id is not null, and no matching entry is found in the key - * table, then return OEMCrypto_ERROR_NO_CONTENT_KEY. - * - * Aside from the key's duration, no other values in the key control block - * should be updated by this function. + * NOTE: OEMCrypto_LoadKeys() or 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 @@ -1339,10 +1851,9 @@ OEMCryptoResult OEMCrypto_LoadEntitledContentKeys( * [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] core_message_length: length of the core submessage, in bytes. * [in] signature: pointer to memory containing the signature. * [in] signature_length: length of the signature, in bytes. - * [in] num_keys: number of keys present. - * [in] key_array: set of key updates. * * Returns: * OEMCrypto_SUCCESS success @@ -1373,6 +1884,12 @@ OEMCryptoResult OEMCrypto_LoadEntitledContentKeys( * Version: * This method changed in API version 12. */ +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); /* * OEMCrypto_QueryKeyControl @@ -1403,6 +1920,7 @@ OEMCryptoResult OEMCrypto_LoadEntitledContentKeys( * OEMCrypto_ERROR_NO_CONTENT_KEY. * * Parameters: + * [in] session: handle for the session to be used. * [in] content_key_id: The unique id of the key of interest. * [in] content_key_id_length: The length of key_id, in bytes. From 1 to 16, * inclusive. @@ -1450,7 +1968,7 @@ OEMCryptoResult OEMCrypto_QueryKeyControl(OEMCrypto_SESSION session, * includes the key value, and the key control block. * * Step 2: Latch the content key into the hardware key ladder. Set permission - * flags and timers based on the key's control block. + * flags based on the key's control block. * * Step 3: use the latched content key to decrypt (AES-128-CTR or * AES-128-CBC) buffers passed in via OEMCrypto_DecryptCENC(). If the key is @@ -1474,10 +1992,14 @@ OEMCryptoResult OEMCrypto_QueryKeyControl(OEMCrypto_SESSION session, * 3. 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. + * 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 * enforce HDCP, then the key is not selected, and - * OEMCrypto_ERROR_INSUFFICIENT_HDCP is returned. + * 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 * the device cannot enforce at least that version of HDCP, then the key * is not selected, and OEMCrypto_ERROR_INSUFFICIENT_HDCP is returned. @@ -1516,7 +2038,7 @@ OEMCryptoResult OEMCrypto_QueryKeyControl(OEMCrypto_SESSION session, * session, and a read lock on the OEMCrypto system. * * Version: - * This method changed in API version 14. + * This method changed in API version 16. */ OEMCryptoResult OEMCrypto_SelectKey(OEMCrypto_SESSION session, const uint8_t* content_key_id, @@ -1527,152 +2049,360 @@ OEMCryptoResult OEMCrypto_SelectKey(OEMCrypto_SESSION session, * OEMCrypto_DecryptCENC * * Description: - * Decrypts or copies the payload in the buffer referenced by the *data_addr - * parameter into the buffer referenced by the out_buffer parameter, using - * the session context indicated by the session parameter. Decryption mode is - * AES-128-CTR or AES-128-CBC depending on the value of cipher_mode passed in - * to OEMCrypto_SelectKey. If is_encrypted is true, the content key - * associated with the session is latched in the active hardware key ladder - * and is used for the decryption operation. If is_encrypted is false, the - * data is simply copied. + * Decrypts or copies a series of input payloads into output buffers using + * the session context indicated by the session parameter. The input payload + * is delivered in the form of samples. The samples are subdivided into + * subsamples. "Samples" and "subsamples" are defined as in the ISO Common + * Encryption standard (ISO/IEC 23001-7:2016). The samples parameter contains + * a list of samples, each of which has its own input and output buffers. + * Each sample contains a buffers field that contains the input and output + * buffers in its input_data and output fields, respectively. * - * After decryption, the data_length bytes are copied to the location - * described by out_buffer. This could be one of + * Each sample contains an array of subsample descriptions in its subsamples + * field. Each subsample is defined as a number of clear bytes followed by a + * number of encrypted bytes. Subsamples are consecutive inside the sample; + * the clear bytes of the second subsample begin immediately after the + * encrypted bytes of the first subsample. This follows the definition in the + * ISO-CENC standard. + * + * Decryption mode is AES-128-CTR or AES-128-CBC depending on the value of + * cipher_mode previously passed in to OEMCrypto_SelectKey. For the encrypted + * portion of subsamples, the content key associated with the session is + * latched in the active hardware key ladder and is used for the decryption + * operation. For the clear portion of subsamples, the data is simply copied. + * + * After decryption, all the input_data bytes are copied to the location + * described by the output field. The output field is an + * OEMCrypto_DestBufferDesc, which could be one of: + * + * 1. The structure OEMCrypto_DestBufferDesc contains a pointer to a clear + * text buffer. The OEMCrypto library shall verify that key control + * allows data to be returned in clear text. If it is not authorized, + * this method should return an error. + * 2. The structure OEMCrypto_DestBufferDesc contains a handle to a secure + * buffer. + * 3. The structure OEMCrypto_DestBufferDesc indicates that the data should + * be sent directly to the decoder and renderer. + * Depending on your platform's needs, you may not need to support all three + * of these options. + * + * SINGLE-SAMPLE DECRYPTION AND SINGLE-SUBSAMPLE DECRYPTION: + * + * If the OEMCrypto implementation is not able to handle the amount of + * samples and subsamples passed into it, it should return + * OEMCrypto_ERROR_BUFFER_TOO_LARGE, in which case the CDM can respond by + * breaking the samples up into smaller pieces and trying to decrypt each of + * them individually. It is possible that the CDM will break the samples + * array up into pieces that are still too large, in which case OEMCrypto may + * return OEMCrypto_ERROR_BUFFER_TOO_LARGE again. + * + * If the OEMCrypto implementation cannot handle multiple samples at once, it + * may return OEMCrypto_ERROR_BUFFER_TOO_LARGE any time it receives more than + * one sample in a single call to OEMCrypto_DecryptCENC. + * + * Similarly, if the OEMCrypto implementation cannot handle multiple + * subsamples at once, it may return OEMCrypto_ERROR_BUFFER_TOO_LARGE any + * time it receives more than one subsample in a single call to + * OEMCrypto_DecryptCENC. + * + * The exact way that the CDM code breaks up the samples array is not + * guaranteed by this specification. The CDM may break down the array of + * samples into many arrays each containing one sample. The CDM may break + * down samples into subsamples and pass individual subsamples into + * OEMCrypto, just like in OEMCrypto v15. The CDM may break down individual + * subsamples into smaller subsamples, just like in OEMCrypto v15. + * + * If OEMCrypto requests that the CDM break samples into subsamples, the + * "samples" passed into OEMCrypto_DecryptCENC will no longer be full + * samples. When a full sample is passed into OEMCrypto_DecryptCENC, the + * first subsample in the subsample array will have the + * OEMCrypto_FirstSubsample flag set in its subsample_flags field and the + * last subsample array will have the OEMCrypto_LastSubsample flag set in its + * subsample_flags field. If this is not the case, OEMCrypto will need to + * accumulate more subsamples from successive calls to OEMCrypto_DecryptCENC + * to receive the full sample. + * + * The first subsample in the sample will always have + * OEMCrypto_FirstSubsample set and the last subsample will always have the + * OEMCrypto_LastSubsample flag set, even if those subsamples are passed in + * separate calls to OEMCrypto_DecryptCENC. This is the same as in OEMCrypto + * v15. The decrypted data will not be used until after the subsample with + * the flag OEMCrypto_LastSubsample has been sent to OEMCrypto. This can be + * relied on by OEMCrypto for optimization by not doing decrypt until the + * last subsample has been received. However, a device that can do decrypt of + * more than one subsample at a time will always have better performance if + * it can receive those subsamples in one OEMCrypto_Decrypt call rather than + * as individual subsamples. + * + * Although the exact way that the CDM code breaks up the samples array when + * it receives OEMCrypto_ERROR_BUFFER_TOO_LARGE is not guaranteed by this + * specification, here is a sample way it might work: + * + * 1. It tries to pass the array of samples to OEMCrypto_DecryptCENC. + * 2. If OEMCrypto returns OEMCrypto_ERROR_BUFFER_TOO_LARGE, it tries to + * pass each sample individually into OEMCrypto_DecryptCENC. + * 3. If OEMCrypto returns OEMCrypto_ERROR_BUFFER_TOO_LARGE, it tries to + * pass the clear and encrypted parts of each subsample individually + * into OEMCrypto_DecryptCENC. At this point, (and in the subsequent + * steps) it is replicating the behavior of OEMCrypto v15 and lower. + * 4. If OEMCrypto returns OEMCrypto_ERROR_BUFFER_TOO_LARGE, it breaks each + * piece of a subsample into smaller pieces, down to the minimum + * subsample size required by the device's resource rating tier. It + * passes these pieces into OEMCrypto_DecryptCENC. + * 5. If OEMCrypto returns OEMCrypto_ERROR_BUFFER_TOO_LARGE, the device has + * failed to meet its resource rating tier requirements. It returns an + * error. + * Because this process requires a lot of back-and-forth between the CDM and + * OEMCrypto, partners are strongly recommended to support decrypting full + * samples or even multiple samples in their OEMCrypto implementation. + * + * ISO-CENC SCHEMES: + * + * The ISO Common Encryption standard (ISO/IEC 23001-7:2016) defines four + * "schemes" that may be used to encrypt content: 'cenc', 'cens', 'cbc1', and + * 'cbcs'. Starting with v16, OEMCrypto only supports 'cenc' and 'cbcs'. The + * schemes 'cens' and 'cbc1' are not supported. + * + * The decryption mode, either OEMCrypto_CipherMode_CTR or + * OEMCrypto_CipherMode_CBC, was already specified in the call to + * OEMCrypto_SelectKey. The encryption pattern is specified by the fields in + * the parameter pattern. A description of partial encryption patterns for + * 'cbcs' can be found in the ISO-CENC standard, section 10.4. + * + * 'cenc' SCHEME: + * + * The 'cenc' scheme is OEMCrypto_CipherMode_CTR without an encryption + * pattern. All the bytes in the encrypted portion of each subsample are + * encrypted. In the pattern parameter, both the encrypt and skip fields will + * be zero. + * + * The length of a crypto block in AES-128 is 16 bytes. In the 'cenc' scheme, + * if an encrypted subsample has a length that is not a multiple of 16 bytes, + * then all the bytes of the encrypted subsample must be decrypted, but the + * next encrypted subsample will begin by completing the incomplete crypto + * block from the previous encrypted subsample. The following diagram + * provides an example: + * + * (See drawing in "Widevine Modular DRM Security Integration Guide") + * + * To help with this, the block_offset field of each subsample will contain + * the number of bytes the initial crypto block of that subsample should be + * offset by. In the example above, the block_offset for the first subsample + * would be 0 and the block_offset for the second subsample would be 12. + * 'cenc' is the only mode that allows for a nonzero block_offset. This field + * satisfies 0 <= block_offset < 16. + * + * 'cbcs' SCHEME: + * + * The 'cbcs' scheme is OEMCrypto_CipherMode_CBC with an encryption pattern. + * Only some of the bytes in the encrypted portion of each subsample are + * encrypted. In the pattern parameter, the encrypt and skip fields will + * usually be non-zero. This mode allows devices to decrypt FMP4 HLS content + * and SAMPLE-AES HLS content. + * + * The skip field of OEMCrypto_CENCEncryptPatternDesc may also be zero. If + * the skip field is zero, then patterns are not in use and all crypto blocks + * in the encrypted subsample are encrypted. It is not valid for the encrypt + * field to be zero. + * + * The length of a crypto block in AES-128 is 16 bytes. In the 'cbcs' scheme, + * if an encrypted subsample has a length that is not a multiple of 16 bytes, + * then the final bytes that do not make up a full crypto block should be + * treated as clear and should not be decrypted. The following diagram + * provides an example: + * + * (See drawing in "Widevine Modular DRM Security Integration Guide") + * + * Of course, if the encrypted subsample has a length that is a multiple of + * 16 bytes, the final bytes should be decrypted. The following diagram + * provides an example: + * + * (See drawing in "Widevine Modular DRM Security Integration Guide") + * + * If the encrypted subsample has a length that is not an even multiple of + * the pattern length, then there may also be extra clear blocks at the end. + * + * If there are not enough bytes at the end of the encrypted subsample to + * complete an iteration of the encrypted part of the pattern, even if there + * are enough bytes to make a full crypto block, then the final bytes that do + * not fill the encrypted part of the pattern should be treated as clear and + * should not be decrypted. The following diagram provides an example: + * + * (See drawing in "Widevine Modular DRM Security Integration Guide") + * + * If there are enough bytes at the end of the encrypted subsample to + * complete an iteration of the encrypted part of the pattern, even if there + * are not enough bytes to complete the clear part of the pattern, then the + * bytes that fill the encrypted part of the pattern should be treated as + * encrypted. The following diagram provides an example: + * + * (See drawing in "Widevine Modular DRM Security Integration Guide") + * + * This behavior is specified by the ISO-CENC standard. Refer to the ISO-CENC + * standard, section 9.6, for full details of how patterns are to be applied + * to content. + * + * INITIALIZATION VECTOR BETWEEN SUBSAMPLES: + * + * The IV is specified for the initial subsample in a sample in the iv field + * of the OEMCrypto_SampleDescription. OEMCrypto is responsible for correctly + * updating the IV for subsequent subsamples according to the ISO Common + * Encryption standard (ISO/IEC 23001-7:2016). Section 9.5.2.3 covers 'cenc' + * and section 9.5.2.5 covers 'cbcs'. A summary of the ISO-CENC behavior + * follows: + * + * For 'cenc', the IV at the end of each subsample carries forward to the + * next subsample and becomes the IV at the beginning of the next subsample. + * If the subsample ends on a crypto block boundary, then the IV should be + * incremented as normal at the end of the crypto block. If the subsample + * ends in the middle of a crypto block, the same IV should continue to be + * used until the crypto block is completed in the next subsample. Only + * increment the IV after the partial crypto block is completed. + * + * For 'cbcs', the IV is reset at the beginning of each subsample. Each + * subsample should start with the IV that was passed into + * OEMCrypto_DecryptCENC. + * + * To phrase it another way: In 'cenc', the encrypted portions of the + * subsamples can be concatenated to form one continuous ciphertext. In + * 'cbcs', each encrypted portion of a subsample is a separate ciphertext. + * Each separate ciphertext begins with the IV specified in the iv field of + * the OEMCrypto_SampleDescription. + * + * INITIALIZATION VECTOR WITHIN SUBSAMPLES: + * + * Once it has the IV for each subsample, OEMCrypto is responsible for + * correctly updating the IV for each crypto block of each encrypted + * subsample portion, as outlined in the ISO Common Encryption standard + * (ISO/IEC 23001-7:2016). Section 9.5.1 includes general information about + * IVs in subsample decryption. A summary of the ISO-CENC behavior follows: + * + * For 'cenc', the subsample's IV is the counter value to be used for the + * initial encrypted block of the subsample. The IV length is the AES block + * size. For subsequent encrypted AES blocks, OEMCrypto must calculate the IV + * by incrementing the lower 64 bits (byte 8-15) of the IV value used for the + * previous block. The counter rolls over to zero when it reaches its maximum + * value (0xFFFFFFFFFFFFFFFF). The upper 64 bits (byte 0-7) of the IV do not + * change. + * + * For 'cbcs', the subsample's IV is the initialization vector for the + * initial encrypted block of the subsample. Within each subsample, each + * crypto block is used as the IV for the next crypto block, as prescribed by + * AES-CBC. * - * 1. The structure out_buffer contains a pointer to a clear text buffer. - * The OEMCrypto library shall verify that key control allows data to be - * returned in clear text. If it is not authorized, this method should - * return an error. - * 2. The structure out_buffer contains a handle to a secure buffer. - * 3. The structure out_buffer indicates that the data should be sent - * directly to the decoder and renderer. * NOTES: * - * For CTR mode, IV points to the counter value to be used for the initial - * encrypted block of the input buffer. The IV length is the AES block size. - * For subsequent encrypted AES blocks the IV is calculated by incrementing - * the lower 64 bits (byte 8-15) of the IV value used for the previous block. - * The counter rolls over to zero when it reaches its maximum value - * (0xFFFFFFFFFFFFFFFF). The upper 64 bits (byte 0-7) of the IV do not change. - * - * For CBC mode, IV points to the initial vector for cipher block chaining. - * Within each subsample, OEMCrypto is responsible for updating the IV as - * prescribed by CBC mode. The calling layer above is responsible for - * updating the IV from one subsample to the next if needed. - * - * This method may be called several times before the decrypted data is used. - * For this reason, the parameter subsample_flags may be used to optimize - * decryption. The first buffer in a chunk of data will have the - * OEMCrypto_FirstSubsample bit set in subsample_flags. The last buffer in a - * chunk of data will have the OEMCrypto_LastSubsample bit set in - * subsample_flags. The decrypted data will not be used until after - * OEMCrypto_LastSubsample has been set. If an implementation decrypts data - * immediately, it may ignore subsample_flags. - * * If the destination buffer is secure, an offset may be specified. - * DecryptCENC begins storing data out_buffer->secure.offset bytes after the - * beginning of the secure buffer. + * OEMCrypto_DecryptCENC begins storing data buffers.output.secure.offset + * bytes after the beginning of the secure buffer. * - * If the session has an entry in the Usage Table, then OEMCrypto will update + * If the session has an entry in the Usage Table, then OEMCrypto must update * 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. * - * The decryption mode, either OEMCrypto_CipherMode_CTR or - * OEMCrypto_CipherMode_CBC, was specified in the call to - * OEMCrypto_SelectKey. The encryption pattern is specified by the fields in - * the parameter pattern. A description of partial encryption patterns can be - * found in the document Draft International Standard ISO/IEC DIS 23001-7. - * Search for the codes "cenc", "cbc1", "cens" or "cbcs". + * OEMCrypto cannot assume that the buffers of consecutive samples are + * consecutive in memory. * - * The most common mode is "cenc", which is OEMCrypto_CipherMode_CTR without - * a pattern. The entire subsample is either encrypted or clear, depending on - * the flag is_encrypted. In the structure pattern, both encrypt and skip - * will be 0. This is the only mode that allows for a nonzero block_offset. + * A subsample may consist entirely of encrypted bytes or clear bytes. In + * this case, the clear or the encrypted part of the subsample will be zero, + * indicating that no bytes of that kind appear in the subsample. * - * A less common mode is "cens", which is OEMCrypto_CipherMode_CTR with an - * encryption pattern. For this mode, OEMCrypto may assume that an encrypted - * subsample will have a length that is a multiple of 16, the AES block - * length. + * The ISO-CENC spec implicitly limits both the skip and encrypt values to be + * 4 bits, so they are at most 15. * - * The mode "cbc1" is OEMCrypto_CipherMode_CBC without a pattern. In the - * structure pattern, both encrypt and skip will be 0. If an encrypted - * subsample has a length that is not a multiple of 16, the final partial - * block will be in the clear. + * (See drawing in "Widevine Modular DRM Security Integration Guide") * - * The mode "cbcs" is OEMCrypto_CipherMode_CBC with an encryption pattern. - * This mode allows devices to decrypt HLS content. If an encrypted subsample - * has a length that is not a multiple of 16, the final partial block will be - * in the clear. In practice, the most common pattern is (1, 9), or 1 - * encrypted block followed by 9 clear blocks. The ISO-CENC spec implicitly - * limits both the skip and encrypt values to be 4 bits, so a value of at - * most 15. + * If OEMCrypto assembles all of the encrypted subsample portions into a + * single buffer and then decrypts it in one pass, it can assume that the + * block offset is 0. * - * A sample may be broken up into a mix of clear and encrypted subsamples. In - * order to support the VP9 standard, the breakup of a subsample into clear - * and encrypted subsamples is not always in pairs. - * - * If OEMCrypto assembles all of the subsamples into a single buffer and then - * decrypts, it can assume that the block offset is 0. + * (See drawing in "Widevine Modular DRM Security Integration Guide") * * Verification: - * The following checks should be performed if is_encrypted is true. If any - * check fails, an error is returned, and no decryption is performed. - * 1. 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. If not, return OEMCrypto_ERROR_KEY_EXPIRED. - * 2. If the current key's control block has the Data_Path_Type bit set, + * The total size of all the subsamples cannot exceed the total size of the + * input buffer. OEMCrypto integrations should validate this and return + * OEMCrypto_ERROR_UNKNOWN_FAILURE if the subsamples are larger than the + * input buffer. No decryption should be performed in this case. + * If the subsamples all contain only clear bytes, then no further + * verification is performed. This call shall copy clear data even when there + * are no keys loaded, or there is no selected key. + * If this is the first use of a key for this session, then OEMCrypto shall + * call ODK_AttemptFirstPlayback to update the session's clock values and + * verify playback is allowed. If this is not the first use of a key for this + * session, then OEMCrypto shall call ODK_UpdateLastPlaybackTime. See the + * document "License Duration and Renewal" for handling the return value of + * these ODK functions. + * The following checks should be performed if any subsamples contain any + * encrypted bytes. If any check fails, an error is returned, and no + * decryption is performed. + * 1. If the current key's control block has the Data_Path_Type bit set, * then the API shall verify that the output buffer is secure or direct. * If not, return OEMCrypto_ERROR_DECRYPT_FAILED. - * 3. If the current key control block has the bit Disable_Analog_Output + * 2. If the current 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 - * OEMCrypto_ERROR_ANALOG_OUTPUT is returned. - * 4. If the current key's control block has the HDCP bit set, then the API + * OEMCrypto_ERROR_ANALOG_OUTPUT is returned. (See note on delayed + * error conditions below) + * 3. If the current key's control block has the HDCP bit set, then the API * shall verify that the buffer will be displayed locally, or output * externally using HDCP only. If not, return - * OEMCrypto_ERROR_INSUFFICIENT_HDCP. - * 5. If the current key's control block has a nonzero value for + * OEMCrypto_ERROR_INSUFFICIENT_HDCP. (See note on delayed error + * conditions below) + * 4. If the current key's control block has a nonzero value for * HDCP_Version, then the current version of HDCP for the device and the * display combined will be compared against the version specified in * the control block. If the current version is not at least as high as - * that in the control block, then return - * OEMCrypto_ERROR_INSUFFICIENT_HDCP. - * 6. If the current session has an entry in the Usage Table, and the + * that in the control block, and the device is not able to restrict + * displays with HDCP levels lower than what's in the control block, + * return OEMCrypto_ERROR_INSUFFICIENT_HDCP. If the device is able to + * restrict those displays, return + * OEMCrypto_WARNING_MIXED_OUTPUT_PROTECTION. (See note on delayed + * error conditions below) + * 5. If the current session has an entry in the Usage Table, and the * status of that entry is either kInactiveUsed or kInactiveUnused, then * return the error OEMCrypto_ERROR_LICENSE_INACTIVE. - * 7. If a Decrypt Hash has been initialized via OEMCrypto_SetDecryptHash, + * 6. If a Decrypt Hash has been initialized via OEMCrypto_SetDecryptHash, * and the current key's control block does not have the * Allow_Hash_Verification bit set, then do not compute a hash and * return OEMCrypto_ERROR_UNKNOWN_FAILURE. - * If the flag is_encrypted is false, then no verification is performed. This - * call shall copy clear data even when there are no keys loaded, or there is - * no selected key. + * + * Delayed Error Conditions + * + * On some devices, the HDCP subsystem is not directly connected to the + * OEMCrypto TA. This means that returning the error + * OEMCrypto_ERROR_INSUFFICIENT_HDCP at the time of the decrypt call is a + * performance hit. However, some devices have the ability to tag output + * buffers with security requirements, such as the required HDCP level. + * For those devices, when a call to OEMCrypto_DecryptCENC is made using a + * key that requires HDCP output, and if the HDCP level on the output does + * not meet the required level. + * - OEMCrypto may tag the output buffer as requiring HDCP at the required + * level and return OEMCrypto_SUCCESS. + * - Output shall not be sent to the display. + * - On the second or third call to OEMCrypto_DecryptCENC with the same + * key, OEMCrypto shall return OEMCrypto_ERROR_INSUFFICIENT_HDCP. + * For those devices, when a call to OEMCrypto_DecryptCENC is made using a + * key that requires HDCP output, and if the HDCP level on some of the + * displays does not meet the required level. + * - OEMCrypto may tag the output buffer as requiring HDCP at the required + * level and return OEMCrypto_SUCCESS. + * - Output shall only be sent to the display with sufficient output + * control, e.g. the local display. + * - On the second or third call to OEMCrypto_DecryptCENC with the same + * key, OEMCrypto shall return OEMCrypto_WARNING_MIXED_OUTPUT_PROTECTION. + * In either case, a call to OEMCrypto_GetHDCPCapability shall return the + * current HDCP level. * * Parameters: - * [in] session: crypto session identifier. - * [in] data_addr: An unaligned pointer to this segment of the stream. - * [in] data_length: The length of this segment of the stream, in bytes. - * [in] is_encrypted: True if the buffer described by data_addr, data_length - * is encrypted. If is_encrypted is false, only the data_addr and - * data_length parameters are used. The iv and offset arguments are - * ignored. - * [in] iv: The initial value block to be used for content decryption. - * This is discussed further below. - * [in] block_offset: If non-zero, the decryption block boundary is different - * from the start of the data. block_offset should be subtracted from - * data_addr to compute the starting address of the first decrypted - * block. The bytes between the decryption block start address and - * data_addr are discarded after decryption. It does not adjust the - * beginning of the source or destination data. This parameter - * satisfies 0 <= block_offset < 16. - * [in] out_buffer: A caller-owned descriptor that specifies the handling of - * the decrypted byte stream. See OEMCrypto_DestbufferDesc for details. + * [in] session: Crypto session identifier. The crypto session in which + * decrypt is to be performed. + * [in] samples: A caller-owned array of OEMCrypto_SampleDescription + * structures. Each entry in this array contains one sample of the + * content. + * [in] samples_length: The length of the array pointed to by the samples + * parameter. * [in] pattern: A caller-owned structure indicating the encrypt/skip pattern - * as specified in the CENC standard. - * [in] subsample_flags: bitwise flags indicating if this is the first, - * middle, or last subsample in a chunk of data. 1 = first subsample, 2 - * = last subsample, 3 = both first and last subsample, 0 = neither - * first nor last subsample. + * as specified in the ISO-CENC standard. * * Returns: * OEMCrypto_SUCCESS @@ -1691,14 +2421,14 @@ OEMCryptoResult OEMCrypto_SelectKey(OEMCrypto_SESSION session, * OEMCrypto_ERROR_SYSTEM_INVALIDATED * * Buffer Sizes: - * OEMCrypto shall support subsample sizes (i.e. data_length) of at least 100 - * KiB. + * OEMCrypto shall support subsample sizes and total input buffer sizes as + * specified by its resource rating tier. * OEMCrypto shall return OEMCrypto_ERROR_BUFFER_TOO_LARGE if the buffer is * larger than the supported size. If OEMCrypto returns - * OEMCrypto_ERROR_BUFFER_TOO_LARGE, the calling function must break the - * buffer into smaller chunks. For high performance devices, OEMCrypto should - * handle larger buffers. We encourage OEMCrypto implementers not to - * artificially restrict the maximum buffer size. + * OEMCrypto_ERROR_BUFFER_TOO_LARGE, the CDM will break the buffer into + * smaller chunks. For high performance devices, OEMCrypto should handle + * larger buffers. We encourage OEMCrypto implementers not to artificially + * restrict the maximum buffer size. * If OEMCrypto detects that the output data is too large, and breaking the * buffer into smaller subsamples will not work, then it returns * OEMCrypto_ERROR_OUTPUT_TOO_LARGE. This error will bubble up to the @@ -1713,22 +2443,21 @@ OEMCryptoResult OEMCrypto_SelectKey(OEMCrypto_SESSION session, * session, and a read lock on the OEMCrypto system. * * Version: - * This method changed in API version 15. This method changed its name in API + * This method changed in API version 16. This method changed its name in API * version 11. */ OEMCryptoResult OEMCrypto_DecryptCENC( - OEMCrypto_SESSION session, const uint8_t* data_addr, size_t data_length, - bool is_encrypted, const uint8_t* iv, - size_t block_offset, // used for CTR "cenc" mode only. - OEMCrypto_DestBufferDesc* out_buffer, - const OEMCrypto_CENCEncryptPatternDesc* pattern, uint8_t subsample_flags); + OEMCrypto_SESSION session, + const OEMCrypto_SampleDescription* samples, // an array of samples. + size_t samples_length, // the number of samples. + const OEMCrypto_CENCEncryptPatternDesc* pattern); /* * OEMCrypto_CopyBuffer * * Description: - * Copies the payload in the buffer referenced by the *data_addr parameter - * into the buffer referenced by the out_buffer parameter. The data is simply + * Copies the payload in the buffer referenced by the *data parameter into + * the buffer referenced by the out_buffer parameter. The data is simply * copied. The definition of OEMCrypto_DestBufferDesc and subsample_flags are * the same as in OEMCrypto_DecryptCENC, above. * @@ -1756,13 +2485,13 @@ OEMCryptoResult OEMCrypto_DecryptCENC( * * Verification: * The following checks should be performed. - * 1. If either data_addr or out_buffer is null, return + * 1. If either data or out_buffer is null, return * OEMCrypto_ERROR_INVALID_CONTEXT. * * Parameters: * [in] session: crypto session identifier. * [in] data_addr: An unaligned pointer to the buffer to be copied. - * [in] data_length: The length of the buffer, in bytes. + * [in] data_addr_length: The length of the buffer, in bytes. * [in] out_buffer: A caller-owned descriptor that specifies the handling of * the byte stream. See OEMCrypto_DestbufferDesc for details. * [in] subsample_flags: bitwise flags indicating if this is the first, @@ -1781,7 +2510,9 @@ OEMCryptoResult OEMCrypto_DecryptCENC( * OEMCrypto_ERROR_SYSTEM_INVALIDATED * * Buffer Sizes: - * OEMCrypto shall support subsample sizes (i.e. data_length) up to 100 KiB. + + * OEMCrypto shall support subsample sizes and total input buffer sizes as + * specified by its resource rating tier. * OEMCrypto shall return OEMCrypto_ERROR_BUFFER_TOO_LARGE if the buffer is * larger than the supported size. If OEMCrypto returns * OEMCrypto_ERROR_BUFFER_TOO_LARGE, the calling function must break the @@ -1804,11 +2535,11 @@ OEMCryptoResult OEMCrypto_DecryptCENC( * Version: * This method is changed in API version 15. */ -OEMCryptoResult OEMCrypto_CopyBuffer(OEMCrypto_SESSION session, - const uint8_t* data_addr, - size_t data_length, - OEMCrypto_DestBufferDesc* out_buffer, - uint8_t subsample_flags); +OEMCryptoResult OEMCrypto_CopyBuffer( + OEMCrypto_SESSION session, const OEMCrypto_SharedMemory* data_addr, + size_t data_addr_length, + const OEMCrypto_DestBufferDesc* out_buffer_descriptor, + uint8_t subsample_flags); /* * OEMCrypto_Generic_Encrypt @@ -1827,9 +2558,12 @@ OEMCryptoResult OEMCrypto_CopyBuffer(OEMCrypto_SESSION session, * returned, and the data is not encrypted. * 1. The control bit for the current key shall have the Allow_Encrypt set. * If not, return OEMCrypto_ERROR_UNKNOWN_FAILURE. - * 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. If not, return OEMCrypto_ERROR_KEY_EXPIRED. + * 2. If this is the first use of a key for this session, then OEMCrypto + * shall call ODK_AttemptFirstPlayback to update the session's clock + * values and verify playback is allowed. If this is not the first use + * of a key for this session, then OEMCrypto shall call + * ODK_UpdateLastPlaybackTime. See the document "License Duration and + * Renewal" for handling the return value of these ODK functions. * 3. If the current session has an entry in the Usage Table, and the * status of that entry is either kInactiveUsed or kInactiveUnused, then * return the error OEMCrypto_ERROR_LICENSE_INACTIVE. @@ -1837,8 +2571,8 @@ OEMCryptoResult OEMCrypto_CopyBuffer(OEMCrypto_SESSION session, * Parameters: * [in] session: crypto session identifier. * [in] in_buffer: pointer to memory containing data to be encrypted. - * [in] buffer_length: length of the buffer, in bytes. The algorithm may - * restrict buffer_length to be a multiple of block size. + * [in] in_buffer_length: length of the buffer, in bytes. The algorithm may + * restrict in_buffer_length to be a multiple of block size. * [in] iv: IV for encrypting data. Size is 128 bits. * [in] algorithm: Specifies which encryption algorithm to use. Currently, * only CBC 128 mode is allowed for encryption. @@ -1855,6 +2589,7 @@ OEMCryptoResult OEMCrypto_CopyBuffer(OEMCrypto_SESSION session, * OEMCrypto_ERROR_BUFFER_TOO_LARGE * OEMCrypto_ERROR_SESSION_LOST_STATE * OEMCrypto_ERROR_SYSTEM_INVALIDATED + * OEMCrypto_ERROR_NOT_IMPLEMENTED * * Buffer Sizes: * OEMCrypto shall support buffers sizes of at least 100 KiB for generic @@ -1870,11 +2605,12 @@ OEMCryptoResult OEMCrypto_CopyBuffer(OEMCrypto_SESSION session, * session, and a read lock on the OEMCrypto system. * * Version: - * This method changed in API version 12. + * This method changed in API version 16. */ OEMCryptoResult OEMCrypto_Generic_Encrypt( - OEMCrypto_SESSION session, const uint8_t* in_buffer, size_t buffer_length, - const uint8_t* iv, OEMCrypto_Algorithm algorithm, uint8_t* out_buffer); + OEMCrypto_SESSION session, const OEMCrypto_SharedMemory* in_buffer, + size_t in_buffer_length, const uint8_t* iv, OEMCrypto_Algorithm algorithm, + OEMCrypto_SharedMemory* out_buffer); /* * OEMCrypto_Generic_Decrypt @@ -1895,9 +2631,12 @@ OEMCryptoResult OEMCrypto_Generic_Encrypt( * If not, return OEMCrypto_ERROR_DECRYPT_FAILED. * 2. If the current key's control block has the Data_Path_Type bit set, * then return OEMCrypto_ERROR_DECRYPT_FAILED. - * 3. 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. If not, return OEMCrypto_ERROR_KEY_EXPIRED. + * 3. If this is the first use of a key for this session, then OEMCrypto + * shall call ODK_AttemptFirstPlayback to update the session's clock + * values and verify playback is allowed. If this is not the first use + * of a key for this session, then OEMCrypto shall call + * ODK_UpdateLastPlaybackTime. See the document "License Duration and + * Renewal" for handling the return value of these ODK functions. * 4. If the current session has an entry in the Usage Table, and the * status of that entry is either kInactiveUsed or kInactiveUnused, then * return the error OEMCrypto_ERROR_LICENSE_INACTIVE. @@ -1905,8 +2644,8 @@ OEMCryptoResult OEMCrypto_Generic_Encrypt( * Parameters: * [in] session: crypto session identifier. * [in] in_buffer: pointer to memory containing data to be encrypted. - * [in] buffer_length: length of the buffer, in bytes. The algorithm may - * restrict buffer_length to be a multiple of block size. + * [in] in_buffer_length: length of the buffer, in bytes. The algorithm may + * restrict in_buffer_length to be a multiple of block size. * [in] iv: IV for encrypting data. Size is 128 bits. * [in] algorithm: Specifies which encryption algorithm to use. Currently, * only CBC 128 mode is allowed for decryption. @@ -1924,6 +2663,7 @@ OEMCryptoResult OEMCrypto_Generic_Encrypt( * OEMCrypto_ERROR_BUFFER_TOO_LARGE * OEMCrypto_ERROR_SESSION_LOST_STATE * OEMCrypto_ERROR_SYSTEM_INVALIDATED + * OEMCrypto_ERROR_NOT_IMPLEMENTED * * Buffer Sizes: * OEMCrypto shall support buffers sizes of at least 100 KiB for generic @@ -1939,11 +2679,12 @@ OEMCryptoResult OEMCrypto_Generic_Encrypt( * session, and a read lock on the OEMCrypto system. * * Version: - * This method changed in API version 12. + * This method changed in API version 16. */ OEMCryptoResult OEMCrypto_Generic_Decrypt( - OEMCrypto_SESSION session, const uint8_t* in_buffer, size_t buffer_length, - const uint8_t* iv, OEMCrypto_Algorithm algorithm, uint8_t* out_buffer); + OEMCrypto_SESSION session, const OEMCrypto_SharedMemory* in_buffer, + size_t in_buffer_length, const uint8_t* iv, OEMCrypto_Algorithm algorithm, + OEMCrypto_SharedMemory* out_buffer); /* * OEMCrypto_Generic_Sign @@ -1959,16 +2700,19 @@ OEMCryptoResult OEMCrypto_Generic_Decrypt( * The following checks should be performed. If any check fails, an error is * returned, and the data is not signed. * 1. The control bit for the current key shall have the Allow_Sign set. - * 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. If not, return OEMCrypto_ERROR_KEY_EXPIRED. + * 2. If this is the first use of a key for this session, then OEMCrypto + * shall call ODK_AttemptFirstPlayback to update the session's clock + * values and verify playback is allowed. If this is not the first use + * of a key for this session, then OEMCrypto shall call + * ODK_UpdateLastPlaybackTime. See the document "License Duration and + * Renewal" for handling the return value of these ODK functions. * 3. If the current session has an entry in the Usage Table, and the * status of that entry is either kInactiveUsed or kInactiveUnused, then * return the error OEMCrypto_ERROR_LICENSE_INACTIVE. * * Parameters: * [in] session: crypto session identifier. - * [in] in_buffer: pointer to memory containing data to be encrypted. + * [in] buffer: pointer to memory containing data to be encrypted. * [in] buffer_length: length of the buffer, in bytes. * [in] algorithm: Specifies which algorithm to use. * [out] signature: pointer to buffer in which signature should be stored. @@ -1988,6 +2732,7 @@ OEMCryptoResult OEMCrypto_Generic_Decrypt( * OEMCrypto_ERROR_BUFFER_TOO_LARGE * OEMCrypto_ERROR_SESSION_LOST_STATE * OEMCrypto_ERROR_SYSTEM_INVALIDATED + * OEMCrypto_ERROR_NOT_IMPLEMENTED * * Buffer Sizes: * OEMCrypto shall support buffers sizes of at least 100 KiB for generic @@ -2003,13 +2748,13 @@ OEMCryptoResult OEMCrypto_Generic_Decrypt( * session, and a read lock on the OEMCrypto system. * * Version: - * This method changed in API version 14. + * This method changed in API version 16. */ OEMCryptoResult OEMCrypto_Generic_Sign(OEMCrypto_SESSION session, - const uint8_t* in_buffer, + const OEMCrypto_SharedMemory* buffer, size_t buffer_length, OEMCrypto_Algorithm algorithm, - uint8_t* signature, + OEMCrypto_SharedMemory* signature, size_t* signature_length); /* @@ -2033,16 +2778,19 @@ OEMCryptoResult OEMCrypto_Generic_Sign(OEMCrypto_SESSION session, * 3. The signature verification shall use a constant-time algorithm (a * signature mismatch will always take the same time as a successful * comparison). - * 4. 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. If not, return OEMCrypto_ERROR_KEY_EXPIRED. + * 4. If this is the first use of a key for this session, then OEMCrypto + * shall call ODK_AttemptFirstPlayback to update the session's clock + * values and verify playback is allowed. If this is not the first use + * of a key for this session, then OEMCrypto shall call + * ODK_UpdateLastPlaybackTime. See the document "License Duration and + * Renewal" for handling the return value of these ODK functions. * 5. If the current session has an entry in the Usage Table, and the * status of that entry is either kInactiveUsed or kInactiveUnused, then * return the error OEMCrypto_ERROR_LICENSE_INACTIVE. * * Parameters: * [in] session: crypto session identifier. - * [in] in_buffer: pointer to memory containing data to be encrypted. + * [in] buffer: pointer to memory containing data to be encrypted. * [in] buffer_length: length of the buffer, in bytes. * [in] algorithm: Specifies which algorithm to use. * [in] signature: pointer to buffer in which signature resides. @@ -2059,6 +2807,7 @@ OEMCryptoResult OEMCrypto_Generic_Sign(OEMCrypto_SESSION session, * OEMCrypto_ERROR_BUFFER_TOO_LARGE * OEMCrypto_ERROR_SESSION_LOST_STATE * OEMCrypto_ERROR_SYSTEM_INVALIDATED + * OEMCrypto_ERROR_NOT_IMPLEMENTED * * Buffer Sizes: * OEMCrypto shall support buffers sizes of at least 100 KiB for generic @@ -2074,14 +2823,12 @@ OEMCryptoResult OEMCrypto_Generic_Sign(OEMCrypto_SESSION session, * session, and a read lock on the OEMCrypto system. * * Version: - * This method changed in API version 14. + * This method changed in API version 16. */ -OEMCryptoResult OEMCrypto_Generic_Verify(OEMCrypto_SESSION session, - const uint8_t* in_buffer, - size_t buffer_length, - OEMCrypto_Algorithm algorithm, - const uint8_t* signature, - size_t signature_length); +OEMCryptoResult OEMCrypto_Generic_Verify( + OEMCrypto_SESSION session, const OEMCrypto_SharedMemory* buffer, + size_t buffer_length, OEMCrypto_Algorithm algorithm, + const OEMCrypto_SharedMemory* signature, size_t signature_length); /* * OEMCrypto_WrapKeyboxOrOEMCert @@ -2109,18 +2856,20 @@ OEMCryptoResult OEMCrypto_Generic_Verify(OEMCrypto_SESSION session, * file system. * * Parameters: - * [in] rot - pointer to root of trust data to encrypt -- this is either a - * keybox or an OEM Certificate private key. May be NULL on the first - * call to test size of wrapped keybox. The keybox may either be clear - * or previously encrypted. - * [in] rotLength - length the keybox data in bytes - * [out] wrappedRot – Pointer to wrapped keybox - * [out] wrappedRotLength – Pointer to the length of the wrapped rot in bytes - * [in] transportKey – Optional. AES transport key. If provided, the rot - * parameter was previously encrypted with this key. The keybox will be - * decrypted with the transport key using AES-CBC and a null IV. - * [in] transportKeyLength – Optional. Number of bytes in the transportKey, - * if used. + * [in] keybox_or_cert - pointer to root of trust data to encrypt -- this is + * either a keybox or an OEM Certificate private key. May be NULL on + * the first call to test size of wrapped keybox. The keybox may either + * be clear or previously encrypted. + * [in] keybox_or_cert_length - length the keybox or cert data in bytes + * [out] wrapped_keybox_or_cert – Pointer to wrapped keybox or cert + * [out] wrapped_keybox_or_cert_length – Pointer to the length of the wrapped + * keybox or certificate key in bytes + * [in] transport_key – Optional. AES transport key. If provided, the + * keybox_or_cert parameter was previously encrypted with this key. The + * keybox will be decrypted with the transport key using AES-CBC and a + * null IV. + * [in] transport_key_length – Optional. Number of bytes in the + * transport_key, if used. * * Returns: * OEMCrypto_SUCCESS success @@ -2139,12 +2888,10 @@ OEMCryptoResult OEMCrypto_Generic_Verify(OEMCrypto_SESSION session, * Version: * This method is supported in all API versions. */ -OEMCryptoResult OEMCrypto_WrapKeyboxOrOEMCert(const uint8_t* rot, - size_t rotLength, - uint8_t* wrappedRot, - size_t* wrappedRotLength, - const uint8_t* transportKey, - size_t transportKeyLength); +OEMCryptoResult OEMCrypto_WrapKeyboxOrOEMCert( + const uint8_t* keybox_or_cert, size_t keybox_or_cert_length, + uint8_t* wrapped_keybox_or_cert, size_t* wrapped_keybox_or_cert_length, + const uint8_t* transport_key, size_t transport_key_length); /* * OEMCrypto_InstallKeyboxOrOEMCert @@ -2161,8 +2908,8 @@ OEMCryptoResult OEMCrypto_WrapKeyboxOrOEMCert(const uint8_t* rot, * file system. * * Parameters: - * [in] rot - pointer to encrypted data as input - * [in] rotLength - length of the data in bytes + * [in] keybox_or_cert - pointer to encrypted data as input + * [in] keybox_or_cert_length - length of the data in bytes * * Returns: * OEMCrypto_SUCCESS success @@ -2180,8 +2927,8 @@ OEMCryptoResult OEMCrypto_WrapKeyboxOrOEMCert(const uint8_t* rot, * Version: * This method is supported in all API versions. */ -OEMCryptoResult OEMCrypto_InstallKeyboxOrOEMCert(const uint8_t* rot, - size_t rotLength); +OEMCryptoResult OEMCrypto_InstallKeyboxOrOEMCert(const uint8_t* keybox_or_cert, + size_t keybox_or_cert_length); /* * OEMCrypto_GetProvisioningMethod @@ -2267,21 +3014,18 @@ OEMCryptoResult OEMCrypto_IsKeyboxOrOEMCertValid(void); * Certificate device, it should set the device ID to a device-unique string, * such as the device serial number. The ID should be device-unique and it * should be stable -- i.e. it should not change across a device reboot or a - * system upgrade. - * - * This function is optional but recommended for Provisioning 3.0 in API v15. - * It may be required for a future version of this API. + * system upgrade. This shall match the device id found in the core + * provisioning request message. * * Parameters: - * [out] deviceId - pointer to the buffer that receives the Device ID - * [in/out] idLength – on input, size of the caller's device ID buffer. On - * output, the number of bytes written into the buffer. + * [out] device_id - pointer to the buffer that receives the Device ID + * [in/out] device_id_length – on input, size of the caller's device ID + * buffer. On output, the number of bytes written into the buffer. * * Returns: * OEMCrypto_SUCCESS success * OEMCrypto_ERROR_SHORT_BUFFER if the buffer is too small to return device ID * OEMCrypto_ERROR_NO_DEVICEID failed to return Device Id - * OEMCrypto_ERROR_NOT_IMPLEMENTED * OEMCrypto_ERROR_SYSTEM_INVALIDATED * * Threading: @@ -2293,7 +3037,8 @@ OEMCryptoResult OEMCrypto_IsKeyboxOrOEMCertValid(void); * Version: * This method is supported in all API versions. */ -OEMCryptoResult OEMCrypto_GetDeviceID(uint8_t* deviceID, size_t* idLength); +OEMCryptoResult OEMCrypto_GetDeviceID(uint8_t* device_id, + size_t* device_id_length); /* * OEMCrypto_GetKeyData @@ -2324,7 +3069,8 @@ OEMCryptoResult OEMCrypto_GetDeviceID(uint8_t* deviceID, size_t* idLength); * Version: * This method is supported in all API versions. */ -OEMCryptoResult OEMCrypto_GetKeyData(uint8_t* keyData, size_t* keyDataLength); +OEMCryptoResult OEMCrypto_GetKeyData(uint8_t* key_data, + size_t* key_data_length); /* * OEMCrypto_LoadTestKeybox @@ -2342,7 +3088,7 @@ OEMCryptoResult OEMCrypto_GetKeyData(uint8_t* keyData, size_t* keyDataLength); * * Parameters: * [in] buffer: pointer to memory containing test keybox, in binary form. - * [in] length: length of the buffer, in bytes. + * [in] buffer_length: length of the buffer, in bytes. * * Returns: * OEMCrypto_SUCCESS success @@ -2361,22 +3107,49 @@ OEMCryptoResult OEMCrypto_GetKeyData(uint8_t* keyData, size_t* keyDataLength); * Version: * This method changed in API version 14. */ -OEMCryptoResult OEMCrypto_LoadTestKeybox(const uint8_t* buffer, size_t length); +OEMCryptoResult OEMCrypto_LoadTestKeybox(const uint8_t* buffer, + size_t buffer_length); + +/* + * OEMCrypto_LoadOEMPrivateKey + * + * Description: + * After a call to this function, all session functions using an RSA key + * should use the OEM certificate's private RSA key. See the section above + * discussing Provisioning 3.0. + * + * Parameters: + * - [in] session: this function affects the specified session only. + * + * Returns: + * OEMCrypto_SUCCESS + * OEMCrypto_ERROR_NOT_IMPLEMENTED - this function is for Provisioning 3.0 + * only. + * OEMCrypto_ERROR_SYSTEM_INVALIDATED + * + * 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 is new API version 16. + */ +OEMCryptoResult OEMCrypto_LoadOEMPrivateKey(OEMCrypto_SESSION session); /* * OEMCrypto_GetOEMPublicCertificate * * Description: * This function should place the OEM public certificate in the buffer - * public_cert. After a call to this function, all methods using an RSA key - * should use the OEM certificate's private RSA key. See the section above - * discussing Provisioning 3.0. + * public_cert. See the section above discussing Provisioning 3.0. * * If the buffer is not large enough, OEMCrypto should update * public_cert_length and return OEMCrypto_ERROR_SHORT_BUFFER. * * Parameters: - * - [in] session: this function affects the specified session only. * - [out] public_cert: the buffer where the public certificate is stored. * - [in/out] public_cert_length: on input, this is the available size of * the buffer. On output, this is the number of bytes needed for the @@ -2390,15 +3163,16 @@ OEMCryptoResult OEMCrypto_LoadTestKeybox(const uint8_t* buffer, size_t length); * OEMCrypto_ERROR_SYSTEM_INVALIDATED * * 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. + * This is a "Property Function" and may be called simultaneously with any + * other property function or session function, but not any initialization or + * usage table function, as if the CDM holds a read lock on the OEMCrypto + * system. * * Version: - * This method is new API version 12. + * This method is new API version 16. */ +OEMCryptoResult OEMCrypto_GetOEMPublicCertificate(uint8_t* public_cert, + size_t* public_cert_length); /* * OEMCrypto_GetRandom @@ -2409,8 +3183,8 @@ OEMCryptoResult OEMCrypto_LoadTestKeybox(const uint8_t* buffer, size_t length); * OEMCrypto_ERROR_RNG_NOT_SUPPORTED. * * Parameters: - * [out] randomData - pointer to the buffer that receives random data - * [in] dataLength - length of the random data buffer in bytes + * [out] random_data- pointer to the buffer that receives random data + * [in] random_data_length- length of the random data buffer in bytes * * Returns: * OEMCrypto_SUCCESS success @@ -2434,6 +3208,8 @@ OEMCryptoResult OEMCrypto_LoadTestKeybox(const uint8_t* buffer, size_t length); * Version: * This method is supported in all API versions. */ +OEMCryptoResult OEMCrypto_GetRandom(uint8_t* random_data, + size_t random_data_length); /* * OEMCrypto_APIVersion @@ -2449,7 +3225,7 @@ OEMCryptoResult OEMCrypto_LoadTestKeybox(const uint8_t* buffer, size_t length); * There is no plan to introduce forward-compatibility. Applications will * reject a library with a newer version of the API. * - * The version specified in this document is 15. Any OEM that returns this + * The version specified in this document is 16. Any OEM that returns this * version number guarantees it passes all unit tests associated with this * version. * @@ -2470,6 +3246,35 @@ OEMCryptoResult OEMCrypto_LoadTestKeybox(const uint8_t* buffer, size_t length); */ uint32_t OEMCrypto_APIVersion(void); +/* + * OEMCrypto_MinorAPIVersion + * + * Description: + * This function returns the current API minor version number. The version + * number allows the calling application to avoid version mis-match errors, + * because this API is part of a shared library. + * + * The minor version specified in this document is 1. Any OEM that returns + * this version number guarantees it passes all unit tests associated with + * this version. + * + * Parameters: + * none + * + * Returns: + * The supported API, as specified in the header file OEMCryptoCENC.h. + * + * Threading: + * This is a "Property Function" and may be called simultaneously with any + * other property function or session function, but not any initialization or + * usage table function, as if the CDM holds a read lock on the OEMCrypto + * system. + * + * Version: + * This method changed in each API version. + */ +uint32_t OEMCrypto_MinorAPIVersion(void); + /* * OEMCrypto_BuildInformation * @@ -2594,10 +3399,11 @@ const char* OEMCrypto_SecurityLevel(void); * * When a key has version HDCP_V2_3 required in the key control block, the * transmitter must have HDCP version 2.3 and have negotiated a connection - * with a version 2.3 receiver or repeater. The transmitter must configure - * the content stream to be Type 1. Since the transmitter cannot distinguish - * between 2.2 and 2.3 downstream receivers when connected to a repeater, it - * may transmit to both 2.2 and 2.3 receivers, but not 2.1 receivers. + * with a version 2.2 or 2.3 receiver or repeater. The transmitter must + * configure the content stream to be Type 1. Since the transmitter cannot + * distinguish between 2.2 and 2.3 downstream receivers when connected to a + * repeater, it may transmit to both 2.2 and 2.3 receivers, but not 2.1 + * receivers. * * For example, if the transmitter is 2.3, and is connected to a receiver * that supports 2.3 then the current level is HDCP_V2_3. If the transmitter @@ -2662,6 +3468,33 @@ OEMCryptoResult OEMCrypto_GetHDCPCapability(OEMCrypto_HDCP_Capability* current, */ bool OEMCrypto_SupportsUsageTable(void); +/* + * OEMCrypto_MaximumUsageTableHeaderSize + * + * Description: + * Estimates the maximum usage table size. If the device does not have a + * fixed size, this returns an estimate. A maximum size of 0 means the header + * is constrained only by dynamic memory allocation. + * + * Widevine requires the size to be at least 300 entries. + * + * Parameters: + * none + * + * Returns: + * Returns an estimate for the maximum size of the usage table header. + * + * Threading: + * This is a "Property Function" and may be called simultaneously with any + * other property function or session function, but not any initialization or + * usage table function, as if the CDM holds a read lock on the OEMCrypto + * system. + * + * Version: + * This method changed in API version 16. + */ +size_t OEMCrypto_MaximumUsageTableHeaderSize(void); + /* * OEMCrypto_IsAntiRollbackHwPresent * @@ -2731,7 +3564,7 @@ OEMCryptoResult OEMCrypto_GetNumberOfOpenSessions(size_t* count); * support more sessions -- we recommend a minimum of 50 sessions. * * Parameters: - * [out] count - this is the current number of opened sessions. + * [out] max - this is the max number of supported sessions. * * Returns: * OEMCrypto_SUCCESS @@ -2776,9 +3609,12 @@ OEMCryptoResult OEMCrypto_GetMaxNumberOfSessions(size_t* max); * - 0x2 = OEMCrypto_Supports_RSA_3072bit - the device can load a DRM * certificate with a 3072 bit RSA key. * - 0x10 = OEMCrypto_Supports_RSA_CAST - the device can load a CAST - * certificate. These certificate are used with + * certificate. These certificates are used with * OEMCrypto_GenerateRSASignature with padding type set to 0x2, PKCS1 * with block type 1 padding. + * - 0x100 = OEMCrypto_Supports_ECC_secp256r1 - Elliptic Curve secp256r1 + * - 0x200 = OEMCrypto_Supports_ECC_secp384r1 - Elliptic Curve secp384r1 + * - 0x200 = OEMCrypto_Supports_ECC_secp521r1 - Elliptic Curve secp521r1 * * Threading: * This is a "Property Function" and may be called simultaneously with any @@ -2787,7 +3623,7 @@ OEMCryptoResult OEMCrypto_GetMaxNumberOfSessions(size_t* max); * system. * * Version: - * This method changed in API version 13. + * This method changed in API version 16. */ uint32_t OEMCrypto_SupportedCertificates(void); @@ -2906,16 +3742,24 @@ uint32_t OEMCrypto_GetAnalogOutputFlags(void); * bandwidth and codec resolution are determined by the platform. * * Some parameters need more explanation. The Sample size is typically the - * size of one encoded frame. Converting this to resolution depends on the - * Codec, which is not specified by OEMCrypto. Some content has the sample - * broken into several subsamples. The "number of subsamples" restriction - * requires that any content can be broken into at least that many - * subsamples. However, this number may be larger if DecryptCENC returns - * OEMCrypto_ERROR_BUFFER_TOO_LARGE. In that case, the layer above OEMCrypto - * will break the sample into subsamples of size "Decrypt Buffer Size" as - * specified in the table below. The "Decrypt Buffer Size" means the size of - * one subsample that may be passed into DecryptCENC or CopyBuffer without - * returning error OEMCrypto_ERROR_BUFFER_TOO_LARGE. + * size of one encoded frame, but might be several frames for AV1. Converting + * this to resolution depends on the Codec, which is not specified by + * OEMCrypto. Some content has the sample broken into several subsamples. The + * "number of subsamples" restriction requires that any content can be broken + * into at least that many subsamples. However, this number may be larger if + * DecryptCENC returns OEMCrypto_ERROR_BUFFER_TOO_LARGE. In that case, the + * layer above OEMCrypto will break the sample into subsamples of size + * "Decrypt Buffer Size" as specified in the table below. The "Decrypt Buffer + * Size" means the size of one subsample that may be passed into DecryptCENC + * or CopyBuffer without returning error OEMCrypto_ERROR_BUFFER_TOO_LARGE. + * + * The minimum subsample buffer size is the smallest buffer that the CDM + * layer above OEMCrypto will use when breaking a sample into subsamples. As + * mentioned above, the CDM layer will only break a sample into smaller + * subsamples if OEMCrypto returns OEMCrypto_ERROR_BUFFER_TOO_LARGE. Because + * this might be a performance problem, OEMCrypto implementers are encouraged + * to process larger subsamples and to process multiple subsamples in a + * single call to DecryptCENC. * * The number of keys per session is an indication of how many different * track types there can be for a piece of content. Typically, content will @@ -2931,29 +3775,48 @@ 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. + * * Decrypted frames per second -- strictly speaking, OEMCrypto only controls * the decryption part of playback and cannot control the decoding and * display part. However, devices that support the higher resource tiers * should also support a higher frame rate. Platforms may enforce these * values. For example Android will enforce a frame rate via a GTS test. * - * +-----------------------------------+-----------+------------+-----------+ - * |Resource Rating Tier |1 - Low |2 - Medium |3 - High | - * +-----------------------------------+-----------+------------+-----------+ - * |Sample size |1 MB |2 MB |4 MB | - * +-----------------------------------+-----------+------------+-----------+ - * |Number of Subsamples |8 |16 |32 | - * +-----------------------------------+-----------+------------+-----------+ - * |Decrypt buffer size |100 KB |500 KB |1 MB | - * +-----------------------------------+-----------+------------+-----------+ - * |Generic crypto buffer size |10 KB |100 KB |500 KB | - * +-----------------------------------+-----------+------------+-----------+ - * |Number of concurrent sessions |10 |20 |20 | - * +-----------------------------------+-----------+------------+-----------+ - * |Number of keys per session |4 |20 |20 | - * +-----------------------------------+-----------+------------+-----------+ - * |Decrypted Frames per Second |30 fps SD |30 fps HD |60 fps HD | - * +-----------------------------------+-----------+------------+-----------+ + * +--------------------------------+---------+----------+---------+---------+ + * |Resource Rating Tier |1 - Low |2 - Medium|3 - High |4 - Very | + * | | | | | High | + * +--------------------------------+---------+----------+---------+---------+ + * |Minimum Sample size |1 MB |2 MB |4 MB |16 MB | + * +--------------------------------+---------+----------+---------+---------+ + * |Minimum Number of Subsamples |10 |16 |32 |64 | + * | (H264 or HEVC) | | | | | + * +--------------------------------+---------+----------+---------+---------+ + * |Minimum Number of Subsamples |9 |9 |9 |9 | + * |(VP9) | | | | | + * +--------------------------------+---------+----------+---------+---------+ + * |Minimum Number of Subsamples |72 |144 |288 |576 | + * |(AV1) | | | | | + * +--------------------------------+---------+----------+---------+---------+ + * |Minimum subsample buffer size |100 KB |500 KB |1 MB |4 MB | + * +--------------------------------+---------+----------+---------+---------+ + * |Minimum Generic crypto buffer |10 KB |100 KB |500 KB |1 MB | + * |size | | | | | + * +--------------------------------+---------+----------+---------+---------+ + * |Minimum number of open sessions |10 |20 |20 |30 | + * +--------------------------------+---------+----------+---------+---------+ + * |Minimum number of keys per |4 |20 |20 |30 | + * |session | | | | | + * +--------------------------------+---------+----------+---------+---------+ + * |Minimum total number of keys |16 |40 |80 |90 | + * | (all sessions) | | | | | + * +--------------------------------+---------+----------+---------+---------+ + * |Decrypted Frames per Second |30 fps SD|30 fps HD |60 fps HD|60 fps 8k| + * +--------------------------------+---------+----------+---------+---------+ * * Parameters: * none. @@ -2973,22 +3836,38 @@ uint32_t OEMCrypto_GetAnalogOutputFlags(void); uint32_t OEMCrypto_ResourceRatingTier(void); /* - * OEMCrypto_RewrapDeviceRSAKey30 + * OEMCrypto_LoadProvisioning * * Description: - * This function is similar to RewrapDeviceRSAKey, except it uses the private - * key from an OEM certificate to decrypt the message key instead of keys - * derived from a keybox. Verifies an RSA provisioning response is valid and - * corresponds to the previous provisioning request by checking the nonce. - * The RSA private key is decrypted and stored in secure memory. The RSA key - * is then re-encrypted and signed for storage on the filesystem. We - * recommend that the OEM use an encryption key and signing key generated - * using an algorithm at least as strong as that in GenerateDerivedKeys. + * Load and parse a provisioning response, and then rewrap the private key + * for storage on the filesystem. We recommend that the OEM use an encryption + * key and signing key generated using an algorithm at least as strong as + * that in GenerateDerivedKeys. + * + * First, OEMCrypto shall verify the signature of the message using + * HMAC-SHA256 with the derived mac_key[server]. The signature verification + * shall use a constant-time algorithm (a signature mismatch will always take + * the same time as a successful comparison). The signature is over the + * entire message buffer starting at message with length message_length. If + * the signature verification fails, ignore all other arguments and return + * OEMCrypto_ERROR_SIGNATURE_FAILURE. + * + * NOTE: The calling software must have previously established the mac_keys + * and encrypt_key with a call to OEMCrypto_DeriveKeysFromSessionKey or + * OEMCrypto_GenerateDerivedKeys. + * + * The function ODK_ParseProvisioning is called to parse the message. If it + * returns an error, OEMCrypto shall return that error to the CDM layer. The + * function ODK_ParseProvisioning is described in the document "Widevine Core + * Message Serialization". + * + * Below, all fields are found in the struct ODK_ParsedLicense parsed_license + * returend by ODK_ParsedProvisioning. * * After decrypting enc_rsa_key, If the first four bytes of the buffer are * the string "SIGN", then the actual RSA key begins on the 9th byte of the * buffer. The second four bytes of the buffer is the 32 bit field - * "allowed_schemes", of type RSA_Padding_Scheme, which is used in + * "allowed_schemes"of type RSA_Padding_Scheme, which is used in * OEMCrypto_GenerateRSASignature. The value of allowed_schemes must also be * wrapped with RSA key. We recommend storing the magic string "SIGN" with * the key to distinguish keys that have a value for allowed_schemes from @@ -3006,150 +3885,18 @@ uint32_t OEMCrypto_ResourceRatingTier(void); * Verification and Algorithm: * The following checks should be performed. If any check fails, an error is * returned, and the key is not loaded. - * - * 1. Verify that in_wrapped_rsa_key_length is large enough to hold the - * rewrapped key, returning OEMCrypto_ERROR_SHORT_BUFFER otherwise. - * 2. Verify that the nonce matches one generated by a previous call to - * OEMCrypto_GenerateNonce(). The matching nonce shall be removed from - * the nonce table. If there is no matching nonce, return - * OEMCRYPTO_ERROR_INVALID_NONCE. Notice that the nonce may not point - * to a word aligned memory location. - * 3. Decrypt encrypted_message_key with the OEM certificate's private RSA - * key using RSA-OAEP into the buffer message_key. This message key is - * a 128 bit AES key used only in step 4. This message_key should be - * kept in secure memory and protected from the user. - * 4. Decrypt enc_rsa_key into the buffer rsa_key using the message_key, - * which was found in step 3. Use enc_rsa_key_iv as the initial vector - * for AES_128-CBC mode, with PKCS#5 padding. The rsa_key should be kept - * in secure memory and protected from the user. - * 5. If the first four bytes of the buffer rsa_key are the string "SIGN", - * then the actual RSA key begins on the 9th byte of the buffer. The - * second four bytes of the buffer is the 32 bit field - * "allowed_schemes", of type RSA_Padding_Scheme, which is used in - * OEMCrypto_GenerateRSASignature. The value of allowed_schemes must - * also be wrapped with RSA key. We recommend storing the magic string - * "SIGN" with the key to distinguish keys that have a value for - * allowed_schemes from those that should use the default - * allowed_schemes. Devices that do not support the alternative signing - * algorithms may refuse to load these keys and return an error of - * OEMCrypto_ERROR_NOT_IMPLEMENTED. The main use case for these - * alternative signing algorithms is to support devices that use X.509 - * certificates for authentication when acting as a ChromeCast receiver. - * This is not needed for devices that wish to send data to a ChromeCast. - * 6. If the first four bytes of the buffer rsa_key are not the string - * "SIGN", then the default value of allowed_schemes = 1 - * (kSign_RSASSA_PSS) will be used. - * 7. After possibly skipping past the first 8 bytes signifying the allowed - * signing algorithm, the rest of the buffer rsa_key contains an RSA - * device key in PKCS#8 binary DER encoded format. The OEMCrypto library - * shall verify that this RSA key is valid. - * 8. Re-encrypt the device RSA key with an internal key (such as the OEM - * key or Widevine Keybox key) and the generated IV using AES-128-CBC - * with PKCS#5 padding. - * 9. Copy the rewrapped key to the buffer specified by wrapped_rsa_key and - * the size of the wrapped key to wrapped_rsa_key_length. - * - * Parameters: - * [in] session: crypto session identifier. - * [in] nonce: A pointer to the nonce provided in the provisioning response. - * (unaligned uint32_t) - * [in] encrypted_message_key : message_key encrypted by private key from - * OEM cert. - * [in] encrypted_message_key_length : length of encrypted_message_key in - * bytes. - * [in] enc_rsa_key: Encrypted device private RSA key received from the - * provisioning server. Format is PKCS#8, binary DER encoded, and - * encrypted with message_key, using AES-128-CBC with PKCS#5 padding. - * [in] enc_rsa_key_length: length of the encrypted RSA key, in bytes. - * [in] enc_rsa_key_iv: IV for decrypting RSA key. Size is 128 bits. - * [out] wrapped_rsa_key: pointer to buffer in which encrypted RSA key should - * be stored. May be null on the first call in order to find required - * buffer size. - * [in/out] wrapped_rsa_key_length: (in) length of the encrypted RSA key, in - * bytes. - * (out) actual length of the encrypted RSA key - * - * Returns: - * OEMCrypto_SUCCESS success - * OEMCrypto_ERROR_NO_DEVICE_KEY - * OEMCrypto_ERROR_INVALID_SESSION - * OEMCrypto_ERROR_INVALID_RSA_KEY - * OEMCrypto_ERROR_SIGNATURE_FAILURE - * OEMCrypto_ERROR_INVALID_NONCE - * OEMCrypto_ERROR_SHORT_BUFFER - * OEMCrypto_ERROR_INSUFFICIENT_RESOURCES - * OEMCrypto_ERROR_UNKNOWN_FAILURE - * OEMCrypto_ERROR_BUFFER_TOO_LARGE - * OEMCrypto_ERROR_SESSION_LOST_STATE - * OEMCrypto_ERROR_SYSTEM_INVALIDATED - * - * Buffer Sizes: - * OEMCrypto shall support message sizes of at least 8 KiB. - * 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 12. - */ - -/* - * OEMCrypto_RewrapDeviceRSAKey - * - * Description: - * This function is similar to RewrapDeviceRSAKey30, except it uses session - * keys derived from the keybox instead of the OEM certificate. Verifies an - * RSA provisioning response is valid and corresponds to the previous - * provisioning request by checking the nonce. The RSA private key is - * decrypted and stored in secure memory. The RSA key is then re-encrypted - * and signed for storage on the filesystem. We recommend that the OEM use an - * encryption key and signing key generated using an algorithm at least as - * strong as that in GenerateDerivedKeys. - * - * After decrypting enc_rsa_key, If the first four bytes of the buffer are - * the string "SIGN", then the actual RSA key begins on the 9th byte of the - * buffer. The second four bytes of the buffer is the 32 bit field - * "allowed_schemes", of type RSA_Padding_Scheme, which is used in - * OEMCrypto_GenerateRSASignature. The value of allowed_schemes must also be - * wrapped with RSA key. We recommend storing the magic string "SIGN" with - * the key to distinguish keys that have a value for allowed_schemes from - * those that should use the default allowed_schemes. Devices that do not - * support the alternative signing algorithms may refuse to load these keys - * and return an error of OEMCrypto_ERROR_NOT_IMPLEMENTED. The main use case - * for these alternative signing algorithms is to support devices that use - * X509 certificates for authentication when acting as a ChromeCast receiver. - * This is not needed for devices that wish to send data to a ChromeCast. - * - * If the first four bytes of the buffer enc_rsa_key are not the string - * "SIGN", then the default value of allowed_schemes = 1 (kSign_RSASSA_PSS) - * will be used. - * - * Verification and Algorithm: - * The following checks should be performed. If any check fails, an error is - * returned, and the key is not loaded. - * * 1. Check that all the pointer values passed into it are within the * buffer specified by message and message_length. * 2. Verify that in_wrapped_rsa_key_length is large enough to hold the * rewrapped key, returning OEMCrypto_ERROR_SHORT_BUFFER otherwise. - * 3. Verify that the nonce matches one generated by a previous call to - * OEMCrypto_GenerateNonce(). The matching nonce shall be removed from - * the nonce table. If there is no matching nonce, return - * OEMCRYPTO_ERROR_INVALID_NONCE. - * 4. Verify the message signature, using the derived signing key + * 3. Verify the message signature, using the derived signing key * (mac_key[server]) from a previous call to - * OEMCrypto_GenerateDerivedKeys. - * 5. Decrypt enc_rsa_key in the buffer rsa_key using the derived - * encryption key (enc_key) from a previous call to - * OEMCrypto_GenerateDerivedKeys. Use enc_rsa_key_iv as the initial - * vector for AES_128-CBC mode, with PKCS#5 padding. The rsa_key should - * be kept in secure memory and protected from the user. + * OEMCrypto_GenerateDerivedKeys or OEMCrypto_DeriveKeysFromSessionKey. + * 4. The function ODK_ParseProvisioning is called to parse the message. + * 5. Decrypt enc_rsa_key in the buffer rsa_key using the session's derived + * encryption key (enc_key). Use enc_rsa_key_iv as the initial vector + * for AES_128-CBC mode, with PKCS#5 padding. The rsa_key should be kept + * in secure memory and protected from the user. * 6. If the first four bytes of the buffer rsa_key are the string "SIGN", * then the actual RSA key begins on the 9th byte of the buffer. The * second four bytes of the buffer is the 32 bit field @@ -3179,18 +3926,11 @@ uint32_t OEMCrypto_ResourceRatingTier(void); * * Parameters: * [in] session: crypto session identifier. - * [in] message: pointer to memory containing message to be verified. + * [in] message: pointer to memory containing data. * [in] message_length: length of the message, in bytes. - * [in] signature: pointer to memory containing the HMAC-SHA256 signature for - * message, received from the provisioning server. + * [in] core_message_length: length of the core submessage, in bytes. + * [in] signature: pointer to memory containing the signature. * [in] signature_length: length of the signature, in bytes. - * [in] nonce: A pointer to the nonce provided in the provisioning response. - * [in] enc_rsa_key: Encrypted device private RSA key received from the - * provisioning server. Format is PKCS#8, binary DER encoded, and - * encrypted with the derived encryption key, using AES-128-CBC with - * PKCS#5 padding. - * [in] enc_rsa_key_length: length of the encrypted RSA key, in bytes. - * [in] enc_rsa_key_iv: IV for decrypting RSA key. Size is 128 bits. * [out] wrapped_rsa_key: pointer to buffer in which encrypted RSA key should * be stored. May be null on the first call in order to find required * buffer size. @@ -3225,22 +3965,29 @@ uint32_t OEMCrypto_ResourceRatingTier(void); * session, and a read lock on the OEMCrypto system. * * Version: - * This method changed in API version 12. + * This method changed in API version 16. */ +OEMCryptoResult OEMCrypto_LoadProvisioning( + OEMCrypto_SESSION session, const uint8_t* message, size_t message_length, + size_t core_message_length, const uint8_t* signature, + size_t signature_length, uint8_t* wrapped_private_key, + size_t* wrapped_private_key_length); /* - * OEMCrypto_LoadDeviceRSAKey + * OEMCrypto_LoadDevicePrivateKey * * Description: - * Loads a wrapped RSA private key to secure memory for use by this session - * in future calls to OEMCrypto_GenerateRSASignature. The wrapped RSA key - * will be the one verified and wrapped by OEMCrypto_RewrapDeviceRSAKey. The - * RSA private key should be stored in secure memory. + * Loads a wrapped RSA or ECC private key to secure memory for use by this + * session in future calls to OEMCrypto_PrepAndSignLicenseRequest or + * OEMCrypto_DeriveKeysFromSessionKey. The wrapped private key will be the + * one verified and wrapped by OEMCrypto_LoadProvisioning. The private key + * should be stored in secure memory. * * If the bit field "allowed_schemes" was wrapped with this RSA key, its - * value will be loaded and stored with the RSA key. If there was not bit - * field wrapped with the RSA key, the key will use a default value of 1 = - * RSASSA-PSS with SHA1. + * value will be loaded and stored with the RSA key, and the key may be used + * with calls to OEMCrypto_GenerateRSASignature. If there was not a bit field + * wrapped with the RSA key, the key will be used for + * OEMCrypto_PrepAndSignLicenseRequest or OEMCrypto_DeriveKeysFromSessionKey * * Verification: * The following checks should be performed. If any check fails, an error is @@ -3253,6 +4000,8 @@ uint32_t OEMCrypto_ResourceRatingTier(void); * * Parameters: * [in] session: crypto session identifier. + * [in] key_type: indicates either an RSA or ECC key for devices that support + * both. * [in] wrapped_rsa_key: wrapped device RSA key stored on the device. Format * is PKCS#8, binary DER encoded, and encrypted with a key internal to * the OEMCrypto instance, using AES-128-CBC with PKCS#5 padding. This @@ -3277,11 +4026,11 @@ uint32_t OEMCrypto_ResourceRatingTier(void); * session, and a read lock on the OEMCrypto system. * * Version: - * This method changed in API version 9. + * This method changed in API version 16. */ -OEMCryptoResult OEMCrypto_LoadDeviceRSAKey(OEMCrypto_SESSION session, - const uint8_t* wrapped_rsa_key, - size_t wrapped_rsa_key_length); +OEMCryptoResult OEMCrypto_LoadDevicePrivateKey( + OEMCrypto_SESSION session, OEMCrypto_PrivateKeyType key_type, + const uint8_t* wrapped_rsa_key, size_t wrapped_rsa_key_length); /* * OEMCrypto_LoadTestRSAKey @@ -3325,48 +4074,33 @@ OEMCryptoResult OEMCrypto_LoadTestRSAKey(void); * OEMCrypto_GenerateRSASignature * * Description: - * The OEMCrypto_GenerateRSASignature method is used to sign messages using - * the device private RSA key, specifically, it is used to sign the initial - * license request. + * The OEMCrypto_GenerateRSASignature method is only used for devices that + * are CAST receivers. This function is called after + * OEMCrypto_LoadDevicePrivateKey for the same session. * - * Refer to the Signing Messages Sent to a Server section above for more - * details. - * - * If this function is called after OEMCrypto_LoadDeviceRSAKey for the same - * session, then this function should use the device RSA key that was loaded. - * If this function is called after a call to - * OEMCrypto_GetOEMPublicCertificate for the same session, then this function - * should use the RSA private key associated with the OEM certificate. The - * only padding scheme that is valid for the OEM certificate is 0x1 - - * RSASSA-PSS with SHA1. Any other padding scheme must generate an error. - * - * For devices that wish to be CAST receivers, there is a new RSA padding - * scheme. The padding_scheme parameter indicates which hashing and padding - * is to be applied to the message so as to generate the encoded message (the - * modulus-sized block to which the integer conversion and RSA decryption is - * applied). The following values are defined: + * The parameter padding_scheme has two possible legacy values: * * 0x1 - RSASSA-PSS with SHA1. * * 0x2 - PKCS1 with block type 1 padding (only). * - * In the first case, a hash algorithm (SHA1) is first applied to the - * message, whose length is not otherwise restricted. In the second case, the - * "message" is already a digest, so no further hashing is applied, and the - * message_length can be no longer than 83 bytes. If the message_length is - * greater than 83 bytes OEMCrypto_ERROR_SIGNATURE_FAILURE shall be returned. + * The only supported padding scheme is 0x2 since version 16 of this API. In + * this second case, the "message" is already a digest, so no further hashing + * is applied, and the message_length can be no longer than 83 bytes. If the + * message_length is greater than 83 bytes OEMCrypto_ERROR_SIGNATURE_FAILURE + * shall be returned. * * The second padding scheme is for devices that use X509 certificates for * authentication. The main example is devices that work as a Cast receiver, * like a ChromeCast, not for devices that wish to send to the Cast device, * such as almost all Android devices. OEMs that do not support X509 - * certificate authentication need not implement the second scheme and can - * return OEMCrypto_ERROR_NOT_IMPLEMENTED. + * certificate authentication need not implement this function and can return + * OEMCrypto_ERROR_NOT_IMPLEMENTED. * * Verification: - * The bitwise AND of the parameter padding_scheme and the RSA key's - * allowed_schemes is computed. If this value is 0, then the signature is not - * computed and the error OEMCrypto_ERROR_INVALID_RSA_KEY is returned. + * Both the padding_scheme and the RSA key's allowed_schemes must be 0x2. If + * not, then the signature is not computed and the error + * OEMCrypto_ERROR_INVALID_RSA_KEY is returned. * * Parameters: * [in] session: crypto session identifier. @@ -3407,7 +4141,7 @@ OEMCryptoResult OEMCrypto_LoadTestRSAKey(void); * session, and a read lock on the OEMCrypto system. * * Version: - * This method changed in API version 12. + * This method changed in API version 16. */ OEMCryptoResult OEMCrypto_GenerateRSASignature( OEMCrypto_SESSION session, const uint8_t* message, size_t message_length, @@ -3425,6 +4159,9 @@ OEMCryptoResult OEMCrypto_GenerateRSASignature( * will encrypt and sign the new, empty, header and return it in the provided * buffer. * + * The new entry should be created with a status of kUnused and all times + * times should be set to 0. + * * Devices that do not implement a Session Usage Table may return * OEMCrypto_ERROR_NOT_IMPLEMENTED. * @@ -3466,7 +4203,7 @@ OEMCryptoResult OEMCrypto_CreateUsageTableHeader(uint8_t* header_buffer, * * Parameters: * [in] buffer: pointer to memory containing encrypted usage table header. - * [in] buffert_length: length of the buffer, in bytes. + * [in] buffer_length: length of the buffer, in bytes. * * Returns: * OEMCrypto_SUCCESS success @@ -3488,7 +4225,7 @@ OEMCryptoResult OEMCrypto_CreateUsageTableHeader(uint8_t* header_buffer, * system. * * Version: - * This method changed in API version 13. + * This method changed in API version 16. */ OEMCryptoResult OEMCrypto_LoadUsageTableHeader(const uint8_t* buffer, size_t buffer_length); @@ -3511,6 +4248,9 @@ OEMCryptoResult OEMCrypto_LoadUsageTableHeader(const uint8_t* buffer, * a rogue application from deleting an entry and then loading an old version * of it. * + * If the session already has a usage entry associated with it, the error + * OEMCrypto_ERROR_MULTIPLE_USAGE_ENTRIES is returned. + * * Parameters: * [in] session: handle for the session to be used. * [out] usage_entry_number: index of new usage entry. @@ -3526,6 +4266,8 @@ OEMCryptoResult OEMCrypto_LoadUsageTableHeader(const uint8_t* buffer, * OEMCrypto_ERROR_UNKNOWN_FAILURE * OEMCrypto_ERROR_SESSION_LOST_STATE * OEMCrypto_ERROR_SYSTEM_INVALIDATED + * OEMCrypto_ERROR_MULTIPLE_USAGE_ENTRIES - if there already is a usage entry + * loaded into this session * * Threading: * This is a "Usage Table Function" and will not be called simultaneously @@ -3546,13 +4288,19 @@ OEMCryptoResult OEMCrypto_CreateNewUsageEntry(OEMCrypto_SESSION session, * signature at the beginning of the buffer is verified and the buffer will * be decrypted. Then the verification field in the entry will be verified. * The index in the entry must match the index passed in. The generation - * number in the entry will be compared against that in the header. If it is - * off by 1, a warning is returned, but the entry is still loaded. This - * warning may be logged by the CDM layer. If the generation number is off by - * more than 1, an error is returned and the entry is not loaded. + * number in the entry will be compared against the entry's corresponding + * generation number in the header. If it is off by 1, a warning is returned, + * but the entry is still loaded. This warning may be logged by the CDM + * layer. If the generation number is off by more than 1, an error is + * returned and the entry is not loaded. + * + * OEMCrypto shall call ODK_ReloadClockValues, as described in "License + * Duration and Renweal" to set the session's clock values. * * If the entry is already loaded into another open session, then this fails - * and returns OEMCrypto_ERROR_INVALID_SESSION. + * and returns OEMCrypto_ERROR_INVALID_SESSION. If the session already has a + * usage entry associated with it, the error + * OEMCrypto_ERROR_MULTIPLE_USAGE_ENTRIES is returned. * * Parameters: * [in] session: handle for the session to be used. @@ -3576,6 +4324,8 @@ OEMCryptoResult OEMCrypto_CreateNewUsageEntry(OEMCrypto_SESSION session, * OEMCrypto_ERROR_BAD_MAGIC - verification string does not match. * OEMCrypto_ERROR_SESSION_LOST_STATE * OEMCrypto_ERROR_SYSTEM_INVALIDATED + * OEMCrypto_ERROR_MULTIPLE_USAGE_ENTRIES - if there already is a usage entry + * loaded into this session * * Threading: * This is a "Usage Table Function" and will not be called simultaneously @@ -3595,18 +4345,25 @@ OEMCryptoResult OEMCrypto_LoadUsageEntry(OEMCrypto_SESSION session, * * Description: * Updates the session's usage entry and fills buffers with the encrypted and - * signed entry and usage table header. OEMCrypto will update all time and - * status values in the entry, and then increment the entry's generation - * number. The corresponding generation number in the usage table header is - * also incremented so that it matches the one in the entry. The master - * generation number in the usage table header is incremented and is copied - * to secure persistent storage. OEMCrypto will encrypt and sign the entry - * into the entry_buffer, and it will encrypt and sign the usage table header - * into the header_buffer. Some actions, such as the first decrypt and - * deactivating an entry, will also increment the entry's generation number - * as well as changing the entry's status and time fields. As in OEMCrypto - * v12, the first decryption will change the status from Inactive to Active, - * and it will set the time stamp "first decrypt". + * signed entry and usage table header. + * + * OEMCrypto shall call ODK_UpdateLastPlaybackTime to update the session's + * clock values, as discussed in the document "License Duration and Renewal". + * The values in the session's clock values structure are copied to the usage + * entry. + * + * OEMCrypto shall update all time and status values in the entry, and then + * increment the entry's generation number. The corresponding generation + * number in the usage table header is also incremented so that it matches + * the one in the entry. The master generation number in the usage table + * header is incremented and the master generation number is copied to secure + * persistent storage. OEMCrypto will encrypt and sign the entry into the + * entry_buffer, and it will encrypt and sign the usage table header into the + * header_buffer. Some actions, such as the first decrypt and deactivating an + * entry, will also increment the entry's generation number as well as + * changing the entry's status and time fields. The first decryption will + * change the status from Inactive to Active, and it will set the time stamp + * "first decrypt". * * If the usage entry has the flag ForbidReport set, then the flag is * cleared. It is the responsibility of the CDM layer to call this function @@ -3614,10 +4371,10 @@ OEMCryptoResult OEMCrypto_LoadUsageEntry(OEMCrypto_SESSION session, * the CDM is terminated. Failure to do so will result in generation number * skew, which will invalidate all of the usage table. * - * If either buffer_length is not large enough, they are set to the needed - * size, and OEMCrypto_ERROR_SHORT_BUFFER. In this case, the entry is not - * updated, ForbidReport is not cleared, generation numbers are not - * incremented, and no other work is done. + * If either entry_buffer_length or header_buffer_length is not large enough, + * they are set to the needed size, and return OEMCrypto_ERROR_SHORT_BUFFER. + * In this case, the entry is not updated, ForbidReport is not cleared, + * generation numbers are not incremented, and no other work is done. * * Parameters: * [in] session: handle for the session to be used. @@ -3627,7 +4384,7 @@ OEMCryptoResult OEMCrypto_LoadUsageEntry(OEMCrypto_SESSION session, * (out) actual length of the header_buffer * [out] entry_buffer: pointer to memory where encrypted usage table entry is * written. - * [in/out] buffer_length: (in) length of the entry_buffer, in bytes. + * [in/out] entry_buffer_length: (in) length of the entry_buffer, in bytes. * (out) actual length of the entry_buffer * * Returns: @@ -3645,20 +4402,19 @@ OEMCryptoResult OEMCrypto_LoadUsageEntry(OEMCrypto_SESSION session, * system. * * Version: - * This method changed in API version 13. + * This method changed in API version 16. */ -OEMCryptoResult OEMCrypto_UpdateUsageEntry(OEMCrypto_SESSION session, - uint8_t* header_buffer, - size_t* header_buffer_length, - uint8_t* entry_buffer, - size_t* entry_buffer_length); +OEMCryptoResult OEMCrypto_UpdateUsageEntry( + OEMCrypto_SESSION session, OEMCrypto_SharedMemory* header_buffer, + size_t* header_buffer_length, OEMCrypto_SharedMemory* entry_buffer, + size_t* entry_buffer_length); /* * OEMCrypto_DeactivateUsageEntry * * Description: * This deactivates the usage entry associated with the current session. This - * means that the state of the usage entry is changed to InactiveUsed if it + * means that the status of the usage entry is changed to InactiveUsed if it * was Active, or InactiveUnused if it was Unused. This also increments the * entry's generation number, and the header's master generation number. The * corresponding generation number in the usage table header is also @@ -3667,6 +4423,9 @@ OEMCryptoResult OEMCrypto_UpdateUsageEntry(OEMCrypto_SESSION session, * generating a report of a deactivated license without first saving the * entry. * + * OEMCrypto shall call ODK_DeactivateUsageEntry to update the session's + * clock values, as discussed in the document "License Duration and Renewal". + * * It is allowed to call this function multiple times. If the state is * already InactiveUsed or InactiveUnused, then this function does not change * the entry or its state. @@ -3697,7 +4456,7 @@ OEMCryptoResult OEMCrypto_UpdateUsageEntry(OEMCrypto_SESSION session, * system. * * Version: - * This method changed in API version 13. + * This method changed in API version 16. */ OEMCryptoResult OEMCrypto_DeactivateUsageEntry(OEMCrypto_SESSION session, const uint8_t* pst, @@ -3712,10 +4471,9 @@ OEMCryptoResult OEMCrypto_DeactivateUsageEntry(OEMCrypto_SESSION session, * If the buffer_length is not sufficient to hold a report structure, set * buffer_length and return OEMCrypto_ERROR_SHORT_BUFFER. * - * If the an entry was not loaded or created with - * OEMCrypto_CreateNewUsageEntry or OEMCRypto_LoadUsageEntry, or if the pst - * does not match that in the entry, return the error - * OEMCrypto_ERROR_INVALID_CONTEXT. + * If an entry was not loaded or created with OEMCrypto_CreateNewUsageEntry + * or OEMCRypto_LoadUsageEntry, or if the pst does not match that in the + * entry, return the error OEMCrypto_ERROR_INVALID_CONTEXT. * * If the usage entry's flag ForbidReport is set, indicating the entry has * not been saved since the entry was deactivated, then the error @@ -3725,9 +4483,10 @@ OEMCryptoResult OEMCrypto_DeactivateUsageEntry(OEMCrypto_SESSION session, * and OEMCrypto returns the error OEMCrypto_ERROR_ENTRY_NEEDS_UPDATE. * * The pst_report is filled out by subtracting the times in the Usage Entry - * from the current time on the secure clock. This is done in case the secure - * clock is not using UTC time, but is instead using something like seconds - * since clock installed. + * from the current time on the secure clock. This design was chosen to avoid + * the device's secure clock with any external clock. + * + * (See drawing in "Widevine Modular DRM Security Integration Guide") * * Valid values for status are: * @@ -3743,7 +4502,8 @@ OEMCryptoResult OEMCrypto_DeactivateUsageEntry(OEMCrypto_SESSION session, * - 0 = Insecure Clock - clock just uses system time. * - 1 = Secure Timer - clock runs from a secure timer which is initialized * from system time when OEMCrypto becomes active and cannot be modified - * by user software or the user while OEMCrypto is active. + * by user software or the user while OEMCrypto is active. A secure + * timer cannot run backwards, even while OEMCrypto is not active. * - 2 = Secure Clock - Real-time clock set from a secure source that * cannot be modified by user software regardless of whether OEMCrypto * is active or inactive. The clock time can only be modified by @@ -3752,6 +4512,8 @@ OEMCryptoResult OEMCrypto_DeactivateUsageEntry(OEMCrypto_SESSION session, * that cannot be modified by user software and there are security * features that prevent the user from modifying the clock in hardware, * such as a tamper proof battery. + * (See drawing in "Widevine Modular DRM Security Integration Guide") + * * After pst_report has been filled in, the HMAC SHA1 signature is computed * for the buffer from bytes 20 to the end of the pst field. The signature is * computed using the mac_key[client] which is stored in the usage table. The @@ -3800,8 +4562,7 @@ OEMCryptoResult OEMCrypto_DeactivateUsageEntry(OEMCrypto_SESSION session, */ OEMCryptoResult OEMCrypto_ReportUsage(OEMCrypto_SESSION session, const uint8_t* pst, size_t pst_length, - uint8_t* buffer, - size_t* buffer_length); + uint8_t* buffer, size_t* buffer_length); /* * OEMCrypto_MoveEntry @@ -3857,15 +4618,16 @@ OEMCryptoResult OEMCrypto_MoveEntry(OEMCrypto_SESSION session, * CDM layer after it has defragmented the usage table and can delete unused * entries. It is an error if any open session is associated with an entry * that will be erased - the error OEMCrypto_ERROR_ENTRY_IN_USE shall be - * returned in this case. If new_table_size is larger than the current size, - * then the header is not changed and the error is returned. If the header - * has not been previously loaded, then an error is returned. OEMCrypto will - * increment the master generation number in the header and store the new - * value in secure persistent storage. Then, OEMCrypto will encrypt and sign - * the header into the provided buffer. The generation numbers of all - * remaining entries will remain unchanged. The next time + * returned in this case, and the header shall not be modified. If + * new_entry_count is larger than the current size, then the header is not + * changed and the error OEMCrypto_ERROR_UNKNOWN_FAILURE is returned. If the + * header has not been previously loaded, then an error is returned. + * OEMCrypto will increment the master generation number in the header and + * store the new value in secure persistent storage. Then, OEMCrypto will + * encrypt and sign the header into the provided buffer. The generation + * numbers of all remaining entries will remain unchanged. The next time * OEMCrypto_CreateNewUsageEntry is called, the new entry will have an index - * of new_table_size. + * of new_entry_count. * * Devices that do not implement a Session Usage Table may return * OEMCrypto_ERROR_NOT_IMPLEMENTED. @@ -3874,6 +4636,9 @@ OEMCryptoResult OEMCrypto_MoveEntry(OEMCrypto_SESSION session, * set to the needed value, the generation number is not incremented, and * OEMCrypto_ERROR_SHORT_BUFFER is returned. * + * If the header has not been loaded or created, return the error + * OEMCrypto_ERROR_UNKNOWN_FAILURE. + * * Parameters: * [in] new_entry_count: number of entries in the to be in the header. * [out] header_buffer: pointer to memory where encrypted usage table header @@ -3901,72 +4666,6 @@ OEMCryptoResult OEMCrypto_ShrinkUsageTableHeader(uint32_t new_entry_count, uint8_t* header_buffer, size_t* header_buffer_length); -/* - * OEMCrypto_CopyOldUsageEntry - * - * Description: - * This function copies an entry from the old v12 table to the new table. The - * new entry will already have been loaded by CreateNewUsageEntry. If the - * device did not support pre-v13 usage tables, this may return - * OEMCrypto_ERROR_NOT_IMPLEMENTED. - * - * This is only needed for devices that are upgrading from a version of - * OEMCrypto before v13 to a recent version. Devices that have an existing - * usage table with customer's offline licenses will use this method to move - * entries from the old table to the new one. - * - * Parameters: - * [in] session: handle for the session to be used. - * [in] pst: pointer to memory containing Provider Session Token. - * [in] pst_length: length of the pst, in bytes. - * - * Returns: - * OEMCrypto_SUCCESS success - * OEMCrypto_ERROR_NOT_IMPLEMENTED - * OEMCrypto_ERROR_UNKNOWN_FAILURE - * OEMCrypto_ERROR_SESSION_LOST_STATE - * OEMCrypto_ERROR_SYSTEM_INVALIDATED - * - * Threading: - * This is a "Usage Table Function" and will not be called simultaneously - * with any other function, as if the CDM holds a write lock on the OEMCrypto - * system. - * - * Version: - * This method is new in API version 13. - */ - -/* - * OEMCrypto_DeleteOldUsageTable - * - * Description: - * This function will delete the old usage table, if possible, freeing any - * nonvolatile secure memory. This may return OEMCrypto_ERROR_NOT_IMPLEMENTED - * if the device did not support pre-v13 usage tables. - * - * This is only needed for devices that are upgrading from a version of - * OEMCrypto before v13 to a recent version. Devices that have an existing - * usage table with customer's offline licenses will use this method to move - * entries from the old table to the new one. - * - * Parameters: - * none - * - * Returns: - * OEMCrypto_SUCCESS success - * OEMCrypto_ERROR_NOT_IMPLEMENTED - * OEMCrypto_ERROR_UNKNOWN_FAILURE - * OEMCrypto_ERROR_SYSTEM_INVALIDATED - * - * Threading: - * This is an "Initialization and Termination Function" and will not be - * called simultaneously with any other function, as if the CDM holds a write - * lock on the OEMCrypto system. - * - * Version: - * This method is new in API version 13. - */ - /* * OEMCrypto_RemoveSRM * @@ -3994,33 +4693,6 @@ OEMCryptoResult OEMCrypto_ShrinkUsageTableHeader(uint32_t new_entry_count, */ OEMCryptoResult OEMCrypto_RemoveSRM(void); -/* - * OEMCrypto_CreateOldUsageEntry - * - * Description: - * This forces the creation of an entry in the old usage table in order to - * test OEMCrypto_CopyOldUsageTable. OEMCrypto will create a new entry, set - * the status and compute the times at license receive, first decrypt and - * last decrypt. The mac keys will be copied to the entry. The mac keys are - * not encrypted, but will only correspond to a test license. - * - * Devices that do not support usage tables, or devices that will not be - * field upgraded from a version of OEMCrypto before v13 to a recent version - * may return OEMCrypto_ERROR_NOT_IMPLEMENTED. - * - * Returns: - * OEMCrypto_ERROR_NOT_IMPLEMENTED - * OEMCrypto_SUCCESS - * - * Threading: - * This is an "Initialization and Termination Function" and will not be - * called simultaneously with any other function, as if the CDM holds a write - * lock on the OEMCrypto system. It is only used when running unit tests. - * - * Version: - * This method is new in API version 13. - */ - /* * OEMCrypto_SupportsDecryptHash * @@ -4170,6 +4842,84 @@ OEMCryptoResult OEMCrypto_SetDecryptHash(OEMCrypto_SESSION session, OEMCryptoResult OEMCrypto_GetHashErrorCode(OEMCrypto_SESSION session, uint32_t* failed_frame_number); +/* + * OEMCrypto_AllocateSecureBuffer + * + * Description: + * Allocates a secure buffer and fills out the destination buffer information + * in output. The integer secure_fd may also be set to indicate the source of + * the buffer. OEMCrypto may use the secure_fd to help track the buffer if it + * wishes. The unit tests will pass a pointer to the same destination buffer + * description and the same secure_fd to OEMCrypto_FreeSecureBuffer when the + * buffer is to be freed. + * + * This is especially helpful if the hash functions above are supported. This + * will only be used by the OEMCrypto unit tests, so we recommend returning + * OEMCrypto_ERROR_NOT_IMPLEMENTED for production devices if performance is + * an issue. If OEMCrypto_ERROR_NOT_IMPLEMENTED is returned, then secure + * buffer unit tests will be skipped. + * + * Parameters: + * [in] session: session id for operation. + * [in] buffer_size: the requested buffer size. + * [out] output: the buffer descriptor for the created buffer. This will be + * passed into the OEMCrypto_DecryptCENC function. + * [out] secure_fd: a pointer to platform dependant file or buffer + * descriptor. This will be passed to OEMCrypto_FreeSecureBuffer. + * + * Returns: + * OEMCrypto_SUCCESS - if the buffer was created + * OEMCrypto_ERROR_NOT_IMPLEMENTED + * OEMCrypto_ERROR_OUTPUT_TOO_LARGE + * OEMCrypto_ERROR_UNKNOWN_FAILURE + * + * 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 is new in API version 16. + */ +OEMCryptoResult OEMCrypto_AllocateSecureBuffer( + OEMCrypto_SESSION session, size_t buffer_size, + OEMCrypto_DestBufferDesc* output_descriptor, int* secure_fd); + +/* + * OEMCrypto_FreeSecureBuffer + * + * Description: + * Frees a secure buffer that had previously been created with + * OEMCrypto_AllocateSecureBuffer. Any return value except OEMCrypto_SUCCESS + * will cause the unit test using secure buffers to fail. + * + * Parameters: + * [in] session: session id for operation. + * [out] output: the buffer descriptor modified by + * OEMCrypto_AllocateSecureBuffer + * [in] secure_fd: The integer returned by OEMCrypto_AllocateSecureBuffer + * + * Returns: + * OEMCrypto_SUCCESS - if the buffer was created + * OEMCrypto_ERROR_NOT_IMPLEMENTED + * OEMCrypto_ERROR_UNKNOWN_FAILURE + * + * 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 is new in API version 16. + */ +OEMCryptoResult OEMCrypto_FreeSecureBuffer( + OEMCrypto_SESSION session, OEMCrypto_DestBufferDesc* output_descriptor, + int secure_fd); + /****************************************************************************/ /****************************************************************************/ /* The following functions are deprecated. They are not required for the @@ -4181,18 +4931,16 @@ OEMCryptoResult OEMCrypto_GenerateSignature(OEMCrypto_SESSION session, size_t message_length, uint8_t* signature, size_t* signature_length); -OEMCryptoResult OEMCrypto_LoadKeys( - OEMCrypto_SESSION session, const uint8_t* message, size_t message_length, - const uint8_t* signature, size_t signature_length, - OEMCrypto_Substring enc_mac_keys_iv, OEMCrypto_Substring enc_mac_keys, - size_t num_keys, const OEMCrypto_KeyObject* key_array, - OEMCrypto_Substring pst, OEMCrypto_Substring srm_restriction_data, - OEMCrypto_LicenseType license_type); +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_GetRandom(uint8_t* randomData, size_t dataLength); OEMCryptoResult OEMCrypto_RewrapDeviceRSAKey30( OEMCrypto_SESSION session, const uint32_t* unaligned_nonce, const uint8_t* encrypted_message_key, size_t encrypted_message_key_length, @@ -4205,7 +4953,7 @@ OEMCryptoResult OEMCrypto_RewrapDeviceRSAKey( const uint32_t* unaligned_nonce, const uint8_t* enc_rsa_key, size_t enc_rsa_key_length, const uint8_t* enc_rsa_key_iv, uint8_t* wrapped_rsa_key, size_t* wrapped_rsa_key_length); -OEMCryptoResult OEMCrypto_UpdateUsageTable(); +OEMCryptoResult OEMCrypto_UpdateUsageTable(void); OEMCryptoResult OEMCrypto_DeleteUsageEntry(OEMCrypto_SESSION, const uint8_t*, size_t, const uint8_t*, size_t, const uint8_t*, size_t); @@ -4223,104 +4971,27 @@ OEMCryptoResult OEMCrypto_GenerateDerivedKeys_V15( OEMCrypto_SESSION session, const uint8_t* mac_key_context, uint32_t mac_key_context_length, const uint8_t* enc_key_context, uint32_t enc_key_context_length); -OEMCryptoResult OEMCrypto_LoadEntitledContentKeys_V15( - OEMCrypto_SESSION session, const uint8_t* message, size_t message_length, - size_t num_keys, const OEMCrypto_EntitledContentKeyObject* key_array); - -// TODO: functions below will be updated in future CL. +typedef struct { + size_t encrypt; // number of 16 byte blocks to decrypt. + size_t skip; // number of 16 byte blocks to leave in clear. + size_t offset; // offset into the pattern in blocks for this call. +} OEMCrypto_CENCEncryptPatternDesc_V15; OEMCryptoResult OEMCrypto_DecryptCENC_V15( OEMCrypto_SESSION session, const uint8_t* data_addr, size_t data_length, bool is_encrypted, const uint8_t* iv, size_t block_offset, // used for CTR "cenc" mode only. - OEMCrypto_DestBufferDesc* out_buffer, - const OEMCrypto_CENCEncryptPatternDesc* pattern, uint8_t subsample_flags); + OEMCrypto_DestBufferDesc* out_buffer_descriptor, + const OEMCrypto_CENCEncryptPatternDesc_V15* pattern, + uint8_t subsample_flags); OEMCryptoResult OEMCrypto_GetOEMPublicCertificate_V15( OEMCrypto_SESSION session, uint8_t* public_cert, size_t* public_cert_length); - +OEMCryptoResult OEMCrypto_LoadDeviceRSAKey(OEMCrypto_SESSION session, + const uint8_t* wrapped_rsa_key, + size_t wrapped_rsa_key_length); /****************************************************************************/ /****************************************************************************/ -/* XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX */ -// Hand added functions. This section will be replaced after docs have been -// updated. -size_t OEMCrypto_MaximumUsageTableHeaderSize(); - -OEMCryptoResult OEMCrypto_SignLicenseRequest( - OEMCrypto_SESSION session, const uint8_t* protobuf_message, - size_t protobuf_message_length, uint8_t* core_message, - size_t* core_message_length, uint8_t* signature, size_t* signature_length); - -OEMCryptoResult OEMCrypto_SignRenewalRequest( - OEMCrypto_SESSION session, const uint8_t* protobuf_message, - size_t protobuf_message_length, uint8_t* core_message, - size_t* core_message_length, uint8_t* signature, size_t* signature_length); - -OEMCryptoResult OEMCrypto_SignProvisioningRequest( - OEMCrypto_SESSION session, const uint8_t* protobuf_message, - size_t protobuf_message_length, uint8_t* core_message, - size_t* core_message_length, uint8_t* signature, size_t* signature_length); - -// TODO: functions below have not yet added. -OEMCryptoResult OEMCrypto_LoadLicense(OEMCrypto_SESSION session, - const uint8_t* protobuf_message, - size_t protobuf_message_length, - const uint8_t* core_message, - size_t core_message_length, - const uint8_t* signature, - size_t signature_length); - -OEMCryptoResult OEMCrypto_ReloadLicense( - OEMCrypto_SESSION session, const uint8_t* protobuf_message, - size_t protobuf_message_length, const uint8_t* core_message, - size_t core_message_length, const uint8_t* signature, - size_t signature_length, bool v15_license); - -OEMCryptoResult OEMCrypto_LoadRenewal(OEMCrypto_SESSION session, - const uint8_t* protobuf_message, - size_t protobuf_message_length, - const uint8_t* core_message, - size_t core_message_length, - const uint8_t* signature, - size_t signature_length); - -OEMCryptoResult OEMCrypto_LoadProvisioning( - OEMCrypto_SESSION session, const uint8_t* protobuf_message, - size_t protobuf_message_length, const uint8_t* core_message, - size_t core_message_length, const uint8_t* signature, - size_t signature_length, const uint8_t* wrapped_private_key, - size_t* wrapped_private_key_length); - -OEMCryptoResult OEMCrypto_LoadOEMPrivateKey(OEMCrypto_SESSION session); - -OEMCryptoResult OEMCrypto_GetOEMPublicCertificate(uint8_t* public_cert, - size_t* public_cert_length); - -// TODO: move up to top and add comments. -typedef struct { - const uint8_t* input_data; // source for encrypted data. - size_t input_data_length; // length of encrypted data. - OEMCrypto_DestBufferDesc output; // destination for clear data. -} InputOutputPair; -typedef struct { - size_t num_bytes_clear; - size_t num_bytes_encrypted; -} SubSampleDescription; -typedef struct { - InputOutputPair buffers; // The source and destination for this sample. - const uint8_t iv[16]; // The IV for the initial subsample. - const SubSampleDescription* subsamples; // an array of subsamples. - size_t subsamples_length; // the number of elements in subsample. -} SampleDescription; - -OEMCryptoResult OEMCrypto_DecryptCENC_TODO( - OEMCrypto_SESSION session, - const SampleDescription* samples, // an array of samples. - size_t sample_length, // the number of samples. - const OEMCrypto_CENCEncryptPatternDesc* pattern); - -// End of hand added functions. -/* XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX */ #ifdef __cplusplus } diff --git a/oemcrypto/include/level3.h b/oemcrypto/include/level3.h index 49031fb..f71584b 100644 --- a/oemcrypto/include/level3.h +++ b/oemcrypto/include/level3.h @@ -187,15 +187,12 @@ OEMCryptoResult Level3_QueryKeyControl(OEMCrypto_SESSION session, size_t key_id_length, uint8_t* key_control_block, size_t* key_control_block_length); -OEMCryptoResult Level3_DecryptCENC(OEMCrypto_SESSION session, - const uint8_t *data_addr, - size_t data_length, - bool is_encrypted, - const uint8_t *iv, - size_t block_offset, - OEMCrypto_DestBufferDesc* out_buffer, - const OEMCrypto_CENCEncryptPatternDesc* pattern, - uint8_t subsample_flags); +OEMCryptoResult Level3_DecryptCENC( + OEMCrypto_SESSION session, const uint8_t* data_addr, size_t data_length, + bool is_encrypted, const uint8_t* iv, size_t block_offset, + OEMCrypto_DestBufferDesc* out_buffer_descriptor, + const OEMCrypto_CENCEncryptPatternDesc_V15* pattern, + uint8_t subsample_flags); OEMCryptoResult Level3_InstallKeyboxOrOEMCert(const uint8_t* rot, size_t rotLength); OEMCryptoResult Level3_IsKeyboxOrOEMCertValid(void); @@ -371,11 +368,10 @@ OEMCryptoResult Level3_RefreshKeys( OEMCryptoResult Level3_LoadEntitledContentKeys( OEMCrypto_SESSION session, const uint8_t* message, size_t message_length, size_t num_keys, const OEMCrypto_EntitledContentKeyObject* key_array); -OEMCryptoResult Level3_CopyBuffer(OEMCrypto_SESSION session, - const uint8_t *data_addr, - size_t data_length, - OEMCrypto_DestBufferDesc* out_buffer, - uint8_t subsample_flags); +OEMCryptoResult Level3_CopyBuffer( + OEMCrypto_SESSION session, const uint8_t* data_addr, size_t data_length, + const OEMCrypto_DestBufferDesc* out_buffer_descriptor, + uint8_t subsample_flags); // The following are specific to Google's Level 3 implementation and are not // required. diff --git a/oemcrypto/include/oemcrypto_types.h b/oemcrypto/include/oemcrypto_types.h index 2bff9d7..d6aecd2 100644 --- a/oemcrypto/include/oemcrypto_types.h +++ b/oemcrypto/include/oemcrypto_types.h @@ -23,34 +23,47 @@ typedef struct WidevineKeybox { // 128 bytes total. uint8_t crc_[4]; } WidevineKeybox; +/* + * SRM_Restriction_Data + * + * Structure passed into LoadKeys to specify required SRM version. + */ +typedef struct { + uint8_t verification[8]; // must be "HDCPDATA" + uint32_t minimum_srm_version; // version number. +} SRM_Restriction_Data; + +// clang-format off // Key Control Block Bit Masks: -const uint32_t kControlObserveDataPath = (1<<31); -const uint32_t kControlObserveHDCP = (1<<30); -const uint32_t kControlObserveCGMS = (1<<29); -const uint32_t kControlRequireAntiRollbackHardware = (1<<28); -const uint32_t kControlAllowHashVerification = (1<<24); -const uint32_t kSharedLicense = (1<<23); -const uint32_t kControlSRMVersionRequired = (1<<22); -const uint32_t kControlDisableAnalogOutput = (1<<21); -const uint32_t kControlSecurityPatchLevelShift = 15; -const uint32_t kControlSecurityPatchLevelMask = - (0x3F<nonce and + * nonce_values->session_id are the same as those in the message. If + * verification fails, then it shall return OEMCrypto_ERROR_INVALID_NONCE. + * + * If initial_license_load is false, and nonce_required is true, then + * ODK_ParseLicense will set the values in nonce_values from those in the + * message. + * + * The function ODK_ParseLicense will verify each substring points to a + * location in the message body. The message body is the buffer starting at + * message + core_message_length with size message_length - + * core_message_length. + * + * If initial_license_load is true, then ODK_ParseLicense shall verify that + * hash matches request_hash in the parsed license. If verification fails, + * then it shall return ODK_ERROR_CORE_MESSAGE. + * + * If usage_entry_present is true, then ODK_ParseLicense shall verify that + * the pst in the license has a nonzero length. * * Parameters: - * [in] message: pointer to license response message - * [in] message_length: length of the license response - * [in] api_version: should be the same as OEMCrypto_APIVersion - * [in] nonce: the last nonce generated by OEMCrypto_GenerateNonce - * [in] session_id: the current session id. - * [in] initial_license_load: - * true when called for OEMCrypto_LoadLicense - * false when called for OEMCrypto_ReloadLicense - * [in] usage_entry_present: - * whether the session has a new usage entry associated with it created via - * OEMCrypto_CreateNewUsageEntry - * [in] max_num_keys: - * the maximum size of the array key_array. - * For many implementations, this is a compile time constant - * [out] parsed_license: destination struct for parsed output + * [in] message: pointer to the message buffer. + * [in] message_length: length of the entire message buffer. + * [in] core_message_size: length of the core message, at the beginning of + * the message buffer. + * [in] initial_license_load: true when called for OEMCrypto_LoadLicense and + * false when called for OEMCrypto_ReloadLicense. + * [in] usage_entry_present: true if the session has a new usage entry + * associated with it created via OEMCrypto_CreateNewUsageEntry. + * [in/out] timer_limits: The session's timer limits. These will be updated. + * [in/out] clock_values: The session's clock values. These will be updated. + * [in/out] nonce_values: The session's nonce values. These will be updated. + * [out] parsed_license: the destination for the data. * * Returns: - * OEMCrypto_SUCCESS success - * ODK_ERROR_CORE_MESSAGE - * if the license response did not parse correctly, - * or there were other incorrect values. + * OEMCrypto_SUCCESS + * ODK_ERROR_CORE_MESSAGE if the message did not parse correctly, or there + * were other incorrect values. An error should be returned to the CDM + * layer. + * ODK_UNSUPPORTED_API + * OEMCrypto_ERROR_INVALID_NONCE + * + * Version: + * This method is new in version 16 of the API. */ -OEMCryptoResult ODK_ParseLicense(const uint8_t* message, size_t message_length, - uint32_t api_version, uint32_t nonce, - uint32_t session_id, bool initial_license_load, - bool usage_entry_present, size_t max_num_keys, - ODK_ParsedLicense* parsed_license); +OEMCryptoResult ODK_ParseLicense( + const uint8_t* message, size_t message_length, size_t core_message_length, + bool initial_license_load, bool usage_entry_present, + const uint8_t request_hash[ODK_SHA256_HASH_SIZE], + ODK_TimerLimits* timer_limits, ODK_ClockValues* clock_values, + ODK_NonceValues* nonce_values, ODK_ParsedLicense* parsed_license); /* * ODK_ParseRenewal * * Description: - * OEMCrypto will use ODK_ParseRenewal to parse and verify the renewal - * response. + * The function ODK_ParseRenewal will parse the message and verify. If the + * message does not parse correctly, an error of ODK_ERROR_CORE_MESSAGE is + * returned. * - * If ODK_ParseRenewal returns success, then the session's timers and clocks - * will be updated as described in the document "Timer and License Renewal - * Updates" and in "Widevine Modular DRM Version 16 Delta". If - * ODK_ParseRenewal returns an error, OEMCrypto returns the error to the CDM - * layer. + * ODK_ParseRenewal shall verify that all fields in nonce_values match those + * in the license. Otherwise it shall return OEMCrypto_ERROR_INVALID_NONCE. + * + * After parsing the message, this function updates the clock_values based on + * the timer_limits and the current system time. If playback may not + * continue, then ODK_TIMER_EXPIRED is returned. + * + * If playback may continue, a return value of ODK_SET_TIMER or + * ODK_TIMER_EXPIRED is returned. If the return value is ODK_SET_TIMER, then + * playback may continue until the timer expires. If the return value is + * ODK_DISABLE_TIMER, then playback time is not limited. + * + * If OEMCrypto uses a hardware timer, and this function returns + * ODK_SET_TIMER, then the timer should be set to the value pointed to by + * timer_value. * * Parameters: - * [in] message: pointer to renewal response message - * [in] message_length: length of the renewal response - * [in] api_version: should be the same as OEMCrypto_APIVersion - * [in] license_nonce: the nonce from the original license. - * [in] session_id: the current session id. - * [in] system_time: the current time on OEMCrypto's clock. + * [in] message: pointer to the message buffer. + * [in] message_length: length of the entire message buffer. + * [in] core_message_size: length of the core message, at the beginning of + * the message buffer. + * [in] nonce_values: pointer to the session's nonce data. + * [in] system_time_seconds: the current time on OEMCrypto's clock, in + * seconds. * [in] timer_limits: timer limits specified in the license. * [in/out] clock_values: the sessions clock values. - * [out] timer_value: - * set to the new timer value. - * Only used if the return value is ODK_SET_TIMER. + * [out] timer_value: set to the new timer value. Only used if the return + * value is ODK_SET_TIMER. This must be non-null if OEMCrypto uses a + * hardware timer. * * Returns: - * ODK_ERROR_CORE_MESSAGE - * if the renewal response did not parse correctly, - * or there were other incorrect values. - * ODK_SET_TIMER Success, reset timer to the specified timer value. - * ODK_DISABLE_TIMER Success, but disable timer. Allow Unlimited playback. - * ODK_TIMER_EXPIRED Disable timer. Playback is not allowed. + * ODK_ERROR_CORE_MESSAGE if the message did not parse correctly, or there + * were other incorrect values. An error should be returned to the CDM + * layer. + * 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_UNSUPPORTED_API + * OEMCrypto_ERROR_INVALID_NONCE + * + * Version: + * This method is new in version 16 of the API. */ OEMCryptoResult ODK_ParseRenewal(const uint8_t* message, size_t message_length, - uint32_t api_version, uint32_t license_nonce, - uint32_t session_id, uint64_t system_time, + size_t core_message_length, + const ODK_NonceValues* nonce_values, + uint64_t system_time_seconds, const ODK_TimerLimits* timer_limits, ODK_ClockValues* clock_values, uint64_t* timer_value); @@ -271,36 +531,49 @@ OEMCryptoResult ODK_ParseRenewal(const uint8_t* message, size_t message_length, * ODK_ParseProvisioning * * Description: - * OEMCrypto will use ODK_ParseProvisioning to parse and verify the - * provisioning response. + * The function ODK_ParseProvisioning will parse the message and verify the + * nonce values match those in the license. * - * After the provisioning response has been parsed, OEMCrypto does the same - * verification and data flow as the v15 functions - * OEMCrypto_RewrapDeviceRSAKey or OEMCrypto_RewrapDeviceRSAKey30 depending - * on if the device has a keybox (Provisioning 2.0) or has an OEM Certificate - * (Provisioning 3.0). + * If the message does not parse correctly, ODK_ParseProvisioning will return + * an error that OEMCrypto should return to the CDM layer above. + * + * If the api in the message is larger than 16, then ODK_UNSUPPORTED_API is + * returned. + * + * ODK_ParseProvisioning shall verify that nonce_values->nonce and + * nonce_values->session_id are the same as those in the message. Otherwise + * it shall return OEMCrypto_ERROR_INVALID_NONCE. + * + * The function ODK_ParseProvisioning will verify each substring points to a + * location in the message body. The message body is the buffer starting at + * message + core_message_length with size message_length - + * core_message_length. * * Parameters: - * [in] message: pointer to renewal response message - * [in] message_length: length of the renewal response - * [in] api_version: should be the same as OEMCrypto_APIVersion - * [in] nonce: the last nonce generated by OEMCrypto_GenerateNonce - * [in] session_id: the current session id. - * [in] device_id: - * For devices with a keybox, this is the device id from the keybox. - * For devices with an OEM Certificate, this is a device unique id string. - * [in] device_id_length: length of device_id, at most 64 bytes. - * [out] parsed_response: destination struct for parsed output + * [in] message: pointer to the message buffer. + * [in] message_length: length of the entire message buffer. + * [in] core_message_size: length of the core message, at the beginning of + * the message buffer. + * [in] nonce_values: pointer to the session's nonce data. + * [in] device_id: a pointer to a buffer containing the device ID of the + * device. The ODK function will verify it matches that in the message. + * [in] device_id_length: the length of the device ID. + * [out] parsed_response: destination for the parse data. * * Returns: - * OEMCrypto_SUCCESS success - * ODK_ERROR_CORE_MESSAGE - * if the provisioning response did not parse correctly, - * or there were other incorrect values. + * OEMCrypto_SUCCESS + * ODK_ERROR_CORE_MESSAGE if the message did not parse correctly, or there + * were other incorrect values. An error should be returned to the CDM + * layer. + * ODK_UNSUPPORTED_API + * OEMCrypto_ERROR_INVALID_NONCE + * + * Version: + * This method is new in version 16 of the API. */ OEMCryptoResult ODK_ParseProvisioning( - const uint8_t* message, size_t message_length, uint32_t api_version, - uint32_t nonce, uint32_t session_id, const uint8_t* device_id, + const uint8_t* message, size_t message_length, size_t core_message_length, + const ODK_NonceValues* nonce_values, const uint8_t* device_id, size_t device_id_length, ODK_ParsedProvisioning* parsed_response); #ifdef __cplusplus diff --git a/oemcrypto/odk/src/odk_serialize.h b/oemcrypto/odk/include/odk_serialize.h similarity index 69% rename from oemcrypto/odk/src/odk_serialize.h rename to oemcrypto/odk/include/odk_serialize.h index 28a10e8..c9488d7 100644 --- a/oemcrypto/odk/src/odk_serialize.h +++ b/oemcrypto/odk/include/odk_serialize.h @@ -17,27 +17,27 @@ extern "C" { #endif -void Pack_ODK_CoreMessage(Message* msg, ODK_CoreMessage const* obj); - +/* odk pack */ void Pack_ODK_PreparedLicense(Message* msg, ODK_PreparedLicense const* obj); - void Pack_ODK_RenewalMessage(Message* msg, ODK_RenewalMessage const* obj); - void Pack_ODK_ProvisioningMessage(Message* msg, ODK_ProvisioningMessage const* obj); -void Unpack_ODK_CoreMessage(Message* msg, ODK_CoreMessage* obj); -void Unpack_ODK_RenewalMessage(Message* msg, ODK_RenewalMessage* obj); -void Unpack_ODK_ProvisioningMessage(Message* msg, ODK_ProvisioningMessage* obj); -void Unpack_OEMCrypto_Substring(Message* msg, OEMCrypto_Substring* obj); -void Unpack_OEMCrypto_KeyObject(Message* msg, OEMCrypto_KeyObject* obj); -void Unpack_ODK_TimerLimits(Message* msg, ODK_TimerLimits* obj); -void Unpack_ODK_ParsedLicense(Message* msg, ODK_ParsedLicense* obj); -void Unpack_ODK_ParsedProvisioning(Message* msg, ODK_ParsedProvisioning* obj); +/* odk unpack */ void Unpack_ODK_LicenseResponse(Message* msg, ODK_LicenseResponse* obj); +void Unpack_ODK_RenewalMessage(Message* msg, ODK_RenewalMessage* obj); void Unpack_ODK_ProvisioningResponse(Message* msg, ODK_ProvisioningResponse* obj); +/* kdo pack */ +void Pack_ODK_LicenseResponse(Message* msg, ODK_LicenseResponse const* obj); +void Pack_ODK_ProvisioningResponse(Message* msg, + ODK_ProvisioningResponse const* obj); + +/* kdo unpack */ +void Unpack_ODK_PreparedLicense(Message* msg, ODK_PreparedLicense* obj); +void Unpack_ODK_ProvisioningMessage(Message* msg, ODK_ProvisioningMessage* obj); + #ifdef __cplusplus } // extern "C" #endif diff --git a/oemcrypto/odk/include/odk_structs.h b/oemcrypto/odk/include/odk_structs.h index 80753ee..82f971c 100644 --- a/oemcrypto/odk/include/odk_structs.h +++ b/oemcrypto/odk/include/odk_structs.h @@ -11,6 +11,8 @@ #include "OEMCryptoCENC.h" #define ODK_MAX_NUM_KEYS 32 +#define ODK_DEVICE_ID_LEN_MAX 64 +#define ODK_SHA256_HASH_SIZE 32 /* * ODK_TimerLimits is filled out by the function ODK_ParseLicense. @@ -22,11 +24,11 @@ */ typedef struct { uint32_t /*boolean*/ soft_expiry; - uint64_t earliest_playback_start_seconds; - uint64_t latest_playback_start_seconds; - uint64_t initial_playback_duration_seconds; - uint64_t renewal_playback_duration_seconds; - uint64_t license_duration_seconds; + uint64_t earliest_playback_start_seconds; // seconds since license signed. + uint64_t latest_playback_start_seconds; // seconds since license signed. + uint64_t initial_playback_duration_seconds; // seconds since playback start. + uint64_t renewal_playback_duration_seconds; // seconds since renewal signed. + uint64_t license_duration_seconds; // seconds since license signed. } ODK_TimerLimits; /* @@ -37,9 +39,10 @@ typedef struct { OEMCrypto_Substring enc_mac_keys; OEMCrypto_Substring pst; OEMCrypto_Substring srm_restriction_data; - uint32_t license_type; + uint32_t /* OEMCrypto_LicenseType */ license_type; uint32_t nonce_required; ODK_TimerLimits timer_limits; + uint8_t request_hash[ODK_SHA256_HASH_SIZE]; uint32_t key_array_length; /* num_keys */ OEMCrypto_KeyObject key_array[ODK_MAX_NUM_KEYS]; } ODK_ParsedLicense; @@ -73,4 +76,21 @@ typedef struct { enum OEMCrypto_Usage_Entry_Status status; } ODK_ClockValues; +/* + * ODK_NonceValues are used to match a license or provisioning request to a + * license or provisioning response. For this reason, the api_version might be + * lower than that supported by OEMCrypto. The api_version matches the version + * of the license. Similarly the nonce and session_id match the session that + * generated the license request. For an offline license, these might not match + * the session that is loading the license. We use the nonce to prevent a + * license from being replayed. By also including a session_id in the license + * request and license response, we prevent an attack using the birthday paradox + * to generate nonce collisions on a single device. + */ +typedef struct { + uint32_t api_version; + uint32_t nonce; + uint32_t session_id; +} ODK_NonceValues; + #endif // ODK_STRUCTS_H_ diff --git a/oemcrypto/odk/src/odk_structs_priv.h b/oemcrypto/odk/include/odk_structs_priv.h similarity index 75% rename from oemcrypto/odk/src/odk_structs_priv.h rename to oemcrypto/odk/include/odk_structs_priv.h index c8d5b9c..52776cb 100644 --- a/oemcrypto/odk/src/odk_structs_priv.h +++ b/oemcrypto/odk/include/odk_structs_priv.h @@ -11,12 +11,19 @@ #include "OEMCryptoCENC.h" #include "odk_structs.h" +typedef enum { + ODK_License_Request_Type = 1, + ODK_License_Response_Type = 2, + ODK_Renewal_Request_Type = 3, + ODK_Renewal_Response_Type = 4, + ODK_Provisioning_Request_Type = 5, + ODK_Provisioning_Response_Type = 6, +} ODK_MessageType; + typedef struct { uint32_t message_type; uint32_t message_length; - uint32_t api_version; - uint32_t nonce; - uint32_t session_id; + ODK_NonceValues nonce_values; } ODK_CoreMessage; typedef struct { @@ -31,13 +38,12 @@ typedef struct { typedef struct { ODK_CoreMessage core_message; uint32_t device_id_length; - uint8_t device_id[64]; + uint8_t device_id[ODK_DEVICE_ID_LEN_MAX]; } ODK_ProvisioningMessage; typedef struct { ODK_CoreMessage core_message; ODK_ParsedLicense* parsed_license; - size_t max_num_keys; } ODK_LicenseResponse; typedef struct { diff --git a/oemcrypto/odk/include/odk_timer.h b/oemcrypto/odk/include/odk_timer.h deleted file mode 100644 index aa237d7..0000000 --- a/oemcrypto/odk/include/odk_timer.h +++ /dev/null @@ -1,59 +0,0 @@ -/* - * Copyright 2019 Google LLC. All Rights Reserved. This file and proprietary - * source code may only be used and distributed under the Widevine Master - * License Agreement. - */ - -/********************************************************************* - * odk_timer.h - * - * OEMCrypto v16 Timer and Renewal Functions - * - *********************************************************************/ - -#ifndef ODK_TIMER_H_ -#define ODK_TIMER_H_ - -#include -#include "OEMCryptoCENC.h" -#include "odk_structs.h" - -#ifdef __cplusplus -extern "C" { -#endif - -/* - Documentation to be added later. Hopefully automaically from doc. - */ -void ODK_InitializeClockValues(ODK_ClockValues* clock_values, - uint64_t system_time_seconds); - -/* - Documentation to be added later. Hopefully automaically from doc. - */ -void ODK_ReloadClockValues(ODK_ClockValues* clock_values, - uint64_t time_of_license_signed, - uint64_t time_of_first_decrypt, - uint64_t time_of_last_decrypt, - enum OEMCrypto_Usage_Entry_Status status, - uint64_t system_time_seconds); - -/* - Documentation to be added later. Hopefully automaically from doc. - */ -uint32_t ODK_AttemptFirstPlayback(uint64_t system_time_seconds, - const ODK_TimerLimits* timer_limits, - ODK_ClockValues* clock_values, - uint64_t* timer_value); -/* - Documentation to be added later. Hopefully automaically from doc. - */ -OEMCryptoResult ODK_UpdateLastPlaybackTime(const ODK_TimerLimits* timer_limits, - uint64_t system_time_seconds, - ODK_ClockValues* clock_values); - -#ifdef __cplusplus -} -#endif - -#endif /* ODK_TIMER_H_ */ diff --git a/oemcrypto/odk/src/serialization_base.h b/oemcrypto/odk/include/serialization_base.h similarity index 96% rename from oemcrypto/odk/src/serialization_base.h rename to oemcrypto/odk/include/serialization_base.h index 3883615..a447aba 100644 --- a/oemcrypto/odk/src/serialization_base.h +++ b/oemcrypto/odk/include/serialization_base.h @@ -30,7 +30,7 @@ extern "C" { */ #define AllocateMessage(msg, blk) \ uint8_t blk[SIZE_OF_MESSAGE_STRUCT]; \ - *(msg) = (Message*)(message_block); + *(msg) = (Message*)(blk); typedef struct _Message Message; @@ -39,6 +39,7 @@ bool ValidMessage(Message* message); void Pack_uint32_t(Message* message, const uint32_t* value); void Pack_uint64_t(Message* message, const uint64_t* value); void PackArray(Message* message, const uint8_t* base, size_t size); +void Pack_OEMCrypto_Substring(Message* msg, const OEMCrypto_Substring* obj); void Unpack_uint32_t(Message* message, uint32_t* value); void Unpack_uint64_t(Message* message, uint64_t* value); diff --git a/oemcrypto/odk/kdo/include/oec_util.h b/oemcrypto/odk/kdo/include/oec_util.h new file mode 100644 index 0000000..20bee97 --- /dev/null +++ b/oemcrypto/odk/kdo/include/oec_util.h @@ -0,0 +1,172 @@ +/* + * Copyright 2019 Google LLC. All Rights Reserved. This file and proprietary + * source code may only be used and distributed under the Widevine Master + * License Agreement. + */ + +// clang-format off +/********************************************************************* + * oec_util.h + * + * OEMCrypto v16 Core Message Serialization library counterpart (a.k.a. KDO) + * + * For Widevine Modular DRM, there are six message types between a server and + * a client device: license request and response, provisioning request and + * response, and renewal request and response. + * + * In OEMCrypto v15 and earlier, messages from the server were parsed by the + * CDM layer above OEMCrypto; the CDM in turn gave OEMCrypto a collection of + * pointers to protected data within the message. However, the pointers + * themselves were not signed by the server. + * + * Starting from OEMCrypto v16, all fields used by OEMCrypto in each of these + * messages have been identified in the document "Widevine Core Message + * Serialization". These fields are called the core of the message. Core + * message fields are (de)serialized using the ODK, a C library provided by + * Widevine. OEMCrypto will parse and verify the core of the message with + * help from the ODK. + * + * The KDO library is the counterpart of ODK used in the CDM & Widevine + * servers. For each message type generated by the ODK, KDO provides a + * corresponding parser. For each message type to be parsed by the ODK, + * KDO provides a corresponding writer. + * + * Table: ODK vs KDO (s: serialize; d: deserialize) + * +----------------------------------------+------------------------------------+ + * | ODK | KDO | + * +---+------------------------------------+---+--------------------------------+ + * | s | ODK_PrepareCoreLicenseRequest | d | ParseLicenseRequest | + * | +------------------------------------+ +--------------------------------+ + * | | ODK_PrepareCoreRenewalRequest | | ParseRenewalRequest | + * | +------------------------------------+ +--------------------------------+ + * | | ODK_PrepareCoreProvisioningRequest | | ParseProvisioningRequest | + * +---+------------------------------------+---+--------------------------------+ + * | d | ODK_ParseLicense | s | CreateCoreLicenseResponse | + * | +------------------------------------+ +--------------------------------+ + * | | ODK_ParseRenewal | | CreateCoreRenewalResponse | + * | +------------------------------------+ +--------------------------------+ + * | | ODK_ParseProvisioning | | CreateCoreProvisioningResponse | + * +---+------------------------------------+---+--------------------------------+ + * + *********************************************************************/ +// clang-format on + +#ifndef OEC_UTIL_H_ +#define OEC_UTIL_H_ + +#include +#include + +#include "odk_structs.h" + +using namespace std; + +namespace oec_util { + +// @ input/output structs + +/** + * Output structure for ParseLicenseRequest + * Input structure for CreateCoreLicenseResponse + */ +struct ODK_LicenseRequest { + uint32_t api_version; + uint32_t nonce; + uint32_t session_id; +}; + +/** + * Output structure for ParseRenewalRequest + * Input structure for CreateCoreRenewalResponse + */ +struct ODK_RenewalRequest { + uint32_t api_version; + uint32_t nonce; + uint32_t session_id; + uint64_t playback_time; +}; + +/** + * Output structure for ParseProvisioningRequest + * Input structure for CreateCoreProvisioningResponse + */ +struct ODK_ProvisioningRequest { + uint32_t api_version; + uint32_t nonce; + uint32_t session_id; + string device_id; +}; + +// @ public parse request (deserializer) functions + +/** + * Counterpart (deserializer) of ODK_PrepareCoreLicenseRequest (serializer) + * + * Parameters: + * [in] oemcrypto_core_message + * [out] core_license_request + */ +bool ParseLicenseRequest(const string& oemcrypto_core_message, + ODK_LicenseRequest* core_license_request); + +/** + * Counterpart (deserializer) of ODK_PrepareCoreRenewalRequest (serializer) + * + * Parameters: + * [in] oemcrypto_core_message + * [out] core_renewal_request + */ +bool ParseRenewalRequest(const string& oemcrypto_core_message, + ODK_RenewalRequest* core_renewal_request); + +/** + * Counterpart (deserializer) of ODK_PrepareCoreProvisioningRequest (serializer) + * + * Parameters: + * [in] oemcrypto_core_message + * [out] core_provisioning_request + */ +bool ParseProvisioningRequest( + const string& oemcrypto_core_message, + ODK_ProvisioningRequest* core_provisioning_request); + +// @ public create response (serializer) functions + +/** + * Counterpart (serializer) of ODK_ParseLicense (deserializer) + * struct-input variant + * + * Parameters: + * [in] parsed_lic + * [in] core_request + * [out] oemcrypto_core_message + */ +bool CreateCoreLicenseResponse(const ODK_ParsedLicense& parsed_lic, + const ODK_LicenseRequest& core_request, + string* oemcrypto_core_message); + +/** + * Counterpart (serializer) of ODK_ParseRenewal (deserializer) + * + * Parameters: + * [in] core_request + * [out] oemcrypto_core_message + */ +bool CreateCoreRenewalResponse(const ODK_RenewalRequest& core_request, + string* oemcrypto_core_message); + +/** + * Counterpart (serializer) of ODK_ParseProvisioning (deserializer) + * struct-input variant + * + * Parameters: + * [in] parsed_prov + * [in] core_request + * [out] oemcrypto_core_message + */ +bool CreateCoreProvisioningResponse(const ODK_ParsedProvisioning& parsed_prov, + const ODK_ProvisioningRequest& core_request, + string* oemcrypto_core_message); +} // namespace oec_util + +#endif // OEC_UTIL_H_ diff --git a/oemcrypto/odk/kdo/include/oec_util_proto.h b/oemcrypto/odk/kdo/include/oec_util_proto.h new file mode 100644 index 0000000..f9d8017 --- /dev/null +++ b/oemcrypto/odk/kdo/include/oec_util_proto.h @@ -0,0 +1,59 @@ +/* + * Copyright 2019 Google LLC. All Rights Reserved. This file and proprietary + * source code may only be used and distributed under the Widevine Master + * License Agreement. + */ + +/********************************************************************* + * oec_util_proto.h + * + * These functions are an extension of those found in oec_util.h. The + * difference is that these use the license and provisioning messages + * in protobuf format to create the core message. + *********************************************************************/ + +#ifndef OEC_UTIL_PROTO_H_ +#define OEC_UTIL_PROTO_H_ + +#include +#include + +#include "license_protocol.pb.h" +#include "oec_util.h" + +using namespace std; +using video_widevine::License; +using video_widevine::License_KeyContainer; + +namespace oec_util { + +// @ public create response (serializer) functions + +/** + * Counterpart (serializer) of ODK_ParseLicense (deserializer) + * + * Parameters: + * [in] license + * [in] core_request + * [out] oemcrypto_core_message + */ +bool CreateCoreLicenseResponse(const video_widevine::License& license, + const ODK_LicenseRequest& core_request, + string* oemcrypto_core_message); + +/** + * Counterpart (serializer) of ODK_ParseProvisioning (deserializer) + * + * Parameters: + * [in] provisioning_response + * [in] core_request + * [out] oemcrypto_core_message + */ +bool CreateCoreProvisioningResponse( + const video_widevine::ProvisioningResponse& provisioning_response, + const ODK_ProvisioningRequest& core_request, + string* oemcrypto_core_message); + +} // namespace oec_util + +#endif // OEC_UTIL_PROTO_H_ diff --git a/oemcrypto/odk/kdo/oec_util.gypi b/oemcrypto/odk/kdo/oec_util.gypi new file mode 100644 index 0000000..19805f1 --- /dev/null +++ b/oemcrypto/odk/kdo/oec_util.gypi @@ -0,0 +1,16 @@ +# Copyright 2019 Google LLC. All Rights Reserved. This file and proprietary +# source code may only be used and distributed under the Widevine Master License +# Agreement. + +{ + 'sources': [ + 'src/oec_util.cpp', + 'src/oec_util_proto.cpp', + ], + 'include_dirs': [ + '../src', + '../include', + 'include', + ], +} + diff --git a/oemcrypto/odk/kdo/src/oec_util.cpp b/oemcrypto/odk/kdo/src/oec_util.cpp new file mode 100644 index 0000000..affd979 --- /dev/null +++ b/oemcrypto/odk/kdo/src/oec_util.cpp @@ -0,0 +1,209 @@ +/* + * Copyright 2019 Google LLC. All Rights Reserved. This file and proprietary + * source code may only be used and distributed under the Widevine Master + * License Agreement. + */ + +#include "oec_util.h" + +#include +#include +#include +#include +#include +#include + +#include "odk_overflow.h" +#include "odk_serialize.h" +#include "odk_structs.h" +#include "odk_structs_priv.h" +#include "oemcrypto_types.h" +#include "serialization_base.h" + +using namespace oec_util; + +namespace oec_util { + +namespace { + +/* @ private functions */ + +const int CURRENT_OEC_VERSION = 16; + +/** + * Template for parsing requests + * + * Template arguments: + * S: kdo output struct + * T: struct serialized by odk + * U: auto-generated deserializing function for |T| + */ +template +bool ParseRequest(uint32_t message_type, const string& oemcrypto_core_message, + S* core_request, T* prepared, const U unpacker) { + if (!core_request) { + return false; + } + + const uint8_t* buf = + reinterpret_cast(oemcrypto_core_message.c_str()); + size_t buf_length = oemcrypto_core_message.size(); + + Message* msg = NULL; + AllocateMessage(&msg, message_block); + InitMessage(msg, const_cast(buf), buf_length); + SetSize(msg, buf_length); + + unpacker(msg, prepared); + if (!ValidMessage(msg)) { + return false; + } + + const auto& core_message = prepared->core_message; + core_request->api_version = core_message.nonce_values.api_version; + core_request->nonce = core_message.nonce_values.nonce; + core_request->session_id = core_message.nonce_values.session_id; + return core_message.message_type == message_type && + core_message.message_length == GetOffset(msg) && + core_request->api_version == CURRENT_OEC_VERSION; +} + +/** + * Template for parsing requests + * + * Template arguments: + * T: struct to be deserialized by odk + * S: kdo input struct + * P: auto-generated serializing function for |T| + */ +template +bool CreateResponse(uint32_t message_type, const S& core_request, + string* oemcrypto_core_message, T& response, + const P& packer) { + if (!oemcrypto_core_message) { + return false; + } + + auto* header = reinterpret_cast(&response); + header->message_type = message_type; + header->nonce_values.api_version = core_request.api_version; + header->nonce_values.nonce = core_request.nonce; + header->nonce_values.session_id = core_request.session_id; + + uint8_t buf[2048] = {0}; + Message* msg = NULL; + AllocateMessage(&msg, message_block); + InitMessage(msg, buf, sizeof(buf)); + packer(msg, &response); + if (!ValidMessage(msg)) { + return false; + } + + uint32_t message_length = GetSize(msg); + InitMessage(msg, buf + sizeof(header->message_type), + sizeof(header->message_length)); + Pack_uint32_t(msg, &message_length); + oemcrypto_core_message->assign(reinterpret_cast(buf), + message_length); + return true; +} + +bool CopyDeviceId(ODK_ProvisioningResponse& dest, + const ODK_ProvisioningRequest& src) { + auto& core_provisioning = dest.core_provisioning; + const string& device_id = src.device_id; + core_provisioning.device_id_length = device_id.size(); + if (core_provisioning.device_id_length > + sizeof(core_provisioning.device_id)) { + return false; + } + memset(core_provisioning.device_id, 0, sizeof(core_provisioning.device_id)); + memcpy(core_provisioning.device_id, device_id.data(), + core_provisioning.device_id_length); + return true; +} + +} // namespace + +// @ public parse request (deserializer) functions + +bool ParseLicenseRequest(const string& oemcrypto_core_message, + ODK_LicenseRequest* core_license_request) { + const auto unpacker = Unpack_ODK_PreparedLicense; + ODK_PreparedLicense prepared_license = {}; + return ParseRequest(ODK_License_Request_Type, oemcrypto_core_message, + core_license_request, &prepared_license, unpacker); +} + +bool ParseRenewalRequest(const string& oemcrypto_core_message, + ODK_RenewalRequest* core_renewal_request) { + const auto unpacker = Unpack_ODK_RenewalMessage; + ODK_RenewalMessage prepared_renewal = {}; + if (!ParseRequest(ODK_Renewal_Request_Type, oemcrypto_core_message, + core_renewal_request, &prepared_renewal, unpacker)) { + return false; + } + core_renewal_request->playback_time = prepared_renewal.playback_time; + return true; +} + +bool ParseProvisioningRequest( + const string& oemcrypto_core_message, + ODK_ProvisioningRequest* core_provisioning_request) { + const auto unpacker = Unpack_ODK_ProvisioningMessage; + ODK_ProvisioningMessage prepared_provision = {}; + if (!ParseRequest(ODK_Provisioning_Request_Type, oemcrypto_core_message, + core_provisioning_request, &prepared_provision, unpacker)) { + return false; + } + const uint8_t* device_id = prepared_provision.device_id; + const uint32_t device_id_length = prepared_provision.device_id_length; + if (device_id_length > ODK_DEVICE_ID_LEN_MAX) { + return false; + } + uint8_t zero[ODK_DEVICE_ID_LEN_MAX] = {}; + if (memcmp(zero, device_id + device_id_length, + ODK_DEVICE_ID_LEN_MAX - device_id_length)) { + return false; + } + core_provisioning_request->device_id.assign( + reinterpret_cast(device_id), device_id_length); + return true; +} + +// @ public create response functions + +bool CreateCoreLicenseResponse(const ODK_ParsedLicense& parsed_lic, + const ODK_LicenseRequest& core_request, + string* oemcrypto_core_message) { + ODK_LicenseResponse license_response{ + {}, const_cast(&parsed_lic)}; + return CreateResponse(ODK_License_Response_Type, core_request, + oemcrypto_core_message, license_response, + Pack_ODK_LicenseResponse); +} + +bool CreateCoreRenewalResponse(const ODK_RenewalRequest& core_request, + string* oemcrypto_core_message) { + ODK_RenewalMessage renewal{{}, core_request.playback_time}; + renewal.playback_time = core_request.playback_time; + return CreateResponse(ODK_Renewal_Response_Type, core_request, + oemcrypto_core_message, renewal, + Pack_ODK_RenewalMessage); +} + +bool CreateCoreProvisioningResponse(const ODK_ParsedProvisioning& parsed_prov, + const ODK_ProvisioningRequest& core_request, + string* oemcrypto_core_message) { + ODK_ProvisioningResponse prov_response{ + {}, const_cast(&parsed_prov)}; + if (!CopyDeviceId(prov_response, core_request)) { + return false; + } + + return CreateResponse(ODK_Provisioning_Response_Type, core_request, + oemcrypto_core_message, prov_response, + Pack_ODK_ProvisioningResponse); +} + +} // namespace oec_util diff --git a/oemcrypto/odk/kdo/src/oec_util_proto.cpp b/oemcrypto/odk/kdo/src/oec_util_proto.cpp new file mode 100644 index 0000000..02474d2 --- /dev/null +++ b/oemcrypto/odk/kdo/src/oec_util_proto.cpp @@ -0,0 +1,161 @@ +/* + * Copyright 2019 Google LLC. All Rights Reserved. This file and proprietary + * source code may only be used and distributed under the Widevine Master + * License Agreement. + */ + +#include "oec_util_proto.h" + +#include +#include +#include +#include +#include +#include + +#include "odk_overflow.h" +#include "odk_serialize.h" +#include "odk_structs.h" +#include "odk_structs_priv.h" +#include "oemcrypto_types.h" +#include "serialization_base.h" + +using namespace oec_util; + +namespace oec_util { + +namespace { + +/* @ private functions */ + +/** + * Extract OEMCrypto_Substring (offset, length) from serialized protobuf + * + * Parameters: + * message: serialized license protobuf + * field: substring value + */ +OEMCrypto_Substring GetOecSubstring(const std::string& message, + const std::string& field) { + OEMCrypto_Substring substring = {}; + size_t pos = message.find(field); + if (pos != std::string::npos) { + substring = OEMCrypto_Substring{pos, field.length()}; + } + return substring; +} + +OEMCrypto_KeyObject KeyContainerToOecKey(const string& proto, + const License::KeyContainer& k) { + OEMCrypto_KeyObject obj = {}; + obj.key_id = GetOecSubstring(proto, k.id()); + obj.key_data_iv = GetOecSubstring(proto, k.iv()); + // Strip off PKCS#5 padding - since we know the key is 16 or 32 bytes, + // the padding will always be 16 bytes. + const string& key_data = k.key(); + const size_t PKCS5_PADDING_SIZE = 16; + obj.key_data = GetOecSubstring( + proto, key_data.substr(0, std::max(PKCS5_PADDING_SIZE, key_data.size()) - + PKCS5_PADDING_SIZE)); + if (k.has_key_control()) { + const auto& key_control = k.key_control(); + obj.key_control_iv = GetOecSubstring(proto, key_control.iv()); + obj.key_control = GetOecSubstring(proto, key_control.key_control_block()); + } + return obj; +} + +} // namespace + +// @ public create response functions + +bool CreateCoreLicenseResponse(const video_widevine::License& lic, + const ODK_LicenseRequest& core_request, + string* oemcrypto_core_message) { + string proto; + if (!lic.SerializeToString(&proto)) { + return false; + } + ODK_ParsedLicense parsed_lic{}; + + for (int i = 0; i < lic.key_size(); ++i) { + const auto& k = lic.key(i); + switch (k.type()) { + case License_KeyContainer::SIGNING: { + parsed_lic.enc_mac_keys_iv = GetOecSubstring(proto, k.iv()); + // Strip off PKCS#5 padding + string mac_keys(k.key(), 2 * wvoec::MAC_KEY_SIZE); + parsed_lic.enc_mac_keys = GetOecSubstring(proto, mac_keys); + break; + } + case License_KeyContainer::CONTENT: { + if (parsed_lic.key_array_length >= ODK_MAX_NUM_KEYS) { + return false; + } + uint32_t& n = parsed_lic.key_array_length; + parsed_lic.key_array[n++] = KeyContainerToOecKey(proto, k); + break; + } + default: { + continue; + } + } + } + + const auto& license_id = lic.id(); + if (license_id.has_provider_session_token()) { + parsed_lic.pst = + GetOecSubstring(proto, license_id.provider_session_token()); + } + + if (lic.has_srm_requirement()) { + parsed_lic.srm_restriction_data = + GetOecSubstring(proto, lic.srm_requirement()); + } + + parsed_lic.license_type = license_id.type(); + // todo: nonce_required + const auto& policy = lic.policy(); + ODK_TimerLimits& timer_limits = parsed_lic.timer_limits; + timer_limits.soft_expiry = policy.soft_enforce_playback_duration(); + timer_limits.earliest_playback_start_seconds = 0; + timer_limits.latest_playback_start_seconds = + policy.license_duration_seconds(); + timer_limits.initial_playback_duration_seconds = + policy.playback_duration_seconds(); + timer_limits.renewal_playback_duration_seconds = + policy.playback_duration_seconds(); + timer_limits.license_duration_seconds = policy.license_duration_seconds(); + + return CreateCoreLicenseResponse(parsed_lic, core_request, + oemcrypto_core_message); +} + +bool CreateCoreProvisioningResponse( + const video_widevine::ProvisioningResponse& prov, + const ODK_ProvisioningRequest& core_request, + string* oemcrypto_core_message) { + ODK_ParsedProvisioning parsed_prov{}; + string proto; + if (!prov.SerializeToString(&proto)) { + return false; + } + + parsed_prov.key_type = 0; // todo: ECC or RSA + if (prov.has_device_rsa_key()) { + parsed_prov.enc_private_key = GetOecSubstring(proto, prov.device_rsa_key()); + } + if (prov.has_device_rsa_key_iv()) { + parsed_prov.enc_private_key_iv = + GetOecSubstring(proto, prov.device_rsa_key_iv()); + } + if (prov.has_wrapping_key()) { + parsed_prov.encrypted_message_key = + GetOecSubstring(proto, prov.wrapping_key()); + } + + return CreateCoreProvisioningResponse(parsed_prov, core_request, + oemcrypto_core_message); +} + +} // namespace oec_util diff --git a/oemcrypto/odk/src/odk.c b/oemcrypto/odk/src/odk.c index d91b260..c62f642 100644 --- a/oemcrypto/odk/src/odk.c +++ b/oemcrypto/odk/src/odk.c @@ -15,27 +15,18 @@ #include "odk_structs_priv.h" #include "serialization_base.h" -typedef enum { - ODK_License_Request_Type = 1, - ODK_License_Response_Type = 2, - ODK_Renewal_Request_Type = 3, - ODK_Renewal_Response_Type = 4, - ODK_Provisioning_Request_Type = 5, - ODK_Provisioning_Response_Type = 6, -} ODK_MessageType; - #define ODK_LICENSE_REQUEST_SIZE 20 #define ODK_RENEWAL_REQUEST_SIZE 28 #define ODK_PROVISIONING_REQUEST_SIZE 88 /* @ private odk functions */ -OEMCryptoResult ODK_PrepareRequest(uint8_t* buffer, size_t buffer_length, - size_t* core_message_length, - uint32_t message_type, uint32_t api_version, - uint32_t nonce, uint32_t session_id, - ODK_CoreMessage* core_message) { - if (!core_message_length || !core_message || +static OEMCryptoResult ODK_PrepareRequest(uint8_t* buffer, size_t buffer_length, + size_t* core_message_length, + uint32_t message_type, + const ODK_NonceValues* nonce_values, + ODK_CoreMessage* core_message) { + if (!nonce_values || !core_message_length || !core_message || *core_message_length > buffer_length) { return ODK_ERROR_CORE_MESSAGE; } @@ -44,7 +35,7 @@ OEMCryptoResult ODK_PrepareRequest(uint8_t* buffer, size_t buffer_length, AllocateMessage(&msg, message_block); InitMessage(msg, buffer, *core_message_length); *core_message = (ODK_CoreMessage){ - message_type, 0, api_version, nonce, session_id, + message_type, 0, *nonce_values, }; switch (message_type) { @@ -76,10 +67,11 @@ OEMCryptoResult ODK_PrepareRequest(uint8_t* buffer, size_t buffer_length, return OEMCrypto_SUCCESS; } -OEMCryptoResult ODK_ParseResponse(const uint8_t* buf, size_t message_length, - uint32_t message_type, uint32_t api_version, - uint32_t nonce, uint32_t session_id, - ODK_CoreMessage* const core_message) { +static OEMCryptoResult ODK_ParseResponse(const uint8_t* buf, + size_t message_length, + uint32_t message_type, + const ODK_NonceValues* nonce_values, + ODK_CoreMessage* const core_message) { Message* msg = NULL; AllocateMessage(&msg, message_block); InitMessage(msg, (uint8_t*)buf, message_length); @@ -106,12 +98,18 @@ OEMCryptoResult ODK_ParseResponse(const uint8_t* buf, size_t message_length, if (GetStatus(msg) != MESSAGE_STATUS_OK || message_type != core_message->message_type || - GetOffset(msg) != core_message->message_length || - api_version != core_message->api_version || - nonce != core_message->nonce || session_id != core_message->session_id) { + GetOffset(msg) != core_message->message_length) { return ODK_ERROR_CORE_MESSAGE; } + if (nonce_values) { + if (nonce_values->api_version != core_message->nonce_values.api_version || + nonce_values->nonce != core_message->nonce_values.nonce || + nonce_values->session_id != core_message->nonce_values.session_id) { + return ODK_ERROR_CORE_MESSAGE; + } + } + return OEMCrypto_SUCCESS; } @@ -121,33 +119,37 @@ OEMCryptoResult ODK_ParseResponse(const uint8_t* buf, size_t message_length, OEMCryptoResult ODK_PrepareCoreLicenseRequest( uint8_t* message, size_t message_length, size_t* core_message_length, - uint32_t api_version, uint32_t nonce, uint32_t session_id) { + const ODK_NonceValues* nonce_values) { ODK_PreparedLicense license_request = {0}; return ODK_PrepareRequest(message, message_length, core_message_length, - ODK_License_Request_Type, api_version, nonce, - session_id, &license_request.core_message); + ODK_License_Request_Type, nonce_values, + &license_request.core_message); } OEMCryptoResult ODK_PrepareCoreRenewalRequest( uint8_t* message, size_t message_length, size_t* core_message_length, - uint32_t api_version, uint32_t license_nonce, uint32_t session_id, + const ODK_NonceValues* nonce_values, const ODK_ClockValues* clock_values, uint64_t system_time_seconds) { - ODK_RenewalMessage renewal_request = {0}; + ODK_RenewalMessage renewal_request = { + {0}, + }; if (odk_sub_overflow_u64(system_time_seconds, clock_values->time_of_first_decrypt, &renewal_request.playback_time)) { return ODK_ERROR_CORE_MESSAGE; } - return ODK_PrepareRequest( - message, message_length, core_message_length, ODK_Renewal_Request_Type, - api_version, license_nonce, session_id, &renewal_request.core_message); + return ODK_PrepareRequest(message, message_length, core_message_length, + ODK_Renewal_Request_Type, nonce_values, + &renewal_request.core_message); } OEMCryptoResult ODK_PrepareCoreProvisioningRequest( uint8_t* message, size_t message_length, size_t* core_message_length, - uint32_t api_version, uint32_t nonce, uint32_t session_id, - const uint8_t* device_id, uint32_t device_id_length) { - ODK_ProvisioningMessage provisioning_request = {0}; + const ODK_NonceValues* nonce_values, + const uint8_t* device_id, size_t device_id_length) { + ODK_ProvisioningMessage provisioning_request = { + {0}, + }; if (device_id_length > sizeof(provisioning_request.device_id)) { return ODK_ERROR_CORE_MESSAGE; } @@ -156,46 +158,82 @@ OEMCryptoResult ODK_PrepareCoreProvisioningRequest( memcpy(provisioning_request.device_id, device_id, device_id_length); } return ODK_PrepareRequest(message, message_length, core_message_length, - ODK_Provisioning_Request_Type, api_version, nonce, - session_id, &provisioning_request.core_message); + ODK_Provisioning_Request_Type, nonce_values, + &provisioning_request.core_message); } /* @@ parse request functions */ OEMCryptoResult ODK_ParseLicense(const uint8_t* message, size_t message_length, - uint32_t api_version, uint32_t nonce, - uint32_t session_id, bool initial_license_load, - bool usage_entry_present, size_t max_num_keys, + size_t core_message_length, + bool initial_license_load, + bool usage_entry_present, + const uint8_t request_hash[ODK_SHA256_HASH_SIZE], + ODK_TimerLimits* timer_limits, + ODK_ClockValues* clock_values, + ODK_NonceValues* nonce_values, ODK_ParsedLicense* parsed_license) { - /* todo: check initial_license_load, usage_entry_present, and nonce_reqiured - */ - if (!parsed_license) { + if (!nonce_values || !parsed_license) { return ODK_ERROR_CORE_MESSAGE; } - ODK_LicenseResponse license_response = {{0}, parsed_license, max_num_keys}; + ODK_LicenseResponse license_response = {{0}, parsed_license}; OEMCryptoResult err = ODK_ParseResponse( - message, message_length, ODK_License_Response_Type, api_version, nonce, - session_id, &license_response.core_message); + message, message_length, ODK_License_Response_Type, NULL, + &license_response.core_message); + + if (err) { + return err; + } + + if (license_response.core_message.nonce_values.api_version != 16) { + return ODK_UNSUPPORTED_API; + } + + if (parsed_license->nonce_required) { + if (initial_license_load) { + if (nonce_values->nonce != license_response.core_message.nonce_values.nonce || + nonce_values->session_id != license_response.core_message.nonce_values.session_id) { + return OEMCrypto_ERROR_INVALID_NONCE; + } + } else { /* !initial_license_load */ + nonce_values->nonce = license_response.core_message.nonce_values.nonce; + nonce_values->session_id = license_response.core_message.nonce_values.session_id; + } + } + + if (initial_license_load && + memcmp(request_hash, parsed_license->request_hash, ODK_SHA256_HASH_SIZE)) { + return ODK_ERROR_CORE_MESSAGE; + } + + if (usage_entry_present) { + nonce_values->nonce = license_response.core_message.nonce_values.nonce; + nonce_values->session_id = license_response.core_message.nonce_values.session_id; + return err; + } return err; } OEMCryptoResult ODK_ParseRenewal(const uint8_t* message, size_t message_length, - uint32_t api_version, uint32_t license_nonce, - uint32_t session_id, uint64_t system_time, + size_t core_message_length, + const ODK_NonceValues* nonce_values, + uint64_t system_time, const ODK_TimerLimits* timer_limits, ODK_ClockValues* clock_values, uint64_t* timer_value) { - if (!timer_limits || !clock_values || !timer_value) { + if (!nonce_values || !timer_limits || !clock_values || !timer_value) { return ODK_ERROR_CORE_MESSAGE; } - ODK_RenewalMessage renewal_response = {0}; + ODK_RenewalMessage renewal_response = { + {0}, + }; OEMCryptoResult err = ODK_ParseResponse( - message, message_length, ODK_Renewal_Response_Type, api_version, - license_nonce, session_id, &renewal_response.core_message); + message, message_length, ODK_Renewal_Response_Type, nonce_values, + &renewal_response.core_message); if (err) { return err; @@ -236,23 +274,44 @@ OEMCryptoResult ODK_ParseRenewal(const uint8_t* message, size_t message_length, } OEMCryptoResult ODK_ParseProvisioning( - const uint8_t* message, size_t message_length, uint32_t api_version, - uint32_t nonce, uint32_t session_id, const uint8_t* device_id, + const uint8_t* message, size_t message_length, + size_t core_message_length, + const ODK_NonceValues* nonce_values, + const uint8_t* device_id, size_t device_id_length, ODK_ParsedProvisioning* parsed_response) { - if (!device_id || !parsed_response) { + if (!nonce_values || !device_id || !parsed_response) { return ODK_ERROR_CORE_MESSAGE; } - ODK_ProvisioningResponse provisioning_response = {{0}, parsed_response}; - OEMCryptoResult err = ODK_ParseResponse( - message, message_length, ODK_Provisioning_Response_Type, api_version, - nonce, session_id, &provisioning_response.core_provisioning.core_message); + ODK_ProvisioningResponse provisioning_response = {{ + {0}, + }, + parsed_response}; + if (device_id_length > ODK_DEVICE_ID_LEN_MAX) { + return ODK_ERROR_CORE_MESSAGE; + } - if (err || - memcmp(device_id, provisioning_response.core_provisioning.device_id, - device_id_length)) { + OEMCryptoResult err = ODK_ParseResponse( + message, message_length, ODK_Provisioning_Response_Type, + nonce_values, &provisioning_response.core_provisioning.core_message); + + if (err) { return err; } + if (memcmp(device_id, provisioning_response.core_provisioning.device_id, + device_id_length)) { + return ODK_ERROR_CORE_MESSAGE; + } + + uint8_t zero[ODK_DEVICE_ID_LEN_MAX] = {0}; + /* check bytes beyond device_id_length are 0 */ + if (memcmp( + zero, + provisioning_response.core_provisioning.device_id + device_id_length, + ODK_DEVICE_ID_LEN_MAX - device_id_length)) { + return ODK_ERROR_CORE_MESSAGE; + } + return OEMCrypto_SUCCESS; } diff --git a/oemcrypto/odk/src/odk_serialize.c b/oemcrypto/odk/src/odk_serialize.c index da220da..dab42be 100644 --- a/oemcrypto/odk/src/odk_serialize.c +++ b/oemcrypto/odk/src/odk_serialize.c @@ -11,14 +11,76 @@ #include "odk_structs_priv.h" #include "serialization_base.h" -void Pack_ODK_CoreMessage(Message* msg, ODK_CoreMessage const* obj) { - Pack_uint32_t(msg, (const uint32_t*)&obj->message_type); - Pack_uint32_t(msg, (const uint32_t*)&obj->message_length); - Pack_uint32_t(msg, (const uint32_t*)&obj->api_version); - Pack_uint32_t(msg, (const uint32_t*)&obj->nonce); - Pack_uint32_t(msg, (const uint32_t*)&obj->session_id); +/* @ serialize */ + +/* @@ private serialize */ + +static void Pack_ODK_NonceValues(Message* msg, ODK_NonceValues const* obj) { + Pack_uint32_t(msg, &obj->api_version); + Pack_uint32_t(msg, &obj->nonce); + Pack_uint32_t(msg, &obj->session_id); } +static void Pack_ODK_CoreMessage(Message* msg, ODK_CoreMessage const* obj) { + Pack_uint32_t(msg, &obj->message_type); + Pack_uint32_t(msg, &obj->message_length); + Pack_ODK_NonceValues(msg, &obj->nonce_values); +} + +static void Pack_OEMCrypto_KeyObject(Message* msg, + OEMCrypto_KeyObject const* obj) { + Pack_OEMCrypto_Substring(msg, (const OEMCrypto_Substring*)&obj->key_id); + Pack_OEMCrypto_Substring(msg, (const OEMCrypto_Substring*)&obj->key_data_iv); + Pack_OEMCrypto_Substring(msg, (const OEMCrypto_Substring*)&obj->key_data); + Pack_OEMCrypto_Substring(msg, + (const OEMCrypto_Substring*)&obj->key_control_iv); + Pack_OEMCrypto_Substring(msg, (const OEMCrypto_Substring*)&obj->key_control); +} + +static void Pack_ODK_TimerLimits(Message* msg, ODK_TimerLimits const* obj) { + Pack_uint32_t(msg, (const uint32_t*)&obj->soft_expiry); + Pack_uint64_t(msg, (const uint64_t*)&obj->earliest_playback_start_seconds); + Pack_uint64_t(msg, (const uint64_t*)&obj->latest_playback_start_seconds); + Pack_uint64_t(msg, (const uint64_t*)&obj->initial_playback_duration_seconds); + Pack_uint64_t(msg, (const uint64_t*)&obj->renewal_playback_duration_seconds); + Pack_uint64_t(msg, (const uint64_t*)&obj->license_duration_seconds); +} + +static void Pack_ODK_ParsedLicense(Message* msg, ODK_ParsedLicense const* obj) { + /* hand-coded */ + if (obj->key_array_length > ODK_MAX_NUM_KEYS) { + SetStatus(msg, MESSAGE_STATUS_OVERFLOW_ERROR); + return; + } + Pack_OEMCrypto_Substring(msg, + (const OEMCrypto_Substring*)&obj->enc_mac_keys_iv); + Pack_OEMCrypto_Substring(msg, (const OEMCrypto_Substring*)&obj->enc_mac_keys); + Pack_OEMCrypto_Substring(msg, (const OEMCrypto_Substring*)&obj->pst); + Pack_OEMCrypto_Substring( + msg, (const OEMCrypto_Substring*)&obj->srm_restriction_data); + Pack_uint32_t(msg, (const uint32_t*)&obj->license_type); + Pack_uint32_t(msg, (const uint32_t*)&obj->nonce_required); + Pack_ODK_TimerLimits(msg, (const ODK_TimerLimits*)&obj->timer_limits); + PackArray(msg, (const uint8_t*)&obj->request_hash[0], sizeof(obj->request_hash)); + Pack_uint32_t(msg, (const uint32_t*)&obj->key_array_length); + for (size_t i = 0; i < (size_t)obj->key_array_length; i++) { + Pack_OEMCrypto_KeyObject(msg, &obj->key_array[i]); + } +} + +static void Pack_ODK_ParsedProvisioning(Message* msg, + ODK_ParsedProvisioning const* obj) { + Pack_uint32_t(msg, (const uint32_t*)&obj->key_type); + Pack_OEMCrypto_Substring(msg, + (const OEMCrypto_Substring*)&obj->enc_private_key); + Pack_OEMCrypto_Substring( + msg, (const OEMCrypto_Substring*)&obj->enc_private_key_iv); + Pack_OEMCrypto_Substring( + msg, (const OEMCrypto_Substring*)&obj->encrypted_message_key); +} + +/* @@ odk serialize */ + void Pack_ODK_PreparedLicense(Message* msg, ODK_PreparedLicense const* obj) { Pack_ODK_CoreMessage(msg, (const ODK_CoreMessage*)&obj->core_message); } @@ -32,31 +94,41 @@ void Pack_ODK_ProvisioningMessage(Message* msg, ODK_ProvisioningMessage const* obj) { Pack_ODK_CoreMessage(msg, (const ODK_CoreMessage*)&obj->core_message); Pack_uint32_t(msg, (const uint32_t*)&obj->device_id_length); - PackArray(msg, (const uint8_t*)&obj->device_id[0], 64); + PackArray(msg, (const uint8_t*)&obj->device_id[0], sizeof(obj->device_id)); } -void Unpack_ODK_CoreMessage(Message* msg, ODK_CoreMessage* obj) { - Unpack_uint32_t(msg, (uint32_t*)&obj->message_type); - Unpack_uint32_t(msg, (uint32_t*)&obj->message_length); - Unpack_uint32_t(msg, (uint32_t*)&obj->api_version); - Unpack_uint32_t(msg, (uint32_t*)&obj->nonce); - Unpack_uint32_t(msg, (uint32_t*)&obj->session_id); - if (!ValidMessage(msg)) return; +/* @@ kdo serialize */ + +void Pack_ODK_LicenseResponse(Message* msg, ODK_LicenseResponse const* obj) { + Pack_ODK_CoreMessage(msg, (const ODK_CoreMessage*)&obj->core_message); + Pack_ODK_ParsedLicense(msg, (const ODK_ParsedLicense*)obj->parsed_license); } -void Unpack_ODK_RenewalMessage(Message* msg, ODK_RenewalMessage* obj) { - Unpack_ODK_CoreMessage(msg, (ODK_CoreMessage*)&obj->core_message); - Unpack_uint64_t(msg, (uint64_t*)&obj->playback_time); +void Pack_ODK_ProvisioningResponse(Message* msg, + ODK_ProvisioningResponse const* obj) { + Pack_ODK_ProvisioningMessage( + msg, (const ODK_ProvisioningMessage*)&obj->core_provisioning); + Pack_ODK_ParsedProvisioning( + msg, (const ODK_ParsedProvisioning*)obj->parsed_provisioning); } -void Unpack_ODK_ProvisioningMessage(Message* msg, - ODK_ProvisioningMessage* obj) { - Unpack_ODK_CoreMessage(msg, (ODK_CoreMessage*)&obj->core_message); - Unpack_uint32_t(msg, (uint32_t*)&obj->device_id_length); - UnpackArray(msg, (uint8_t*)&obj->device_id[0], 64); +/* @ deserialize */ + +/* @@ private deserialize */ + +static void Unpack_ODK_NonceValues(Message* msg, ODK_NonceValues* obj) { + Unpack_uint32_t(msg, &obj->api_version); + Unpack_uint32_t(msg, &obj->nonce); + Unpack_uint32_t(msg, &obj->session_id); } -void Unpack_OEMCrypto_KeyObject(Message* msg, OEMCrypto_KeyObject* obj) { +static void Unpack_ODK_CoreMessage(Message* msg, ODK_CoreMessage* obj) { + Unpack_uint32_t(msg, &obj->message_type); + Unpack_uint32_t(msg, &obj->message_length); + Unpack_ODK_NonceValues(msg, &obj->nonce_values); +} + +static void Unpack_OEMCrypto_KeyObject(Message* msg, OEMCrypto_KeyObject* obj) { Unpack_OEMCrypto_Substring(msg, (OEMCrypto_Substring*)&obj->key_id); Unpack_OEMCrypto_Substring(msg, (OEMCrypto_Substring*)&obj->key_data_iv); Unpack_OEMCrypto_Substring(msg, (OEMCrypto_Substring*)&obj->key_data); @@ -64,7 +136,7 @@ void Unpack_OEMCrypto_KeyObject(Message* msg, OEMCrypto_KeyObject* obj) { Unpack_OEMCrypto_Substring(msg, (OEMCrypto_Substring*)&obj->key_control); } -void Unpack_ODK_TimerLimits(Message* msg, ODK_TimerLimits* obj) { +static void Unpack_ODK_TimerLimits(Message* msg, ODK_TimerLimits* obj) { Unpack_uint32_t(msg, (uint32_t*)&obj->soft_expiry); Unpack_uint64_t(msg, (uint64_t*)&obj->earliest_playback_start_seconds); Unpack_uint64_t(msg, (uint64_t*)&obj->latest_playback_start_seconds); @@ -73,7 +145,7 @@ void Unpack_ODK_TimerLimits(Message* msg, ODK_TimerLimits* obj) { Unpack_uint64_t(msg, (uint64_t*)&obj->license_duration_seconds); } -void Unpack_ODK_ParsedLicense(Message* msg, ODK_ParsedLicense* obj) { +static void Unpack_ODK_ParsedLicense(Message* msg, ODK_ParsedLicense* obj) { Unpack_OEMCrypto_Substring(msg, (OEMCrypto_Substring*)&obj->enc_mac_keys_iv); Unpack_OEMCrypto_Substring(msg, (OEMCrypto_Substring*)&obj->enc_mac_keys); Unpack_OEMCrypto_Substring(msg, (OEMCrypto_Substring*)&obj->pst); @@ -82,17 +154,19 @@ void Unpack_ODK_ParsedLicense(Message* msg, ODK_ParsedLicense* obj) { Unpack_uint32_t(msg, (uint32_t*)&obj->license_type); Unpack_uint32_t(msg, (uint32_t*)&obj->nonce_required); Unpack_ODK_TimerLimits(msg, (ODK_TimerLimits*)&obj->timer_limits); + UnpackArray(msg, (uint8_t*)&obj->request_hash[0], sizeof(obj->request_hash)); Unpack_uint32_t(msg, (uint32_t*)&obj->key_array_length); if (obj->key_array_length > ODK_MAX_NUM_KEYS) { SetStatus(msg, MESSAGE_STATUS_OVERFLOW_ERROR); return; } - for (size_t i = 0; i < (size_t)obj->key_array_length; i++) { + for (uint32_t i = 0; i < obj->key_array_length; i++) { Unpack_OEMCrypto_KeyObject(msg, &obj->key_array[i]); } } -void Unpack_ODK_ParsedProvisioning(Message* msg, ODK_ParsedProvisioning* obj) { +static void Unpack_ODK_ParsedProvisioning(Message* msg, + ODK_ParsedProvisioning* obj) { Unpack_uint32_t(msg, (uint32_t*)&obj->key_type); Unpack_OEMCrypto_Substring(msg, (OEMCrypto_Substring*)&obj->enc_private_key); Unpack_OEMCrypto_Substring(msg, @@ -101,11 +175,31 @@ void Unpack_ODK_ParsedProvisioning(Message* msg, ODK_ParsedProvisioning* obj) { (OEMCrypto_Substring*)&obj->encrypted_message_key); } +/* @ kdo deserialize */ + +void Unpack_ODK_PreparedLicense(Message* msg, ODK_PreparedLicense* obj) { + Unpack_ODK_CoreMessage(msg, (ODK_CoreMessage*)&obj->core_message); +} + +void Unpack_ODK_ProvisioningMessage(Message* msg, + ODK_ProvisioningMessage* obj) { + Unpack_ODK_CoreMessage(msg, (ODK_CoreMessage*)&obj->core_message); + Unpack_uint32_t(msg, (uint32_t*)&obj->device_id_length); + UnpackArray(msg, (uint8_t*)&obj->device_id[0], sizeof(obj->device_id)); +} + +/* @@ odk deserialize */ + void Unpack_ODK_LicenseResponse(Message* msg, ODK_LicenseResponse* obj) { Unpack_ODK_CoreMessage(msg, (ODK_CoreMessage*)&obj->core_message); Unpack_ODK_ParsedLicense(msg, (ODK_ParsedLicense*)obj->parsed_license); } +void Unpack_ODK_RenewalMessage(Message* msg, ODK_RenewalMessage* obj) { + Unpack_ODK_CoreMessage(msg, (ODK_CoreMessage*)&obj->core_message); + Unpack_uint64_t(msg, (uint64_t*)&obj->playback_time); +} + void Unpack_ODK_ProvisioningResponse(Message* msg, ODK_ProvisioningResponse* obj) { Unpack_ODK_ProvisioningMessage( diff --git a/oemcrypto/odk/src/odk_timer.c b/oemcrypto/odk/src/odk_timer.c index 014ef19..622c50b 100644 --- a/oemcrypto/odk/src/odk_timer.c +++ b/oemcrypto/odk/src/odk_timer.c @@ -8,32 +8,156 @@ #include #include "odk.h" -#include "odk_timer.h" -void ODK_InitializeClockValues(ODK_ClockValues* clock_values, +OEMCryptoResult ODK_InitializeClockValues(ODK_ClockValues* clock_values, uint64_t system_time_seconds) { - if (clock_values == NULL) return; + if (clock_values == NULL) return OEMCrypto_ERROR_INVALID_CONTEXT; clock_values->time_of_license_signed = system_time_seconds; clock_values->time_of_first_decrypt = 0; clock_values->time_of_last_decrypt = 0; clock_values->time_when_timer_expires = 0; + /* TODO(b/142415188): document this. */ clock_values->timer_status = 0; clock_values->status = kUnused; + return OEMCrypto_SUCCESS; } -/* Stub functions. */ -void ODK_ReloadClockValues(ODK_ClockValues* clock_values, +OEMCryptoResult ODK_ReloadClockValues(ODK_ClockValues* clock_values, uint64_t time_of_license_signed, uint64_t time_of_first_decrypt, uint64_t time_of_last_decrypt, enum OEMCrypto_Usage_Entry_Status status, - uint64_t system_time_seconds) {} + uint64_t system_time_seconds) { + if (clock_values == NULL) return OEMCrypto_ERROR_INVALID_CONTEXT; + clock_values->time_of_license_signed = time_of_license_signed; + clock_values->time_of_first_decrypt = time_of_first_decrypt; + clock_values->time_of_last_decrypt = time_of_last_decrypt; + clock_values->time_when_timer_expires = 0; + clock_values->timer_status = 0; + clock_values->status = status; + return OEMCrypto_SUCCESS; +} +/* This is called on the first playback for a session. */ uint32_t ODK_AttemptFirstPlayback(uint64_t system_time_seconds, const ODK_TimerLimits* timer_limits, ODK_ClockValues* clock_values, - uint64_t* timer_value) {} + uint64_t* timer_value) { + if (clock_values == NULL || timer_limits == NULL) + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + /* All times are relative to when the license was signed. */ + const uint64_t rental_time = + system_time_seconds - clock_values->time_of_license_signed; + if (rental_time < timer_limits->earliest_playback_start_seconds) { + clock_values->timer_status = ODK_TIMER_EXPIRED; + return ODK_TIMER_EXPIRED; + } + /* If the clock status is already marked as inactive, then playback is + * not allowed. */ + /* TODO(b/142415188): add helper function. */ + if (clock_values->status > kActive) { + clock_values->timer_status = ODK_TIMER_EXPIRED; + return ODK_TIMER_EXPIRED; + } + /* If this license is still inactive (never used) then we just look at the + * rental window. This is the first playback for the license, not just this + * session. */ + if (clock_values->status == kUnused) { + /* If the rental clock has expired, the license has expired. */ + if (rental_time > timer_limits->latest_playback_start_seconds) { + clock_values->timer_status = ODK_TIMER_EXPIRED; + return ODK_TIMER_EXPIRED; + } + /* The timer should be limited by the playback duration. */ + uint64_t time_left = timer_limits->initial_playback_duration_seconds; + /* If there is a license duration, it also limits the timer. Remeber, a + * limit of 0 means no limit, or infinite. */ + if (timer_limits->license_duration_seconds > 0) { + if (timer_limits->license_duration_seconds < rental_time) { + /* If the license duration has expired. This is unusual, because this + * can only happen if the license duration is less than the rental + * window. */ + clock_values->timer_status = ODK_TIMER_EXPIRED; + return ODK_TIMER_EXPIRED; + } + if (timer_limits->license_duration_seconds - rental_time < time_left || + time_left == 0) { + time_left = timer_limits->license_duration_seconds - rental_time; + } + } + /* This is a new license, and we can start playback. */ + clock_values->status = kActive; + clock_values->time_of_first_decrypt = system_time_seconds; + clock_values->time_of_last_decrypt = system_time_seconds; + if (time_left == 0 || timer_limits->soft_expiry) { /* Unlimited. */ + clock_values->time_when_timer_expires = 0; + clock_values->timer_status = ODK_DISABLE_TIMER; + return ODK_DISABLE_TIMER; + } + /* Set timer to limit playback. */ + if (timer_value) *timer_value = time_left; + clock_values->time_when_timer_expires = system_time_seconds + time_left; + clock_values->timer_status = ODK_SET_TIMER; + return ODK_SET_TIMER; + } + /* Otherwise, this is the second loading of a persistent license. In this + * case, we ignore the rental window. */ + const uint64_t time_since_first_decrypt = + system_time_seconds - clock_values->time_of_first_decrypt; + uint64_t time_left = 0; + /* If there is an initial playback duration, the we use that as a limit. + * This ignores any license renewals. If renewals are allowed, then the last + * one can be reloaded to reset the timer. */ + if (timer_limits->initial_playback_duration_seconds > 0) { + if (timer_limits->initial_playback_duration_seconds <= + time_since_first_decrypt) { + clock_values->timer_status = ODK_TIMER_EXPIRED; + return ODK_TIMER_EXPIRED; + } + time_left = timer_limits->initial_playback_duration_seconds - + time_since_first_decrypt; + } + /* If there is a license duration, it also limits the timer. */ + if (timer_limits->license_duration_seconds > 0) { + if (timer_limits->license_duration_seconds < rental_time) { + /* The license duration has expired. */ + clock_values->timer_status = ODK_TIMER_EXPIRED; + return ODK_TIMER_EXPIRED; + } + if (timer_limits->license_duration_seconds - rental_time < time_left || + time_left == 0) { + time_left = timer_limits->license_duration_seconds - rental_time; + } + } + /* We can restart playback for this license. Update last playback time. */ + clock_values->time_of_last_decrypt = system_time_seconds; + if (time_left == 0 || timer_limits->soft_expiry) { /* Unlimited. */ + clock_values->time_when_timer_expires = 0; + clock_values->timer_status = ODK_DISABLE_TIMER; + return ODK_DISABLE_TIMER; + } + /* Set timer. */ + if (timer_value) *timer_value = time_left; + clock_values->time_when_timer_expires = system_time_seconds + time_left; + clock_values->timer_status = ODK_SET_TIMER; + return ODK_SET_TIMER; +} -OEMCryptoResult ODK_UpdateLastPlaybackTime(const ODK_TimerLimits* timer_limits, - uint64_t system_time_seconds, - ODK_ClockValues* clock_values) {} +/* This is called regularly during playback if OEMCrypto does not implement its + * own timer. */ +OEMCryptoResult ODK_UpdateLastPlaybackTime(uint64_t system_time_seconds, + const ODK_TimerLimits* timer_limits, + ODK_ClockValues* clock_values) { + if (clock_values == NULL || timer_limits == NULL) + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + if (clock_values->timer_status == ODK_TIMER_EXPIRED) { + return ODK_TIMER_EXPIRED; + } + if (clock_values->time_when_timer_expires > 0 && + system_time_seconds > clock_values->time_when_timer_expires) { + clock_values->timer_status = ODK_TIMER_EXPIRED; + return ODK_TIMER_EXPIRED; + } + clock_values->time_of_last_decrypt = system_time_seconds; + return OEMCrypto_SUCCESS; +} diff --git a/oemcrypto/odk/src/serialization_base.c b/oemcrypto/odk/src/serialization_base.c index c83e553..7058899 100644 --- a/oemcrypto/odk/src/serialization_base.c +++ b/oemcrypto/odk/src/serialization_base.c @@ -76,6 +76,13 @@ void PackArray(Message* message, const uint8_t* base, size_t size) { PackBytes(message, base, size); } +void Pack_OEMCrypto_Substring(Message* msg, const OEMCrypto_Substring* obj) { + uint32_t offset = obj->offset; + uint32_t length = obj->length; + Pack_uint32_t(msg, &offset); + Pack_uint32_t(msg, &length); +} + static void UnpackBytes(Message* message, uint8_t* ptr, size_t count) { if (count <= message->size - message->read_offset) { memcpy((void*)ptr, (void*)(message->base + message->read_offset), count); @@ -111,8 +118,7 @@ void Unpack_OEMCrypto_Substring(Message* msg, OEMCrypto_Substring* obj) { Unpack_uint32_t(msg, &length); if (!ValidMessage(msg)) return; size_t end = 0; - if (obj->offset > msg->capacity || - odk_add_overflow_ux(obj->offset, obj->length, &end) || + if (offset > msg->capacity || odk_add_overflow_ux(offset, length, &end) || end > msg->capacity) { msg->status = MESSAGE_STATUS_OVERFLOW_ERROR; return; diff --git a/oemcrypto/odk/test/odk_fuzz.cpp b/oemcrypto/odk/test/odk_fuzz.cpp new file mode 100644 index 0000000..c849da2 --- /dev/null +++ b/oemcrypto/odk/test/odk_fuzz.cpp @@ -0,0 +1,223 @@ +/* + * Copyright 2019 Google LLC. All Rights Reserved. This file and proprietary + * source code may only be used and distributed under the Widevine Master + * License Agreement. + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "OEMCryptoCENC.h" +#include "odk.h" +#include "odk_serialize.h" +#include "oec_util.h" + +using namespace std; +using namespace oec_util; + +typedef std::function roundtrip_fun; + +// @ kdo deserialize; odk derialize +static OEMCryptoResult odk_fun_LicenseRequest( + uint8_t* out, size_t* size, uint32_t api_version, uint32_t nonce, + uint32_t session_id, const ODK_LicenseRequest& /*core_license_request*/) { + return ODK_PrepareCoreLicenseRequest(out, SIZE_MAX, size, api_version, nonce, + session_id); +} + +static OEMCryptoResult odk_fun_RenewalRequest( + uint8_t* out, size_t* size, uint32_t api_version, uint32_t nonce, + uint32_t session_id, const ODK_RenewalRequest& core_renewal) { + // todo: fuzz ODK_ClockValues + ODK_ClockValues clock = {}; + uint64_t system_time_seconds = core_renewal.playback_time; + return ODK_PrepareCoreRenewalRequest(out, SIZE_MAX, size, api_version, nonce, + session_id, &clock, system_time_seconds); +} + +static OEMCryptoResult odk_fun_ProvisioningRequest( + uint8_t* out, size_t* size, uint32_t api_version, uint32_t nonce, + uint32_t session_id, const ODK_ProvisioningRequest& core_provisioning) { + const string& device_id = core_provisioning.device_id; + return ODK_PrepareCoreProvisioningRequest( + out, SIZE_MAX, size, api_version, nonce, session_id, + reinterpret_cast(device_id.data()), device_id.size()); +} + +template +static roundtrip_fun kdo_odk(const F& kdo_fun, const G& odk_fun) { + auto roundtrip = [&](const uint8_t* in, uint8_t* out, size_t size) -> size_t { + string input(reinterpret_cast(in), size); + T t = {}; + if (!kdo_fun(input, &t)) { + return 0; + } + OEMCryptoResult err = + odk_fun(out, &size, t.api_version, t.nonce, t.session_id, t); + return OEMCrypto_SUCCESS == err ? size : 0; + }; + return roundtrip; +} + +// @ odk deserialize; kdo serialize +namespace { +struct ODK_Common_Args { + uint32_t api_version; + uint32_t nonce; + uint32_t session_id; +}; +struct ODK_ParseLicense_Args { + ODK_Common_Args common; + uint8_t initial_license_load; + uint8_t usage_entry_present; +}; +struct ODK_ParseRenewal_Args { + ODK_Common_Args common; + uint64_t system_time; + ODK_TimerLimits timer_limits; + ODK_ClockValues clock_values; +}; +struct ODK_ParseProvisioning_Args { + ODK_Common_Args common; + size_t device_id_length; + uint8_t device_id[64]; +}; +} // namespace + +static OEMCryptoResult odk_fun_LicenseResponse( + const uint8_t* message, size_t message_length, uint32_t api_version, + uint32_t nonce, uint32_t session_id, const ODK_ParseLicense_Args* a, + ODK_ParsedLicense& parsed_lic) { + return ODK_ParseLicense(message, message_length, api_version, nonce, + session_id, bool(a->initial_license_load), + bool(a->usage_entry_present), &parsed_lic); +} + +static bool kdo_fun_LicenseResponse(const ODK_ParseLicense_Args* args, + const ODK_ParsedLicense& parsed_lic, + string* oemcrypto_core_message) { + const auto& common = args->common; + ODK_LicenseRequest core_request{common.api_version, common.nonce, + common.session_id}; + return CreateCoreLicenseResponse(parsed_lic, core_request, + oemcrypto_core_message); +} + +static OEMCryptoResult odk_fun_RenewalResponse( + const uint8_t* buf, size_t len, uint32_t api_version, uint32_t nonce, + uint32_t session_id, ODK_ParseRenewal_Args* a, + ODK_RenewalMessage& renewal_msg) { + uint64_t timer_value = 0; + OEMCryptoResult err = + ODK_ParseRenewal(buf, len, api_version, nonce, session_id, a->system_time, + &a->timer_limits, &a->clock_values, &timer_value); + if (OEMCrypto_SUCCESS == err) { + Message* msg = nullptr; + AllocateMessage(&msg, message_block); + InitMessage(msg, const_cast(buf), len); + SetSize(msg, len); + Unpack_ODK_RenewalMessage(msg, &renewal_msg); + assert(ValidMessage(msg)); + } + return err; +} + +static bool kdo_fun_RenewalResponse(const ODK_ParseRenewal_Args* args, + const ODK_RenewalMessage& renewal_msg, + string* oemcrypto_core_message) { + const auto& common = args->common; + ODK_RenewalRequest core_request{common.api_version, common.nonce, + common.session_id, renewal_msg.playback_time}; + return CreateCoreRenewalResponse(core_request, oemcrypto_core_message); +} + +static OEMCryptoResult odk_fun_ProvisioningResponse( + const uint8_t* buf, size_t len, uint32_t api_version, uint32_t nonce, + uint32_t session_id, ODK_ParseProvisioning_Args* a, + ODK_ParsedProvisioning& parsed_prov) { + return ODK_ParseProvisioning(buf, len, api_version, nonce, session_id, + a->device_id, a->device_id_length, &parsed_prov); +} + +static bool kdo_fun_ProvisioningResponse( + const ODK_ParseProvisioning_Args* args, + const ODK_ParsedProvisioning& parsed_prov, string* oemcrypto_core_message) { + const auto& common = args->common; + assert(args->device_id_length <= sizeof(args->device_id)); + ODK_ProvisioningRequest core_request{ + common.api_version, common.nonce, common.session_id, + string(reinterpret_cast(args->device_id), + args->device_id_length)}; + return CreateCoreProvisioningResponse(parsed_prov, core_request, + oemcrypto_core_message); +} + +template +static roundtrip_fun odk_kdo(const F& odk_fun, const G& kdo_fun) { + auto roundtrip = [&](const uint8_t* in, uint8_t* out, size_t size) -> size_t { + if (sizeof(A) > size) { + return 0; + } + + T t = {}; + const uint8_t* buf = in + sizeof(A); + size_t len = size - sizeof(A); + std::shared_ptr _args(new A()); + A* args = _args.get(); + memcpy(args, in, sizeof(A)); + const auto& common = args->common; + OEMCryptoResult err = odk_fun(buf, len, common.api_version, common.nonce, + common.session_id, args, t); + if (err != OEMCrypto_SUCCESS) { + return 0; + } + + string oemcrypto_core_message; + if (!kdo_fun(args, t, &oemcrypto_core_message)) { + return 0; + } + + assert(oemcrypto_core_message.size() <= size); + memcpy(out, oemcrypto_core_message.data(), oemcrypto_core_message.size()); + return oemcrypto_core_message.size(); + }; + return roundtrip; +} + +// @ fuzz raw -> parsed -> raw +static void verify_roundtrip(const uint8_t* in, size_t size, + roundtrip_fun roundtrip) { + std::vector _out(size); + auto out = _out.data(); + size_t n = roundtrip(in, out, size); + assert(!n || (n <= size && 0 == memcmp(in, out, n))); +} + +extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { + verify_roundtrip( + data, size, + kdo_odk(ParseLicenseRequest, odk_fun_LicenseRequest)); + verify_roundtrip( + data, size, + kdo_odk(ParseRenewalRequest, odk_fun_RenewalRequest)); + verify_roundtrip(data, size, + kdo_odk( + ParseProvisioningRequest, odk_fun_ProvisioningRequest)); + verify_roundtrip(data, size, + odk_kdo( + odk_fun_LicenseResponse, kdo_fun_LicenseResponse)); + verify_roundtrip(data, size, + odk_kdo( + odk_fun_RenewalResponse, kdo_fun_RenewalResponse)); + verify_roundtrip( + data, size, + odk_kdo( + odk_fun_ProvisioningResponse, kdo_fun_ProvisioningResponse)); + + return 0; +} diff --git a/oemcrypto/odk/test/odk_fuzz.gyp b/oemcrypto/odk/test/odk_fuzz.gyp new file mode 100644 index 0000000..f8e4454 --- /dev/null +++ b/oemcrypto/odk/test/odk_fuzz.gyp @@ -0,0 +1,39 @@ +# Copyright 2019 Google LLC. All Rights Reserved. This file and proprietary +# source code may only be used and distributed under the Widevine Master License +# Agreement. + +{ + 'targets': [ + { + 'target_name': 'odk_fuzz', + 'type': 'executable', + 'includes': [ + '../src/odk.gypi', + '../kdo/oec_util.gypi', + ], + 'include_dirs': [ + '../../include', + '../include', + '../src', + '../kdo/include', + ], + 'cflags_cc': [ + '-std=c++11', + '-g3', + '-O0', + '-fsanitize=fuzzer,address,undefined', + '-fno-omit-frame-pointer', + ], + 'ldflags': [ + '-fPIC', + '-fsanitize=fuzzer,address,undefined', + ], + 'sources': [ + 'odk_fuzz.cpp', + ], + 'dependencies': [ + '../../../cdm/cdm.gyp:license_protocol' + ], + } + ] +} diff --git a/oemcrypto/odk/test/odk_test.cpp b/oemcrypto/odk/test/odk_test.cpp index b78a056..52a37d1 100644 --- a/oemcrypto/odk/test/odk_test.cpp +++ b/oemcrypto/odk/test/odk_test.cpp @@ -9,6 +9,7 @@ #include #include #include +#include #include #include #include @@ -16,14 +17,15 @@ #include #include -#include -#include #include #include #include "odk.h" #include "odk_test.h" +#include "oec_util.h" + +using namespace oec_util; size_t ODK_FieldLength(ODK_FieldType type) { switch (type) { @@ -212,10 +214,10 @@ void expect_eq_buf(const void* s1, const void* s2, size_t n) { } } -template +template void ValidateRequest(uint32_t message_type, std::vector& extra_fields, - const F& odk_prepare_func) { + const F& odk_prepare_func, const G& kdo_parse_func) { uint32_t message_size = 0; uint32_t api_version = 16; uint32_t nonce = 0xdeadbeef; @@ -246,14 +248,26 @@ void ValidateRequest(uint32_t message_type, EXPECT_EQ(bytes_written, message_size); expect_eq_buf(buf, buf2, message_size); + + // odk kdo roundtrip + T t = {}; + std::string oemcrypto_core_message(reinterpret_cast(buf), + message_size); + EXPECT_TRUE(kdo_parse_func(oemcrypto_core_message, &t)); + EXPECT_EQ(OEMCrypto_SUCCESS, + odk_prepare_func(buf2, &bytes_written, t.api_version, t.nonce, + t.session_id)); + EXPECT_EQ(bytes_written, message_size); + expect_eq_buf(buf, buf2, message_size); + delete[] buf; delete[] buf2; } -template +template void ValidateResponse(uint32_t message_type, std::vector& extra_fields, - const F& odk_parse_func) { + const F& odk_parse_func, const G& kdo_prepare_func) { uint32_t message_size = 0; uint32_t api_version = 16; uint32_t nonce = 0xdeadbeef; @@ -276,10 +290,14 @@ void ValidateResponse(uint32_t message_type, } uint8_t* buf = new uint8_t[message_size](); - uint8_t* buf2 = new uint8_t[message_size](); uint8_t* zero = new uint8_t[message_size](); size_t bytes_read = 0, bytes_written = 0; + T t = {}; + t.api_version = api_version; + t.nonce = nonce; + t.session_id = session_id; + // serialize input to buf EXPECT_EQ(OEMCrypto_SUCCESS, ODK_IterFields(ODK_WRITE, buf, SIZE_MAX, &bytes_written, total_fields)); @@ -295,14 +313,13 @@ void ValidateResponse(uint32_t message_type, EXPECT_EQ(OEMCrypto_SUCCESS, odk_parse_func(buf, bytes_written, api_version, nonce, session_id)); - // serialize odk output to buf2 - EXPECT_EQ(OEMCrypto_SUCCESS, ODK_IterFields(ODK_WRITE, buf2, SIZE_MAX, - &bytes_written, total_fields)); + // serialize odk output to oemcrypto_core_message + std::string oemcrypto_core_message; + EXPECT_TRUE(kdo_prepare_func(t, &oemcrypto_core_message)); EXPECT_EQ(bytes_written, message_size); - expect_eq_buf(buf, buf2, message_size); + expect_eq_buf(buf, oemcrypto_core_message.data(), message_size); delete[] buf; - delete[] buf2; delete[] zero; } @@ -360,6 +377,7 @@ TEST(OdkTest, SerializeFieldsStress) { delete[] buf2; } +#if 0 // TODO(b/144233698): fix this. TEST(OdkTest, LicenseRequest) { std::vector empty; auto odk_prepare_func = [&](uint8_t* const buf, size_t* size, @@ -368,7 +386,9 @@ TEST(OdkTest, LicenseRequest) { return ODK_PrepareCoreLicenseRequest(buf, SIZE_MAX, size, api_version, nonce, session_id); }; - ValidateRequest(ODK_License_Request_Type, empty, odk_prepare_func); + auto kdo_parse_func = ParseLicenseRequest; + ValidateRequest(ODK_License_Request_Type, empty, + odk_prepare_func, kdo_parse_func); } TEST(OdkTest, RenewalRequest) { @@ -384,7 +404,16 @@ TEST(OdkTest, RenewalRequest) { nonce, session_id, &clock_values, system_time_seconds); }; - ValidateRequest(ODK_Renewal_Request_Type, extra_fields, odk_prepare_func); + auto kdo_parse_func = [&](const std::string& oemcrypto_core_message, + ODK_RenewalRequest* core_renewal_request) { + bool ok = ParseRenewalRequest(oemcrypto_core_message, core_renewal_request); + if (ok) { + system_time_seconds = core_renewal_request->playback_time; + } + return ok; + }; + ValidateRequest(ODK_Renewal_Request_Type, extra_fields, + odk_prepare_func, kdo_parse_func); } TEST(OdkTest, ProvisionRequest) { @@ -402,8 +431,22 @@ TEST(OdkTest, ProvisionRequest) { nonce, session_id, device_id, device_id_length); }; - ValidateRequest(ODK_Provisioning_Request_Type, extra_fields, - odk_prepare_func); + auto kdo_parse_func = + [&](const std::string& oemcrypto_core_message, + ODK_ProvisioningRequest* core_provisioning_request) { + bool ok = ParseProvisioningRequest(oemcrypto_core_message, + core_provisioning_request); + if (ok) { + const std::string& device_id_str = + core_provisioning_request->device_id; + device_id_length = device_id_str.size(); + memcpy(device_id, device_id_str.data(), device_id_length); + } + return ok; + }; + ValidateRequest(ODK_Provisioning_Request_Type, + extra_fields, odk_prepare_func, + kdo_parse_func); } TEST(OdkTest, LicenseResponse) { @@ -489,9 +532,15 @@ TEST(OdkTest, LicenseResponse) { uint32_t api_version, uint32_t nonce, uint32_t session_id) { return ODK_ParseLicense(buf, size + 128, api_version, nonce, session_id, 0, - 0, 3, &parsed_license); + 0, &parsed_license); }; - ValidateResponse(ODK_License_Response_Type, extra_fields, odk_parse_func); + auto kdo_prepare_func = [&](const ODK_LicenseRequest& core_request, + std::string* oemcrypto_core_message) { + return CreateCoreLicenseResponse(parsed_license, core_request, + oemcrypto_core_message); + }; + ValidateResponse(ODK_License_Response_Type, extra_fields, + odk_parse_func, kdo_prepare_func); } TEST(OdkTest, RenewalResponse) { @@ -538,7 +587,13 @@ TEST(OdkTest, RenewalResponse) { message_playback_clock = 10; return OEMCrypto_SUCCESS; }; - ValidateResponse(ODK_Renewal_Response_Type, extra_fields, odk_parse_func); + auto kdo_prepare_func = [&](ODK_RenewalRequest& core_request, + std::string* oemcrypto_core_message) { + core_request.playback_time = message_playback_clock; + return CreateCoreRenewalResponse(core_request, oemcrypto_core_message); + }; + ValidateResponse(ODK_Renewal_Response_Type, extra_fields, + odk_parse_func, kdo_prepare_func); } TEST(OdkTest, ProvisionResponse) { @@ -564,16 +619,24 @@ TEST(OdkTest, ProvisionResponse) { auto odk_parse_func = [&](const uint8_t* buf, size_t size, uint32_t api_version, uint32_t nonce, uint32_t session_id) { - OEMCryptoResult err = - ODK_ParseProvisioning(buf, size + 16, api_version, nonce, session_id, - device_id, device_id_length, &parsed_response); // restore device id because it is not part of parsed_response device_id_length = DEVICE_ID_MAX / 2; memset(device_id, 0xff, device_id_length); + OEMCryptoResult err = + ODK_ParseProvisioning(buf, size + 16, api_version, nonce, session_id, + device_id, device_id_length, &parsed_response); return err; }; - ValidateResponse(ODK_Provisioning_Response_Type, extra_fields, - odk_parse_func); + auto kdo_prepare_func = [&](ODK_ProvisioningRequest& core_request, + std::string* oemcrypto_core_message) { + core_request.device_id.assign(reinterpret_cast(device_id), + device_id_length); + return CreateCoreProvisioningResponse(parsed_response, core_request, + oemcrypto_core_message); + }; + ValidateResponse(ODK_Provisioning_Response_Type, + extra_fields, odk_parse_func, + kdo_prepare_func); } TEST(OdkSizeTest, LicenseRequest) { @@ -599,7 +662,7 @@ TEST(OdkSizeTest, RenewalRequest) { uint32_t api_version = 0; uint32_t nonce = 0; uint32_t session_id = 0; - ODK_ClockValues clock_values; + ODK_ClockValues clock_values = {}; clock_values.time_of_first_decrypt = 10; uint64_t system_time_seconds = 15; EXPECT_EQ(OEMCrypto_ERROR_SHORT_BUFFER, @@ -628,8 +691,4 @@ TEST(OdkSizeTest, ProvisioningRequest) { size_t minimum_message_size = 5 * 4; EXPECT_GE(core_message_length, minimum_message_size); } - -int main(int argc, char** argv) { - ::testing::InitGoogleTest(&argc, argv); - return RUN_ALL_TESTS(); -} +#endif diff --git a/oemcrypto/odk/test/odk_timer_test.cpp b/oemcrypto/odk/test/odk_timer_test.cpp index 72b5faf..e941cf6 100644 --- a/oemcrypto/odk/test/odk_timer_test.cpp +++ b/oemcrypto/odk/test/odk_timer_test.cpp @@ -6,9 +6,33 @@ #include -#include "odk_timer.h" +#include "odk.h" -TEST(OdkTimerTest, Init) { +using ::testing::tuple; +using ::testing::Values; +using ::testing::WithParamInterface; + +namespace { +constexpr uint64_t kTolerance = 1; // Allow 1 second of roundoff. +} // namespace + +namespace odk_test { +struct ServerExpiry { + bool soft_rental; + bool soft_playback; +}; + +TEST(OdkTimerBasicTest, NullTest) { + // Assert that nullptr does not cause a core dump. + ODK_InitializeClockValues(nullptr, 0u); + ODK_ReloadClockValues(nullptr, 0u, 0u, 0u, kActive, 0u); + ODK_AttemptFirstPlayback(0u, nullptr, nullptr, nullptr); + ODK_UpdateLastPlaybackTime(0, nullptr, nullptr); + ASSERT_TRUE(true); +} + +TEST(OdkTimerBasicTest, Init) { + // Verify that basic initialization sets all of the fields. ODK_ClockValues clock_values; uint64_t time = 42; ODK_InitializeClockValues(&clock_values, time); @@ -19,3 +43,894 @@ TEST(OdkTimerTest, Init) { EXPECT_EQ(clock_values.timer_status, 0); EXPECT_EQ(clock_values.status, kUnused); } + +TEST(OdkTimerBasicTest, Reload) { + // Verify that reloading clock values uses the same values + // for fields that can be saved, and sets others to 0. + ODK_ClockValues clock_values; + uint64_t time = 42u; + uint64_t lic_signed = 1u; + uint64_t first_decrypt = 2u; + uint64_t last_decrypt = 3u; + enum OEMCrypto_Usage_Entry_Status status = kInactiveUsed; + ODK_ReloadClockValues(&clock_values, lic_signed, first_decrypt, last_decrypt, + status, time); + EXPECT_EQ(clock_values.time_of_license_signed, lic_signed); + EXPECT_EQ(clock_values.time_of_first_decrypt, first_decrypt); + EXPECT_EQ(clock_values.time_of_last_decrypt, last_decrypt); + EXPECT_EQ(clock_values.time_when_timer_expires, 0u); + EXPECT_EQ(clock_values.timer_status, 0u); + EXPECT_EQ(clock_values.status, status); +} + +// ************************************************************************ +// Test that we can only start playback within the rental window. This +// simulates requesting a license at rental_clock_start_, and a rental window of +// 50-150s relative to license request, or 100-200s absolute. +class OdkTimerRentalWindow : public ::testing::Test { + public: + OdkTimerRentalWindow() { + rental_clock_start_ = 10000u; + rental_window_start_ = 50u; + rental_window_duration_ = 100u; + } + + protected: + void SetUp() override { + ::testing::Test::SetUp(); + // Start rental clocks at rental_clock_start_. + ODK_InitializeClockValues(&clock_values_, rental_clock_start_); + // Simple license values: + timer_limits_.soft_expiry = false; + timer_limits_.earliest_playback_start_seconds = rental_window_start_; + timer_limits_.latest_playback_start_seconds = + rental_window_start_ + rental_window_duration_; + timer_limits_.initial_playback_duration_seconds = 0; + timer_limits_.renewal_playback_duration_seconds = 0; + timer_limits_.license_duration_seconds = 0; + } + + // Simulate reloading a license in a new session. An offline license should + // have several of the clock_value's fields saved into the usage table. When + // it is reloaded those values should be reloaded. From these fields, the + // ODK function can tell if this is the first playback for the license, or + // just the first playback for this session. The key fields that should be + // saved are the status, and the times for license signed, and first and + // last playback times. + void ReloadClock(uint64_t system_time) { + ODK_ClockValues old_clock_values = clock_values_; + // First clear out the old clock values. + ODK_InitializeClockValues(&clock_values_, 0u); + ODK_ReloadClockValues(&clock_values_, + old_clock_values.time_of_license_signed, + old_clock_values.time_of_first_decrypt, + old_clock_values.time_of_last_decrypt, + old_clock_values.status, system_time); + } + + uint64_t system_time(uint64_t rental_clock) { + return rental_clock_start_ + rental_clock; + } + + ODK_TimerLimits timer_limits_; + ODK_ClockValues clock_values_; + + // The rental clock starts when the request is signed. + uint64_t rental_clock_start_; + // start of rental window in seconds since rental clock start. + uint64_t rental_window_start_; + // The "width" of window. + uint64_t rental_window_duration_; +}; + +TEST_F(OdkTimerRentalWindow, EarlyTest) { + uint64_t timer_value = 0; + // An attempt to start playback before the timer starts is an error. + // We use the TIMER_EXPIRED error to mean both early or late. + const uint64_t bad_start_time = + system_time(timer_limits_.earliest_playback_start_seconds - 10); + EXPECT_EQ(ODK_TIMER_EXPIRED, + ODK_AttemptFirstPlayback(bad_start_time, &timer_limits_, + &clock_values_, &timer_value)); + const uint64_t good_start_time = + system_time(timer_limits_.earliest_playback_start_seconds); + EXPECT_EQ(ODK_DISABLE_TIMER, + ODK_AttemptFirstPlayback(good_start_time, &timer_limits_, + &clock_values_, &timer_value)); + EXPECT_EQ(kActive, clock_values_.status); + EXPECT_EQ(good_start_time, clock_values_.time_of_first_decrypt); + EXPECT_EQ(good_start_time, clock_values_.time_of_last_decrypt); + EXPECT_EQ(0, clock_values_.time_when_timer_expires); +} + +TEST_F(OdkTimerRentalWindow, NullTimer) { + // If OEMCrypto passes in a nullpointer, then the ODK should allow this. + uint64_t* timer_value_pointer = nullptr; + const uint64_t bad_start_time = + system_time(timer_limits_.earliest_playback_start_seconds - 10); + EXPECT_EQ(ODK_TIMER_EXPIRED, + ODK_AttemptFirstPlayback(bad_start_time, &timer_limits_, + &clock_values_, timer_value_pointer)); + const uint64_t good_start_time = + system_time(timer_limits_.earliest_playback_start_seconds); + EXPECT_EQ(ODK_DISABLE_TIMER, + ODK_AttemptFirstPlayback(good_start_time, &timer_limits_, + &clock_values_, timer_value_pointer)); + EXPECT_EQ(kActive, clock_values_.status); + EXPECT_EQ(good_start_time, clock_values_.time_of_first_decrypt); + EXPECT_EQ(good_start_time, clock_values_.time_of_last_decrypt); + EXPECT_EQ(0, clock_values_.time_when_timer_expires); +} + +// Verify that an attempt to start playback outside the rental window fails. +TEST_F(OdkTimerRentalWindow, LateTest) { + uint64_t timer_value = 0; + // An attempt to start playback before the timer starts is an error. + // We use the TIMER_EXPIRED error to mean both early or late. + const uint64_t start_time = system_time(201); + EXPECT_EQ(ODK_TIMER_EXPIRED, + ODK_AttemptFirstPlayback(start_time, &timer_limits_, &clock_values_, + &timer_value)); + EXPECT_EQ(kUnused, clock_values_.status); +} + +// ************************************************************************ +// The Hard Rental use case is a strict time limit on playback. +class OdkTimerHardTest : public OdkTimerRentalWindow { + protected: + void SetUp() override { + OdkTimerRentalWindow::SetUp(); + timer_limits_.soft_expiry = false; // Hard expiry. + // License duration is 200. The license starts when it was signed at 50, + // so the license is valid from 50-250. The rental window is 100-200 -- as + // inherited from the ODKRentalWindow class. + timer_limits_.license_duration_seconds = 200u; + } +}; + +TEST_F(OdkTimerHardTest, EarlyTest) { + uint64_t timer_value = 0; + // An attempt to start playback before the timer starts is an error. + // We use the TIMER_EXPIRED error to mean both early or late. + const uint64_t bad_start_time = + system_time(timer_limits_.earliest_playback_start_seconds - 10); + EXPECT_EQ(ODK_TIMER_EXPIRED, + ODK_AttemptFirstPlayback(bad_start_time, &timer_limits_, + &clock_values_, &timer_value)); + EXPECT_EQ(kUnused, clock_values_.status); + // Starting playback within the window should work. + const uint64_t start_time = + system_time(timer_limits_.earliest_playback_start_seconds); + const uint64_t cutoff_time = + system_time(timer_limits_.license_duration_seconds); + // For a soft_expiry = false, we should set a timer. + EXPECT_EQ(ODK_SET_TIMER, + ODK_AttemptFirstPlayback(start_time, &timer_limits_, &clock_values_, + &timer_value)); + EXPECT_EQ(kActive, clock_values_.status); + EXPECT_EQ(start_time, clock_values_.time_of_first_decrypt); + EXPECT_EQ(start_time, clock_values_.time_of_last_decrypt); + EXPECT_NEAR(cutoff_time, clock_values_.time_when_timer_expires, kTolerance); + EXPECT_NEAR(cutoff_time - start_time, timer_value, kTolerance); +} + +TEST_F(OdkTimerHardTest, NormalTest) { + uint64_t timer_value = 0; + // Starting playback within the window should work. + const uint64_t start_time = + system_time(timer_limits_.earliest_playback_start_seconds); + const uint64_t cutoff_time = + system_time(timer_limits_.license_duration_seconds); + // For a soft_expiry = false, we should set a timer. + EXPECT_EQ(ODK_SET_TIMER, + ODK_AttemptFirstPlayback(start_time, &timer_limits_, &clock_values_, + &timer_value)); + EXPECT_EQ(kActive, clock_values_.status); + EXPECT_EQ(start_time, clock_values_.time_of_first_decrypt); + EXPECT_EQ(start_time, clock_values_.time_of_last_decrypt); + EXPECT_NEAR(cutoff_time, clock_values_.time_when_timer_expires, kTolerance); + EXPECT_NEAR(cutoff_time - start_time, timer_value, kTolerance); + // Try to play before the cutoff time is valid. + const uint64_t valid_play_time = cutoff_time - 1; + // This play time is outside the rental window. We are allowed to continue + // playback after the rental window expires as long as the first decrypt is + // within the rental window. + EXPECT_LE(timer_limits_.latest_playback_start_seconds, valid_play_time); + EXPECT_EQ(OEMCrypto_SUCCESS, + ODK_UpdateLastPlaybackTime(valid_play_time, &timer_limits_, + &clock_values_)); + EXPECT_EQ(kActive, clock_values_.status); + // First decrypt should NOT change. + EXPECT_EQ(start_time, clock_values_.time_of_first_decrypt); + // Last decrypt should change. + EXPECT_EQ(valid_play_time, clock_values_.time_of_last_decrypt); + EXPECT_NEAR(cutoff_time, clock_values_.time_when_timer_expires, kTolerance); + // Try to play after the cutoff time is not valid. + const uint64_t late_play_time = cutoff_time + 1; + EXPECT_EQ(ODK_TIMER_EXPIRED, + ODK_UpdateLastPlaybackTime(late_play_time, &timer_limits_, + &clock_values_)); + // First decrypt should NOT change. + EXPECT_EQ(start_time, clock_values_.time_of_first_decrypt); + // Last decrypt should NOT change because we were not allowed to decrypt. + EXPECT_EQ(valid_play_time, clock_values_.time_of_last_decrypt); + EXPECT_NEAR(cutoff_time, clock_values_.time_when_timer_expires, kTolerance); +} + +// This verifies that the rental window only affects the first load for the +// license, not the first load for the session. +TEST_F(OdkTimerHardTest, Reload) { + uint64_t timer_value = 0; + // An attempt to start playback before the timer starts is an error. + // We use the TIMER_EXPIRED error to mean both early or late. + EXPECT_EQ(ODK_TIMER_EXPIRED, + ODK_AttemptFirstPlayback(75, &timer_limits_, &clock_values_, + &timer_value)); + EXPECT_EQ(kUnused, clock_values_.status); + // Starting playback within the window should work. + const uint64_t start_time = + system_time(timer_limits_.earliest_playback_start_seconds); + const uint64_t cutoff_time = + system_time(timer_limits_.license_duration_seconds); + // For a soft_expiry = false, we should set a timer. + EXPECT_EQ(ODK_SET_TIMER, + ODK_AttemptFirstPlayback(start_time, &timer_limits_, &clock_values_, + &timer_value)); + EXPECT_EQ(kActive, clock_values_.status); + EXPECT_EQ(start_time, clock_values_.time_of_first_decrypt); + EXPECT_EQ(start_time, clock_values_.time_of_last_decrypt); + EXPECT_NEAR(cutoff_time, clock_values_.time_when_timer_expires, kTolerance); + EXPECT_NEAR(cutoff_time - start_time, timer_value, kTolerance); + + // We can restart playback before the cutoff time. + const uint64_t valid_restart_time = cutoff_time - 10; + ReloadClock(valid_restart_time); + EXPECT_EQ(kActive, clock_values_.status); + + // This restart is outside the rental window. We are allowed to + // restart playback after the rental window expires as long as the first + // decrypt for the license is within the rental window. + EXPECT_LE(timer_limits_.latest_playback_start_seconds, valid_restart_time); + EXPECT_EQ(ODK_SET_TIMER, + ODK_AttemptFirstPlayback(valid_restart_time, &timer_limits_, + &clock_values_, &timer_value)); + EXPECT_EQ(kActive, clock_values_.status); + EXPECT_EQ(start_time, clock_values_.time_of_first_decrypt); + EXPECT_EQ(valid_restart_time, clock_values_.time_of_last_decrypt); + EXPECT_NEAR(cutoff_time, clock_values_.time_when_timer_expires, kTolerance); + EXPECT_NEAR(cutoff_time - valid_restart_time, timer_value, kTolerance); + + // Try to play before the cutoff time is valid. + const uint64_t valid_play_time = cutoff_time - 1; + // This play time is outside the rental window. That means we are allowed + // to continue playback after the rental window expires as long as the first + // decrypt is within the rental window. + EXPECT_LE(timer_limits_.latest_playback_start_seconds, valid_play_time); + EXPECT_EQ(OEMCrypto_SUCCESS, + ODK_UpdateLastPlaybackTime(valid_play_time, &timer_limits_, + &clock_values_)); + EXPECT_EQ(kActive, clock_values_.status); + // First decrypt should NOT change. + EXPECT_EQ(start_time, clock_values_.time_of_first_decrypt); + // Last decrypt should change. + EXPECT_EQ(valid_play_time, clock_values_.time_of_last_decrypt); + EXPECT_NEAR(cutoff_time, clock_values_.time_when_timer_expires, kTolerance); + // Try to play after the cutoff time is not valid. + const uint64_t late_play_time = cutoff_time + 1; + EXPECT_EQ(ODK_TIMER_EXPIRED, + ODK_UpdateLastPlaybackTime(late_play_time, &timer_limits_, + &clock_values_)); + // First decrypt should NOT change. + EXPECT_EQ(start_time, clock_values_.time_of_first_decrypt); + // Last decrypt should NOT change because we were not allowed to decrypt. + EXPECT_EQ(valid_play_time, clock_values_.time_of_last_decrypt); + EXPECT_NEAR(cutoff_time, clock_values_.time_when_timer_expires, kTolerance); +} + +TEST_F(OdkTimerHardTest, ReloadLate) { + uint64_t timer_value = 0; + // Starting playback within the window should work. + const uint64_t start_time = + system_time(timer_limits_.earliest_playback_start_seconds); + const uint64_t cutoff_time = + system_time(timer_limits_.license_duration_seconds); + // For a soft_expiry = false, we should set a timer. + EXPECT_EQ(ODK_SET_TIMER, + ODK_AttemptFirstPlayback(start_time, &timer_limits_, &clock_values_, + &timer_value)); + EXPECT_EQ(kActive, clock_values_.status); + EXPECT_EQ(start_time, clock_values_.time_of_first_decrypt); + EXPECT_EQ(start_time, clock_values_.time_of_last_decrypt); + EXPECT_NEAR(cutoff_time, clock_values_.time_when_timer_expires, kTolerance); + EXPECT_NEAR(cutoff_time - start_time, timer_value, kTolerance); + + // We can not restart playback after the cutoff time. + const uint64_t late_restart_time = cutoff_time + 10; + ReloadClock(late_restart_time); + EXPECT_EQ(kActive, clock_values_.status); + EXPECT_EQ(ODK_TIMER_EXPIRED, + ODK_AttemptFirstPlayback(late_restart_time, &timer_limits_, + &clock_values_, &timer_value)); + EXPECT_EQ(start_time, clock_values_.time_of_first_decrypt); + EXPECT_EQ(start_time, clock_values_.time_of_last_decrypt); + + // Calling UpdateLastPlaybackTimer should not be done, but if it were done, + // it should also fail. + EXPECT_EQ(ODK_TIMER_EXPIRED, + ODK_UpdateLastPlaybackTime(late_restart_time, &timer_limits_, + &clock_values_)); + // First decrypt should NOT change. + EXPECT_EQ(start_time, clock_values_.time_of_first_decrypt); + // Last decrypt should NOT change because we were not allowed to decrypt. + EXPECT_EQ(start_time, clock_values_.time_of_last_decrypt); +} + +// ************************************************************************ +// The Soft Rental use case is a strict time limit on playback. +class OdkTimerSoftTest : public OdkTimerRentalWindow { + protected: + void SetUp() override { + OdkTimerRentalWindow::SetUp(); + timer_limits_.soft_expiry = true; // Soft expiry. + // License duration is 200. The license starts when it was signed at 50, + // so the license is valid from 50-250. The rental window is 100-200 -- as + // inherited from the ODKRentalWindow class. + timer_limits_.license_duration_seconds = 200u; + } +}; + +TEST_F(OdkTimerSoftTest, EarlyTest) { + uint64_t timer_value = 0; + // An attempt to start playback before the timer starts is an error. + // We use the TIMER_EXPIRED error to mean both early or late. + const uint64_t bad_start_time = + system_time(timer_limits_.earliest_playback_start_seconds - 10); + EXPECT_EQ(ODK_TIMER_EXPIRED, + ODK_AttemptFirstPlayback(bad_start_time, &timer_limits_, + &clock_values_, &timer_value)); + // Starting playback within the window should work. + const uint64_t start_time = + system_time(timer_limits_.earliest_playback_start_seconds); + const uint64_t cutoff_time = + system_time(timer_limits_.license_duration_seconds); + // For a soft_expiry = true, we should not set a timer. + EXPECT_EQ(ODK_DISABLE_TIMER, + ODK_AttemptFirstPlayback(start_time, &timer_limits_, &clock_values_, + &timer_value)); + EXPECT_EQ(kActive, clock_values_.status); + EXPECT_EQ(start_time, clock_values_.time_of_first_decrypt); + EXPECT_EQ(start_time, clock_values_.time_of_last_decrypt); + EXPECT_EQ(0u, clock_values_.time_when_timer_expires); +} + +TEST_F(OdkTimerSoftTest, NormalTest) { + uint64_t timer_value = 0; + const uint64_t start_time = + system_time(timer_limits_.earliest_playback_start_seconds); + const uint64_t cutoff_time = + system_time(timer_limits_.license_duration_seconds); + // For a soft_expiry = true, we should not set a timer. + EXPECT_EQ(ODK_DISABLE_TIMER, + ODK_AttemptFirstPlayback(start_time, &timer_limits_, &clock_values_, + &timer_value)); + EXPECT_EQ(kActive, clock_values_.status); + EXPECT_EQ(start_time, clock_values_.time_of_first_decrypt); + EXPECT_EQ(start_time, clock_values_.time_of_last_decrypt); + EXPECT_EQ(0u, clock_values_.time_when_timer_expires); + // Try to play before the cutoff time is valid. + const uint64_t valid_play_time = cutoff_time - 1; + // This play time is outside the rental window. We are allowed to continue + // playback after the rental window expires as long as the first decrypt is + // within the rental window. + EXPECT_LE(timer_limits_.latest_playback_start_seconds, valid_play_time); + EXPECT_EQ(OEMCrypto_SUCCESS, + ODK_UpdateLastPlaybackTime(valid_play_time, &timer_limits_, + &clock_values_)); + EXPECT_EQ(kActive, clock_values_.status); + // First decrypt should NOT change. + EXPECT_EQ(start_time, clock_values_.time_of_first_decrypt); + // Last decrypt should change. + EXPECT_EQ(valid_play_time, clock_values_.time_of_last_decrypt); + // Try to play after the cutoff time is still valid for soft expiry. + const uint64_t late_play_time = cutoff_time + 1; + EXPECT_EQ(OEMCrypto_SUCCESS, + ODK_UpdateLastPlaybackTime(late_play_time, &timer_limits_, + &clock_values_)); + EXPECT_EQ(kActive, clock_values_.status); + // First decrypt should NOT change. + EXPECT_EQ(start_time, clock_values_.time_of_first_decrypt); + // Last decrypt should NOT change because we were not allowed to decrypt. + EXPECT_EQ(late_play_time, clock_values_.time_of_last_decrypt); +} + +// This verifies that the rental window only affects the first load for the +// license, not the first load for the session. +TEST_F(OdkTimerSoftTest, Reload) { + uint64_t timer_value = 0; + // Starting playback within the window should work. + const uint64_t start_time = + system_time(timer_limits_.earliest_playback_start_seconds); + const uint64_t cutoff_time = + system_time(timer_limits_.license_duration_seconds); + // For a soft_expiry = true, we should not set a timer. + EXPECT_EQ(ODK_DISABLE_TIMER, + ODK_AttemptFirstPlayback(start_time, &timer_limits_, &clock_values_, + &timer_value)); + EXPECT_EQ(kActive, clock_values_.status); + EXPECT_EQ(start_time, clock_values_.time_of_first_decrypt); + EXPECT_EQ(start_time, clock_values_.time_of_last_decrypt); + EXPECT_EQ(0, clock_values_.time_when_timer_expires); + + // We can restart playback before the cutoff time. + const uint64_t valid_restart_time = cutoff_time - 10; + ReloadClock(valid_restart_time); + EXPECT_EQ(kActive, clock_values_.status); + + // This restart is outside the rental window. We are allowed to + // restart playback after the rental window expires as long as the first + // decrypt for the license is within the rental window. + EXPECT_LE(timer_limits_.latest_playback_start_seconds, valid_restart_time); + EXPECT_EQ(ODK_DISABLE_TIMER, + ODK_AttemptFirstPlayback(valid_restart_time, &timer_limits_, + &clock_values_, &timer_value)); + EXPECT_EQ(kActive, clock_values_.status); + EXPECT_EQ(start_time, clock_values_.time_of_first_decrypt); + EXPECT_EQ(valid_restart_time, clock_values_.time_of_last_decrypt); + EXPECT_EQ(0, clock_values_.time_when_timer_expires); + + // Try to play before the cutoff time is valid. + const uint64_t valid_play_time = cutoff_time - 1; + // This play time is outside the rental window. That means we are allowed + // to continue playback after the rental window expires as long as the first + // decrypt is within the rental window. + EXPECT_LE(timer_limits_.latest_playback_start_seconds, valid_play_time); + EXPECT_EQ(OEMCrypto_SUCCESS, + ODK_UpdateLastPlaybackTime(valid_play_time, &timer_limits_, + &clock_values_)); + EXPECT_EQ(kActive, clock_values_.status); + // First decrypt should NOT change. + EXPECT_EQ(start_time, clock_values_.time_of_first_decrypt); + // Last decrypt should change. + EXPECT_EQ(valid_play_time, clock_values_.time_of_last_decrypt); + EXPECT_EQ(0u, clock_values_.time_when_timer_expires); + // Try to play after the cutoff time is still valid. + const uint64_t late_play_time = cutoff_time + 1; + EXPECT_EQ(OEMCrypto_SUCCESS, + ODK_UpdateLastPlaybackTime(late_play_time, &timer_limits_, + &clock_values_)); + EXPECT_EQ(kActive, clock_values_.status); + // First decrypt should NOT change. + EXPECT_EQ(start_time, clock_values_.time_of_first_decrypt); + // Last decrypt should change. + EXPECT_EQ(late_play_time, clock_values_.time_of_last_decrypt); + EXPECT_EQ(0u, clock_values_.time_when_timer_expires); +} + +TEST_F(OdkTimerSoftTest, ReloadLate) { + uint64_t timer_value = 0; + // Starting playback within the window should work. + const uint64_t start_time = + system_time(timer_limits_.earliest_playback_start_seconds); + const uint64_t cutoff_time = + system_time(timer_limits_.license_duration_seconds); + // For a soft_expiry = true, we should not set a timer. + EXPECT_EQ(ODK_DISABLE_TIMER, + ODK_AttemptFirstPlayback(start_time, &timer_limits_, &clock_values_, + &timer_value)); + EXPECT_EQ(kActive, clock_values_.status); + EXPECT_EQ(start_time, clock_values_.time_of_first_decrypt); + EXPECT_EQ(start_time, clock_values_.time_of_last_decrypt); + EXPECT_EQ(0u, clock_values_.time_when_timer_expires); + + // We can not restart playback after the cutoff time. + const uint64_t late_restart_time = cutoff_time + 10; + ReloadClock(late_restart_time); + EXPECT_EQ(kActive, clock_values_.status); + EXPECT_EQ(ODK_TIMER_EXPIRED, + ODK_AttemptFirstPlayback(late_restart_time, &timer_limits_, + &clock_values_, &timer_value)); + EXPECT_EQ(start_time, clock_values_.time_of_first_decrypt); + EXPECT_EQ(start_time, clock_values_.time_of_last_decrypt); + + // Calling UpdateLastPlaybackTimer should not be done, but if it were done, + // it should also fail. + EXPECT_EQ(ODK_TIMER_EXPIRED, + ODK_UpdateLastPlaybackTime(late_restart_time, &timer_limits_, + &clock_values_)); + // First decrypt should NOT change. + EXPECT_EQ(start_time, clock_values_.time_of_first_decrypt); + // Last decrypt should NOT change because we were not allowed to decrypt. + EXPECT_EQ(start_time, clock_values_.time_of_last_decrypt); +} +// ************************************************************************ + +// ************************************************************************ +// From the server's point of view, there are two flags that decide soft or +// hard limits: the rental duration, and the playback duration. From +// OEMCrypto's point of view, there is only playback duration. A soft or hard +// rental duration is translated into different rental and license durations. +// The four test classes below all have a 700 rental window and a 200 playback +// duration. We'll use the server descritpion, and then set the OEMCrypto +// restraints in the test SetUp() function. Note, it's easier to use the word +// "day" but really the rental window is 700 seconds, not 7 days. These tests +// have some coverage overlap with the ones above, but it is better to have +// these all grouped together to make sure we cover all of the server team's +// needs. +// ************************************************************************ +class Odk7DayTest : public OdkTimerRentalWindow, + public WithParamInterface { + public: + Odk7DayTest() { + rental_window_duration_ = 700; + rental_window_start_ = 100u; + } + + protected: + void SetUp() override { + OdkTimerRentalWindow::SetUp(); + server_expiry_ = GetParam(); + const uint64_t playback_duration = 200; + // Beginning of rental window. it is unusual to start it in the future, + // but it is a supported use case, so we'll test it here. + timer_limits_.earliest_playback_start_seconds = rental_window_start_; + // The rental window is 700 long. + timer_limits_.latest_playback_start_seconds = + timer_limits_.earliest_playback_start_seconds + rental_window_duration_; + // Playback duration is 200 starting from first playback. + timer_limits_.initial_playback_duration_seconds = playback_duration; + if (server_expiry_.soft_rental) { + // The license duration limits any restart. For soft rental window, the + // server will set this to the rental end + playback duration. + // License duration is in seconds since license signed. + timer_limits_.license_duration_seconds = + timer_limits_.latest_playback_start_seconds + playback_duration; + } else { + // The license duration limits any restart. For hard rental window, the + // server will set this to the rental end. + // License duration is in seconds since license signed. + timer_limits_.license_duration_seconds = + timer_limits_.latest_playback_start_seconds; + } + timer_limits_.soft_expiry = server_expiry_.soft_playback; + } + ServerExpiry server_expiry_; +}; + +TEST_P(Odk7DayTest, StartDay3) { + uint64_t timer_value = 0; + // Starting playback within the window should work. + const uint64_t three_days = 300u; + const uint64_t start_time = system_time(rental_window_start_ + three_days); + const uint64_t cutoff_time = + start_time + timer_limits_.initial_playback_duration_seconds; + if (server_expiry_.soft_playback) { + // If the playback expiry is soft, then the timer is disabled. + EXPECT_EQ(ODK_DISABLE_TIMER, + ODK_AttemptFirstPlayback(start_time, &timer_limits_, + &clock_values_, &timer_value)); + } else { + // If the playback expiry is hard, then the timer should be set. + EXPECT_EQ(ODK_SET_TIMER, + ODK_AttemptFirstPlayback(start_time, &timer_limits_, + &clock_values_, &timer_value)); + EXPECT_NEAR(cutoff_time, clock_values_.time_when_timer_expires, kTolerance); + EXPECT_NEAR(cutoff_time - start_time, timer_value, kTolerance); + } + EXPECT_EQ(kActive, clock_values_.status); + EXPECT_EQ(start_time, clock_values_.time_of_first_decrypt); + EXPECT_EQ(start_time, clock_values_.time_of_last_decrypt); + // Try to play before the cutoff time is valid. + const uint64_t valid_play_time = cutoff_time - 50; + EXPECT_EQ(OEMCrypto_SUCCESS, + ODK_UpdateLastPlaybackTime(valid_play_time, &timer_limits_, + &clock_values_)); + EXPECT_EQ(kActive, clock_values_.status); + // First decrypt should NOT change. + EXPECT_EQ(start_time, clock_values_.time_of_first_decrypt); + // Last decrypt should change. + EXPECT_EQ(valid_play_time, clock_values_.time_of_last_decrypt); + if (!server_expiry_.soft_playback) { + EXPECT_NEAR(cutoff_time, clock_values_.time_when_timer_expires, kTolerance); + } +} + +TEST_P(Odk7DayTest, StartDay3Reload) { + uint64_t timer_value = 0; + // Starting playback within the window should work. + const uint64_t three_days = 300u; + const uint64_t start_time = system_time(rental_window_start_ + three_days); + const uint64_t cutoff_time = + start_time + timer_limits_.initial_playback_duration_seconds; + EXPECT_NE(ODK_TIMER_EXPIRED, + ODK_AttemptFirstPlayback(start_time, &timer_limits_, &clock_values_, + &timer_value)); + // ----------------------------------------------------------------------- + // Try to reload and play before the cutoff time is valid. + uint64_t valid_restart_time = cutoff_time - 10; + ReloadClock(valid_restart_time); + EXPECT_EQ(kActive, clock_values_.status); + if (server_expiry_.soft_playback) { + // If the playback expiry is soft, then the timer is disabled. + EXPECT_EQ(ODK_DISABLE_TIMER, + ODK_AttemptFirstPlayback(valid_restart_time, &timer_limits_, + &clock_values_, &timer_value)); + } else { + // If the playback expiry is hard, then the timer should be set. + EXPECT_EQ(ODK_SET_TIMER, + ODK_AttemptFirstPlayback(valid_restart_time, &timer_limits_, + &clock_values_, &timer_value)); + EXPECT_NEAR(cutoff_time, clock_values_.time_when_timer_expires, kTolerance); + EXPECT_NEAR(cutoff_time - valid_restart_time, timer_value, kTolerance); + } + + // Try to play before the cutoff time is valid. + const uint64_t valid_play_time = cutoff_time - 1; + EXPECT_EQ(OEMCrypto_SUCCESS, + ODK_UpdateLastPlaybackTime(valid_play_time, &timer_limits_, + &clock_values_)); + EXPECT_EQ(kActive, clock_values_.status); + // First decrypt should NOT change. + EXPECT_EQ(start_time, clock_values_.time_of_first_decrypt); + // Last decrypt should change. + EXPECT_EQ(valid_play_time, clock_values_.time_of_last_decrypt); + if (!server_expiry_.soft_playback) { + EXPECT_NEAR(cutoff_time, clock_values_.time_when_timer_expires, kTolerance); + } + + // Try to play after the cutoff time is valid for soft expiry, but not hard. + const uint64_t late_play_time = cutoff_time + 1; + if (server_expiry_.soft_playback) { + EXPECT_EQ(OEMCrypto_SUCCESS, + ODK_UpdateLastPlaybackTime(late_play_time, &timer_limits_, + &clock_values_)); + // Last decrypt should change because we were allowed to decrypt. + EXPECT_EQ(late_play_time, clock_values_.time_of_last_decrypt); + } else { + EXPECT_EQ(ODK_TIMER_EXPIRED, + ODK_UpdateLastPlaybackTime(late_play_time, &timer_limits_, + &clock_values_)); + // Last decrypt should NOT change because we were not allowed to decrypt. + EXPECT_EQ(valid_play_time, clock_values_.time_of_last_decrypt); + EXPECT_NEAR(cutoff_time, clock_values_.time_when_timer_expires, kTolerance); + } + // First decrypt should NOT change for either soft or hard. + EXPECT_EQ(start_time, clock_values_.time_of_first_decrypt); + + // ----------------------------------------------------------------------- + // Try to reload after the cutoff time is not valid for soft or hard + // playback expiry. + const uint64_t late_restart_time = cutoff_time + 1; + ReloadClock(late_restart_time); + EXPECT_EQ(kActive, clock_values_.status); + EXPECT_EQ(ODK_TIMER_EXPIRED, + ODK_AttemptFirstPlayback(late_restart_time, &timer_limits_, + &clock_values_, &timer_value)); + EXPECT_EQ(start_time, clock_values_.time_of_first_decrypt); + + // Calling UpdateLastPlaybackTimer should not be done, but if it were done, + // it should also fail. + EXPECT_EQ(ODK_TIMER_EXPIRED, + ODK_UpdateLastPlaybackTime(late_restart_time, &timer_limits_, + &clock_values_)); + // First decrypt should NOT change. + EXPECT_EQ(start_time, clock_values_.time_of_first_decrypt); + // Last decrypt should not change. + if (server_expiry_.soft_playback) { + EXPECT_EQ(late_play_time, clock_values_.time_of_last_decrypt); + } else { + EXPECT_EQ(valid_play_time, clock_values_.time_of_last_decrypt); + } +} + +TEST_P(Odk7DayTest, StartDay6Reload) { + uint64_t timer_value = 0; + // Starting playback within the window should work. + const uint64_t six_days = 600u; + const uint64_t start_time = system_time(rental_window_start_ + six_days); + const uint64_t cutoff_time = + server_expiry_.soft_rental + ? + // If the rental expiry is soft, we can continue playing and + // reloading for the full playback duration. + start_time + timer_limits_.initial_playback_duration_seconds + // If the rental expiry is hard, we can reload only within the + // rental window. + : system_time(timer_limits_.latest_playback_start_seconds); + // Let's double check that math: + if (server_expiry_.soft_rental) { + // If that was not clear, the cutoff = 100 start of window + 600 start time + // + 200 playback duration... + EXPECT_EQ(system_time(900u), cutoff_time); + } else { + // ...and for hard rental, the cutoff = 100 start of window + 700 rental + // duration. + EXPECT_EQ(system_time(800u), cutoff_time); + } + + if (server_expiry_.soft_playback) { + // If the playback expiry is soft, then the timer is disabled. + EXPECT_EQ(ODK_DISABLE_TIMER, + ODK_AttemptFirstPlayback(start_time, &timer_limits_, + &clock_values_, &timer_value)); + } else { + // If the playback expiry is hard, then the timer should be set. + EXPECT_EQ(ODK_SET_TIMER, + ODK_AttemptFirstPlayback(start_time, &timer_limits_, + &clock_values_, &timer_value)); + EXPECT_NEAR(cutoff_time, clock_values_.time_when_timer_expires, kTolerance); + EXPECT_NEAR(cutoff_time - start_time, timer_value, kTolerance); + } + EXPECT_EQ(kActive, clock_values_.status); + EXPECT_EQ(start_time, clock_values_.time_of_first_decrypt); + EXPECT_EQ(start_time, clock_values_.time_of_last_decrypt); + // Try to play before the cutoff time is valid. + uint64_t valid_play_time = cutoff_time - 50; + EXPECT_EQ(OEMCrypto_SUCCESS, + ODK_UpdateLastPlaybackTime(valid_play_time, &timer_limits_, + &clock_values_)); + EXPECT_EQ(kActive, clock_values_.status); + // First decrypt should NOT change. + EXPECT_EQ(start_time, clock_values_.time_of_first_decrypt); + // Last decrypt should change. + EXPECT_EQ(valid_play_time, clock_values_.time_of_last_decrypt); + if (!server_expiry_.soft_playback) { + EXPECT_NEAR(cutoff_time, clock_values_.time_when_timer_expires, kTolerance); + } + + // ----------------------------------------------------------------------- + // Try to reload and play before the cutoff time is valid. + uint64_t valid_restart_time = cutoff_time - 10; + ReloadClock(valid_restart_time); + EXPECT_EQ(kActive, clock_values_.status); + if (server_expiry_.soft_playback) { + // If the playback expiry is soft, then the timer is disabled. + EXPECT_EQ(ODK_DISABLE_TIMER, + ODK_AttemptFirstPlayback(valid_restart_time, &timer_limits_, + &clock_values_, &timer_value)); + } else { + // If the playback expiry is hard, then the timer should be set. + EXPECT_EQ(ODK_SET_TIMER, + ODK_AttemptFirstPlayback(valid_restart_time, &timer_limits_, + &clock_values_, &timer_value)); + EXPECT_NEAR(cutoff_time, clock_values_.time_when_timer_expires, kTolerance); + EXPECT_NEAR(cutoff_time - valid_restart_time, timer_value, kTolerance); + } + + // Try to play before the cutoff time is valid. + valid_play_time = cutoff_time - 1; + EXPECT_EQ(OEMCrypto_SUCCESS, + ODK_UpdateLastPlaybackTime(valid_play_time, &timer_limits_, + &clock_values_)); + EXPECT_EQ(kActive, clock_values_.status); + // First decrypt should NOT change. + EXPECT_EQ(start_time, clock_values_.time_of_first_decrypt); + // Last decrypt should change. + EXPECT_EQ(valid_play_time, clock_values_.time_of_last_decrypt); + if (!server_expiry_.soft_playback) { + EXPECT_NEAR(cutoff_time, clock_values_.time_when_timer_expires, kTolerance); + } + + // Try to play after the cutoff time is valid for soft expiry, but not hard. + const uint64_t late_play_time = cutoff_time + 1; + if (server_expiry_.soft_playback) { + EXPECT_EQ(OEMCrypto_SUCCESS, + ODK_UpdateLastPlaybackTime(late_play_time, &timer_limits_, + &clock_values_)); + // Last decrypt should change because we were allowed to decrypt. + EXPECT_EQ(late_play_time, clock_values_.time_of_last_decrypt); + } else { + EXPECT_EQ(ODK_TIMER_EXPIRED, + ODK_UpdateLastPlaybackTime(late_play_time, &timer_limits_, + &clock_values_)); + // Last decrypt should NOT change because we were not allowed to decrypt. + EXPECT_EQ(valid_play_time, clock_values_.time_of_last_decrypt); + EXPECT_NEAR(cutoff_time, clock_values_.time_when_timer_expires, kTolerance); + } + // First decrypt should NOT change for either soft or hard. + EXPECT_EQ(start_time, clock_values_.time_of_first_decrypt); + + // ----------------------------------------------------------------------- + // Try to reload after the cutoff time is not valid for soft or hard + // playback expiry. + const uint64_t late_restart_time = cutoff_time + 2; + ReloadClock(late_restart_time); + EXPECT_EQ(kActive, clock_values_.status); + EXPECT_EQ(ODK_TIMER_EXPIRED, + ODK_AttemptFirstPlayback(late_restart_time, &timer_limits_, + &clock_values_, &timer_value)); + EXPECT_EQ(start_time, clock_values_.time_of_first_decrypt); + + // Calling UpdateLastPlaybackTimer should not be done, but if it were done, + // it should also fail. + EXPECT_EQ(ODK_TIMER_EXPIRED, + ODK_UpdateLastPlaybackTime(late_restart_time, &timer_limits_, + &clock_values_)); + // First decrypt should NOT change. + EXPECT_EQ(start_time, clock_values_.time_of_first_decrypt); + // Last decrypt should not change. + if (server_expiry_.soft_playback) { + EXPECT_EQ(late_play_time, clock_values_.time_of_last_decrypt); + } else { + EXPECT_EQ(valid_play_time, clock_values_.time_of_last_decrypt); + } +} + +// This test explicitly shows the difference between hard and soft rental +// expiry. This is not an OEMCrypto concept, but it is used on the serer, and +// this test verifies the logic is handled correctly when we translate it into +// OEMCrypto concepts. +TEST_P(Odk7DayTest, StartDay6ReloadDay7) { + uint64_t timer_value = 0; + // Starting playback within the window should work. + const uint64_t six_days = 600u; + const uint64_t seven_days = 700u; + const uint64_t start_time = system_time(rental_window_start_ + six_days); + EXPECT_NE(ODK_TIMER_EXPIRED, + ODK_AttemptFirstPlayback(start_time, &timer_limits_, &clock_values_, + &timer_value)); + + // Reload time is 1 second after end of rental window. + const uint64_t restart_time = + system_time(timer_limits_.latest_playback_start_seconds + 1); + ReloadClock(restart_time); + if (server_expiry_.soft_rental) { + if (server_expiry_.soft_playback) { + // If the playback expiry is soft, then the timer is disabled. + EXPECT_EQ(ODK_DISABLE_TIMER, + ODK_AttemptFirstPlayback(restart_time, &timer_limits_, + &clock_values_, &timer_value)); + } else { + const uint64_t cutoff_time = + start_time + timer_limits_.initial_playback_duration_seconds; + // If the playback expiry is hard, then the timer should be set. + EXPECT_EQ(ODK_SET_TIMER, + ODK_AttemptFirstPlayback(restart_time, &timer_limits_, + &clock_values_, &timer_value)); + EXPECT_NEAR(cutoff_time, clock_values_.time_when_timer_expires, + kTolerance); + EXPECT_NEAR(cutoff_time - restart_time, timer_value, kTolerance); + } + } else { + // For hard rental expiry, reloading after the rental window fails. + EXPECT_EQ(ODK_TIMER_EXPIRED, + ODK_AttemptFirstPlayback(restart_time, &timer_limits_, + &clock_values_, &timer_value)); + } +} + +TEST_P(Odk7DayTest, StartDay8) { + uint64_t timer_value = 0; + // Starting playback after the rental window should not work. + const uint64_t eight_days = 800u; + const uint64_t start_time = system_time(rental_window_start_ + eight_days); + EXPECT_EQ(ODK_TIMER_EXPIRED, + ODK_AttemptFirstPlayback(start_time, &timer_limits_, &clock_values_, + &timer_value)); + EXPECT_EQ(kUnused, clock_values_.status); + EXPECT_EQ(0u, clock_values_.time_of_first_decrypt); + EXPECT_EQ(0u, clock_values_.time_of_last_decrypt); + // Calling UpdateLastPlaybackTimer should not be done, but if it were done, + // it should also fail. + EXPECT_EQ(ODK_TIMER_EXPIRED, ODK_UpdateLastPlaybackTime( + start_time, &timer_limits_, &clock_values_)); + EXPECT_EQ(kUnused, clock_values_.status); + EXPECT_EQ(0u, clock_values_.time_of_first_decrypt); + EXPECT_EQ(0u, clock_values_.time_of_last_decrypt); +} + +INSTANTIATE_TEST_CASE_P(OdkSoftHard, Odk7DayTest, + Values(ServerExpiry({true, true}), + ServerExpiry({true, false}), + ServerExpiry({false, true}), + ServerExpiry({false, false}))); + +// ************************************************************************ + +// ************************************************************************ +// TODO(b/140765031): Cover all tests in Use Cases document. +// Limited Duration License +// 7 day with renewal. +// Streaming with renewal +// Persistent with renewal + +} // namespace odk_test diff --git a/oemcrypto/oemcrypto_unittests.gyp b/oemcrypto/oemcrypto_unittests.gyp index 6c09275..d62921a 100644 --- a/oemcrypto/oemcrypto_unittests.gyp +++ b/oemcrypto/oemcrypto_unittests.gyp @@ -19,11 +19,14 @@ 'type': 'executable', 'sources': [ 'test/oemcrypto_test_main.cpp', + 'odk/kdo/src/oec_util.cpp', '<(platform_specific_dir)/file_store.cpp', '<(platform_specific_dir)/log.cpp', '<(util_dir)/src/platform.cpp', '<(util_dir)/src/rw_lock.cpp', '<(util_dir)/src/string_conversions.cpp', + '<(util_dir)/test/test_sleep.cpp', + '<(util_dir)/test/test_clock.cpp', ], 'includes': [ 'test/oemcrypto_unittests.gypi', @@ -35,22 +38,5 @@ '<(gmock_dependency)', ], }, - { - 'target_name': 'odk_tests', - 'type': 'executable', - 'includes': [ - 'odk/test/odk_test.gypi', - 'odk/src/odk.gypi', - ], - 'include_dirs': [ - 'include', - 'odk/include', - 'odk/src', - ], - 'dependencies': [ - '<(gtest_dependency)', - '<(gmock_main_dependency)', - ], - }, ], } diff --git a/oemcrypto/ref/src/oemcrypto_engine_ref.cpp b/oemcrypto/ref/src/oemcrypto_engine_ref.cpp index bdc8d90..de42f00 100644 --- a/oemcrypto/ref/src/oemcrypto_engine_ref.cpp +++ b/oemcrypto/ref/src/oemcrypto_engine_ref.cpp @@ -7,7 +7,6 @@ #include "oemcrypto_engine_ref.h" #include -#include #include #include #include @@ -17,6 +16,7 @@ #include #include +#include "clock.h" #include "keys.h" #include "log.h" #include "oemcrypto_key_ref.h" @@ -91,20 +91,23 @@ SessionContext* CryptoEngine::FindSession(SessionId sid) { return nullptr; } -time_t CryptoEngine::OnlineTime() { +int64_t CryptoEngine::OnlineTime() { // Use the monotonic clock for times that don't have to be stable across // device boots. - std::chrono::steady_clock clock; - return clock.now().time_since_epoch() / std::chrono::seconds(1); + int64_t now = wvcdm::Clock().GetCurrentTime(); + static int64_t then = now; + if (now < then) now = then; + then = now; + return now; } -time_t CryptoEngine::RollbackCorrectedOfflineTime() { +int64_t CryptoEngine::RollbackCorrectedOfflineTime() { struct TimeInfo { // The max time recorded through this function call. - time_t previous_time; + int64_t previous_time; // If the wall time is rollbacked to before the previous_time, this member // is updated to reflect the offset. - time_t rollback_offset; + int64_t rollback_offset; // Pad the struct so that TimeInfo is a multiple of 16. uint8_t padding[16 - (2 * sizeof(time_t)) % 16]; }; @@ -135,7 +138,7 @@ time_t CryptoEngine::RollbackCorrectedOfflineTime() { if (!file) { LOGE("RollbackCorrectedOfflineTime: File open failed: %s", filename.c_str()); - return time(nullptr); + return OnlineTime(); } file->Read(reinterpret_cast(&encrypted_buffer[0]), sizeof(TimeInfo)); // Decrypt the encrypted TimeInfo buffer. @@ -147,9 +150,9 @@ time_t CryptoEngine::RollbackCorrectedOfflineTime() { memcpy(&time_info, &clear_buffer[0], sizeof(TimeInfo)); } - time_t current_time; + int64_t current_time; // Add any time offsets in the past to the current time. - current_time = time(nullptr) + time_info.rollback_offset; + current_time = OnlineTime() + time_info.rollback_offset; if (time_info.previous_time > current_time) { // Time has been rolled back. // Update the rollback offset. @@ -174,7 +177,7 @@ time_t CryptoEngine::RollbackCorrectedOfflineTime() { if (!file) { LOGE("RollbackCorrectedOfflineTime: File open failed: %s", filename.c_str()); - return time(nullptr); + return OnlineTime(); } file->Write(reinterpret_cast(&encrypted_buffer[0]), sizeof(TimeInfo)); @@ -199,19 +202,19 @@ OEMCrypto_HDCP_Capability CryptoEngine::config_maximum_hdcp_capability() { } OEMCryptoResult CryptoEngine::SetDestination( - OEMCrypto_DestBufferDesc* out_description, size_t data_length, + const OEMCrypto_DestBufferDesc* out_description, size_t data_length, uint8_t subsample_flags) { size_t max_length = 0; switch (out_description->type) { case OEMCrypto_BufferType_Clear: destination_ = out_description->buffer.clear.address; - max_length = out_description->buffer.clear.max_length; + max_length = out_description->buffer.clear.address_length; break; case OEMCrypto_BufferType_Secure: destination_ = reinterpret_cast(out_description->buffer.secure.handle) + out_description->buffer.secure.offset; - max_length = out_description->buffer.secure.max_length - + max_length = out_description->buffer.secure.handle_length - out_description->buffer.secure.offset; break; case OEMCrypto_BufferType_Direct: diff --git a/oemcrypto/ref/src/oemcrypto_engine_ref.h b/oemcrypto/ref/src/oemcrypto_engine_ref.h index 182873c..963f0eb 100644 --- a/oemcrypto/ref/src/oemcrypto_engine_ref.h +++ b/oemcrypto/ref/src/oemcrypto_engine_ref.h @@ -31,6 +31,8 @@ typedef std::map ActiveSessions; class CryptoEngine { public: + static const uint32_t kApiVersion = 16; + // This is like a factory method, except we choose which version to use at // compile time. It is defined in several source files. The build system // should choose which one to use by only linking in the correct one. @@ -87,9 +89,11 @@ class CryptoEngine { return kMaxSupportedOEMCryptoSessions; } - time_t OnlineTime(); + // System clock, measuring time in seconds. + int64_t OnlineTime(); - time_t RollbackCorrectedOfflineTime(); + // System clock with antirollback protection, measuring time in seconds. + int64_t RollbackCorrectedOfflineTime(); // Verify that this nonce does not collide with another nonce in any session. virtual bool NonceCollision(uint32_t nonce); @@ -188,20 +192,22 @@ class CryptoEngine { virtual uint32_t resource_rating() { return 1; } // Set destination pointer based on the output destination description. - OEMCryptoResult SetDestination(OEMCrypto_DestBufferDesc* out_description, - size_t data_length, uint8_t subsample_flags); + OEMCryptoResult SetDestination( + const OEMCrypto_DestBufferDesc* out_description, size_t data_length, + uint8_t subsample_flags); // The current destination. uint8_t* destination() { return destination_; } // Subclasses can adjust the destination -- for use in testing. - virtual void adjust_destination(OEMCrypto_DestBufferDesc* out_description, - size_t data_length, uint8_t subsample_flags) { - } + virtual void adjust_destination( + const OEMCrypto_DestBufferDesc* out_description, size_t data_length, + uint8_t subsample_flags) {} // Push destination buffer to output -- used by subclasses for testing. virtual OEMCryptoResult PushDestination( - OEMCrypto_DestBufferDesc* out_description, uint8_t subsample_flags) { + const OEMCrypto_DestBufferDesc* out_description, + uint8_t subsample_flags) { return OEMCrypto_SUCCESS; } diff --git a/oemcrypto/ref/src/oemcrypto_keybox_ref.cpp b/oemcrypto/ref/src/oemcrypto_keybox_ref.cpp index d7ec635..0e4a630 100644 --- a/oemcrypto/ref/src/oemcrypto_keybox_ref.cpp +++ b/oemcrypto/ref/src/oemcrypto_keybox_ref.cpp @@ -18,7 +18,10 @@ namespace wvoec_ref { -WvKeybox::WvKeybox() : loaded_(false) {} +WvKeybox::WvKeybox() : loaded_(false) { + static std::string fake_device_id = "device_with_no_keybox"; + device_id_.assign(fake_device_id.begin(), fake_device_id.end()); +} KeyboxError WvKeybox::Validate() { if (!loaded_) { diff --git a/oemcrypto/ref/src/oemcrypto_ref.cpp b/oemcrypto/ref/src/oemcrypto_ref.cpp index dc00cc5..cd3a4da 100644 --- a/oemcrypto/ref/src/oemcrypto_ref.cpp +++ b/oemcrypto/ref/src/oemcrypto_ref.cpp @@ -207,116 +207,55 @@ OEMCRYPTO_API OEMCryptoResult OEMCrypto_GenerateNonce(OEMCrypto_SESSION session, return OEMCrypto_SUCCESS; } -OEMCRYPTO_API OEMCryptoResult OEMCrypto_SignLicenseRequest( - OEMCrypto_SESSION session, const uint8_t* protobuf_message, - size_t protobuf_message_length, uint8_t* core_message, +OEMCRYPTO_API OEMCryptoResult OEMCrypto_PrepAndSignLicenseRequest( + OEMCrypto_SESSION session, uint8_t* message, size_t message_length, size_t* core_message_length, uint8_t* signature, size_t* signature_length) { if (crypto_engine == nullptr) { - LOGE("OEMCrypto_SignLicenseRequest: OEMCrypto Not Initialized."); + LOGE("OEMCrypto Not Initialized."); return OEMCrypto_ERROR_UNKNOWN_FAILURE; } - if (signature_length == nullptr || core_message_length == nullptr) { - return OEMCrypto_ERROR_UNKNOWN_FAILURE; - } - if (*signature_length < SHA256_DIGEST_LENGTH) { - *signature_length = SHA256_DIGEST_LENGTH; - return OEMCrypto_ERROR_SHORT_BUFFER; - } - - if (protobuf_message == nullptr || protobuf_message_length == 0 || - signature == nullptr) { - LOGE("[OEMCrypto_SignLicenseRequest(): OEMCrypto_ERROR_INVALID_CONTEXT]"); - return OEMCrypto_ERROR_INVALID_CONTEXT; - } - SessionContext* session_ctx = crypto_engine->FindSession(session); if (session_ctx == nullptr || !session_ctx->isValid()) { - LOGE("[OEMCrypto_SignLicenseRequest(): ERROR_INVALID_SESSION]"); + LOGE("ERROR_INVALID_SESSION"); return OEMCrypto_ERROR_INVALID_SESSION; } - - // TODO(b/135288420): Add core message functionality. - *core_message_length = 0; - - if (session_ctx->GenerateSignature(protobuf_message, protobuf_message_length, - signature, signature_length, false)) { - return OEMCrypto_SUCCESS; - } - return OEMCrypto_ERROR_UNKNOWN_FAILURE; + return session_ctx->PrepAndSignLicenseRequest(message, message_length, + core_message_length, signature, + signature_length); } -OEMCRYPTO_API OEMCryptoResult OEMCrypto_SignRenewalRequest( - OEMCrypto_SESSION session, const uint8_t* protobuf_message, - size_t protobuf_message_length, uint8_t* core_message, +OEMCRYPTO_API OEMCryptoResult OEMCrypto_PrepAndSignRenewalRequest( + OEMCrypto_SESSION session, uint8_t* message, size_t message_length, size_t* core_message_length, uint8_t* signature, size_t* signature_length) { if (crypto_engine == nullptr) { - LOGE("OEMCrypto_SignRenewalRequest: OEMCrypto Not Initialized."); + LOGE("OEMCrypto Not Initialized."); return OEMCrypto_ERROR_UNKNOWN_FAILURE; } - if (signature_length == nullptr || core_message_length == nullptr) { - return OEMCrypto_ERROR_UNKNOWN_FAILURE; - } - if (*signature_length < SHA256_DIGEST_LENGTH) { - *signature_length = SHA256_DIGEST_LENGTH; - return OEMCrypto_ERROR_SHORT_BUFFER; - } - if (protobuf_message == nullptr || protobuf_message_length == 0 || - signature == nullptr) { - LOGE("[OEMCrypto_SignRenewalRequest(): OEMCrypto_ERROR_INVALID_CONTEXT]"); - return OEMCrypto_ERROR_INVALID_CONTEXT; - } - SessionContext* session_ctx = crypto_engine->FindSession(session); if (session_ctx == nullptr || !session_ctx->isValid()) { - LOGE("[OEMCrypto_SignRenewalRequest(): ERROR_INVALID_SESSION]"); + LOGE("ERROR_INVALID_SESSION"); return OEMCrypto_ERROR_INVALID_SESSION; } - - // TODO(b/135288420): Add core message functionality. - *core_message_length = 0; - - if (session_ctx->GenerateSignature(protobuf_message, protobuf_message_length, - signature, signature_length, true)) { - return OEMCrypto_SUCCESS; - } - return OEMCrypto_ERROR_UNKNOWN_FAILURE; + return session_ctx->PrepAndSignRenewalRequest(message, message_length, + core_message_length, signature, + signature_length); } -OEMCRYPTO_API OEMCryptoResult OEMCrypto_SignProvisioningRequest( - OEMCrypto_SESSION session, const uint8_t* protobuf_message, - size_t protobuf_message_length, uint8_t* core_message, +OEMCRYPTO_API OEMCryptoResult OEMCrypto_PrepAndSignProvisioningRequest( + OEMCrypto_SESSION session, uint8_t* message, size_t message_length, size_t* core_message_length, uint8_t* signature, size_t* signature_length) { if (crypto_engine == nullptr) { - LOGE("OEMCrypto_SignProvisioningRequest: OEMCrypto Not Initialized."); + LOGE("OEMCrypto Not Initialized."); return OEMCrypto_ERROR_UNKNOWN_FAILURE; } - if (signature_length == nullptr || core_message_length == nullptr) { - return OEMCrypto_ERROR_UNKNOWN_FAILURE; - } - if (*signature_length < SHA256_DIGEST_LENGTH) { - *signature_length = SHA256_DIGEST_LENGTH; - return OEMCrypto_ERROR_SHORT_BUFFER; - } - if (protobuf_message == nullptr || protobuf_message_length == 0 || - signature == nullptr) { - LOGE("OEMCrypto_ERROR_INVALID_CONTEXT"); - return OEMCrypto_ERROR_INVALID_CONTEXT; - } - SessionContext* session_ctx = crypto_engine->FindSession(session); if (session_ctx == nullptr || !session_ctx->isValid()) { - LOGE("[OEMCrypto_SignProvisioningRequest(): ERROR_INVALID_SESSION]"); + LOGE("ERROR_INVALID_SESSION"); return OEMCrypto_ERROR_INVALID_SESSION; } - - // TODO(b/135288420): Add core message functionality. - *core_message_length = 0; - - if (session_ctx->GenerateSignature(protobuf_message, protobuf_message_length, - signature, signature_length, false)) { - return OEMCrypto_SUCCESS; - } - return OEMCrypto_ERROR_UNKNOWN_FAILURE; + return session_ctx->PrepAndSignProvisioningRequest( + message, message_length, core_message_length, signature, + signature_length); } bool RangeCheck(const uint8_t* message, uint32_t message_length, @@ -404,8 +343,8 @@ OEMCRYPTO_API OEMCryptoResult OEMCrypto_LoadKeys( OEMCRYPTO_API OEMCryptoResult OEMCrypto_LoadEntitledContentKeys( OEMCrypto_SESSION session, const uint8_t* message, size_t message_length, - const OEMCrypto_EntitledContentKeyObject* key_array, - size_t key_array_length) { + size_t key_array_length, + const OEMCrypto_EntitledContentKeyObject* key_array) { if (key_array_length == 0) { LOGE("[OEMCrypto_LoadEntitledContentKeys(): key_array is empty."); return OEMCrypto_SUCCESS; @@ -437,7 +376,7 @@ OEMCRYPTO_API OEMCryptoResult OEMCrypto_LoadEntitledContentKeys( } } return session_ctx->LoadEntitledContentKeys(message, message_length, - key_array, key_array_length); + key_array_length, key_array); } OEMCRYPTO_API OEMCryptoResult OEMCrypto_RefreshKeys( @@ -584,17 +523,18 @@ OEMCRYPTO_API OEMCryptoResult OEMCrypto_SelectKey( return session_ctx->SelectContentKey(key_id_str, cipher_mode); } -OEMCRYPTO_API OEMCryptoResult OEMCrypto_DecryptCENC( +OEMCRYPTO_API OEMCryptoResult OEMCrypto_DecryptCENC_V15( OEMCrypto_SESSION session, const uint8_t* data_addr, size_t data_length, bool is_encrypted, const uint8_t* iv, size_t block_offset, - OEMCrypto_DestBufferDesc* out_buffer, - const OEMCrypto_CENCEncryptPatternDesc* pattern, uint8_t subsample_flags) { + OEMCrypto_DestBufferDesc* out_buffer_descriptor, + const OEMCrypto_CENCEncryptPatternDesc_V15* pattern, + uint8_t subsample_flags) { if (crypto_engine == nullptr) { LOGE("OEMCrypto_DecryptCENC: OEMCrypto Not Initialized."); return OEMCrypto_ERROR_UNKNOWN_FAILURE; } if (data_addr == nullptr || data_length == 0 || iv == nullptr || - out_buffer == nullptr) { + out_buffer_descriptor == nullptr) { LOGE("[OEMCrypto_DecryptCENC(): OEMCrypto_ERROR_INVALID_CONTEXT]"); return OEMCrypto_ERROR_INVALID_CONTEXT; } @@ -605,8 +545,8 @@ OEMCRYPTO_API OEMCryptoResult OEMCrypto_DecryptCENC( LOGE("[OEMCrypto_DecryptCENC(): OEMCrypto_ERROR_BUFFER_TOO_LARGE]"); return OEMCrypto_ERROR_BUFFER_TOO_LARGE; } - OEMCryptoResult status = - crypto_engine->SetDestination(out_buffer, data_length, subsample_flags); + OEMCryptoResult status = crypto_engine->SetDestination( + out_buffer_descriptor, data_length, subsample_flags); if (status != OEMCrypto_SUCCESS) { LOGE("[OEMCrypto_DecryptCENC(): destination status: %d]", status); return status; @@ -624,21 +564,23 @@ OEMCRYPTO_API OEMCryptoResult OEMCrypto_DecryptCENC( return OEMCrypto_ERROR_INVALID_SESSION; } - OEMCryptoResult result = session_ctx->DecryptCENC( + OEMCryptoResult result = session_ctx->DecryptCENC_V15( iv, block_offset, pattern, data_addr, data_length, is_encrypted, - crypto_engine->destination(), out_buffer->type, subsample_flags); + crypto_engine->destination(), out_buffer_descriptor->type, + subsample_flags); if (result != OEMCrypto_SUCCESS) return result; - return crypto_engine->PushDestination(out_buffer, subsample_flags); + return crypto_engine->PushDestination(out_buffer_descriptor, subsample_flags); } OEMCRYPTO_API OEMCryptoResult OEMCrypto_CopyBuffer( OEMCrypto_SESSION session, const uint8_t* data_addr, size_t data_length, - OEMCrypto_DestBufferDesc* out_buffer, uint8_t subsample_flags) { + const OEMCrypto_DestBufferDesc* out_buffer_descriptor, + uint8_t subsample_flags) { if (crypto_engine == nullptr) { LOGE("OEMCrypto_CopyBuffer: OEMCrypto Not Initialized."); return OEMCrypto_ERROR_UNKNOWN_FAILURE; } - if (data_addr == nullptr || out_buffer == nullptr) { + if (data_addr == nullptr || out_buffer_descriptor == nullptr) { LOGE("[OEMCrypto_CopyBuffer(): OEMCrypto_ERROR_INVALID_CONTEXT]"); return OEMCrypto_ERROR_INVALID_CONTEXT; } @@ -649,13 +591,13 @@ OEMCRYPTO_API OEMCryptoResult OEMCrypto_CopyBuffer( LOGE("[OEMCrypto_CopyBuffer(): OEMCrypto_ERROR_BUFFER_TOO_LARGE]"); return OEMCrypto_ERROR_BUFFER_TOO_LARGE; } - OEMCryptoResult status = - crypto_engine->SetDestination(out_buffer, data_length, subsample_flags); + OEMCryptoResult status = crypto_engine->SetDestination( + out_buffer_descriptor, data_length, subsample_flags); if (status != OEMCrypto_SUCCESS) return status; if (crypto_engine->destination() != nullptr) { memmove(crypto_engine->destination(), data_addr, data_length); } - return crypto_engine->PushDestination(out_buffer, subsample_flags); + return crypto_engine->PushDestination(out_buffer_descriptor, subsample_flags); } OEMCRYPTO_API OEMCryptoResult OEMCrypto_WrapKeyboxOrOEMCert( @@ -750,7 +692,7 @@ OEMCrypto_LoadOEMPrivateKey(OEMCrypto_SESSION session) { return OEMCrypto_ERROR_UNKNOWN_FAILURE; } if (crypto_engine->config_provisioning_method() != OEMCrypto_OEMCertificate) { - LOGE("Provisioning method = %d.", + LOGE("Unexpected provisioning method = %d.", crypto_engine->config_provisioning_method()); return OEMCrypto_ERROR_NOT_IMPLEMENTED; } @@ -769,7 +711,7 @@ OEMCRYPTO_API OEMCryptoResult OEMCrypto_GetOEMPublicCertificate( return OEMCrypto_ERROR_UNKNOWN_FAILURE; } if (crypto_engine->config_provisioning_method() != OEMCrypto_OEMCertificate) { - LOGE("Provisioning method = %d.", + LOGE("Unexpected provisioning method = %d.", crypto_engine->config_provisioning_method()); return OEMCrypto_ERROR_NOT_IMPLEMENTED; } @@ -782,11 +724,6 @@ OEMCRYPTO_API OEMCryptoResult OEMCrypto_GetDeviceID(uint8_t* deviceID, LOGE("OEMCrypto_GetDeviceID: OEMCrypto Not Initialized."); return OEMCrypto_ERROR_UNKNOWN_FAILURE; } - if (crypto_engine->config_provisioning_method() != OEMCrypto_Keybox) { - return OEMCrypto_ERROR_NOT_IMPLEMENTED; - } - // Devices that do not support a keybox should use some other method to - // store the device id. const std::vector& dev_id_string = crypto_engine->DeviceRootId(); if (dev_id_string.empty()) { LOGE("[OEMCrypto_GetDeviceId(): Keybox Invalid]"); @@ -849,7 +786,8 @@ OEMCRYPTO_API OEMCryptoResult OEMCrypto_GetRandom(uint8_t* randomData, return OEMCrypto_ERROR_UNKNOWN_FAILURE; } -OEMCRYPTO_API OEMCryptoResult OEMCrypto_RewrapDeviceRSAKey30( +// This function is no longer exported -- it is only used by LoadProvisioning. +OEMCryptoResult OEMCrypto_RewrapDeviceRSAKey30( OEMCrypto_SESSION session, const uint32_t* unaligned_nonce, const uint8_t* encrypted_message_key, size_t encrypted_message_key_length, const uint8_t* enc_rsa_key, size_t enc_rsa_key_length, @@ -871,7 +809,7 @@ OEMCRYPTO_API OEMCryptoResult OEMCrypto_RewrapDeviceRSAKey30( // key are the same size -- just encrypted with different keys. // We add 32 bytes for a context, 32 for iv, and 32 bytes for a signature. // Important: This layout must match OEMCrypto_LoadDeviceRSAKey below. - size_t buffer_size = enc_rsa_key_length + sizeof(WrappedRSAKey); + const size_t buffer_size = enc_rsa_key_length + sizeof(WrappedRSAKey); if (wrapped_rsa_key == nullptr || *wrapped_rsa_key_length < buffer_size) { *wrapped_rsa_key_length = buffer_size; @@ -915,11 +853,8 @@ OEMCRYPTO_API OEMCryptoResult OEMCrypto_RewrapDeviceRSAKey30( } size_t padding = pkcs8_rsa_key[enc_rsa_key_length - 1]; if (padding > 16) { - LOGE( - "[OEMCrypto_RewrapDeviceRSAKey30(): " - "Encrypted RSA has bad padding: %d]", - padding); - return OEMCrypto_ERROR_INVALID_RSA_KEY; + // Do not return an error at this point, to avoid a padding oracle attack. + padding = 0; } size_t rsa_key_length = enc_rsa_key_length - padding; if (!session_ctx->LoadRSAKey(&pkcs8_rsa_key[0], rsa_key_length)) { @@ -965,7 +900,8 @@ OEMCRYPTO_API OEMCryptoResult OEMCrypto_RewrapDeviceRSAKey30( return OEMCrypto_SUCCESS; } -OEMCRYPTO_API OEMCryptoResult OEMCrypto_RewrapDeviceRSAKey( +// This function is no longer exported -- it is only used by LoadProvisioning. +OEMCryptoResult OEMCrypto_RewrapDeviceRSAKey( OEMCrypto_SESSION session, const uint8_t* message, size_t message_length, const uint8_t* signature, size_t signature_length, const uint32_t* unaligned_nonce, const uint8_t* enc_rsa_key, @@ -990,7 +926,7 @@ OEMCRYPTO_API OEMCryptoResult OEMCrypto_RewrapDeviceRSAKey( // key are the same size -- just encrypted with different keys. // We add 32 bytes for a context, 32 for iv, and 32 bytes for a signature. // Important: This layout must match OEMCrypto_LoadDeviceRSAKey below. - size_t buffer_size = enc_rsa_key_length + sizeof(WrappedRSAKey); + const size_t buffer_size = enc_rsa_key_length + sizeof(WrappedRSAKey); if (wrapped_rsa_key == nullptr || *wrapped_rsa_key_length < buffer_size) { *wrapped_rsa_key_length = buffer_size; @@ -1013,40 +949,33 @@ OEMCRYPTO_API OEMCryptoResult OEMCrypto_RewrapDeviceRSAKey( return OEMCrypto_ERROR_INVALID_CONTEXT; } - // Range check - if (!RangeCheck(message, message_length, - reinterpret_cast(unaligned_nonce), - sizeof(uint32_t), true) || - !RangeCheck(message, message_length, enc_rsa_key, enc_rsa_key_length, - true) || - !RangeCheck(message, message_length, enc_rsa_key_iv, wvoec::KEY_IV_SIZE, true)) { - LOGE("[OEMCrypto_RewrapDeviceRSAKey(): - range check.]"); - return OEMCrypto_ERROR_INVALID_CONTEXT; + // verify signature. + if (!session_ctx->ValidateMessage(message, message_length, signature, + signature_length)) { + LOGE("[RewrapDeviceRSAKey(): Could not verify signature]"); + return OEMCrypto_ERROR_SIGNATURE_FAILURE; } + // Range check performed by ODK library. + // Validate nonce if (!session_ctx->CheckNonce(nonce)) { return OEMCrypto_ERROR_INVALID_NONCE; } - // Decrypt RSA key. + // Decrypt RSA key and verify it. std::vector pkcs8_rsa_key(enc_rsa_key_length); if (!session_ctx->DecryptRSAKey(enc_rsa_key, enc_rsa_key_length, enc_rsa_key_iv, &pkcs8_rsa_key[0])) { return OEMCrypto_ERROR_INVALID_RSA_KEY; } + size_t padding = pkcs8_rsa_key[enc_rsa_key_length - 1]; if (padding > 16) { - LOGE("[RewrapDeviceRSAKey(): Encrypted RSA has bad padding: %d]", padding); - return OEMCrypto_ERROR_INVALID_RSA_KEY; + // Do not return an error at this point, to avoid a padding oracle attack. + padding = 0; } size_t rsa_key_length = enc_rsa_key_length - padding; - // verify signature, verify RSA key, and load it. - if (!session_ctx->ValidateMessage(message, message_length, signature, - signature_length)) { - LOGE("[RewrapDeviceRSAKey(): Could not verify signature]"); - return OEMCrypto_ERROR_SIGNATURE_FAILURE; - } if (!session_ctx->LoadRSAKey(&pkcs8_rsa_key[0], rsa_key_length)) { return OEMCrypto_ERROR_INVALID_RSA_KEY; } @@ -1085,6 +1014,84 @@ OEMCRYPTO_API OEMCryptoResult OEMCrypto_RewrapDeviceRSAKey( return OEMCrypto_SUCCESS; } +OEMCRYPTO_API OEMCryptoResult OEMCrypto_LoadProvisioning( + OEMCrypto_SESSION session, const uint8_t* message, size_t message_length, + size_t core_message_length, const uint8_t* signature, + size_t signature_length, uint8_t* wrapped_private_key, + size_t* wrapped_private_key_length) { + if (crypto_engine == nullptr) { + LOGE("OEMCrypto Not Initialized"); + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + if (wrapped_private_key_length == nullptr) { + LOGE("OEMCrypto_ERROR_INVALID_CONTEXT"); + return OEMCrypto_ERROR_INVALID_CONTEXT; + } + if (message == nullptr || message_length == 0 || signature == nullptr || + signature_length == 0) { + LOGE("OEMCrypto_ERROR_INVALID_CONTEXT"); + return OEMCrypto_ERROR_INVALID_CONTEXT; + } + 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; + } + std::vector device_id = crypto_engine->DeviceRootId(); + ODK_ParsedProvisioning parsed_response; + const uint32_t nonce = session_ctx->nonce(); + const OEMCryptoResult result = + ODK_ParseProvisioning(message, message_length, core_message_length, + &(session_ctx->nonce_values()), device_id.data(), + device_id.size(), &parsed_response); + if (result != OEMCrypto_SUCCESS) { + LOGE("ODK Error %d", result); + return result; + } + + // For the reference implementation, the wrapped key and the encrypted + // key are the same size -- just encrypted with different keys. + // We add 32 bytes for a context, 32 for iv, and 32 bytes for a signature. + // Important: This layout must match OEMCrypto_LoadDeviceRSAKey below. + const size_t buffer_size = + parsed_response.enc_private_key.length + sizeof(WrappedRSAKey); + + if (wrapped_private_key == nullptr || + *wrapped_private_key_length < buffer_size) { + *wrapped_private_key_length = buffer_size; + return OEMCrypto_ERROR_SHORT_BUFFER; + } + *wrapped_private_key_length = + buffer_size; // Tell caller how much space we used. + const uint8_t* message_body = message + core_message_length; + if (crypto_engine->config_provisioning_method() == OEMCrypto_Keybox) { + return OEMCrypto_RewrapDeviceRSAKey( + session, message, message_length, signature, signature_length, &nonce, + message_body + parsed_response.enc_private_key.offset, + parsed_response.enc_private_key.length, + message_body + parsed_response.enc_private_key_iv.offset, + wrapped_private_key, wrapped_private_key_length); + } else if (crypto_engine->config_provisioning_method() == + OEMCrypto_OEMCertificate) { + return OEMCrypto_RewrapDeviceRSAKey30( + session, &nonce, + message_body + parsed_response.encrypted_message_key.offset, + parsed_response.encrypted_message_key.length, + message_body + parsed_response.enc_private_key.offset, + parsed_response.enc_private_key.length, + message_body + parsed_response.enc_private_key_iv.offset, + wrapped_private_key, wrapped_private_key_length); + } else { + LOGE("Invalid provisioning method: %d.", + crypto_engine->config_provisioning_method()); + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } +} + OEMCRYPTO_API OEMCryptoResult OEMCrypto_LoadDeviceRSAKey( OEMCrypto_SESSION session, const uint8_t* wrapped_rsa_key, size_t wrapped_rsa_key_length) { @@ -1127,6 +1134,13 @@ OEMCRYPTO_API OEMCryptoResult OEMCrypto_LoadDeviceRSAKey( context)) { return OEMCrypto_ERROR_UNKNOWN_FAILURE; } + // verify signature. + if (!session_ctx->ValidateMessage( + wrapped->context, wrapped_rsa_key_length - sizeof(wrapped->signature), + wrapped->signature, sizeof(wrapped->signature))) { + LOGE("[LoadDeviceRSAKey(): Could not verify signature]"); + return OEMCrypto_ERROR_SIGNATURE_FAILURE; + } // Decrypt RSA key. std::vector pkcs8_rsa_key(wrapped_rsa_key_length - sizeof(wrapped->signature)); @@ -1137,17 +1151,10 @@ OEMCRYPTO_API OEMCryptoResult OEMCrypto_LoadDeviceRSAKey( } size_t padding = pkcs8_rsa_key[enc_rsa_key_length - 1]; if (padding > 16) { - LOGE("[LoadDeviceRSAKey(): Encrypted RSA has bad padding: %d]", padding); - return OEMCrypto_ERROR_INVALID_RSA_KEY; + // Do not return an error at this point, to avoid a padding oracle attack. + padding = 0; } size_t rsa_key_length = enc_rsa_key_length - padding; - // verify signature. - if (!session_ctx->ValidateMessage( - wrapped->context, wrapped_rsa_key_length - sizeof(wrapped->signature), - wrapped->signature, sizeof(wrapped->signature))) { - LOGE("[LoadDeviceRSAKey(): Could not verify signature]"); - return OEMCrypto_ERROR_SIGNATURE_FAILURE; - } if (!session_ctx->LoadRSAKey(&pkcs8_rsa_key[0], rsa_key_length)) { return OEMCrypto_ERROR_INVALID_RSA_KEY; } @@ -1239,7 +1246,9 @@ OEMCRYPTO_API OEMCryptoResult OEMCrypto_DeriveKeysFromSessionKey( return OEMCrypto_SUCCESS; } -OEMCRYPTO_API uint32_t OEMCrypto_APIVersion() { return 16; } +OEMCRYPTO_API uint32_t OEMCrypto_APIVersion() { + return CryptoEngine::kApiVersion; +} OEMCRYPTO_API uint8_t OEMCrypto_Security_Patch_Level() { uint8_t security_patch_level = crypto_engine->config_security_patch_level(); diff --git a/oemcrypto/ref/src/oemcrypto_session.cpp b/oemcrypto/ref/src/oemcrypto_session.cpp index c08a86d..f5c4bd7 100644 --- a/oemcrypto/ref/src/oemcrypto_session.cpp +++ b/oemcrypto/ref/src/oemcrypto_session.cpp @@ -157,6 +157,32 @@ EntitlementKey* EntitlementKeysContext::GetEntitlementKey( /***************************************/ +SessionContext::SessionContext(CryptoEngine* ce, SessionId sid, + const RSA_shared_ptr& rsa_key) + : valid_(true), + ce_(ce), + id_(sid), + current_content_key_(nullptr), + session_keys_(nullptr), + rsa_key_(rsa_key), + allowed_schemes_(kSign_RSASSA_PSS), + timer_limits_(), + clock_values_(), + usage_entry_(nullptr), + srm_requirements_status_(NoSRMVersion), + usage_entry_status_(kNoUsageEntry), + compute_hash_(false), + current_hash_(0), + bad_frame_number_(0), + hash_error_(OEMCrypto_SUCCESS), + state_nonce_created_(false), + state_request_signed_(false), + state_response_loaded_(false) { + nonce_values_.api_version = 16; + nonce_values_.nonce = 0; + nonce_values_.session_id = sid; +} + SessionContext::~SessionContext() { if (usage_entry_) { delete usage_entry_; @@ -287,130 +313,257 @@ bool SessionContext::RSADeriveKeys( return DeriveKeys(session_key_, mac_key_context, enc_key_context); } +OEMCryptoResult SessionContext::PrepAndSignLicenseRequest( + uint8_t* message, size_t message_length, size_t* core_message_length, + uint8_t* signature, size_t* signature_length) { + if (signature_length == nullptr || core_message_length == nullptr) { + return OEMCrypto_ERROR_INVALID_CONTEXT; + } + const size_t required_signature_size = CertSignatureSize(); + OEMCryptoResult result = ODK_PrepareCoreLicenseRequest( + message, message_length, core_message_length, &nonce_values_); + if (*signature_length < required_signature_size || + result == OEMCrypto_ERROR_SHORT_BUFFER) { + *signature_length = required_signature_size; + return OEMCrypto_ERROR_SHORT_BUFFER; + } + if (result != OEMCrypto_SUCCESS) { + LOGE("ODK error: %d", result); + return result; + } + if (message == nullptr || message_length == 0 || signature == nullptr) { + LOGE("OEMCrypto_ERROR_INVALID_CONTEXT"); + return OEMCrypto_ERROR_INVALID_CONTEXT; + } + if (state_request_signed_) { + LOGE("Attempt to sign two license requests"); + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + result = GenerateCertSignature(message, message_length, signature, + signature_length); + if (result == OEMCrypto_SUCCESS) state_request_signed_ = true; + return result; +} + +OEMCryptoResult SessionContext::PrepAndSignRenewalRequest( + uint8_t* message, size_t message_length, size_t* core_message_length, + uint8_t* signature, size_t* signature_length) { + if (signature_length == nullptr || core_message_length == nullptr) { + return OEMCrypto_ERROR_INVALID_CONTEXT; + } + // If we have signed a request, but have not loaded it, something is wrong. + // On the other hand, we can sign a license release using the mac keys from + // the usage table. + if (state_request_signed_ && !state_response_loaded_) { + LOGE("Attempt to sign renewal before load"); + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + const size_t required_signature_size = SHA256_DIGEST_LENGTH; + const uint64_t now = CurrentTimer(); + const OEMCryptoResult result = ODK_PrepareCoreRenewalRequest( + message, message_length, core_message_length, &nonce_values_, + &clock_values_, now); + if (*signature_length < required_signature_size || + result == OEMCrypto_ERROR_SHORT_BUFFER) { + *signature_length = required_signature_size; + return OEMCrypto_ERROR_SHORT_BUFFER; + } + if (result != OEMCrypto_SUCCESS) { + LOGE("ODK error: %d", result); + return result; + } + if (message == nullptr || message_length == 0 || signature == nullptr) { + LOGE("OEMCrypto_ERROR_INVALID_CONTEXT"); + return OEMCrypto_ERROR_INVALID_CONTEXT; + } + return GenerateSignature(message, message_length, signature, + signature_length); +} + +OEMCryptoResult SessionContext::PrepAndSignProvisioningRequest( + uint8_t* message, size_t message_length, size_t* core_message_length, + uint8_t* signature, size_t* signature_length) { + if (signature_length == nullptr || core_message_length == nullptr) { + return OEMCrypto_ERROR_INVALID_CONTEXT; + } + if (state_request_signed_) { + LOGE("Attempt to sign prov request after license request"); + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + const size_t required_signature_size = ROTSignatureSize(); + if (required_signature_size == 0) return OEMCrypto_ERROR_UNKNOWN_FAILURE; + const std::vector& device_id = ce_->DeviceRootId(); + OEMCryptoResult result = ODK_PrepareCoreProvisioningRequest( + message, message_length, core_message_length, &nonce_values_, + device_id.data(), device_id.size()); + if (*signature_length < required_signature_size || + result == OEMCrypto_ERROR_SHORT_BUFFER) { + *signature_length = required_signature_size; + return OEMCrypto_ERROR_SHORT_BUFFER; + } + if (result != OEMCrypto_SUCCESS) { + LOGE("ODK error: %d", result); + return result; + } + if (message == nullptr || message_length == 0 || signature == nullptr) { + LOGE("OEMCrypto_ERROR_INVALID_CONTEXT"); + return OEMCrypto_ERROR_INVALID_CONTEXT; + } + if (ce_->config_provisioning_method() == OEMCrypto_Keybox) { + result = + GenerateSignature(message, message_length, signature, signature_length); + } else if (ce_->config_provisioning_method() == OEMCrypto_OEMCertificate) { + result = GenerateCertSignature(message, message_length, signature, + signature_length); + } else { + LOGE("Bad prov method = %d", ce_->config_provisioning_method()); + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + if (result == OEMCrypto_SUCCESS) state_request_signed_ = true; + return result; +} + // Utility function to generate a message signature -bool SessionContext::GenerateSignature(const uint8_t* message, - size_t message_length, - uint8_t* signature, - size_t* signature_length, - bool renewal_message) { +OEMCryptoResult SessionContext::GenerateSignature(const uint8_t* message, + size_t message_length, + uint8_t* signature, + size_t* signature_length) { if (message == nullptr || message_length == 0 || signature == nullptr || signature_length == nullptr) { - LOGE("[OEMCrypto_GenerateSignature(): OEMCrypto_ERROR_INVALID_CONTEXT]"); - return false; + LOGE("OEMCrypto_ERROR_INVALID_CONTEXT"); + return OEMCrypto_ERROR_INVALID_CONTEXT; } - if (mac_key_client_.size() != wvoec::MAC_KEY_SIZE) { - return false; + return OEMCrypto_ERROR_INVALID_CONTEXT; } - if (*signature_length != SHA256_DIGEST_LENGTH) { *signature_length = SHA256_DIGEST_LENGTH; - return false; + return OEMCrypto_ERROR_SHORT_BUFFER; } - - if (!renewal_message) { - if (state_request_signed_) { - return false; - } - state_request_signed_ = true; - } - unsigned int md_len = *signature_length; if (HMAC(EVP_sha256(), &mac_key_client_[0], wvoec::MAC_KEY_SIZE, message, message_length, signature, &md_len)) { *signature_length = md_len; - return true; + return OEMCrypto_SUCCESS; } - return false; + return OEMCrypto_ERROR_UNKNOWN_FAILURE; } +// This is ussd when the device is a cast receiver. size_t SessionContext::RSASignatureSize() { if (!rsa_key()) { - LOGE("[GenerateRSASignature(): no RSA key set]"); + LOGE("no RSA key set"); return 0; } return static_cast(RSA_size(rsa_key())); } +size_t SessionContext::CertSignatureSize() { + // TODO(b/67735947): Add ECC cert support. + if (!rsa_key()) { + LOGE("No private key set"); + return 0; + } + return static_cast(RSA_size(rsa_key())); +} + +size_t SessionContext::ROTSignatureSize() { + if (ce_->config_provisioning_method() == OEMCrypto_Keybox) + return SHA256_DIGEST_LENGTH; + if (ce_->config_provisioning_method() == OEMCrypto_OEMCertificate) + return CertSignatureSize(); + LOGE("Bad prov method = %d", ce_->config_provisioning_method()); + return 0; +} + +OEMCryptoResult SessionContext::GenerateCertSignature( + const uint8_t* message, size_t message_length, uint8_t* signature, + size_t* signature_length) { + // TODO(b/67735947): Add ECC cert support. + if (message == nullptr || message_length == 0 || signature == nullptr || + signature_length == 0) { + LOGE("OEMCrypto_ERROR_INVALID_CONTEXT"); + return OEMCrypto_ERROR_INVALID_CONTEXT; + } + if (!rsa_key()) { + LOGE("no RSA key set"); + return OEMCrypto_ERROR_INVALID_RSA_KEY; + } + if (*signature_length < static_cast(RSA_size(rsa_key()))) { + *signature_length = CertSignatureSize(); + return OEMCrypto_ERROR_SHORT_BUFFER; + } + if (allowed_schemes_ != kSign_RSASSA_PSS) { + LOGE("message signing not allowed"); + return OEMCrypto_ERROR_INVALID_RSA_KEY; + } + + // Hash the message using SHA1. + uint8_t hash[SHA_DIGEST_LENGTH]; + if (!SHA1(message, message_length, hash)) { + LOGE("Error creating signature hash"); + dump_boringssl_error(); + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + + // Add PSS padding. + std::vector padded_digest(*signature_length); + int status = RSA_padding_add_PKCS1_PSS_mgf1( + rsa_key(), &padded_digest[0], hash, EVP_sha1(), nullptr, kPssSaltLength); + if (status == -1) { + LOGE("Error padding hash"); + dump_boringssl_error(); + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + + // Encrypt PSS padded digest. + status = RSA_private_encrypt(*signature_length, &padded_digest[0], signature, + rsa_key(), RSA_NO_PADDING); + if (status == -1) { + LOGE("Error in private encrypt"); + dump_boringssl_error(); + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + return OEMCrypto_SUCCESS; +} + OEMCryptoResult SessionContext::GenerateRSASignature( const uint8_t* message, size_t message_length, uint8_t* signature, size_t* signature_length, RSA_Padding_Scheme padding_scheme) { if (message == nullptr || message_length == 0 || signature == nullptr || signature_length == 0) { - LOGE("[GenerateRSASignature(): OEMCrypto_ERROR_INVALID_CONTEXT]"); + LOGE("OEMCrypto_ERROR_INVALID_CONTEXT"); return OEMCrypto_ERROR_INVALID_CONTEXT; } if (!rsa_key()) { - LOGE("[GenerateRSASignature(): no RSA key set]"); + LOGE("no RSA key set"); return OEMCrypto_ERROR_INVALID_RSA_KEY; } if (*signature_length < static_cast(RSA_size(rsa_key()))) { *signature_length = RSA_size(rsa_key()); return OEMCrypto_ERROR_SHORT_BUFFER; } - if ((padding_scheme & allowed_schemes_) != padding_scheme) { - LOGE("[GenerateRSASignature(): padding_scheme not allowed]"); + if (((padding_scheme & allowed_schemes_) != padding_scheme) || + (padding_scheme != kSign_PKCS1_Block1)) { + LOGE("padding_scheme not allowed"); return OEMCrypto_ERROR_INVALID_RSA_KEY; } - // This is the standard padding scheme used for license requests. - // TODO(b/135288022): This first padding scheme will be used only for signing - // messages, as in OEMCrypto_Sign*Request. - if (padding_scheme == kSign_RSASSA_PSS) { - if (state_request_signed_) { - return OEMCrypto_ERROR_INVALID_CONTEXT; - } - state_request_signed_ = true; - - // Hash the message using SHA1. - uint8_t hash[SHA_DIGEST_LENGTH]; - if (!SHA1(message, message_length, hash)) { - LOGE("[GenerateRSASignature(): error creating signature hash.]"); - dump_boringssl_error(); - return OEMCrypto_ERROR_UNKNOWN_FAILURE; - } - - // Add PSS padding. - std::vector padded_digest(*signature_length); - int status = - RSA_padding_add_PKCS1_PSS_mgf1(rsa_key(), &padded_digest[0], hash, - EVP_sha1(), nullptr, kPssSaltLength); - if (status == -1) { - LOGE("[GenerateRSASignature(): error padding hash.]"); - dump_boringssl_error(); - return OEMCrypto_ERROR_UNKNOWN_FAILURE; - } - - // Encrypt PSS padded digest. - status = RSA_private_encrypt(*signature_length, &padded_digest[0], - signature, rsa_key(), RSA_NO_PADDING); - if (status == -1) { - LOGE("[GenerateRSASignature(): error in private encrypt.]"); - dump_boringssl_error(); - return OEMCrypto_ERROR_UNKNOWN_FAILURE; - } - // This is the alternate padding scheme used by cast receivers only. - } else if (padding_scheme == kSign_PKCS1_Block1) { - // TODO(b/135288022): Alternate padding scheme is not used for messages, so - // we do not need to keep track of the state, like we do above. This - // padding scheme will be left over as the only valid option for this - // function. The padding scheme above will be used for signing messages. - - if (message_length > 83) { - LOGE("[GenerateRSASignature(): RSA digest too large.]"); - return OEMCrypto_ERROR_SIGNATURE_FAILURE; - } - // Pad the message with PKCS1 padding, and then encrypt. - size_t status = RSA_private_encrypt(message_length, message, signature, - rsa_key(), RSA_PKCS1_PADDING); - if (status != *signature_length) { - LOGE("[GenerateRSASignature(): error in RSA private encrypt. status=%d]", - status); - dump_boringssl_error(); - return OEMCrypto_ERROR_UNKNOWN_FAILURE; - } - } else { // Bad RSA_Padding_Scheme - return OEMCrypto_ERROR_INVALID_RSA_KEY; + // This is the maximum digest size possible for PKCS1 block type 1, + // as used for a CAST receiver. + const size_t max_digest_size = 83u; + if (message_length > max_digest_size) { + LOGE("RSA digest too large"); + return OEMCrypto_ERROR_SIGNATURE_FAILURE; } + // Pad the message with PKCS1 padding, and then encrypt. + int status = RSA_private_encrypt(message_length, message, signature, + rsa_key(), RSA_PKCS1_PADDING); + if (status < 0) { + LOGE("error in RSA private encrypt. status=%d", status); + dump_boringssl_error(); + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + *signature_length = static_cast(RSA_size(rsa_key())); return OEMCrypto_SUCCESS; } @@ -427,7 +580,7 @@ bool SessionContext::ValidateMessage(const uint8_t* given_message, unsigned int md_len = SHA256_DIGEST_LENGTH; if (!HMAC(EVP_sha256(), mac_key_server_.data(), mac_key_server_.size(), given_message, message_length, computed_signature, &md_len)) { - LOGE("ValidateMessage: Could not compute signature."); + LOGE("ValidateMessage: Could not compute signature"); return false; } if (memcmp(given_signature, computed_signature, signature_length)) { @@ -443,16 +596,16 @@ bool SessionContext::ValidateMessage(const uint8_t* given_message, OEMCryptoResult SessionContext::CheckStatusOnline(uint32_t nonce, uint32_t control) { if (!(control & wvoec::kControlNonceEnabled)) { - LOGE("LoadKeys: Server provided Nonce_Required but Nonce_Enabled = 0."); + LOGE("LoadKeys: Server provided Nonce_Required but Nonce_Enabled = 0"); // Server error. Continue, and assume nonce required. } if (!CheckNonce(nonce)) return OEMCrypto_ERROR_INVALID_NONCE; switch (usage_entry_status_) { case kNoUsageEntry: - LOGE("LoadKeys: Session did not create usage entry."); + LOGE("LoadKeys: Session did not create usage entry"); return OEMCrypto_ERROR_INVALID_CONTEXT; case kUsageEntryLoaded: - LOGE("LoadKeys: Session reloaded existing entry."); + LOGE("LoadKeys: Session reloaded existing entry"); return OEMCrypto_ERROR_INVALID_CONTEXT; case kUsageEntryNew: return OEMCrypto_SUCCESS; @@ -464,12 +617,12 @@ OEMCryptoResult SessionContext::CheckStatusOnline(uint32_t nonce, OEMCryptoResult SessionContext::CheckStatusOffline(uint32_t nonce, uint32_t control) { if (control & wvoec::kControlNonceEnabled) { - LOGE("KCB: Server provided NonceOrEntry but Nonce_Enabled = 1."); + LOGE("KCB: Server provided NonceOrEntry but Nonce_Enabled = 1"); // Server error. Continue, and assume nonce required. } switch (usage_entry_status_) { case kNoUsageEntry: - LOGE("LoadKeys: Session did not create or load usage entry."); + LOGE("LoadKeys: Session did not create or load usage entry"); return OEMCrypto_ERROR_INVALID_CONTEXT; case kUsageEntryLoaded: // Repeat load. Calling function will verify pst and keys. @@ -507,8 +660,8 @@ OEMCryptoResult SessionContext::CheckNonceOrEntry( void SessionContext::StartTimer() { timer_start_ = ce_->OnlineTime(); } uint32_t SessionContext::CurrentTimer() { - time_t now = ce_->OnlineTime(); - return now - timer_start_; + int64_t now = ce_->OnlineTime(); + return static_cast((now >= timer_start_) ? now - timer_start_ : 0); } OEMCryptoResult SessionContext::LoadKeys( @@ -523,7 +676,7 @@ OEMCryptoResult SessionContext::LoadKeys( return OEMCrypto_ERROR_SIGNATURE_FAILURE; } if (state_response_loaded_) { - return OEMCrypto_ERROR_INVALID_CONTEXT; + return OEMCrypto_ERROR_LICENSE_RELOAD; } state_response_loaded_ = true; @@ -560,7 +713,7 @@ OEMCryptoResult SessionContext::LoadKeys( message + srm_restriction_data.offset + 8)); uint16_t current_version = 0; if (OEMCrypto_SUCCESS != ce_->current_srm_version(¤t_version)) { - LOGW("[LoadKeys: SRM Version not available."); + LOGW("[LoadKeys: SRM Version not available"); srm_requirements_status_ = InvalidSRMVersion; } else if (current_version < minimum_version) { LOGW("[LoadKeys: SRM Version is too small %d, required: %d", @@ -635,7 +788,7 @@ OEMCryptoResult SessionContext::LoadKeys( switch (usage_entry_status_) { case kNoUsageEntry: if (pst.length > 0) { - LOGE("LoadKeys: PST specified but no usage entry loaded."); + LOGE("LoadKeys: PST specified but no usage entry loaded"); return OEMCrypto_ERROR_INVALID_CONTEXT; } break; // no extra check. @@ -666,9 +819,8 @@ OEMCryptoResult SessionContext::LoadKeys( } OEMCryptoResult SessionContext::LoadEntitledContentKeys( - const uint8_t* message, size_t message_length, - const OEMCrypto_EntitledContentKeyObject* key_array, - size_t key_array_length) { + const uint8_t* message, size_t message_length, size_t key_array_length, + const OEMCrypto_EntitledContentKeyObject* key_array) { if (!key_array) { return OEMCrypto_ERROR_UNKNOWN_FAILURE; } @@ -745,18 +897,18 @@ OEMCryptoResult SessionContext::InstallKey( KeyControlBlock key_control_block(key_control_str); if (!key_control_block.valid()) { - LOGE("Error parsing key control."); + LOGE("Error parsing key control"); return OEMCrypto_ERROR_INVALID_CONTEXT; } if ((key_control_block.control_bits() & wvoec::kControlRequireAntiRollbackHardware) && !ce_->config_is_anti_rollback_hw_present()) { - LOGE("Anti-rollback hardware is required but hardware not present."); + LOGE("Anti-rollback hardware is required but hardware not present"); return OEMCrypto_ERROR_UNKNOWN_FAILURE; } - uint8_t minimum_patch_level = - (key_control_block.control_bits() & wvoec::kControlSecurityPatchLevelMask) >> - wvoec::kControlSecurityPatchLevelShift; + uint8_t minimum_patch_level = (key_control_block.control_bits() & + wvoec::kControlSecurityPatchLevelMask) >> + wvoec::kControlSecurityPatchLevelShift; if (minimum_patch_level > OEMCrypto_Security_Patch_Level()) { LOGE("[InstallKey(): security patch level: %d. Minimum:%d]", OEMCrypto_Security_Patch_Level(), minimum_patch_level); @@ -764,7 +916,7 @@ OEMCryptoResult SessionContext::InstallKey( } OEMCryptoResult result = CheckNonceOrEntry(key_control_block); if (result != OEMCrypto_SUCCESS) { - LOGE("LoadKeys: Failed Nonce/PST check."); + LOGE("LoadKeys: Failed Nonce/PST check"); return result; } if (key_control_block.control_bits() & wvoec::kControlSRMVersionRequired) { @@ -818,7 +970,7 @@ OEMCryptoResult SessionContext::RefreshKey( // 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."); + LOGE("Parse key control error"); return OEMCrypto_ERROR_INVALID_CONTEXT; } if ((key_control_block.control_bits() & wvoec::kControlNonceEnabled) && @@ -834,7 +986,7 @@ OEMCryptoResult SessionContext::RefreshKey( Key* content_key = session_keys_->Find(key_id); if (content_key == nullptr) { - LOGE("Key ID not found."); + LOGE("Key ID not found"); return OEMCrypto_ERROR_NO_CONTENT_KEY; } @@ -857,7 +1009,7 @@ OEMCryptoResult SessionContext::RefreshKey( KeyControlBlock key_control_block(control); if (!key_control_block.valid()) { - LOGE("Error parsing key control."); + LOGE("Error parsing key control"); return OEMCrypto_ERROR_INVALID_CONTEXT; } if ((key_control_block.control_bits() & wvoec::kControlNonceEnabled) && @@ -920,7 +1072,7 @@ OEMCryptoResult SessionContext::CheckKeyUse(const std::string& log_string, OEMCryptoBufferType buffer_type) { const KeyControlBlock& control = current_content_key()->control(); if (use_type && (!(control.control_bits() & use_type))) { - LOGE("[%s(): control bit says not allowed.", log_string.c_str()); + LOGE("[%s(): control bit says not allowed", log_string.c_str()); return OEMCrypto_ERROR_UNKNOWN_FAILURE; } if (control.control_bits() & wvoec::kControlDataPathSecure) { @@ -938,7 +1090,9 @@ OEMCryptoResult SessionContext::CheckKeyUse(const std::string& log_string, } if (control.duration() > 0) { if (control.duration() < CurrentTimer()) { - LOGE("[%s(): key expired.", log_string.c_str()); + LOGE("%s: key expired. duration=%d, timer=%d, now=%ld", + log_string.c_str(), control.duration(), CurrentTimer(), + ce_->OnlineTime()); return OEMCrypto_ERROR_KEY_EXPIRED; } } @@ -965,18 +1119,18 @@ OEMCryptoResult SessionContext::CheckKeyUse(const std::string& log_string, if ((control.control_bits() & wvoec::kControlDisableAnalogOutput) && (ce_->analog_display_active() || (buffer_type == OEMCrypto_BufferType_Clear))) { - LOGE("[%s(): control bit says disable analog.", log_string.c_str()); + LOGE("[%s(): control bit says disable analog", log_string.c_str()); return OEMCrypto_ERROR_ANALOG_OUTPUT; } // Check if CGMS is required. if (control.control_bits() & wvoec::kControlCGMSMask) { // We can't control CGMS for a clear buffer. if (buffer_type == OEMCrypto_BufferType_Clear) { - LOGE("[%s(): CGMS required, but buffer is clear.", log_string.c_str()); + LOGE("[%s(): CGMS required, but buffer is clear", log_string.c_str()); return OEMCrypto_ERROR_ANALOG_OUTPUT; } if ( ce_->analog_display_active() && !ce_->cgms_a_active()) { - LOGE("[%s(): control bit says CGMS required.", log_string.c_str()); + LOGE("[%s(): control bit says CGMS required", log_string.c_str()); return OEMCrypto_ERROR_ANALOG_OUTPUT; } } @@ -1003,11 +1157,11 @@ OEMCryptoResult SessionContext::Generic_Encrypt(const uint8_t* in_buffer, OEMCrypto_BufferType_Clear); if (result != OEMCrypto_SUCCESS) return result; if (algorithm != OEMCrypto_AES_CBC_128_NO_PADDING) { - LOGE("[Generic_Encrypt(): algorithm bad."); + LOGE("[Generic_Encrypt(): algorithm bad"); return OEMCrypto_ERROR_UNKNOWN_FAILURE; } if (buffer_length % AES_BLOCK_SIZE != 0) { - LOGE("[Generic_Encrypt(): buffers size bad."); + LOGE("[Generic_Encrypt(): buffers size bad"); return OEMCrypto_ERROR_UNKNOWN_FAILURE; } const uint8_t* key_u8 = &key[0]; @@ -1036,7 +1190,7 @@ OEMCryptoResult SessionContext::Generic_Decrypt(const uint8_t* in_buffer, const std::vector& key = current_content_key()->value(); // Set the AES key. if (static_cast(key.size()) != AES_BLOCK_SIZE) { - LOGE("[Generic_Decrypt(): CONTENT_KEY has wrong size."); + LOGE("[Generic_Decrypt(): CONTENT_KEY has wrong size"); return OEMCrypto_ERROR_UNKNOWN_FAILURE; } OEMCryptoResult result = CheckKeyUse("Generic_Decrypt", wvoec::kControlAllowDecrypt, @@ -1044,11 +1198,11 @@ OEMCryptoResult SessionContext::Generic_Decrypt(const uint8_t* in_buffer, if (result != OEMCrypto_SUCCESS) return result; if (algorithm != OEMCrypto_AES_CBC_128_NO_PADDING) { - LOGE("[Generic_Decrypt(): bad algorithm."); + LOGE("[Generic_Decrypt(): bad algorithm"); return OEMCrypto_ERROR_UNKNOWN_FAILURE; } if (buffer_length % AES_BLOCK_SIZE != 0) { - LOGE("[Generic_Decrypt(): bad buffer size."); + LOGE("[Generic_Decrypt(): bad buffer size"); return OEMCrypto_ERROR_UNKNOWN_FAILURE; } const uint8_t* key_u8 = &key[0]; @@ -1076,7 +1230,7 @@ OEMCryptoResult SessionContext::Generic_Sign(const uint8_t* in_buffer, } if (*signature_length < SHA256_DIGEST_LENGTH) { *signature_length = SHA256_DIGEST_LENGTH; - LOGE("[Generic_Sign(): bad signature length."); + LOGE("[Generic_Sign(): bad signature length"); return OEMCrypto_ERROR_UNKNOWN_FAILURE; } const std::vector& key = current_content_key()->value(); @@ -1088,7 +1242,7 @@ OEMCryptoResult SessionContext::Generic_Sign(const uint8_t* in_buffer, OEMCrypto_BufferType_Clear); if (result != OEMCrypto_SUCCESS) return result; if (algorithm != OEMCrypto_HMAC_SHA256) { - LOGE("[Generic_Sign(): bad algorithm."); + LOGE("[Generic_Sign(): bad algorithm"); return OEMCrypto_ERROR_UNKNOWN_FAILURE; } unsigned int md_len = *signature_length; @@ -1097,7 +1251,7 @@ OEMCryptoResult SessionContext::Generic_Sign(const uint8_t* in_buffer, *signature_length = md_len; return OEMCrypto_SUCCESS; } - LOGE("[Generic_Sign(): hmac failed."); + LOGE("[Generic_Sign(): hmac failed"); dump_boringssl_error(); return OEMCrypto_ERROR_UNKNOWN_FAILURE; } @@ -1124,7 +1278,7 @@ OEMCryptoResult SessionContext::Generic_Verify(const uint8_t* in_buffer, OEMCrypto_BufferType_Clear); if (result != OEMCrypto_SUCCESS) return result; if (algorithm != OEMCrypto_HMAC_SHA256) { - LOGE("[Generic_Verify(): bad algorithm."); + LOGE("[Generic_Verify(): bad algorithm"); return OEMCrypto_ERROR_UNKNOWN_FAILURE; } unsigned int md_len = signature_length; @@ -1137,7 +1291,7 @@ OEMCryptoResult SessionContext::Generic_Verify(const uint8_t* in_buffer, return OEMCrypto_ERROR_SIGNATURE_FAILURE; } } - LOGE("[Generic_Verify(): HMAC failed."); + LOGE("[Generic_Verify(): HMAC failed"); dump_boringssl_error(); return OEMCrypto_ERROR_UNKNOWN_FAILURE; } @@ -1176,7 +1330,7 @@ bool SessionContext::QueryKeyControlBlock(const KeyId& key_id, uint32_t* data) { OEMCryptoResult SessionContext::SelectContentKey( const KeyId& key_id, OEMCryptoCipherMode cipher_mode) { if (session_keys_ == nullptr) { - LOGE("Select Key: no session keys."); + LOGE("Select Key: no session keys"); return OEMCrypto_ERROR_INVALID_CONTEXT; } Key* content_key = session_keys_->Find(key_id); @@ -1190,8 +1344,8 @@ OEMCryptoResult SessionContext::SelectContentKey( if (control.duration() > 0) { if (control.duration() < CurrentTimer()) { - LOGE("[SelectContentKey(): KEY_EXPIRED %d versus %d]", control.duration(), - CurrentTimer()); + LOGE("KEY_EXPIRED duration=%d, timer=%d, now=%ld", control.duration(), + CurrentTimer(), ce_->OnlineTime()); return OEMCrypto_ERROR_KEY_EXPIRED; } } @@ -1207,7 +1361,7 @@ OEMCryptoResult SessionContext::CreateNewUsageEntry( uint32_t* usage_entry_number) { if (usage_entry_) { // Can only load one entry per session. - return OEMCrypto_ERROR_INVALID_CONTEXT; + return OEMCrypto_ERROR_MULTIPLE_USAGE_ENTRIES; } OEMCryptoResult result = ce_->usage_table().CreateNewUsageEntry( this, &usage_entry_, usage_entry_number); @@ -1221,7 +1375,7 @@ OEMCryptoResult SessionContext::LoadUsageEntry( uint32_t index, const std::vector& buffer) { if (usage_entry_) { // Can only load one entry per session. - return OEMCrypto_ERROR_INVALID_CONTEXT; + return OEMCrypto_ERROR_MULTIPLE_USAGE_ENTRIES; } OEMCryptoResult result = ce_->usage_table().LoadUsageEntry(this, &usage_entry_, index, buffer); @@ -1243,7 +1397,7 @@ OEMCryptoResult SessionContext::UpdateUsageEntry(uint8_t* header_buffer, uint8_t* entry_buffer, size_t* entry_buffer_length) { if (!usage_entry_) { - LOGE("UpdateUsageEntry: Session has no entry."); + LOGE("UpdateUsageEntry: Session has no entry"); return OEMCrypto_ERROR_INVALID_CONTEXT; } return ce_->usage_table().UpdateUsageEntry(this, usage_entry_, header_buffer, @@ -1290,11 +1444,12 @@ bool SessionContext::DecryptMessage(const std::vector& key, return true; } -OEMCryptoResult SessionContext::DecryptCENC( +OEMCryptoResult SessionContext::DecryptCENC_V15( const uint8_t* iv, size_t block_offset, - const OEMCrypto_CENCEncryptPatternDesc* pattern, const uint8_t* cipher_data, - size_t cipher_data_length, bool is_encrypted, uint8_t* clear_data, - OEMCryptoBufferType buffer_type, uint8_t subsample_flags) { + const OEMCrypto_CENCEncryptPatternDesc_V15* pattern, + const uint8_t* cipher_data, size_t cipher_data_length, bool is_encrypted, + uint8_t* clear_data, OEMCryptoBufferType buffer_type, + uint8_t subsample_flags) { OEMCryptoResult result = ChooseDecrypt(iv, block_offset, pattern, cipher_data, cipher_data_length, is_encrypted, clear_data, buffer_type); @@ -1332,9 +1487,9 @@ OEMCryptoResult SessionContext::DecryptCENC( OEMCryptoResult SessionContext::ChooseDecrypt( const uint8_t* iv, size_t block_offset, - const OEMCrypto_CENCEncryptPatternDesc* pattern, const uint8_t* cipher_data, - size_t cipher_data_length, bool is_encrypted, uint8_t* clear_data, - OEMCryptoBufferType buffer_type) { + const OEMCrypto_CENCEncryptPatternDesc_V15* pattern, + const uint8_t* cipher_data, size_t cipher_data_length, bool is_encrypted, + uint8_t* clear_data, OEMCryptoBufferType buffer_type) { // If the data is clear, we do not need a current key selected. if (!is_encrypted) { if (buffer_type != OEMCrypto_BufferType_Direct) { @@ -1384,8 +1539,9 @@ OEMCryptoResult SessionContext::ChooseDecrypt( OEMCryptoResult SessionContext::DecryptCBC( const uint8_t* key, const uint8_t* initial_iv, - const OEMCrypto_CENCEncryptPatternDesc* pattern, const uint8_t* cipher_data, - size_t cipher_data_length, uint8_t* clear_data) { + const OEMCrypto_CENCEncryptPatternDesc_V15* pattern, + const uint8_t* cipher_data, size_t cipher_data_length, + uint8_t* clear_data) { AES_KEY aes_key; AES_set_decrypt_key(&key[0], AES_BLOCK_SIZE * 8, &aes_key); uint8_t iv[AES_BLOCK_SIZE]; @@ -1393,7 +1549,8 @@ OEMCryptoResult SessionContext::DecryptCBC( memcpy(iv, &initial_iv[0], AES_BLOCK_SIZE); size_t l = 0; - size_t pattern_offset = pattern->offset; + // TODO(b/135285640): remove this. + size_t pattern_offset = 0; while (l < cipher_data_length) { size_t size = std::min(cipher_data_length - l, static_cast(AES_BLOCK_SIZE)); @@ -1423,15 +1580,17 @@ OEMCryptoResult SessionContext::DecryptCBC( OEMCryptoResult SessionContext::PatternDecryptCTR( const uint8_t* key, const uint8_t* initial_iv, size_t block_offset, - const OEMCrypto_CENCEncryptPatternDesc* pattern, const uint8_t* cipher_data, - size_t cipher_data_length, uint8_t* clear_data) { + const OEMCrypto_CENCEncryptPatternDesc_V15* pattern, + const uint8_t* cipher_data, size_t cipher_data_length, + uint8_t* clear_data) { AES_KEY aes_key; AES_set_encrypt_key(&key[0], AES_BLOCK_SIZE * 8, &aes_key); uint8_t iv[AES_BLOCK_SIZE]; memcpy(iv, &initial_iv[0], AES_BLOCK_SIZE); size_t l = 0; - size_t pattern_offset = pattern->offset; + // TODO(b/135285640): remove this. + size_t pattern_offset = 0; while (l < cipher_data_length) { size_t size = std::min(cipher_data_length - l, AES_BLOCK_SIZE - block_offset); @@ -1579,8 +1738,9 @@ OEMCryptoResult SessionContext::GetHashErrorCode( bool SessionContext::set_nonce(uint32_t nonce) { if (state_nonce_created_) return false; + if (nonce == 0) return false; state_nonce_created_ = true; - nonce_ = nonce; + nonce_values_.nonce = nonce; return true; } diff --git a/oemcrypto/ref/src/oemcrypto_session.h b/oemcrypto/ref/src/oemcrypto_session.h index ca6aba1..7a2f87f 100644 --- a/oemcrypto/ref/src/oemcrypto_session.h +++ b/oemcrypto/ref/src/oemcrypto_session.h @@ -62,25 +62,8 @@ class SessionContext { SessionContext() {} public: - SessionContext(CryptoEngine* ce, SessionId sid, const RSA_shared_ptr& rsa_key) - : valid_(true), - ce_(ce), - id_(sid), - current_content_key_(nullptr), - session_keys_(nullptr), - nonce_(0), - rsa_key_(rsa_key), - allowed_schemes_(kSign_RSASSA_PSS), - usage_entry_(nullptr), - srm_requirements_status_(NoSRMVersion), - usage_entry_status_(kNoUsageEntry), - compute_hash_(false), - current_hash_(0), - bad_frame_number_(0), - hash_error_(OEMCrypto_SUCCESS), - state_nonce_created_(false), - state_request_signed_(false), - state_response_loaded_(false) {} + SessionContext(CryptoEngine* ce, SessionId sid, + const RSA_shared_ptr& rsa_key); virtual ~SessionContext(); bool isValid() { return valid_; } @@ -91,10 +74,21 @@ class SessionContext { virtual bool RSADeriveKeys(const std::vector& enc_session_key, const std::vector& mac_context, const std::vector& enc_context); - // TODO(b/135288022): remove renewal_message hack. - virtual bool GenerateSignature(const uint8_t* message, size_t message_length, - uint8_t* signature, size_t* signature_length, - bool renewal_message); + virtual OEMCryptoResult PrepAndSignLicenseRequest(uint8_t* message, + size_t message_length, + size_t* core_message_length, + uint8_t* signature, + size_t* signature_length); + virtual OEMCryptoResult PrepAndSignRenewalRequest(uint8_t* message, + size_t message_length, + size_t* core_message_length, + uint8_t* signature, + size_t* signature_length); + virtual OEMCryptoResult PrepAndSignProvisioningRequest( + uint8_t* message, size_t message_length, size_t* core_message_length, + uint8_t* signature, size_t* signature_length); + // The size of an RSA signature. This is used when signing as a CAST + // receiver. size_t RSASignatureSize(); virtual OEMCryptoResult GenerateRSASignature( const uint8_t* message, size_t message_length, uint8_t* signature, @@ -102,13 +96,12 @@ class SessionContext { virtual bool ValidateMessage(const uint8_t* message, size_t message_length, const uint8_t* signature, size_t signature_length); - OEMCryptoResult DecryptCENC(const uint8_t* iv, size_t block_offset, - const OEMCrypto_CENCEncryptPatternDesc* pattern, - const uint8_t* cipher_data, - size_t cipher_data_length, bool is_encrypted, - uint8_t* clear_data, - OEMCryptoBufferType buffer_type, - uint8_t subsample_flags); + OEMCryptoResult DecryptCENC_V15( + const uint8_t* iv, size_t block_offset, + const OEMCrypto_CENCEncryptPatternDesc_V15* pattern, + const uint8_t* cipher_data, size_t cipher_data_length, bool is_encrypted, + uint8_t* clear_data, OEMCryptoBufferType buffer_type, + uint8_t subsample_flags); OEMCryptoResult Generic_Encrypt(const uint8_t* in_buffer, size_t buffer_length, const uint8_t* iv, @@ -135,9 +128,8 @@ class SessionContext { OEMCrypto_Substring srm_restriction_data, OEMCrypto_LicenseType license_type); virtual OEMCryptoResult LoadEntitledContentKeys( - const uint8_t* message, size_t message_length, - const OEMCrypto_EntitledContentKeyObject* key_array, - size_t key_array_length); + const uint8_t* message, size_t message_length, size_t key_array_length, + const OEMCrypto_EntitledContentKeyObject* key_array); virtual OEMCryptoResult InstallKey( const KeyId& key_id, const std::vector& key_data, const std::vector& key_data_iv, @@ -180,10 +172,11 @@ class SessionContext { // Return true if nonce was set. bool set_nonce(uint32_t nonce); - uint32_t nonce() const { return nonce_; } + uint32_t nonce() const { return nonce_values_.nonce; } + ODK_NonceValues& nonce_values() { return nonce_values_; } bool CheckNonce(uint32_t nonce) const { - return nonce != 0 && nonce == nonce_; + return nonce != 0 && nonce == nonce_values_.nonce; }; virtual OEMCryptoResult CreateNewUsageEntry(uint32_t* usage_entry_number); @@ -199,6 +192,18 @@ class SessionContext { OEMCryptoResult MoveEntry(uint32_t new_index); protected: + // Signature size of the currently loaded private key. + size_t CertSignatureSize(); + // Signature size when using a keybox or OEM Cert's private key. + size_t ROTSignatureSize(); + virtual OEMCryptoResult GenerateCertSignature(const uint8_t* message, + size_t message_length, + uint8_t* signature, + size_t* signature_length); + virtual OEMCryptoResult GenerateSignature(const uint8_t* message, + size_t message_length, + uint8_t* signature, + size_t* signature_length); bool DeriveKey(const std::vector& key, const std::vector& context, int counter, std::vector* out); @@ -217,19 +222,19 @@ class SessionContext { OEMCryptoResult CheckStatusOnline(uint32_t nonce, uint32_t control); // Check that the usage entry status is valid for offline use. OEMCryptoResult CheckStatusOffline(uint32_t nonce, uint32_t control); - OEMCryptoResult ChooseDecrypt(const uint8_t* iv, size_t block_offset, - const OEMCrypto_CENCEncryptPatternDesc* pattern, - const uint8_t* cipher_data, - size_t cipher_data_length, bool is_encrypted, - uint8_t* clear_data, - OEMCryptoBufferType buffer_type); - OEMCryptoResult DecryptCBC(const uint8_t* key, const uint8_t* iv, - const OEMCrypto_CENCEncryptPatternDesc* pattern, - const uint8_t* cipher_data, - size_t cipher_data_length, uint8_t* clear_data); + OEMCryptoResult ChooseDecrypt( + const uint8_t* iv, size_t block_offset, + const OEMCrypto_CENCEncryptPatternDesc_V15* pattern, + const uint8_t* cipher_data, size_t cipher_data_length, bool is_encrypted, + uint8_t* clear_data, OEMCryptoBufferType buffer_type); + OEMCryptoResult DecryptCBC( + const uint8_t* key, const uint8_t* iv, + const OEMCrypto_CENCEncryptPatternDesc_V15* pattern, + const uint8_t* cipher_data, size_t cipher_data_length, + uint8_t* clear_data); OEMCryptoResult PatternDecryptCTR( const uint8_t* key, const uint8_t* iv, size_t block_offset, - const OEMCrypto_CENCEncryptPatternDesc* pattern, + const OEMCrypto_CENCEncryptPatternDesc_V15* pattern, const uint8_t* cipher_data, size_t cipher_data_length, uint8_t* clear_data); OEMCryptoResult DecryptCTR(const uint8_t* key_u8, const uint8_t* iv, @@ -250,10 +255,12 @@ class SessionContext { std::vector session_key_; const Key* current_content_key_; SessionContextKeys* session_keys_; - uint32_t nonce_; + ODK_NonceValues nonce_values_; RSA_shared_ptr rsa_key_; uint32_t allowed_schemes_; // for RSA signatures. - time_t timer_start_; + int64_t timer_start_; // TODO(b/140764222): delete. + ODK_TimerLimits timer_limits_; + ODK_ClockValues clock_values_; UsageTableEntry* usage_entry_; SRMVersionStatus srm_requirements_status_; enum UsageEntryStatus { diff --git a/oemcrypto/test/oec_device_features.cpp b/oemcrypto/test/oec_device_features.cpp index d1c5624..76c3ef5 100644 --- a/oemcrypto/test/oec_device_features.cpp +++ b/oemcrypto/test/oec_device_features.cpp @@ -76,7 +76,6 @@ void DeviceFeatures::Initialize(bool is_cast_receiver, printf("OEMCrypto_Initialize failed. All tests will fail.\n"); return; } - uint32_t nonce = 0; uint8_t buffer[1]; size_t size = 0; provisioning_method = OEMCrypto_GetProvisioningMethod(); @@ -92,14 +91,10 @@ void DeviceFeatures::Initialize(bool is_cast_receiver, } // If the device uses a keybox, check to see if loading a certificate is // installed. - if (provisioning_method == OEMCrypto_Keybox) { - loads_certificate = - (OEMCrypto_ERROR_NOT_IMPLEMENTED != - OEMCrypto_RewrapDeviceRSAKey(session, buffer, 0, buffer, 0, &nonce, - buffer, 0, buffer, buffer, &size)); - } else if (provisioning_method == OEMCrypto_OEMCertificate) { - // If the device says it uses Provisioning 3.0, then it should be able to - // load a DRM certificate. These devices must support RewrapDeviceRSAKey30. + if (provisioning_method == OEMCrypto_Keybox || + provisioning_method == OEMCrypto_OEMCertificate) { + // Devices with a keybox or OEM Certificate are required to support loading + // a DRM certificate. loads_certificate = true; } else { // Other devices are either broken, or they have a baked in certificate. diff --git a/oemcrypto/test/oec_key_deriver.cpp b/oemcrypto/test/oec_key_deriver.cpp index e56ffc1..d39e836 100644 --- a/oemcrypto/test/oec_key_deriver.cpp +++ b/oemcrypto/test/oec_key_deriver.cpp @@ -137,7 +137,7 @@ void KeyDeriver::set_mac_keys(const uint8_t* mac_keys) { } void KeyDeriver::ServerSignBuffer(const uint8_t* data, size_t data_length, - std::vector* signature) { + std::vector* signature) const { ASSERT_LE(data_length, kMaxMessageSize); ASSERT_EQ(mac_key_server_.size(), MAC_KEY_SIZE); signature->assign(SHA256_DIGEST_LENGTH, 0); @@ -147,7 +147,7 @@ void KeyDeriver::ServerSignBuffer(const uint8_t* data, size_t data_length, } void KeyDeriver::ClientSignBuffer(const vector& buffer, - std::vector* signature) { + std::vector* signature) const { ASSERT_EQ(mac_key_client_.size(), MAC_KEY_SIZE); signature->assign(SHA256_DIGEST_LENGTH, 0); unsigned int sig_len = SHA256_DIGEST_LENGTH; @@ -156,7 +156,7 @@ void KeyDeriver::ClientSignBuffer(const vector& buffer, } void KeyDeriver::ClientSignPstReport(const vector& pst_report_buffer, - std::vector* signature) { + std::vector* signature) const { ASSERT_EQ(mac_key_client_.size(), MAC_KEY_SIZE); signature->assign(SHA_DIGEST_LENGTH, 0); unsigned int sig_len = SHA_DIGEST_LENGTH; diff --git a/oemcrypto/test/oec_key_deriver.h b/oemcrypto/test/oec_key_deriver.h index beb82ed..7e05038 100644 --- a/oemcrypto/test/oec_key_deriver.h +++ b/oemcrypto/test/oec_key_deriver.h @@ -18,6 +18,7 @@ namespace wvoec { constexpr size_t kMaxTestRSAKeyLength = 2000; // Rough estimate. +constexpr size_t kMaxCoreProvRequest = 150; // Rough estimate. // This structure will be signed to simulate a provisioning response from the // server. @@ -25,6 +26,8 @@ struct RSAPrivateKeyMessage { uint8_t rsa_key[kMaxTestRSAKeyLength]; uint8_t rsa_key_iv[KEY_IV_SIZE]; size_t rsa_key_length; + uint8_t enc_message_key[kMaxTestRSAKeyLength]; + size_t enc_message_key_length; uint32_t nonce; }; @@ -38,7 +41,7 @@ class Encryptor { void set_enc_key(const std::vector& enc_key); // This encrypts an RSAPrivateKeyMessage with encryption_key so that it may be - // loaded with OEMCrypto_RewrapDeviceRSAKey. + // loaded with OEMCrypto_LoadProvisioningResponse. // This modifies the clear data: it adds padding and generates a random iv. void PadAndEncryptProvisioningMessage(RSAPrivateKeyMessage* data, RSAPrivateKeyMessage* encrypted) const; @@ -64,16 +67,16 @@ class KeyDeriver : public Encryptor { const std::vector& enc_key_context); // Sign the buffer with server's mac key. void ServerSignBuffer(const uint8_t* data, size_t data_length, - std::vector* signature); + std::vector* signature) const; // Sign the buffer with client's known mac key. Known test keys must be // installed first. This uses HMAC with SHA256, so is suitable for a message. void ClientSignBuffer(const std::vector& buffer, - std::vector* signature); + std::vector* signature) const; // Sign the pst buffer with client's known mac key. Known test keys must be // installed first. This uses HMAC with SHA128, and skips the beginning of the // buffer, so is only suitable for a pst report. void ClientSignPstReport(const std::vector& pst_report_buffer, - std::vector* signature); + std::vector* signature) const; void set_mac_keys(const uint8_t* mac_keys); private: diff --git a/oemcrypto/test/oec_session_util.cpp b/oemcrypto/test/oec_session_util.cpp index 21510f9..e2efb4a 100644 --- a/oemcrypto/test/oec_session_util.cpp +++ b/oemcrypto/test/oec_session_util.cpp @@ -25,13 +25,16 @@ #include #include "OEMCryptoCENC.h" +#include "clock.h" #include "disallow_copy_and_assign.h" #include "log.h" #include "oec_device_features.h" #include "oec_test_data.h" +#include "oec_util.h" #include "oemcrypto_types.h" #include "platform.h" #include "string_conversions.h" +#include "test_sleep.h" using namespace std; @@ -118,19 +121,207 @@ OEMCrypto_Substring GetSubstring(const std::string& message, return substring; } +Test_PST_Report::Test_PST_Report(const std::string& pst_in, + OEMCrypto_Usage_Entry_Status status_in) + : status(status_in), pst(pst_in) { + time_created = wvcdm::Clock().GetCurrentTime(); +} + +template +void RoundTrip::SignAndVerifyRequest() { + // In the real world, a message should be signed by the client and + // verified by the server. This simulates that. + vector data(message_size_); + for (size_t i = 0; i < data.size(); i++) data[i] = i & 0xFF; + OEMCryptoResult sts; + size_t gen_signature_length = 0; + size_t core_message_length = 0; + sts = + PrepAndSignRequest(session()->session_id(), data.data(), data.size(), + &core_message_length, nullptr, &gen_signature_length); + ASSERT_EQ(OEMCrypto_ERROR_SHORT_BUFFER, sts); + vector gen_signature(gen_signature_length); + sts = PrepAndSignRequest(session()->session_id(), data.data(), data.size(), + &core_message_length, gen_signature.data(), + &gen_signature_length); + ASSERT_EQ(OEMCrypto_SUCCESS, sts); + if (global_features.api_version >= 16) { + ASSERT_GT(data.size(), core_message_length); + std::string core_message(reinterpret_cast(data.data()), + core_message_length); + FillAndVerifyCoreRequest(core_message); + } + VerifyRequestSignature(data, gen_signature); +} + +template +OEMCrypto_Substring RoundTrip::FindSubstring(const void* pointer, + size_t length) { + OEMCrypto_Substring substring; + if (length == 0 || pointer == nullptr) { + substring.offset = 0; + substring.length = 0; + } else { + // TODO(b/143850949): This adds the core message size to the offset so + // that it can be used more easily. This computation should be done in the + // ODK parse function? To be discussed. + substring.offset = reinterpret_cast(pointer) - + reinterpret_cast(&response_data_) + + serialized_core_message_.size(); + substring.length = length; + } + return substring; +} + +void ProvisioningRoundTrip::PrepareSession( + const wvoec::WidevineKeybox& keybox) { + ASSERT_NO_FATAL_FAILURE(session_->open()); + session_->GenerateNonce(); + if (global_features.provisioning_method == OEMCrypto_Keybox) { + session_->GenerateDerivedKeysFromKeybox(keybox); + encryptor_ = session_->key_deriver(); + } else { + EXPECT_EQ(global_features.provisioning_method, OEMCrypto_OEMCertificate); + session_->LoadOEMCert(true); + session_->GenerateRSASessionKey(&message_key_, &encrypted_message_key_); + encryptor_.set_enc_key(message_key_); + } +} + +void ProvisioningRoundTrip::VerifyRequestSignature( + const vector& data, const vector& generated_signature) { + if (global_features.provisioning_method == OEMCrypto_OEMCertificate) { + session()->VerifyRSASignature(data, generated_signature.data(), + generated_signature.size(), kSign_RSASSA_PSS); + } else { + EXPECT_EQ(global_features.provisioning_method, OEMCrypto_Keybox); + ASSERT_EQ(HMAC_SHA256_SIGNATURE_SIZE, generated_signature.size()); + std::vector expected_signature; + session()->key_deriver().ClientSignBuffer(data, &expected_signature); + ASSERT_EQ(expected_signature, generated_signature); + } +} + +void ProvisioningRoundTrip::FillAndVerifyCoreRequest( + const std::string& core_message_string) { + EXPECT_TRUE( + oec_util::ParseProvisioningRequest(core_message_string, &core_request_)); + EXPECT_EQ(global_features.api_version, core_request_.api_version); + EXPECT_EQ(session()->nonce(), core_request_.nonce); + EXPECT_EQ(session()->session_id(), core_request_.session_id); + size_t device_id_length = core_request_.device_id.size(); + std::vector device_id(device_id_length); + EXPECT_EQ(OEMCrypto_SUCCESS, + OEMCrypto_GetDeviceID(device_id.data(), &device_id_length)); + EXPECT_EQ(core_request_.device_id.size(), device_id_length); + std::string device_id_string(reinterpret_cast(device_id.data()), + device_id_length); + EXPECT_EQ(device_id_string, core_request_.device_id); +} + +void ProvisioningRoundTrip::CreateDefaultResponse() { + if (allowed_schemes_ != kSign_RSASSA_PSS) { + uint32_t algorithm_n = htonl(allowed_schemes_); + memcpy(response_data_.rsa_key, "SIGN", 4); + memcpy(response_data_.rsa_key + 4, &algorithm_n, 4); + memcpy(response_data_.rsa_key + 8, encoded_rsa_key_.data(), + encoded_rsa_key_.size()); + response_data_.rsa_key_length = 8 + encoded_rsa_key_.size(); + } else { + memcpy(response_data_.rsa_key, encoded_rsa_key_.data(), + encoded_rsa_key_.size()); + response_data_.rsa_key_length = encoded_rsa_key_.size(); + } + response_data_.nonce = session_->nonce(); + if (encrypted_message_key_.size() > 0) { + ASSERT_LE(encrypted_message_key_.size(), kMaxTestRSAKeyLength); + memcpy(response_data_.enc_message_key, encrypted_message_key_.data(), + encrypted_message_key_.size()); + response_data_.enc_message_key_length = encrypted_message_key_.size(); + } else { + response_data_.enc_message_key_length = 0; + } + // TODO(b/143850949): There is a problem that when the offset is + // computed, it is based on the protobuf message, but when it is used, we + // include the core message. This is a hack around that: we serialize + // twice -- once to get the size, and later, to really serialize the data. + ASSERT_TRUE(CreateCoreProvisioningResponse(core_response_, core_request_, + &serialized_core_message_)); + // TODO(b/67735947): set key type. use key type. + core_response_.key_type = OEMCrypto_Supports_RSA_2048bit; + core_response_.enc_private_key = + FindSubstring(response_data_.rsa_key, response_data_.rsa_key_length); + core_response_.enc_private_key_iv = FindSubstring( + response_data_.rsa_key_iv, sizeof(response_data_.rsa_key_iv)); + core_response_.encrypted_message_key = FindSubstring( + response_data_.enc_message_key, response_data_.enc_message_key_length); +} + +void ProvisioningRoundTrip::EncryptAndSignResponse() { + encryptor_.PadAndEncryptProvisioningMessage(&response_data_, + &encrypted_response_data_); + core_response_.enc_private_key.length = + encrypted_response_data_.rsa_key_length; + ASSERT_TRUE(CreateCoreProvisioningResponse(core_response_, core_request_, + &serialized_core_message_)); + // Stripe the encrypted message. + encrypted_response_.resize(message_size_); + for (size_t i = 0; i < encrypted_response_.size(); i++) { + encrypted_response_[i] = i % 0x100; + } + 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_)); + if (global_features.provisioning_method == OEMCrypto_OEMCertificate) { + session()->GenerateDerivedKeysFromSessionKey(); + } + session()->key_deriver().ServerSignBuffer(encrypted_response_.data(), + encrypted_response_.size(), + &response_signature_); +} + +OEMCryptoResult ProvisioningRoundTrip::LoadResponse() { + size_t wrapped_key_length = 0; + const OEMCryptoResult sts = OEMCrypto_LoadProvisioning( + session_->session_id(), encrypted_response_.data(), + encrypted_response_.size(), serialized_core_message_.size(), + response_signature_.data(), response_signature_.size(), nullptr, + &wrapped_key_length); + if (sts != OEMCrypto_ERROR_SHORT_BUFFER) return sts; + wrapped_rsa_key_.clear(); + wrapped_rsa_key_.assign(wrapped_key_length, 0); + return OEMCrypto_LoadProvisioning( + session_->session_id(), encrypted_response_.data(), + encrypted_response_.size(), serialized_core_message_.size(), + response_signature_.data(), response_signature_.size(), + wrapped_rsa_key_.data(), &wrapped_key_length); +} + +void ProvisioningRoundTrip::VerifyLoadFailed() { + if (wrapped_rsa_key_.size() == 0) return; + std::vector zero(wrapped_rsa_key_.size(), 0); + ASSERT_EQ(zero, wrapped_rsa_key_); +} + Session::Session() : open_(false), forced_session_id_(false), session_id_(0), + nonce_(0), public_rsa_(0), message_size_(sizeof(MessageData)), // Most tests only use 4 keys. Other tests will explicitly call // set_num_keys. num_keys_(4) { - // Stripe the padded message. - for (size_t i = 0; i < sizeof(padded_message_.padding); i++) { - padded_message_.padding[i] = i % 0x100; - } } Session::~Session() { @@ -168,7 +359,7 @@ void Session::GenerateNonce(int* error_counter) { if (error_counter) { (*error_counter)++; } else { - sleep(1); // wait a second, then try again. + wvcdm::TestSleep::Sleep(1); // wait a second, then try again. // The following is after a 1 second pause, so it cannot be from a nonce // flood. ASSERT_EQ(OEMCrypto_SUCCESS, @@ -371,8 +562,8 @@ void Session::LoadEntitledContentKeys(OEMCryptoResult expected_sts) { session_id(), reinterpret_cast(encrypted_entitled_message_.data()), encrypted_entitled_message_.size(), - encrypted_entitled_key_array.data(), - encrypted_entitled_key_array.size())); + encrypted_entitled_key_array.size(), + encrypted_entitled_key_array.data())); if (expected_sts != OEMCrypto_SUCCESS) { return; } @@ -454,12 +645,12 @@ void Session::RefreshTestKeys(const size_t key_count, uint32_t control_bits, ASSERT_NO_FATAL_FAILURE(TestDecryptCTR()); // This should still be valid key, even if the refresh failed, because this // is before the original license duration. - sleep(kShortSleep); + wvcdm::TestSleep::Sleep(kShortSleep); ASSERT_NO_FATAL_FAILURE(TestDecryptCTR(false)); // This should be after duration of the original license, but before the // expiration of the refresh message. This should succeed if and only if the // refresh succeeded. - sleep(kShortSleep + kLongSleep); + wvcdm::TestSleep::Sleep(kShortSleep + kLongSleep); if (expected_result == OEMCrypto_SUCCESS) { ASSERT_NO_FATAL_FAILURE(TestDecryptCTR(false, OEMCrypto_SUCCESS)); } else { @@ -609,6 +800,7 @@ void Session::SetLoadKeysSubstringParams() { } void Session::EncryptAndSign() { + ASSERT_NO_FATAL_FAILURE(GenerateDerivedKeysFromSessionKey()); encrypted_license() = license_; uint8_t iv_buffer[16]; @@ -644,57 +836,24 @@ void Session::VerifyLicenseRequestSignature(size_t data_length) { for (size_t i = 0; i < data.size(); i++) data[i] = i & 0xFF; OEMCryptoResult sts; size_t gen_signature_length = 0; - - // TODO(b/135288420): Test core message functionality. - // This function should be split into three versions, one for each core - // message. size_t core_message_length = 0; - sts = OEMCrypto_SignLicenseRequest(session_id(), data.data(), data.size(), - nullptr, &core_message_length, nullptr, - &gen_signature_length); + sts = OEMCrypto_PrepAndSignLicenseRequest(session_id(), data.data(), + data.size(), &core_message_length, + nullptr, &gen_signature_length); ASSERT_EQ(OEMCrypto_ERROR_SHORT_BUFFER, sts); - const size_t hmac_signature_size = 32u; - ASSERT_EQ(hmac_signature_size, gen_signature_length); vector gen_signature(gen_signature_length); - sts = OEMCrypto_SignLicenseRequest( - session_id(), data.data(), data.size(), nullptr, &core_message_length, + sts = OEMCrypto_PrepAndSignLicenseRequest( + session_id(), data.data(), data.size(), &core_message_length, gen_signature.data(), &gen_signature_length); ASSERT_EQ(OEMCrypto_SUCCESS, sts); - std::vector expected_signature; - key_deriver_.ClientSignBuffer(data, &expected_signature); - ASSERT_EQ(expected_signature, gen_signature); -} - -// TODO(b/135288022): This function only handles the keybox case. -// It should do something different for Prov 3.0. -void Session::VerifyProvisioningRequestSignature(size_t data_length) { - // In the real world, a message should be signed by the client and - // verified by the server. This simulates that. - vector data(data_length); - for (size_t i = 0; i < data.size(); i++) data[i] = i & 0xFF; - OEMCryptoResult sts; - size_t gen_signature_length = 0; - - // TODO(b/135288420): Test core message functionality. - // This function should be split into three versions, one for each core - // message. - size_t core_message_length = 0; - sts = OEMCrypto_SignProvisioningRequest( - session_id(), data.data(), data.size(), nullptr, &core_message_length, - nullptr, &gen_signature_length); - ASSERT_EQ(OEMCrypto_ERROR_SHORT_BUFFER, sts); - const size_t hmac_signature_size = 32u; - ASSERT_EQ(hmac_signature_size, gen_signature_length); - vector gen_signature(gen_signature_length); - // TODO(b/135288022): This function should pick the right type of signature, - // and then call SignProvisioningRequest. - sts = OEMCrypto_SignLicenseRequest( - session_id(), data.data(), data.size(), nullptr, &core_message_length, - gen_signature.data(), &gen_signature_length); - ASSERT_EQ(OEMCrypto_SUCCESS, sts); - std::vector expected_signature; - key_deriver_.ClientSignBuffer(data, &expected_signature); - ASSERT_EQ(expected_signature, gen_signature); + if (global_features.api_version >= 16) { + ASSERT_GT(data.size(), core_message_length); + std::string core_message(reinterpret_cast(data.data()), + core_message_length); + VerifyCoreLicenseRequest(core_message); + } + VerifyRSASignature(data, gen_signature.data(), gen_signature.size(), + kSign_RSASSA_PSS); } void Session::VerifyRenewalRequestSignature(size_t data_length) { @@ -704,27 +863,47 @@ void Session::VerifyRenewalRequestSignature(size_t data_length) { for (size_t i = 0; i < data.size(); i++) data[i] = i & 0xFF; OEMCryptoResult sts; size_t gen_signature_length = 0; - - // TODO(b/135288420): Test core message functionality. - // This function should be split into three versions, one for each core - // message. size_t core_message_length = 0; - sts = OEMCrypto_SignRenewalRequest(session_id(), data.data(), data.size(), - nullptr, &core_message_length, nullptr, - &gen_signature_length); + sts = OEMCrypto_PrepAndSignRenewalRequest(session_id(), data.data(), + data.size(), &core_message_length, + nullptr, &gen_signature_length); ASSERT_EQ(OEMCrypto_ERROR_SHORT_BUFFER, sts); - const size_t hmac_signature_size = 32u; - ASSERT_EQ(hmac_signature_size, gen_signature_length); + ASSERT_EQ(HMAC_SHA256_SIGNATURE_SIZE, gen_signature_length); vector gen_signature(gen_signature_length); - sts = OEMCrypto_SignRenewalRequest( - session_id(), data.data(), data.size(), nullptr, &core_message_length, + sts = OEMCrypto_PrepAndSignRenewalRequest( + session_id(), data.data(), data.size(), &core_message_length, gen_signature.data(), &gen_signature_length); ASSERT_EQ(OEMCrypto_SUCCESS, sts); + if (global_features.api_version >= 16) { + ASSERT_GT(data.size(), core_message_length); + std::string core_message(reinterpret_cast(data.data()), + core_message_length); + VerifyCoreRenewalRequest(core_message); + } std::vector expected_signature; key_deriver_.ClientSignBuffer(data, &expected_signature); ASSERT_EQ(expected_signature, gen_signature); } +void Session::VerifyCoreLicenseRequest(const std::string& core_message) { + oec_util::ODK_LicenseRequest core_license_request; + EXPECT_TRUE( + oec_util::ParseLicenseRequest(core_message, &core_license_request)); + EXPECT_EQ(global_features.api_version, core_license_request.api_version); + if (nonce_) EXPECT_EQ(nonce_, core_license_request.nonce); + EXPECT_EQ(session_id_, core_license_request.session_id); +} + +void Session::VerifyCoreRenewalRequest(const std::string& core_message) { + oec_util::ODK_RenewalRequest core_renewal_request; + EXPECT_TRUE( + oec_util::ParseRenewalRequest(core_message, &core_renewal_request)); + EXPECT_EQ(global_features.api_version, core_renewal_request.api_version); + // We do not check the nonce, because we don't know it if it comes from a + // previous session. + EXPECT_EQ(session_id_, core_renewal_request.session_id); +} + void Session::FillKeyArray(const MessageData& data, OEMCrypto_KeyObject* key_array) { const uint8_t* data_ptr = reinterpret_cast(&data); @@ -818,19 +997,20 @@ void Session::TestDecryptCTR(bool select_key_first, // Describe the output vector outputBuffer(256); - OEMCrypto_DestBufferDesc destBuffer; - destBuffer.type = OEMCrypto_BufferType_Clear; - destBuffer.buffer.clear.address = outputBuffer.data(); - destBuffer.buffer.clear.max_length = outputBuffer.size(); + OEMCrypto_DestBufferDesc out_buffer_descriptor; + out_buffer_descriptor.type = OEMCrypto_BufferType_Clear; + out_buffer_descriptor.buffer.clear.address = outputBuffer.data(); + out_buffer_descriptor.buffer.clear.address_length = outputBuffer.size(); OEMCrypto_CENCEncryptPatternDesc pattern; pattern.encrypt = 0; pattern.skip = 0; - pattern.offset = 0; // Decrypt the data +#if 0 // TODO(b/135285640): fix this. sts = OEMCrypto_DecryptCENC( session_id(), encryptedData.data(), encryptedData.size(), true, - encryptionIv.data(), 0, &destBuffer, &pattern, + encryptionIv.data(), 0, &out_buffer_descriptor, &pattern, OEMCrypto_FirstSubsample | OEMCrypto_LastSubsample); +#endif // We only have a few errors that we test are reported. if (expected_result == OEMCrypto_SUCCESS) { // No error. ASSERT_EQ(OEMCrypto_SUCCESS, sts); @@ -850,6 +1030,13 @@ void Session::TestDecryptResult(OEMCryptoResult expected_result, global_features.api_version >= 9) { // Report stale keys, required in v9 and beyond. ASSERT_EQ(OEMCrypto_ERROR_KEY_EXPIRED, actual_result); + } else if (expected_result == OEMCrypto_WARNING_MIXED_OUTPUT_PROTECTION && + global_features.api_version >= 16) { + // OEMCrypto is allowed to report either this warning or + // OEMCrypto_ERROR_INSUFFICIENT_HDCP depending on if it can disable + // restricted displays. + ASSERT_TRUE(actual_result == OEMCrypto_WARNING_MIXED_OUTPUT_PROTECTION || + actual_result == OEMCrypto_ERROR_INSUFFICIENT_HDCP); } else if (expected_result == OEMCrypto_ERROR_INSUFFICIENT_HDCP) { // Report HDCP errors. ASSERT_EQ(OEMCrypto_ERROR_INSUFFICIENT_HDCP, actual_result); @@ -944,82 +1131,6 @@ void Session::LoadOEMCert(bool verify_cert) { } } -void Session::MakeRSACertificate(struct RSAPrivateKeyMessage* encrypted, - size_t message_size, - std::vector* signature, - uint32_t allowed_schemes, - const vector& rsa_key, - const Encryptor* encryptor) { - if (encryptor == nullptr) encryptor = &key_deriver_; - struct RSAPrivateKeyMessage message; - if (allowed_schemes != kSign_RSASSA_PSS) { - uint32_t algorithm_n = htonl(allowed_schemes); - memcpy(message.rsa_key, "SIGN", 4); - memcpy(message.rsa_key + 4, &algorithm_n, 4); - memcpy(message.rsa_key + 8, rsa_key.data(), rsa_key.size()); - message.rsa_key_length = 8 + rsa_key.size(); - } else { - memcpy(message.rsa_key, rsa_key.data(), rsa_key.size()); - message.rsa_key_length = rsa_key.size(); - } - GenerateNonce(); - message.nonce = nonce_; - encryptor->PadAndEncryptProvisioningMessage(&message, encrypted); - key_deriver_.ServerSignBuffer(reinterpret_cast(encrypted), - message_size, signature); -} - -void Session::RewrapRSAKey(const struct RSAPrivateKeyMessage& encrypted, - size_t message_size, - const std::vector& signature, - vector* wrapped_key, bool force) { - size_t wrapped_key_length = 0; - const uint8_t* message_ptr = reinterpret_cast(&encrypted); - ASSERT_EQ(OEMCrypto_ERROR_SHORT_BUFFER, - OEMCrypto_RewrapDeviceRSAKey( - session_id(), message_ptr, message_size, signature.data(), - signature.size(), &encrypted.nonce, encrypted.rsa_key, - encrypted.rsa_key_length, encrypted.rsa_key_iv, nullptr, - &wrapped_key_length)); - wrapped_key->clear(); - wrapped_key->assign(wrapped_key_length, 0); - OEMCryptoResult sts = OEMCrypto_RewrapDeviceRSAKey( - session_id(), message_ptr, message_size, signature.data(), - signature.size(), &encrypted.nonce, encrypted.rsa_key, - encrypted.rsa_key_length, encrypted.rsa_key_iv, &(wrapped_key->front()), - &wrapped_key_length); - if (force) { - ASSERT_EQ(OEMCrypto_SUCCESS, sts); - } - if (OEMCrypto_SUCCESS != sts) { - wrapped_key->clear(); - } -} - -void Session::RewrapRSAKey30(const struct RSAPrivateKeyMessage& encrypted, - const std::vector& encrypted_message_key, - vector* wrapped_key, bool force) { - size_t wrapped_key_length = 0; - ASSERT_EQ(OEMCrypto_ERROR_SHORT_BUFFER, - OEMCrypto_RewrapDeviceRSAKey30( - session_id(), &nonce_, encrypted_message_key.data(), - encrypted_message_key.size(), encrypted.rsa_key, - encrypted.rsa_key_length, encrypted.rsa_key_iv, nullptr, - &wrapped_key_length)); - wrapped_key->clear(); - wrapped_key->assign(wrapped_key_length, 0); - OEMCryptoResult sts = OEMCrypto_RewrapDeviceRSAKey30( - session_id(), &nonce_, encrypted_message_key.data(), - encrypted_message_key.size(), encrypted.rsa_key, encrypted.rsa_key_length, - encrypted.rsa_key_iv, &(wrapped_key->front()), &wrapped_key_length); - if (force) { - ASSERT_EQ(OEMCrypto_SUCCESS, sts); - } - if (OEMCrypto_SUCCESS != sts) { - wrapped_key->clear(); - } -} - void Session::PreparePublicKey(const uint8_t* rsa_key, size_t rsa_key_length) { if (rsa_key == nullptr) { rsa_key = kTestRSAPKCS8PrivateKeyInfo2_2048; @@ -1164,7 +1275,6 @@ void Session::InstallRSASessionTestKey(const vector& wrapped_rsa_key) { ASSERT_EQ(OEMCrypto_SUCCESS, OEMCrypto_LoadDeviceRSAKey(session_id(), wrapped_rsa_key.data(), wrapped_rsa_key.size())); - GenerateDerivedKeysFromSessionKey(); } void Session::CreateNewUsageEntry(OEMCryptoResult* status) { @@ -1258,7 +1368,8 @@ void Session::GenerateReport(const std::string& pst, EXPECT_EQ(pst.length(), pst_report().pst_length()); EXPECT_EQ(0, memcmp(pst.c_str(), pst_report().pst(), pst.length())); // Also, we the session to be able to sign the release message with the - // correct mac keys from the usage table entry. + // correct mac keys from the usage table entry. Note that we sign both the PST + // report above with ClientSignPstReport, and we sign the renewal request. ASSERT_NO_FATAL_FAILURE(VerifyRenewalRequestSignature()); } @@ -1268,7 +1379,7 @@ void Session::VerifyPST(const Test_PST_Report& expected) { char* pst_ptr = reinterpret_cast(computed.pst()); std::string computed_pst(pst_ptr, pst_ptr + computed.pst_length()); ASSERT_EQ(expected.pst, computed_pst); - time_t now = time(nullptr); + int64_t now = wvcdm::Clock().GetCurrentTime(); int64_t age = now - expected.time_created; // How old is this report. EXPECT_NEAR(expected.seconds_since_license_received + age, computed.seconds_since_license_received(), @@ -1291,7 +1402,7 @@ void Session::VerifyPST(const Test_PST_Report& expected) { // This might adjust t to be "seconds since now". If t is small, we assume it // is "seconds since now", but if the value of t is large, assume it is // "absolute time" and convert to "seconds since now". -static int64_t MaybeAdjustTime(int64_t t, time_t now) { +static int64_t MaybeAdjustTime(int64_t t, int64_t now) { int64_t k10Minutes = 60 * 10; // in seconds. if (t > k10Minutes) return now - t; return t; @@ -1301,7 +1412,7 @@ void Session::VerifyReport(Test_PST_Report expected, int64_t time_license_received, int64_t time_first_decrypt, int64_t time_last_decrypt) { - time_t now = time(nullptr); + const int64_t now = wvcdm::Clock().GetCurrentTime(); expected.seconds_since_license_received = MaybeAdjustTime(time_license_received, now); expected.seconds_since_first_decrypt = diff --git a/oemcrypto/test/oec_session_util.h b/oemcrypto/test/oec_session_util.h index 555e0e6..c1b6834 100644 --- a/oemcrypto/test/oec_session_util.h +++ b/oemcrypto/test/oec_session_util.h @@ -7,13 +7,16 @@ // // OEMCrypto unit tests // +#include #include #include #include #include +#include "odk.h" #include "oec_device_features.h" #include "oec_key_deriver.h" +#include "oec_util.h" #include "oemcrypto_types.h" #include "pst_report.h" @@ -30,21 +33,21 @@ void PrintTo(const vector& value, ostream* os); namespace wvoec { // Make sure this is larger than kMaxKeysPerSession, in oemcrypto_test.cpp -const size_t kMaxNumKeys = 20; +constexpr size_t kMaxNumKeys = 35; namespace { #if defined(TEST_SPEED_MULTIPLIER) // Can slow test time limits when // debugging is slowing everything. -const int kSpeedMultiplier = TEST_SPEED_MULTIPLIER; +constexpr int kSpeedMultiplier = TEST_SPEED_MULTIPLIER; #else -const int kSpeedMultiplier = 1; +constexpr int kSpeedMultiplier = 1; #endif -const int kShortSleep = 1 * kSpeedMultiplier; -const int kLongSleep = 2 * kSpeedMultiplier; -const uint32_t kDuration = 2 * kSpeedMultiplier; -const uint32_t kLongDuration = 5 * kSpeedMultiplier; -const int32_t kTimeTolerance = 3 * kSpeedMultiplier; -const time_t kUsageTableTimeTolerance = 10 * kSpeedMultiplier; +constexpr int kShortSleep = 1 * kSpeedMultiplier; +constexpr int kLongSleep = 2 * kSpeedMultiplier; +constexpr uint32_t kDuration = 2 * kSpeedMultiplier; +constexpr uint32_t kLongDuration = 5 * kSpeedMultiplier; +constexpr int32_t kTimeTolerance = 3 * kSpeedMultiplier; +constexpr int64_t kUsageTableTimeTolerance = 10 * kSpeedMultiplier; } // namespace typedef struct { @@ -56,13 +59,14 @@ typedef struct { // Note: The API does not specify a maximum key id length. We specify a // maximum just for these tests, so that we have a fixed message size. -const size_t kTestKeyIdMaxLength = 16; +constexpr size_t kTestKeyIdMaxLength = 16; // Most content will use a key id that is 16 bytes long. -const int kDefaultKeyIdLength = 16; +constexpr int kDefaultKeyIdLength = 16; -const size_t kMaxPSTLength = 255; // In specification. -const size_t kMaxMessageSize = 8 * 1024; // In specification. +constexpr size_t kMaxPSTLength = 255; // In specification. +constexpr size_t kMaxMessageSize = 8 * 1024; // In specification. +constexpr size_t kMaxCoreMessage = 20 * kMaxNumKeys + 150; // Rough estimate. typedef struct { uint8_t key_id[kTestKeyIdMaxLength]; @@ -79,6 +83,7 @@ typedef struct { // This structure will be signed to simulate a message from the server. struct MessageData { + uint8_t core_message[kMaxCoreMessage]; MessageKeyData keys[kMaxNumKeys]; uint8_t mac_key_iv[KEY_IV_SIZE]; uint8_t padding[KEY_IV_SIZE]; @@ -88,15 +93,14 @@ struct MessageData { struct Test_PST_Report { Test_PST_Report(const std::string& pst_in, - OEMCrypto_Usage_Entry_Status status_in) - : status(status_in), pst(pst_in), time_created(time(nullptr)) {} + OEMCrypto_Usage_Entry_Status status_in); OEMCrypto_Usage_Entry_Status status; int64_t seconds_since_license_received; int64_t seconds_since_first_decrypt; int64_t seconds_since_last_decrypt; std::string pst; - time_t time_created; + int64_t time_created; }; struct EntitledContentKeyData { @@ -128,6 +132,158 @@ OEMCrypto_Substring GetSubstring(const std::string& message = "", const std::string& field = "", bool set_zero = false); +class Session; +// The signature of the OEMCrypto function to prepare and sign a request. +typedef OEMCryptoResult (*PrepAndSignRequest_t)( + OEMCrypto_SESSION session, uint8_t* message, size_t message_length, + size_t* core_message_length, uint8_t* signature, size_t* signature_length); + +// A RoundTrip helps generate and verify a request message, helps generate the +// corresponding response, and then helps verify loading the response. +template +class RoundTrip { + public: + RoundTrip() = delete; + RoundTrip(Session* session) + : session_(session), + message_size_(sizeof(ResponseData) + kMaxCoreMessage), + nonce_(0){}; + virtual ~RoundTrip() {} + + // Have OEMCrypto sign a request message and then verify the signature and the + // core message. + virtual void SignAndVerifyRequest(); + // Create a default |core_response| and |response_data|. + virtual void CreateDefaultResponse() = 0; + // Copy fields from |reponse_data| to |padded_response_data|, encrypting those + // that should be encrypted. Serialize the core message. Then sign the + // response. + virtual void EncryptAndSignResponse() = 0; + // Attempt to load the response and return the error. Short buffer errors are + // handled by LoadResponse, not the caller. All other errors should be + // handled by the caller. + virtual OEMCryptoResult LoadResponse() = 0; + + // Accessors are all read/write because tests modify default values. + Session* session() { return session_; } + CoreRequest& core_request() { return core_request_; } + CoreResponse& core_response() { return core_response_; } + ResponseData& response_data() { return response_data_; } + ResponseData& encrypted_response_data() { return encrypted_response_data_; } + std::vector& encrypted_response_buffer() { + return encrypted_response_; + } + + // Set the size of the buffer used the encrypted license. + // Must be between sizeof(MessageData) and kMaxMessageSize. + void set_message_size(size_t size) { + message_size_ = size; + ASSERT_GE(message_size_, sizeof(ResponseData) + kMaxCoreMessage); + ASSERT_LE(message_size_, kMaxMessageSize); + } + // The size of the encrypted message. + size_t message_size() { return message_size_; } + std::vector& response_signature() { return response_signature_; } + + protected: + // ---------------------------------------------------------------------- + // Specialized functionality for each message type. + + // Verify the signature of the request. + virtual void VerifyRequestSignature( + const vector& data, + const vector& generated_signature) = 0; + // Verify the values of the core response. + virtual void FillAndVerifyCoreRequest( + const std::string& core_message_string) = 0; + // Find the given pointer in the response_data_. + virtual OEMCrypto_Substring FindSubstring(const void* pointer, size_t length); + + // ---------------------------------------------------------------------- + // Member variables. + Session* session_; + CoreRequest core_request_; + CoreResponse core_response_; + ResponseData response_data_, encrypted_response_data_; + size_t message_size_; // How much of the padded message to use. + std::vector response_signature_; + uint32_t nonce_; + std::string serialized_core_message_; + std::vector encrypted_response_; +}; + +class ProvisioningRoundTrip + : public RoundTrip { + public: + ProvisioningRoundTrip(Session* session, + const std::vector& encoded_rsa_key) + : RoundTrip(session), + allowed_schemes_(kSign_RSASSA_PSS), + encoded_rsa_key_(encoded_rsa_key) {} + // Prepare the session for signing the request. + virtual void PrepareSession(const wvoec::WidevineKeybox& keybox); + void CreateDefaultResponse() override; + void EncryptAndSignResponse() override; + OEMCryptoResult LoadResponse() override; + void VerifyLoadFailed(); + const std::vector& encoded_rsa_key() { return encoded_rsa_key_; } + const std::vector& wrapped_rsa_key() { return wrapped_rsa_key_; } + void set_allowed_schemes(uint32_t allowed_schemes) { + allowed_schemes_ = allowed_schemes; + } + + protected: + void VerifyRequestSignature( + const vector& data, + const vector& generated_signature) override; + // Verify the values of the core response. + virtual void FillAndVerifyCoreRequest( + const std::string& core_message_string) override; + uint32_t allowed_schemes_; + Encryptor encryptor_; + // The message key used for Prov 3.0. + std::vector message_key_; + std::vector encrypted_message_key_; + std::vector encoded_rsa_key_; + std::vector wrapped_rsa_key_; +}; + +class LicenseRoundTrip : public RoundTrip { + public: + LicenseRoundTrip(Session* session) : RoundTrip(session) {} + + protected: + void VerifyRequestSignature( + const vector& data, + const vector& generated_signature) override; + // Verify the values of the core response. + virtual void FillAndVerifyCoreRequest( + const std::string& core_message_string) override; +}; + +class RenewalRoundTrip + : public RoundTrip { + public: + RenewalRoundTrip(Session* session) : RoundTrip(session) {} + + protected: + void VerifyRequestSignature( + const vector& data, + const vector& generated_signature) override; + // Verify the values of the core response. + virtual void FillAndVerifyCoreRequest( + const std::string& core_message_string) override; +}; + class Session { public: Session(); @@ -218,15 +374,16 @@ class Session { // the server mac keys. It then fills out the key_array_ so that pointers in // that array point to the locations in the encrypted message. void EncryptAndSign(); - // This checks the signature generated by OEMCrypto_SignProvisioningRequest + // This checks the signature generated by OEMCrypto_PrepAndSignLicenseRequest // against that generaged by ClientSignBuffer. - void VerifyProvisioningRequestSignature(size_t data_length = 400); - // This checks the signature generated by OEMCrypto_SignLicenseRequest against - // that generaged by ClientSignBuffer. void VerifyLicenseRequestSignature(size_t data_length = 400); - // This checks the signature generated by OEMCrypto_SignRenewalRequest against - // that generaged by ClientSignBuffer. + // This checks the signature generated by OEMCrypto_PrepAndSignRenewalRequest + // against that generaged by ClientSignBuffer. void VerifyRenewalRequestSignature(size_t data_length = 400); + // Verify the core message matches data from this session. + void VerifyCoreLicenseRequest(const std::string& core_message); + // Verify the core message matches data from this session. + void VerifyCoreRenewalRequest(const std::string& core_message); // Set the pointers in key_array[*] to point values inside data. This is // needed to satisfy range checks in OEMCrypto_LoadKeys. void FillKeyArray(const MessageData& data, OEMCrypto_KeyObject* key_array); @@ -251,14 +408,6 @@ class Session { // Calls OEMCrypto_GetOEMPublicCertificate and OEMCrypto_LoadOEMPrivateKey and // loads the OEM cert's public rsa key into public_rsa_. void LoadOEMCert(bool verify_cert = false); - // Creates RSAPrivateKeyMessage for the specified rsa_key, encrypts it with - // the specified encryption key, and then signs it with the server's mac key. - // If encryption_key is null, use the session's enc_key_. - void MakeRSACertificate(struct RSAPrivateKeyMessage* encrypted, - size_t message_size, std::vector* signature, - uint32_t allowed_schemes, - const vector& rsa_key, - const Encryptor* encryptor = nullptr); // Calls OEMCrypto_RewrapDeviceRSAKey with the given provisioning response // message. If force is true, we assert that the key loads successfully. void RewrapRSAKey(const struct RSAPrivateKeyMessage& encrypted, @@ -318,7 +467,7 @@ class Session { } // Generates a usage report for the specified pst. If there is success, // the report's signature is verified, and several fields are given sanity - // checks. If other is not null, then the mac keys are copied from other in + // checks. If |other| is not null, then the mac keys are copied from other in // order to verify signatures. void GenerateReport(const std::string& pst, OEMCryptoResult expected_result = OEMCrypto_SUCCESS, @@ -400,6 +549,7 @@ class Session { // Pointer to buffer holding |encrypted_entitled_message_| const uint8_t* encrypted_entitled_message_ptr(); + const KeyDeriver& key_deriver() const { return key_deriver_; } private: bool open_; diff --git a/oemcrypto/test/oemcrypto_session_tests_helper.cpp b/oemcrypto/test/oemcrypto_session_tests_helper.cpp index 7ec0704..3214269 100644 --- a/oemcrypto/test/oemcrypto_session_tests_helper.cpp +++ b/oemcrypto/test/oemcrypto_session_tests_helper.cpp @@ -20,68 +20,16 @@ const uint8_t* find(const vector& message, return &(*pos); } -// This creates a wrapped RSA key for devices that have the test keybox -// installed. If force is true, we assert that the key loads successfully. -void SessionUtil::CreateWrappedRSAKeyFromKeybox(uint32_t allowed_schemes, - bool force) { +// This creates a wrapped RSA key. +void SessionUtil::CreateWrappedRSAKey() { Session s; - ASSERT_NO_FATAL_FAILURE(s.open()); - ASSERT_NO_FATAL_FAILURE(s.GenerateDerivedKeysFromKeybox(keybox_)); - // Provisioning request would be signed by the client and verified by the - // server. - ASSERT_NO_FATAL_FAILURE(s.VerifyProvisioningRequestSignature()); - struct RSAPrivateKeyMessage encrypted; - std::vector signature; - ASSERT_NO_FATAL_FAILURE( - s.MakeRSACertificate(&encrypted, sizeof(encrypted), - &signature, allowed_schemes, - encoded_rsa_key_)); - ASSERT_NO_FATAL_FAILURE(s.RewrapRSAKey( - encrypted, sizeof(encrypted), signature, &wrapped_rsa_key_, force)); - // Verify that the clear key is not contained in the wrapped key. - // It should be encrypted. - ASSERT_EQ(nullptr, find(wrapped_rsa_key_, encoded_rsa_key_)); -} - -// This creates a wrapped RSA key for devices using provisioning 3.0. If force -// is true, we assert that the key loads successfully. -void SessionUtil::CreateWrappedRSAKeyFromOEMCert( - uint32_t allowed_schemes, bool force) { - Session s; - ASSERT_NO_FATAL_FAILURE(s.open()); - ASSERT_NO_FATAL_FAILURE(s.LoadOEMCert()); - struct RSAPrivateKeyMessage encrypted; - std::vector signature; - std::vector message_key; - std::vector encrypted_message_key; - s.GenerateRSASessionKey(&message_key, &encrypted_message_key); - Encryptor encryptor(message_key); - ASSERT_NO_FATAL_FAILURE(s.MakeRSACertificate(&encrypted, sizeof(encrypted), - &signature, allowed_schemes, - encoded_rsa_key_, &encryptor)); - ASSERT_NO_FATAL_FAILURE( - s.RewrapRSAKey30(encrypted, encrypted_message_key, - &wrapped_rsa_key_, force)); - // Verify that the clear key is not contained in the wrapped key. - // It should be encrypted. - ASSERT_EQ(nullptr, find(wrapped_rsa_key_, encoded_rsa_key_)); -} - -// If force is true, we assert that the key loads successfully. -void SessionUtil::CreateWrappedRSAKey(uint32_t allowed_schemes, - bool force) { - switch (global_features.provisioning_method) { - case OEMCrypto_OEMCertificate: - CreateWrappedRSAKeyFromOEMCert(allowed_schemes, force); - break; - case OEMCrypto_Keybox: - CreateWrappedRSAKeyFromKeybox(allowed_schemes, force); - break; - default: - FAIL() << "Cannot generate wrapped RSA key if provision method = " - << wvoec::ProvisioningMethodName( - global_features.provisioning_method); - } + ProvisioningRoundTrip provisioning_messages(&s, encoded_rsa_key_); + provisioning_messages.PrepareSession(keybox_); + ASSERT_NO_FATAL_FAILURE(provisioning_messages.SignAndVerifyRequest()); + ASSERT_NO_FATAL_FAILURE(provisioning_messages.CreateDefaultResponse()); + ASSERT_NO_FATAL_FAILURE(provisioning_messages.EncryptAndSignResponse()); + ASSERT_EQ(OEMCrypto_SUCCESS, provisioning_messages.LoadResponse()); + wrapped_rsa_key_ = provisioning_messages.wrapped_rsa_key(); } void SessionUtil::InstallKeybox(const wvoec::WidevineKeybox& keybox, @@ -104,10 +52,10 @@ void SessionUtil::EnsureTestKeys() { switch (global_features.derive_key_method) { case DeviceFeatures::LOAD_TEST_KEYBOX: keybox_ = kTestKeybox; - ASSERT_EQ(OEMCrypto_SUCCESS, - OEMCrypto_LoadTestKeybox( - reinterpret_cast(&keybox_), - sizeof(keybox_))); + ASSERT_EQ( + OEMCrypto_SUCCESS, + OEMCrypto_LoadTestKeybox(reinterpret_cast(&keybox_), + sizeof(keybox_))); break; case DeviceFeatures::LOAD_TEST_RSA_KEY: ASSERT_EQ(OEMCrypto_SUCCESS, OEMCrypto_LoadTestRSAKey()); @@ -126,26 +74,18 @@ void SessionUtil::EnsureTestKeys() { // This makes sure that the derived keys (encryption key and two mac keys) // are installed in OEMCrypto and in the test session. -void SessionUtil::InstallTestSessionKeys(Session* s) { - if (global_features.uses_certificate) { - if (global_features.loads_certificate) { - if (wrapped_rsa_key_.size() == 0) { - // If we don't have a wrapped key yet, create one. - // This wrapped key will be shared by all sessions in the test. - ASSERT_NO_FATAL_FAILURE( - CreateWrappedRSAKey(kSign_RSASSA_PSS, true)); - } - // Load the wrapped rsa test key. - ASSERT_NO_FATAL_FAILURE( - s->InstallRSASessionTestKey(wrapped_rsa_key_)); +void SessionUtil::InstallTestRSAKey(Session* s) { + ASSERT_TRUE(global_features.uses_certificate); + if (global_features.loads_certificate) { + if (wrapped_rsa_key_.size() == 0) { + // If we don't have a wrapped key yet, create one. + // This wrapped key will be shared by all sessions in the test. + ASSERT_NO_FATAL_FAILURE(CreateWrappedRSAKey()); } - // Test RSA key should be loaded. - ASSERT_NO_FATAL_FAILURE( - s->GenerateDerivedKeysFromSessionKey()); - } else { // Just uses keybox. Test keybox should already be installed. - ASSERT_NO_FATAL_FAILURE( - s->GenerateDerivedKeysFromKeybox(keybox_)); + // Load the wrapped rsa test key. + ASSERT_NO_FATAL_FAILURE(s->InstallRSASessionTestKey(wrapped_rsa_key_)); } + // Test RSA key should be loaded. + ASSERT_NO_FATAL_FAILURE(s->GenerateDerivedKeysFromSessionKey()); } - -} +} // namespace wvoec diff --git a/oemcrypto/test/oemcrypto_session_tests_helper.h b/oemcrypto/test/oemcrypto_session_tests_helper.h index 70469bd..1089843 100644 --- a/oemcrypto/test/oemcrypto_session_tests_helper.h +++ b/oemcrypto/test/oemcrypto_session_tests_helper.h @@ -12,33 +12,27 @@ namespace wvoec { class SessionUtil { public: - SessionUtil() - : encoded_rsa_key_(kTestRSAPKCS8PrivateKeyInfo2_2048, - kTestRSAPKCS8PrivateKeyInfo2_2048 + - sizeof(kTestRSAPKCS8PrivateKeyInfo2_2048)) {} + SessionUtil() + : encoded_rsa_key_(kTestRSAPKCS8PrivateKeyInfo2_2048, + kTestRSAPKCS8PrivateKeyInfo2_2048 + + sizeof(kTestRSAPKCS8PrivateKeyInfo2_2048)) {} - // If force is true, we assert that the key loads successfully. - void CreateWrappedRSAKeyFromKeybox(uint32_t allowed_schemes, bool force); + // Create a new wrapped DRM Certificate. + void CreateWrappedRSAKey(); - // If force is true, we assert that the key loads successfully. - void CreateWrappedRSAKeyFromOEMCert(uint32_t allowed_schemes, bool force); + // This is used to force installation of a keybox. This overwrites the + // production keybox -- it does NOT use OEMCrypto_LoadTestKeybox. + void InstallKeybox(const wvoec::WidevineKeybox& keybox, bool good); - // If force is true, we assert that the key loads successfully. - void CreateWrappedRSAKey(uint32_t allowed_schemes, bool force); + // This loads the test keybox or the test RSA key, using LoadTestKeybox or + // LoadTestRSAKey as needed. + void EnsureTestKeys(); - // This is used to force installation of a keybox. This overwrites the - // production keybox -- it does NOT use OEMCrypto_LoadTestKeybox. - void InstallKeybox(const wvoec::WidevineKeybox& keybox, bool good); + void InstallTestRSAKey(Session* s); - // This loads the test keybox or the test RSA key, using LoadTestKeybox or - // LoadTestRSAKey as needed. - void EnsureTestKeys(); - - void InstallTestSessionKeys(Session* s); - - std::vector encoded_rsa_key_; - std::vector wrapped_rsa_key_; - wvoec::WidevineKeybox keybox_; + std::vector encoded_rsa_key_; + std::vector wrapped_rsa_key_; + wvoec::WidevineKeybox keybox_; }; } // namespace wvoec diff --git a/oemcrypto/test/oemcrypto_test.cpp b/oemcrypto/test/oemcrypto_test.cpp index 63e1547..b6032b6 100644 --- a/oemcrypto/test/oemcrypto_test.cpp +++ b/oemcrypto/test/oemcrypto_test.cpp @@ -13,7 +13,6 @@ #include #include #include -#include #ifdef _WIN32 # include @@ -31,6 +30,7 @@ #include #include "OEMCryptoCENC.h" +#include "clock.h" #include "log.h" #include "oec_device_features.h" #include "oec_session_util.h" @@ -39,6 +39,7 @@ #include "oemcrypto_types.h" #include "platform.h" #include "string_conversions.h" +#include "test_sleep.h" #include "wvcrc32.h" using ::testing::Bool; @@ -125,6 +126,7 @@ class OEMCryptoClientTest : public ::testing::Test, public SessionUtil { void SetUp() override { ::testing::Test::SetUp(); + wvcdm::TestSleep::SyncFakeClock(); const ::testing::TestInfo* const test_info = ::testing::UnitTest::GetInstance()->current_test_info(); LOGD("Running test %s.%s", test_info->test_case_name(), test_info->name()); @@ -385,7 +387,7 @@ TEST_F(OEMCryptoClientTest, GenerateNonce) { // Prevent a nonce flood even if each nonce is in a different session. TEST_F(OEMCryptoClientTest, PreventNonceFlood2API09) { int error_counter = 0; - time_t test_start = time(nullptr); + const int64_t test_start = wvcdm::Clock().GetCurrentTime(); // More than 20 nonces per second should generate an error. // To allow for some slop, we actually test for more. const int kFloodCount = 80; @@ -394,7 +396,7 @@ TEST_F(OEMCryptoClientTest, PreventNonceFlood2API09) { ASSERT_NO_FATAL_FAILURE(s.open()); s.GenerateNonce(&error_counter); } - time_t test_end = time(nullptr); + const int64_t test_end = wvcdm::Clock().GetCurrentTime(); int valid_counter = kFloodCount - error_counter; // Either oemcrypto should enforce a delay, or it should return an error from // GenerateNonce -- in either case the number of valid nonces is rate @@ -402,7 +404,8 @@ TEST_F(OEMCryptoClientTest, PreventNonceFlood2API09) { // test_start and test_end. EXPECT_LE(valid_counter, 20 * (test_end - test_start + 2)); error_counter = 0; - sleep(2); // After a pause, we should be able to regenerate nonces. + // After a pause, we should be able to regenerate nonces. + wvcdm::TestSleep::Sleep(2); Session s; ASSERT_NO_FATAL_FAILURE(s.open()); s.GenerateNonce(&error_counter); @@ -416,7 +419,7 @@ TEST_F(OEMCryptoClientTest, PreventNonceFlood2API09) { TEST_F(OEMCryptoClientTest, PreventNonceFlood3API09) { int request_counter = 0; int error_counter = 0; - time_t test_start = time(nullptr); + const int64_t test_start = wvcdm::Clock().GetCurrentTime(); // More than 20 nonces per second should generate an error. // To allow for some slop, we actually test for more. for (int i = 0; i < 10; i++) { @@ -427,7 +430,7 @@ TEST_F(OEMCryptoClientTest, PreventNonceFlood3API09) { s[j].GenerateNonce(&error_counter); } } - time_t test_end = time(nullptr); + const int64_t test_end = wvcdm::Clock().GetCurrentTime(); int valid_counter = request_counter - error_counter; // Either oemcrypto should enforce a delay, or it should return an error from // GenerateNonce -- in either case the number of valid nonces is rate @@ -435,7 +438,8 @@ TEST_F(OEMCryptoClientTest, PreventNonceFlood3API09) { // test_start and test_end. EXPECT_LE(valid_counter, 20 * (test_end - test_start + 2)); error_counter = 0; - sleep(2); // After a pause, we should be able to regenerate nonces. + // After a pause, we should be able to regenerate nonces. + wvcdm::TestSleep::Sleep(2); Session s; ASSERT_NO_FATAL_FAILURE(s.open()); s.GenerateNonce(&error_counter); @@ -450,36 +454,37 @@ TEST_F(OEMCryptoClientTest, ClearCopyTestAPI10) { vector input_buffer(kDataSize); GetRandBytes(input_buffer.data(), input_buffer.size()); vector output_buffer(kDataSize); - OEMCrypto_DestBufferDesc dest_buffer; - dest_buffer.type = OEMCrypto_BufferType_Clear; - dest_buffer.buffer.clear.address = output_buffer.data(); - dest_buffer.buffer.clear.max_length = output_buffer.size(); + OEMCrypto_DestBufferDesc dest_buffer_descriptor; + dest_buffer_descriptor.type = OEMCrypto_BufferType_Clear; + dest_buffer_descriptor.buffer.clear.address = output_buffer.data(); + dest_buffer_descriptor.buffer.clear.address_length = output_buffer.size(); ASSERT_EQ( OEMCrypto_SUCCESS, OEMCrypto_CopyBuffer(s.session_id(), input_buffer.data(), - input_buffer.size(), &dest_buffer, + input_buffer.size(), &dest_buffer_descriptor, OEMCrypto_FirstSubsample | OEMCrypto_LastSubsample)); ASSERT_EQ(input_buffer, output_buffer); - ASSERT_EQ(OEMCrypto_ERROR_INVALID_CONTEXT, - OEMCrypto_CopyBuffer( - s.session_id(), nullptr, input_buffer.size(), &dest_buffer, - OEMCrypto_FirstSubsample | OEMCrypto_LastSubsample)); + ASSERT_EQ( + OEMCrypto_ERROR_INVALID_CONTEXT, + OEMCrypto_CopyBuffer(s.session_id(), nullptr, input_buffer.size(), + &dest_buffer_descriptor, + OEMCrypto_FirstSubsample | OEMCrypto_LastSubsample)); ASSERT_EQ(OEMCrypto_ERROR_INVALID_CONTEXT, OEMCrypto_CopyBuffer( s.session_id(), input_buffer.data(), input_buffer.size(), nullptr, OEMCrypto_FirstSubsample | OEMCrypto_LastSubsample)); - dest_buffer.buffer.clear.address = nullptr; + dest_buffer_descriptor.buffer.clear.address = nullptr; ASSERT_EQ( OEMCrypto_ERROR_INVALID_CONTEXT, OEMCrypto_CopyBuffer(s.session_id(), input_buffer.data(), - input_buffer.size(), &dest_buffer, + input_buffer.size(), &dest_buffer_descriptor, OEMCrypto_FirstSubsample | OEMCrypto_LastSubsample)); - dest_buffer.buffer.clear.address = output_buffer.data(); - dest_buffer.buffer.clear.max_length = output_buffer.size() - 1; + dest_buffer_descriptor.buffer.clear.address = output_buffer.data(); + dest_buffer_descriptor.buffer.clear.address_length = output_buffer.size() - 1; ASSERT_EQ( OEMCrypto_ERROR_SHORT_BUFFER, OEMCrypto_CopyBuffer(s.session_id(), input_buffer.data(), - input_buffer.size(), &dest_buffer, + input_buffer.size(), &dest_buffer_descriptor, OEMCrypto_FirstSubsample | OEMCrypto_LastSubsample)); } @@ -491,14 +496,14 @@ TEST_F(OEMCryptoClientTest, ClearCopyTestLargeSubsample) { vector input_buffer(max_size); GetRandBytes(input_buffer.data(), input_buffer.size()); vector output_buffer(max_size); - OEMCrypto_DestBufferDesc dest_buffer; - dest_buffer.type = OEMCrypto_BufferType_Clear; - dest_buffer.buffer.clear.address = output_buffer.data(); - dest_buffer.buffer.clear.max_length = output_buffer.size(); + OEMCrypto_DestBufferDesc dest_buffer_descriptor; + dest_buffer_descriptor.type = OEMCrypto_BufferType_Clear; + dest_buffer_descriptor.buffer.clear.address = output_buffer.data(); + dest_buffer_descriptor.buffer.clear.address_length = output_buffer.size(); ASSERT_EQ( OEMCrypto_SUCCESS, OEMCrypto_CopyBuffer(s.session_id(), input_buffer.data(), - input_buffer.size(), &dest_buffer, + input_buffer.size(), &dest_buffer_descriptor, OEMCrypto_FirstSubsample | OEMCrypto_LastSubsample)); ASSERT_EQ(input_buffer, output_buffer); } @@ -613,12 +618,13 @@ TEST_F(OEMCryptoProv30Test, GetDeviceId) { std::vector dev_id(128, 0); size_t dev_id_len = dev_id.size(); sts = OEMCrypto_GetDeviceID(dev_id.data(), &dev_id_len); - if (sts == OEMCrypto_ERROR_NOT_IMPLEMENTED) return; if (sts == OEMCrypto_ERROR_SHORT_BUFFER) { ASSERT_GT(dev_id_len, 0u); dev_id.resize(dev_id_len); sts = OEMCrypto_GetDeviceID(dev_id.data(), &dev_id_len); } + ASSERT_EQ(OEMCrypto_SUCCESS, sts); + dev_id.resize(dev_id_len); cout << " NormalGetDeviceId: dev_id = " << dev_id.data() << " len = " << dev_id_len << endl; ASSERT_EQ(OEMCrypto_SUCCESS, sts); @@ -637,35 +643,6 @@ TEST_F(OEMCryptoProv30Test, OEMCertValid) { ASSERT_NO_FATAL_FAILURE(s.LoadOEMCert(kVerify)); // Load and verify. } -// This verifies that an OEM Certificate can be used to generate a signature. -TEST_F(OEMCryptoProv30Test, OEMCertSignature) { - Session s; - ASSERT_NO_FATAL_FAILURE(s.open()); - ASSERT_NO_FATAL_FAILURE(s.LoadOEMCert()); - OEMCryptoResult sts; - // Sign a Message - vector data(500); - GetRandBytes(data.data(), data.size()); - size_t signature_length = 0; - vector signature(1); - - sts = OEMCrypto_GenerateRSASignature(s.session_id(), data.data(), data.size(), - signature.data(), &signature_length, - kSign_RSASSA_PSS); - - ASSERT_EQ(OEMCrypto_ERROR_SHORT_BUFFER, sts); - ASSERT_NE(static_cast(0), signature_length); - signature.resize(signature_length, 0); - - sts = OEMCrypto_GenerateRSASignature(s.session_id(), data.data(), data.size(), - signature.data(), &signature_length, - kSign_RSASSA_PSS); - - ASSERT_EQ(OEMCrypto_SUCCESS, sts); - ASSERT_NO_FATAL_FAILURE(s.VerifyRSASignature( - data, signature.data(), signature_length, kSign_RSASSA_PSS)); -} - // This verifies that the OEM Certificate cannot be used for other RSA padding // schemes. Those schemes should only be used by cast receiver certificates. TEST_F(OEMCryptoProv30Test, OEMCertForbiddenPaddingScheme) { @@ -698,42 +675,13 @@ TEST_F(OEMCryptoProv30Test, OEMCertForbiddenPaddingScheme) { ASSERT_EQ(zero, signature); // signature should not be computed. } -// Verify that the OEM Certificate can be used to sign a large buffer. -TEST_F(OEMCryptoProv30Test, OEMCertSignatureLargeBuffer) { - Session s; - ASSERT_NO_FATAL_FAILURE(s.open()); - ASSERT_NO_FATAL_FAILURE(s.LoadOEMCert()); - OEMCryptoResult sts; - // Sign a Message - vector data(kMaxMessageSize); - GetRandBytes(data.data(), data.size()); - size_t signature_length = 0; - vector signature(1); - - sts = OEMCrypto_GenerateRSASignature(s.session_id(), data.data(), data.size(), - signature.data(), &signature_length, - kSign_RSASSA_PSS); - - ASSERT_EQ(OEMCrypto_ERROR_SHORT_BUFFER, sts); - ASSERT_NE(static_cast(0), signature_length); - signature.resize(signature_length); - - sts = OEMCrypto_GenerateRSASignature(s.session_id(), data.data(), data.size(), - signature.data(), &signature_length, - kSign_RSASSA_PSS); - - ASSERT_EQ(OEMCrypto_SUCCESS, sts); - ASSERT_NO_FATAL_FAILURE(s.VerifyRSASignature( - data, signature.data(), signature_length, kSign_RSASSA_PSS)); -} - // Calling OEMCrypto_GetOEMPublicCertificate should not change the session's // private key. TEST_F(OEMCryptoProv30Test, GetCertOnlyAPI16) { if (wrapped_rsa_key_.size() == 0) { // If we don't have a wrapped key yet, create one. // This wrapped key will be shared by all sessions in the test. - ASSERT_NO_FATAL_FAILURE(CreateWrappedRSAKey(kSign_RSASSA_PSS, true)); + ASSERT_NO_FATAL_FAILURE(CreateWrappedRSAKey()); } Session s; ASSERT_NO_FATAL_FAILURE(s.open()); @@ -858,63 +806,39 @@ TEST_F(OEMCryptoSessionTestKeyboxTest, BadDataForceKeybox) { ASSERT_EQ(OEMCrypto_ERROR_BAD_CRC, sts); } -// TODO(b/140764295) -- replace this test with a provisioning test. -#if 0 -// Verify that keys can be derived from the test keybox, and then those derived -// keys can be used to sign a message. -TEST_F(OEMCryptoSessionTestKeyboxTest, GenerateSignature) { +// Verify that a license may be signed. +TEST_F(OEMCryptoSessionTests, SignLicenseRequest) { Session s; ASSERT_NO_FATAL_FAILURE(s.open()); - ASSERT_NO_FATAL_FAILURE(s.GenerateDerivedKeysFromKeybox(keybox_)); - - // Dummy context for testing signature generation. - vector context = wvcdm::a2b_hex( - "0a4c08001248000000020000101907d9ffde13aa95c122678053362136bdf840" - "8f8276e4c2d87ec52b61aa1b9f646e58734930acebe899b3e464189a14a87202" - "fb02574e70640bd22ef44b2d7e3912250a230a14080112100915007caa9b5931" - "b76a3a85f046523e10011a09393837363534333231180120002a0c3138383637" - "38373430350000"); - - static const uint32_t SignatureBufferMaxLength = SHA256_DIGEST_LENGTH; - vector signature(SignatureBufferMaxLength); - size_t signature_length = signature.size(); - - // TODO(b/135288420): Test core message functionality. - // This function should be split into three versions, one for each core - // message. - size_t core_message_length = 0; - - OEMCryptoResult sts; - sts = OEMCrypto_SignProvisioningRequest( - s.session_id(), context.data(), context.size(), nullptr, - &core_message_length, signature.data(), &signature_length); - ASSERT_EQ(OEMCrypto_SUCCESS, sts); - - static const uint32_t SignatureExpectedLength = 32; - ASSERT_EQ(SignatureExpectedLength, signature_length); - signature.resize(signature_length); - - std::vector expected_signature; - s.ClientSignBuffer(context, &expected_signature); - ASSERT_EQ(expected_signature, signature); + ASSERT_NO_FATAL_FAILURE(InstallTestRSAKey(&s)); + s.GenerateNonce(); + s.VerifyLicenseRequestSignature(); +} + +// Verify that a large license request may be signed. +TEST_F(OEMCryptoSessionTests, SignLargeLicenseRequest) { + Session s; + ASSERT_NO_FATAL_FAILURE(s.open()); + ASSERT_NO_FATAL_FAILURE(InstallTestRSAKey(&s)); + s.GenerateNonce(); + s.VerifyLicenseRequestSignature(kMaxMessageSize); } -#endif // Verify that a license may be loaded without a nonce. TEST_F(OEMCryptoSessionTests, LoadKeyNoNonce) { Session s; ASSERT_NO_FATAL_FAILURE(s.open()); - ASSERT_NO_FATAL_FAILURE(InstallTestSessionKeys(&s)); + 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()); } // Verify that a second license may be not be loaded in a session. -TEST_F(OEMCryptoSessionTests, LoadKeyNoNonceTwice) { +TEST_F(OEMCryptoSessionTests, LoadKeyNoNonceTwiceAPI16) { Session s; ASSERT_NO_FATAL_FAILURE(s.open()); - ASSERT_NO_FATAL_FAILURE(InstallTestSessionKeys(&s)); + 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()); @@ -932,7 +856,7 @@ TEST_F(OEMCryptoSessionTests, LoadKeyNoNonceTwice) { TEST_F(OEMCryptoSessionTests, LoadKeyWithNonce) { Session s; ASSERT_NO_FATAL_FAILURE(s.open()); - ASSERT_NO_FATAL_FAILURE(InstallTestSessionKeys(&s)); + ASSERT_NO_FATAL_FAILURE(InstallTestRSAKey(&s)); ASSERT_NO_FATAL_FAILURE(s.GenerateNonce()); ASSERT_NO_FATAL_FAILURE( s.FillSimpleMessage(0, wvoec::kControlNonceEnabled, s.nonce())); @@ -944,7 +868,7 @@ TEST_F(OEMCryptoSessionTests, LoadKeyWithNonce) { TEST_F(OEMCryptoSessionTests, LoadKeyWithNonceTwice) { Session s; ASSERT_NO_FATAL_FAILURE(s.open()); - ASSERT_NO_FATAL_FAILURE(InstallTestSessionKeys(&s)); + ASSERT_NO_FATAL_FAILURE(InstallTestRSAKey(&s)); ASSERT_NO_FATAL_FAILURE(s.GenerateNonce()); ASSERT_NO_FATAL_FAILURE( s.FillSimpleMessage(0, wvoec::kControlNonceEnabled, s.nonce())); @@ -959,57 +883,11 @@ TEST_F(OEMCryptoSessionTests, LoadKeyWithNonceTwice) { GetSubstring(), OEMCrypto_ContentLicense)); } -// TODO(b/140764295): this test should be replaced something that generates a -// renewal request. -#if 0 -// A license might update the mac keys and it might not. This tests that -// OEMCrypto keeps the old mac keys if the license does not update them. -TEST_F(OEMCryptoSessionTests, LoadKeyWithNoMAC) { - Session s; - ASSERT_NO_FATAL_FAILURE(s.open()); - ASSERT_NO_FATAL_FAILURE(InstallTestSessionKeys(&s)); - ASSERT_NO_FATAL_FAILURE(s.FillSimpleMessage(0, 0, 0)); - ASSERT_NO_FATAL_FAILURE(s.EncryptAndSign()); - ASSERT_NO_FATAL_FAILURE(s.LoadTestKeys("", false)); - - vector context = wvcdm::a2b_hex( - "0a4c08001248000000020000101907d9ffde13aa95c122678053362136bdf840" - "8f8276e4c2d87ec52b61aa1b9f646e58734930acebe899b3e464189a14a87202" - "fb02574e70640bd22ef44b2d7e3912250a230a14080112100915007caa9b5931" - "b76a3a85f046523e10011a09393837363534333231180120002a0c3138383637" - "38373430350000"); - - static const uint32_t SignatureBufferMaxLength = SHA256_DIGEST_LENGTH; - vector signature(SignatureBufferMaxLength); - size_t signature_length = signature.size(); - - // TODO(http://b/135288420): Test core message functionality. - // This function should be split into three versions, one for each core - // message. - size_t core_message_length = 0; - - OEMCryptoResult sts; - sts = OEMCrypto_SignLicenseRequest( - s.session_id(), context.data(), context.size(), nullptr, - &core_message_length, signature.data(), &signature_length); - - ASSERT_EQ(OEMCrypto_SUCCESS, sts); - - static const uint32_t SignatureExpectedLength = 32; - ASSERT_EQ(SignatureExpectedLength, signature_length); - signature.resize(signature_length); - - std::vector expected_signature; - s.ClientSignBuffer(context, &expected_signature); - ASSERT_EQ(expected_signature, signature); -} -#endif - // 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(InstallTestSessionKeys(&s)); + 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()); @@ -1024,7 +902,7 @@ TEST_F(OEMCryptoSessionTests, LoadEntitlementKeysAPI14) { TEST_F(OEMCryptoSessionTests, LoadEntitlementKeysNoEntitlementKeysAPI14) { Session s; ASSERT_NO_FATAL_FAILURE(s.open()); - ASSERT_NO_FATAL_FAILURE(InstallTestSessionKeys(&s)); + 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. @@ -1037,7 +915,7 @@ TEST_F(OEMCryptoSessionTests, LoadEntitlementKeysNoEntitlementKeysAPI14) { TEST_F(OEMCryptoSessionTests, LoadEntitlementKeysWrongEntitlementKeysAPI14) { Session s; ASSERT_NO_FATAL_FAILURE(s.open()); - ASSERT_NO_FATAL_FAILURE(InstallTestSessionKeys(&s)); + 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()); @@ -1050,54 +928,11 @@ TEST_F(OEMCryptoSessionTests, LoadEntitlementKeysWrongEntitlementKeysAPI14) { s.LoadEntitledContentKeys(OEMCrypto_KEY_NOT_ENTITLED); } -// TODO(b/140764295): Replace by sign request for each request type. -#if 0 -// This tests GenerateSignature with an 8k licnese request. -TEST_F(OEMCryptoSessionTests, ClientSignatureLargeBuffer) { - Session s; - ASSERT_NO_FATAL_FAILURE(s.open()); - ASSERT_NO_FATAL_FAILURE(InstallTestSessionKeys(&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("", false)); - - vector context(kMaxMessageSize); - for (size_t i = 0; i < kMaxMessageSize; i++) { - context[i] = i % 0x100; - } - static const uint32_t SignatureBufferMaxLength = SHA256_DIGEST_LENGTH; - vector signature(SignatureBufferMaxLength); - size_t signature_length = signature.size(); - - // TODO(http://b/135288420): Test core message functionality. - // This function should be split into three versions, one for each core - // message. - size_t core_message_length = 0; - - OEMCryptoResult sts; - sts = OEMCrypto_SignLicenseRequest( - s.session_id(), context.data(), context.size(), nullptr, - &core_message_length, signature.data(), &signature_length); - ASSERT_EQ(OEMCrypto_SUCCESS, sts); - - static const uint32_t SignatureExpectedLength = 32; - ASSERT_EQ(SignatureExpectedLength, signature_length); - signature.resize(signature_length); - - std::vector expected_signature; - s.ClientSignBuffer(context, &expected_signature); - ASSERT_EQ(expected_signature, signature); -} - -#endif - // This tests LoadKeys with an 8k license response. TEST_F(OEMCryptoSessionTests, LoadKeyLargeBuffer) { Session s; ASSERT_NO_FATAL_FAILURE(s.open()); - ASSERT_NO_FATAL_FAILURE(InstallTestSessionKeys(&s)); + ASSERT_NO_FATAL_FAILURE(InstallTestRSAKey(&s)); s.set_message_size(kMaxMessageSize); ASSERT_NO_FATAL_FAILURE(s.GenerateNonce()); ASSERT_NO_FATAL_FAILURE( @@ -1121,7 +956,7 @@ std::string DuplicateMessage(MessageData& message) { TEST_F(OEMCryptoSessionTests, LoadKeyWithBadRange1) { Session s; ASSERT_NO_FATAL_FAILURE(s.open()); - ASSERT_NO_FATAL_FAILURE(InstallTestSessionKeys(&s)); + 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()); @@ -1144,7 +979,7 @@ TEST_F(OEMCryptoSessionTests, LoadKeyWithBadRange1) { TEST_F(OEMCryptoSessionTests, LoadKeyWithBadRange2) { Session s; ASSERT_NO_FATAL_FAILURE(s.open()); - ASSERT_NO_FATAL_FAILURE(InstallTestSessionKeys(&s)); + 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()); @@ -1166,7 +1001,7 @@ TEST_F(OEMCryptoSessionTests, LoadKeyWithBadRange2) { TEST_F(OEMCryptoSessionTests, LoadKeyWithBadRange3) { Session s; ASSERT_NO_FATAL_FAILURE(s.open()); - ASSERT_NO_FATAL_FAILURE(InstallTestSessionKeys(&s)); + 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()); @@ -1186,7 +1021,7 @@ TEST_F(OEMCryptoSessionTests, LoadKeyWithBadRange3) { TEST_F(OEMCryptoSessionTests, LoadKeyWithBadRange4) { Session s; ASSERT_NO_FATAL_FAILURE(s.open()); - ASSERT_NO_FATAL_FAILURE(InstallTestSessionKeys(&s)); + ASSERT_NO_FATAL_FAILURE(InstallTestRSAKey(&s)); ASSERT_NO_FATAL_FAILURE(s.FillSimpleMessage(0, 0, 0)); ASSERT_NO_FATAL_FAILURE(s.EncryptAndSign()); @@ -1207,7 +1042,7 @@ TEST_F(OEMCryptoSessionTests, LoadKeyWithBadRange4) { TEST_F(OEMCryptoSessionTests, LoadKeyWithBadRange5) { Session s; ASSERT_NO_FATAL_FAILURE(s.open()); - ASSERT_NO_FATAL_FAILURE(InstallTestSessionKeys(&s)); + 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()); @@ -1226,7 +1061,7 @@ TEST_F(OEMCryptoSessionTests, LoadKeyWithBadRange5) { TEST_F(OEMCryptoSessionTests, LoadKeyWithBadRange6) { Session s; ASSERT_NO_FATAL_FAILURE(s.open()); - ASSERT_NO_FATAL_FAILURE(InstallTestSessionKeys(&s)); + ASSERT_NO_FATAL_FAILURE(InstallTestRSAKey(&s)); ASSERT_NO_FATAL_FAILURE(s.FillSimpleMessage(0, 0, 0)); ASSERT_NO_FATAL_FAILURE(s.EncryptAndSign()); @@ -1247,7 +1082,7 @@ TEST_F(OEMCryptoSessionTests, LoadKeyWithBadRange6) { TEST_F(OEMCryptoSessionTests, LoadKeyWithBadRange7) { Session s; ASSERT_NO_FATAL_FAILURE(s.open()); - ASSERT_NO_FATAL_FAILURE(InstallTestSessionKeys(&s)); + 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()); @@ -1266,7 +1101,7 @@ TEST_F(OEMCryptoSessionTests, LoadKeyWithBadRange7) { TEST_F(OEMCryptoSessionTests, LoadKeyWithSuspiciousIV) { Session s; ASSERT_NO_FATAL_FAILURE(s.open()); - ASSERT_NO_FATAL_FAILURE(InstallTestSessionKeys(&s)); + ASSERT_NO_FATAL_FAILURE(InstallTestRSAKey(&s)); ASSERT_NO_FATAL_FAILURE(s.FillSimpleMessage(0, 0, 0)); // This is suspicious: the data right before the mac keys is identical to the // iv. @@ -1286,7 +1121,7 @@ TEST_F(OEMCryptoSessionTests, LoadKeyWithSuspiciousIV) { TEST_F(OEMCryptoSessionTests, LoadKeyWithNullKeyControl) { Session s; ASSERT_NO_FATAL_FAILURE(s.open()); - ASSERT_NO_FATAL_FAILURE(InstallTestSessionKeys(&s)); + 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; @@ -1304,7 +1139,7 @@ TEST_F(OEMCryptoSessionTests, LoadKeyWithNullKeyControl) { TEST_F(OEMCryptoSessionTests, LoadKeyWithNullKeyControlIv) { Session s; ASSERT_NO_FATAL_FAILURE(s.open()); - ASSERT_NO_FATAL_FAILURE(InstallTestSessionKeys(&s)); + 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; @@ -1322,7 +1157,7 @@ TEST_F(OEMCryptoSessionTests, LoadKeyWithNullKeyControlIv) { TEST_F(OEMCryptoSessionTests, LoadKeyWithBadNonce) { Session s; ASSERT_NO_FATAL_FAILURE(s.open()); - ASSERT_NO_FATAL_FAILURE(InstallTestSessionKeys(&s)); + ASSERT_NO_FATAL_FAILURE(InstallTestRSAKey(&s)); ASSERT_NO_FATAL_FAILURE(s.FillSimpleMessage(0, wvoec::kControlNonceEnabled, 42)); // bad nonce. ASSERT_NO_FATAL_FAILURE(s.EncryptAndSign()); @@ -1339,7 +1174,7 @@ TEST_F(OEMCryptoSessionTests, LoadKeyWithBadNonce) { TEST_F(OEMCryptoSessionTests, LoadKeyWithRepeatNonce) { Session s; ASSERT_NO_FATAL_FAILURE(s.open()); - ASSERT_NO_FATAL_FAILURE(InstallTestSessionKeys(&s)); + ASSERT_NO_FATAL_FAILURE(InstallTestRSAKey(&s)); ASSERT_NO_FATAL_FAILURE(s.GenerateNonce()); uint32_t nonce = s.nonce(); ASSERT_NO_FATAL_FAILURE( @@ -1350,7 +1185,7 @@ TEST_F(OEMCryptoSessionTests, LoadKeyWithRepeatNonce) { ASSERT_NO_FATAL_FAILURE(s.close()); ASSERT_NO_FATAL_FAILURE(s.open()); - ASSERT_NO_FATAL_FAILURE(InstallTestSessionKeys(&s)); + 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()); @@ -1372,7 +1207,7 @@ TEST_F(OEMCryptoSessionTests, LoadKeyWithRepeatNonce) { TEST_F(OEMCryptoSessionTests, LoadKeyNonceReopenSession) { Session s; ASSERT_NO_FATAL_FAILURE(s.open()); - ASSERT_NO_FATAL_FAILURE(InstallTestSessionKeys(&s)); + ASSERT_NO_FATAL_FAILURE(InstallTestRSAKey(&s)); ASSERT_NO_FATAL_FAILURE(s.GenerateNonce()); uint32_t nonce = s.nonce(); // Do not use the nonce now. Close session and use it after re-opening. @@ -1382,7 +1217,7 @@ TEST_F(OEMCryptoSessionTests, LoadKeyNonceReopenSession) { // 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(InstallTestSessionKeys(&s)); + 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()); @@ -1400,7 +1235,7 @@ TEST_F(OEMCryptoSessionTests, LoadKeyNonceReopenSession) { TEST_F(OEMCryptoSessionTests, LoadKeyNonceWrongSession) { Session s1; ASSERT_NO_FATAL_FAILURE(s1.open()); - ASSERT_NO_FATAL_FAILURE(InstallTestSessionKeys(&s1)); + 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 @@ -1409,7 +1244,7 @@ TEST_F(OEMCryptoSessionTests, LoadKeyNonceWrongSession) { Session s2; ASSERT_NO_FATAL_FAILURE(s2.open()); - ASSERT_NO_FATAL_FAILURE(InstallTestSessionKeys(&s2)); + 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 @@ -1427,7 +1262,7 @@ TEST_F(OEMCryptoSessionTests, LoadKeyNonceWrongSession) { TEST_F(OEMCryptoSessionTests, LoadKeyWithBadVerification) { Session s; ASSERT_NO_FATAL_FAILURE(s.open()); - ASSERT_NO_FATAL_FAILURE(InstallTestSessionKeys(&s)); + 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()); @@ -1445,7 +1280,7 @@ TEST_F(OEMCryptoSessionTests, LoadKeyWithBadVerification) { TEST_F(OEMCryptoSessionTests, LoadKeyUnalignedMessage) { Session s; ASSERT_NO_FATAL_FAILURE(s.open()); - ASSERT_NO_FATAL_FAILURE(InstallTestSessionKeys(&s)); + ASSERT_NO_FATAL_FAILURE(InstallTestRSAKey(&s)); ASSERT_NO_FATAL_FAILURE(s.GenerateNonce()); ASSERT_NO_FATAL_FAILURE( s.FillSimpleMessage(kDuration, wvoec::kControlNonceEnabled, s.nonce())); @@ -1468,6 +1303,33 @@ TEST_F(OEMCryptoSessionTests, LoadKeyUnalignedMessage) { ASSERT_EQ(OEMCrypto_SUCCESS, sts); } +// 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)); +} + // This tests each key control block verification string in the range kc09-kc1?. // This test is parameterized by the API number in the key control lock. class OEMCryptoSessionTestAlternateVerification @@ -1483,10 +1345,11 @@ class OEMCryptoSessionTestAlternateVerification uint32_t target_api_; }; +// TODO(b/140765227): XXX This will be replaced with reload license test. TEST_P(OEMCryptoSessionTestAlternateVerification, LoadKeys) { Session s; ASSERT_NO_FATAL_FAILURE(s.open()); - ASSERT_NO_FATAL_FAILURE(InstallTestSessionKeys(&s)); + ASSERT_NO_FATAL_FAILURE(InstallTestRSAKey(&s)); ASSERT_NO_FATAL_FAILURE(s.FillSimpleMessage(0, 0, 0)); char buffer[5] = "kctl"; // This is the default verification string, required // for all API versions. @@ -1521,7 +1384,7 @@ INSTANTIATE_TEST_CASE_P(TestAll, OEMCryptoSessionTestAlternateVerification, TEST_F(OEMCryptoSessionTests, LoadKeysBadSignature) { Session s; ASSERT_NO_FATAL_FAILURE(s.open()); - ASSERT_NO_FATAL_FAILURE(InstallTestSessionKeys(&s)); + 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. @@ -1533,26 +1396,11 @@ TEST_F(OEMCryptoSessionTests, LoadKeysBadSignature) { ASSERT_NE(OEMCrypto_SUCCESS, sts); } -// We should not be able to load keys if we haven't derived the mac keys. -TEST_F(OEMCryptoSessionTests, LoadKeysWithNoDerivedKeys) { - Session s; - ASSERT_NO_FATAL_FAILURE(s.open()); - // don't do this: InstallTestSessionKeys(&s). - ASSERT_NO_FATAL_FAILURE(s.FillSimpleMessage(0, 0, 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); - ASSERT_NE(OEMCrypto_SUCCESS, sts); -} - // 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(InstallTestSessionKeys(&s)); + ASSERT_NO_FATAL_FAILURE(InstallTestRSAKey(&s)); ASSERT_NO_FATAL_FAILURE(s.FillSimpleMessage(kDuration, 0, 0)); ASSERT_NO_FATAL_FAILURE(s.EncryptAndSign()); int kNoKeys = 0; @@ -1568,7 +1416,7 @@ TEST_F(OEMCryptoSessionTests, LoadKeyNoKeys) { TEST_F(OEMCryptoSessionTests, LoadKeyNoKeyWithNonce) { Session s; ASSERT_NO_FATAL_FAILURE(s.open()); - ASSERT_NO_FATAL_FAILURE(InstallTestSessionKeys(&s)); + ASSERT_NO_FATAL_FAILURE(InstallTestRSAKey(&s)); ASSERT_NO_FATAL_FAILURE(s.GenerateNonce()); ASSERT_NO_FATAL_FAILURE( s.FillSimpleMessage(0, wvoec::kControlNonceEnabled, s.nonce())); @@ -1587,7 +1435,7 @@ TEST_F(OEMCryptoSessionTests, LoadKeyNoKeyWithNonce) { TEST_F(OEMCryptoSessionTests, SelectKeyNotThereAPI15) { Session s; ASSERT_NO_FATAL_FAILURE(s.open()); - ASSERT_NO_FATAL_FAILURE(InstallTestSessionKeys(&s)); + ASSERT_NO_FATAL_FAILURE(InstallTestRSAKey(&s)); ASSERT_NO_FATAL_FAILURE(s.GenerateNonce()); ASSERT_NO_FATAL_FAILURE( s.FillSimpleMessage(0, wvoec::kControlNonceEnabled, s.nonce())); @@ -1608,20 +1456,21 @@ TEST_F(OEMCryptoSessionTests, SelectKeyNotThereAPI15) { EXPECT_EQ(1, GetRandBytes(encryptionIv.data(), AES_BLOCK_SIZE)); // Describe the output vector out_buffer(in_buffer.size()); - const bool is_encrypted = true; OEMCrypto_DestBufferDesc destBuffer; destBuffer.type = OEMCrypto_BufferType_Clear; destBuffer.buffer.clear.address = out_buffer.data(); - destBuffer.buffer.clear.max_length = out_buffer.size(); + destBuffer.buffer.clear.address_length = out_buffer.size(); OEMCrypto_CENCEncryptPatternDesc pattern; pattern.encrypt = 0; pattern.skip = 0; - pattern.offset = 0; // Decrypt the data +#if 0 // TODO(b/135285640): fix this. + const bool is_encrypted = true; sts = OEMCrypto_DecryptCENC( s.session_id(), in_buffer.data(), in_buffer.size(), is_encrypted, encryptionIv.data(), 0, &destBuffer, &pattern, OEMCrypto_FirstSubsample | OEMCrypto_LastSubsample); +#endif EXPECT_EQ(OEMCrypto_ERROR_NO_CONTENT_KEY, sts); } } @@ -1632,7 +1481,7 @@ TEST_F(OEMCryptoSessionTests, SelectKeyNotThereAPI15) { TEST_F(OEMCryptoSessionTests, QueryKeyControl) { Session s; ASSERT_NO_FATAL_FAILURE(s.open()); - ASSERT_NO_FATAL_FAILURE(InstallTestSessionKeys(&s)); + ASSERT_NO_FATAL_FAILURE(InstallTestRSAKey(&s)); ASSERT_NO_FATAL_FAILURE(s.GenerateNonce()); ASSERT_NO_FATAL_FAILURE( s.FillSimpleMessage(0, wvoec::kControlNonceEnabled, s.nonce())); @@ -1663,7 +1512,7 @@ TEST_F(OEMCryptoSessionTests, QueryKeyControl) { TEST_F(OEMCryptoSessionTests, AntiRollbackHardwareRequired) { Session s; ASSERT_NO_FATAL_FAILURE(s.open()); - ASSERT_NO_FATAL_FAILURE(InstallTestSessionKeys(&s)); + ASSERT_NO_FATAL_FAILURE(InstallTestRSAKey(&s)); ASSERT_NO_FATAL_FAILURE(s.FillSimpleMessage( 0, wvoec::kControlRequireAntiRollbackHardware, 0)); ASSERT_NO_FATAL_FAILURE(s.EncryptAndSign()); @@ -1688,7 +1537,7 @@ TEST_F(OEMCryptoSessionTests, CheckMinimumPatchLevel) { { Session s; ASSERT_NO_FATAL_FAILURE(s.open()); - ASSERT_NO_FATAL_FAILURE(InstallTestSessionKeys(&s)); + ASSERT_NO_FATAL_FAILURE(InstallTestRSAKey(&s)); ASSERT_NO_FATAL_FAILURE(s.FillSimpleMessage( 0, patch_level << wvoec::kControlSecurityPatchLevelShift, 0)); ASSERT_NO_FATAL_FAILURE(s.EncryptAndSign()); @@ -1704,7 +1553,7 @@ TEST_F(OEMCryptoSessionTests, CheckMinimumPatchLevel) { if (patch_level < 0x3F) { Session s; ASSERT_NO_FATAL_FAILURE(s.open()); - ASSERT_NO_FATAL_FAILURE(InstallTestSessionKeys(&s)); + 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()); @@ -1720,7 +1569,7 @@ TEST_F(OEMCryptoSessionTests, CheckMinimumPatchLevel) { if (patch_level > 0) { Session s; ASSERT_NO_FATAL_FAILURE(s.open()); - ASSERT_NO_FATAL_FAILURE(InstallTestSessionKeys(&s)); + 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()); @@ -1739,7 +1588,7 @@ TEST_F(OEMCryptoSessionTests, CheckMinimumPatchLevel) { TEST_F(OEMCryptoSessionTests, MinimumKeysAPI12) { Session s; ASSERT_NO_FATAL_FAILURE(s.open()); - ASSERT_NO_FATAL_FAILURE(InstallTestSessionKeys(&s)); + 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); @@ -1765,7 +1614,7 @@ class OEMCryptoSessionTestDecryptWithHDCP : public OEMCryptoSessionTests, ASSERT_EQ(OEMCrypto_SUCCESS, sts); Session s; ASSERT_NO_FATAL_FAILURE(s.open()); - ASSERT_NO_FATAL_FAILURE(InstallTestSessionKeys(&s)); + ASSERT_NO_FATAL_FAILURE(InstallTestRSAKey(&s)); ASSERT_NO_FATAL_FAILURE(s.FillSimpleMessage( 0, (version << wvoec::kControlHDCPVersionShift) | @@ -1774,9 +1623,20 @@ class OEMCryptoSessionTestDecryptWithHDCP : public OEMCryptoSessionTests, ASSERT_NO_FATAL_FAILURE(s.EncryptAndSign()); ASSERT_NO_FATAL_FAILURE(s.LoadTestKeys()); - if (version > current) { + if (version > maximum) { ASSERT_NO_FATAL_FAILURE( s.TestDecryptCTR(true, OEMCrypto_ERROR_INSUFFICIENT_HDCP)); + } else if (version > current) { + if (global_features.api_version >= 16) { + // Can provide either OEMCrypto_WARNING_MIXED_OUTPUT_PROTECTION or + // OEMCrypto_ERROR_INSUFFICIENT_HDCP. TestDecryptCTR allows either to be + // reported if OEMCrypto_WARNING_MIXED_OUTPUT_PROTECTION is expected. + ASSERT_NO_FATAL_FAILURE( + s.TestDecryptCTR(true, OEMCrypto_WARNING_MIXED_OUTPUT_PROTECTION)); + } else { + ASSERT_NO_FATAL_FAILURE( + s.TestDecryptCTR(true, OEMCrypto_ERROR_INSUFFICIENT_HDCP)); + } } else { ASSERT_NO_FATAL_FAILURE(s.TestDecryptCTR(true, OEMCrypto_SUCCESS)); } @@ -1818,7 +1678,7 @@ class OEMCryptoSessionTestRefreshKeyTestAPI16 TEST_P(OEMCryptoSessionTestRefreshKeyTestAPI16, RefreshWithNonce) { Session s; ASSERT_NO_FATAL_FAILURE(s.open()); - ASSERT_NO_FATAL_FAILURE(InstallTestSessionKeys(&s)); + ASSERT_NO_FATAL_FAILURE(InstallTestRSAKey(&s)); ASSERT_NO_FATAL_FAILURE(s.GenerateNonce()); ASSERT_NO_FATAL_FAILURE( s.FillSimpleMessage(0, wvoec::kControlNonceEnabled, s.nonce())); @@ -1834,7 +1694,7 @@ TEST_P(OEMCryptoSessionTestRefreshKeyTestAPI16, RefreshWithNonce) { TEST_P(OEMCryptoSessionTestRefreshKeyTestAPI16, RefreshNoNonce) { Session s; ASSERT_NO_FATAL_FAILURE(s.open()); - ASSERT_NO_FATAL_FAILURE(InstallTestSessionKeys(&s)); + 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_)); @@ -1849,7 +1709,7 @@ TEST_P(OEMCryptoSessionTestRefreshKeyTestAPI16, RefreshNoNonce) { TEST_P(OEMCryptoSessionTestRefreshKeyTestAPI16, RefreshBadNonceAPI11) { Session s; ASSERT_NO_FATAL_FAILURE(s.open()); - ASSERT_NO_FATAL_FAILURE(InstallTestSessionKeys(&s)); + ASSERT_NO_FATAL_FAILURE(InstallTestRSAKey(&s)); ASSERT_NO_FATAL_FAILURE(s.GenerateNonce()); ASSERT_NO_FATAL_FAILURE( s.FillSimpleMessage(kDuration, wvoec::kControlNonceEnabled, s.nonce())); @@ -1868,7 +1728,7 @@ TEST_P(OEMCryptoSessionTestRefreshKeyTestAPI16, RefreshLargeBuffer) { Session s; s.set_message_size(kMaxMessageSize); ASSERT_NO_FATAL_FAILURE(s.open()); - ASSERT_NO_FATAL_FAILURE(InstallTestSessionKeys(&s)); + ASSERT_NO_FATAL_FAILURE(InstallTestRSAKey(&s)); ASSERT_NO_FATAL_FAILURE(s.GenerateNonce()); ASSERT_NO_FATAL_FAILURE( s.FillSimpleMessage(kDuration, wvoec::kControlNonceEnabled, s.nonce())); @@ -1881,15 +1741,13 @@ TEST_P(OEMCryptoSessionTestRefreshKeyTestAPI16, RefreshLargeBuffer) { num_keys_, wvoec::kControlNonceEnabled, s.nonce(), OEMCrypto_SUCCESS)); } -// TODO(b/140764295): Replace with proper signing tests. -#if 0 // This situation would occur if an app only uses one key in the license. When // that happens, SelectKey would be called before the first decrypt, and then // would not need to be called again, even if the license is refreshed. TEST_P(OEMCryptoSessionTestRefreshKeyTestAPI16, RefreshWithNoSelectKey) { Session s; ASSERT_NO_FATAL_FAILURE(s.open()); - ASSERT_NO_FATAL_FAILURE(InstallTestSessionKeys(&s)); + ASSERT_NO_FATAL_FAILURE(InstallTestRSAKey(&s)); ASSERT_NO_FATAL_FAILURE(s.GenerateNonce()); ASSERT_NO_FATAL_FAILURE( s.FillSimpleMessage(kDuration, wvoec::kControlNonceEnabled, s.nonce())); @@ -1906,8 +1764,9 @@ TEST_P(OEMCryptoSessionTestRefreshKeyTestAPI16, RefreshWithNoSelectKey) { // FillRefreshMessage fills the message with a duration of kLongDuration. ASSERT_NO_FATAL_FAILURE( s.FillRefreshMessage(num_keys_, wvoec::kControlNonceEnabled, s.nonce())); - s.ServerSignBuffer(reinterpret_cast(&s.encrypted_license()), - s.message_size(), &s.signature()); + s.key_deriver().ServerSignBuffer( + reinterpret_cast(&s.encrypted_license()), + s.message_size(), &s.signature()); std::vector key_array(num_keys_); s.FillRefreshArray(key_array.data(), num_keys_); ASSERT_EQ( @@ -1918,15 +1777,14 @@ TEST_P(OEMCryptoSessionTestRefreshKeyTestAPI16, RefreshWithNoSelectKey) { ASSERT_NO_FATAL_FAILURE(s.TestDecryptCTR(false)); // This should still be valid key, even if the refresh failed, because this // is before the original license duration. - sleep(kShortSleep); + wvcdm::TestSleep::Sleep(kShortSleep); ASSERT_NO_FATAL_FAILURE(s.TestDecryptCTR(false)); // This should be after duration of the original license, but before the // expiration of the refresh message. This should succeed if and only if the // refresh succeeded. - sleep(kShortSleep + kLongSleep); + wvcdm::TestSleep::Sleep(kShortSleep + kLongSleep); ASSERT_NO_FATAL_FAILURE(s.TestDecryptCTR(false)); } -#endif // If only one key control block in the refesh, we update all the keys. INSTANTIATE_TEST_CASE_P(TestRefreshAllKeys, @@ -1947,7 +1805,7 @@ TEST_F(OEMCryptoSessionTests, HashForbiddenAPI15) { if (hash_type != OEMCrypto_CRC_Clear_Buffer) return; Session s; ASSERT_NO_FATAL_FAILURE(s.open()); - ASSERT_NO_FATAL_FAILURE(InstallTestSessionKeys(&s)); + 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()); @@ -1971,7 +1829,7 @@ TEST_F(OEMCryptoSessionTests, HashForbiddenAPI15) { TEST_F(OEMCryptoSessionTests, Decrypt) { Session s; ASSERT_NO_FATAL_FAILURE(s.open()); - ASSERT_NO_FATAL_FAILURE(InstallTestSessionKeys(&s)); + 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()); @@ -1982,7 +1840,7 @@ TEST_F(OEMCryptoSessionTests, Decrypt) { TEST_F(OEMCryptoSessionTests, DecryptZeroDuration) { Session s; ASSERT_NO_FATAL_FAILURE(s.open()); - ASSERT_NO_FATAL_FAILURE(InstallTestSessionKeys(&s)); + ASSERT_NO_FATAL_FAILURE(InstallTestRSAKey(&s)); ASSERT_NO_FATAL_FAILURE(s.FillSimpleMessage(0, 0, 0)); ASSERT_NO_FATAL_FAILURE(s.EncryptAndSign()); ASSERT_NO_FATAL_FAILURE(s.LoadTestKeys()); @@ -1995,7 +1853,7 @@ TEST_F(OEMCryptoSessionTests, SimultaneousDecrypt) { vector s(8); for (int i = 0; i < 8; i++) { ASSERT_NO_FATAL_FAILURE(s[i].open()); - ASSERT_NO_FATAL_FAILURE(InstallTestSessionKeys(&s[i])); + ASSERT_NO_FATAL_FAILURE(InstallTestRSAKey(&s[i])); } for (int i = 0; i < 8; i++) { ASSERT_NO_FATAL_FAILURE(s[i].GenerateNonce()); @@ -2081,7 +1939,7 @@ class OEMCryptoSessionTestsDecryptTests clear_buffer_.resize(total_size_ + 16, 0xaa); output_descriptor_.buffer.clear.address = clear_buffer_.data(); } - output_descriptor_.buffer.clear.max_length = total_size_; + output_descriptor_.buffer.clear.address_length = total_size_; } void UpdateOutputOffset(size_t offset) { @@ -2091,7 +1949,7 @@ class OEMCryptoSessionTestsDecryptTests } else { output_descriptor_.buffer.clear.address = clear_buffer_.data() + offset; } - output_descriptor_.buffer.clear.max_length = total_size_ - offset; + output_descriptor_.buffer.clear.address_length = total_size_ - offset; } void EncryptData() { @@ -2174,7 +2032,7 @@ class OEMCryptoSessionTestsDecryptTests void LoadLicense() { // First we open a session and load a license. ASSERT_NO_FATAL_FAILURE(session_.open()); - ASSERT_NO_FATAL_FAILURE(InstallTestSessionKeys(&session_)); + ASSERT_NO_FATAL_FAILURE(InstallTestRSAKey(&session_)); uint32_t control = 0; if (verify_crc_) control |= kControlAllowHashVerification; ASSERT_NO_FATAL_FAILURE(session_.FillSimpleMessage(kDuration, control, 0)); @@ -2200,8 +2058,7 @@ class OEMCryptoSessionTestsDecryptTests } size_t buffer_offset = 0; for (size_t i = 0; i < subsample_size_.size(); i++) { - OEMCrypto_CENCEncryptPatternDesc pattern = pattern_; - pattern.offset = 0; // Final CENC spec says pattern offset always 0. + // TODO(b/135285640): OEMCrypto_CENCEncryptPatternDesc pattern = pattern_; bool is_encrypted = false; size_t block_offset = 0; uint8_t subsample_flags = 0; @@ -2212,11 +2069,13 @@ class OEMCryptoSessionTestsDecryptTests (subsample_size_[i].encrypted_size == 0)) { subsample_flags |= OEMCrypto_LastSubsample; } +#if 0 // TODO(b/135285640): fix this. sts = OEMCrypto_DecryptCENC( session_.session_id(), &encrypted_buffer_[buffer_offset], subsample_size_[i].clear_size, is_encrypted, sample_init_data_[i].iv, block_offset, &output_descriptor_, &pattern, subsample_flags); +#endif ASSERT_EQ(OEMCrypto_SUCCESS, sts); buffer_offset += subsample_size_[i].clear_size; } @@ -2231,11 +2090,13 @@ class OEMCryptoSessionTestsDecryptTests if (i == subsample_size_.size() - 1) { subsample_flags |= OEMCrypto_LastSubsample; } +#if 0 // TODO(b/135285640): fix this. sts = OEMCrypto_DecryptCENC( session_.session_id(), &encrypted_buffer_[buffer_offset], subsample_size_[i].encrypted_size, is_encrypted, sample_init_data_[i].iv, block_offset, &output_descriptor_, &pattern, subsample_flags); +#endif // CBC mode should not accept a block offset. if ((block_offset > 0) && (cipher_mode_ == OEMCrypto_CipherMode_CBC)) { ASSERT_EQ(OEMCrypto_ERROR_INVALID_CONTEXT, sts) @@ -2490,7 +2351,6 @@ OEMCrypto_CENCEncryptPatternDesc MakePattern(size_t encrypt, size_t skip) { OEMCrypto_CENCEncryptPatternDesc pattern; pattern.encrypt = encrypt; pattern.skip = skip; - pattern.offset = 0; // offset is deprecated. return pattern; } @@ -2541,7 +2401,7 @@ INSTANTIATE_TEST_CASE_P( TEST_F(OEMCryptoSessionTests, DecryptSecureToClear) { Session s; ASSERT_NO_FATAL_FAILURE(s.open()); - ASSERT_NO_FATAL_FAILURE(InstallTestSessionKeys(&s)); + ASSERT_NO_FATAL_FAILURE(InstallTestRSAKey(&s)); ASSERT_NO_FATAL_FAILURE(s.FillSimpleMessage( kDuration, wvoec::kControlObserveDataPath | wvoec::kControlDataPathSecure, 0)); @@ -2555,7 +2415,7 @@ TEST_F(OEMCryptoSessionTests, DecryptSecureToClear) { TEST_F(OEMCryptoSessionTests, DecryptNoAnalogToClearAPI13) { Session s; ASSERT_NO_FATAL_FAILURE(s.open()); - ASSERT_NO_FATAL_FAILURE(InstallTestSessionKeys(&s)); + ASSERT_NO_FATAL_FAILURE(InstallTestRSAKey(&s)); ASSERT_NO_FATAL_FAILURE( s.FillSimpleMessage(kDuration, wvoec::kControlDisableAnalogOutput, 0)); ASSERT_NO_FATAL_FAILURE(s.EncryptAndSign()); @@ -2568,16 +2428,16 @@ TEST_F(OEMCryptoSessionTests, DecryptNoAnalogToClearAPI13) { TEST_F(OEMCryptoSessionTests, KeyDuration) { Session s; ASSERT_NO_FATAL_FAILURE(s.open()); - ASSERT_NO_FATAL_FAILURE(InstallTestSessionKeys(&s)); + 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()); ASSERT_NO_FATAL_FAILURE(s.LoadTestKeys()); ASSERT_NO_FATAL_FAILURE(s.TestDecryptCTR(true, OEMCrypto_SUCCESS)); - sleep(kShortSleep); // Should still be valid key. + wvcdm::TestSleep::Sleep(kShortSleep); // Should still be valid key. ASSERT_NO_FATAL_FAILURE(s.TestDecryptCTR(false, OEMCrypto_SUCCESS)); - sleep(kLongSleep); // Should be expired key. + wvcdm::TestSleep::Sleep(kLongSleep); // Should be expired key. ASSERT_NO_FATAL_FAILURE(s.TestDecryptCTR(false, OEMCrypto_ERROR_KEY_EXPIRED)); ASSERT_NO_FATAL_FAILURE(s.TestSelectExpired(0)); } @@ -2589,308 +2449,219 @@ class OEMCryptoLoadsCertificate : public OEMCryptoSessionTestKeyboxTest {}; // This test verifies that we can create a wrapped RSA key, and then reload it. TEST_F(OEMCryptoLoadsCertificate, LoadRSASessionKey) { - CreateWrappedRSAKey(kSign_RSASSA_PSS, true); + CreateWrappedRSAKey(); Session s; ASSERT_NO_FATAL_FAILURE(s.open()); ASSERT_NO_FATAL_FAILURE(s.InstallRSASessionTestKey(wrapped_rsa_key_)); } +TEST_F(OEMCryptoLoadsCertificate, SignProvisioningRequest) { + Session s; + ASSERT_NO_FATAL_FAILURE(s.open()); + if (global_features.provisioning_method == OEMCrypto_OEMCertificate) { + s.LoadOEMCert(true); + } else { + EXPECT_EQ(global_features.provisioning_method, OEMCrypto_Keybox); + s.GenerateDerivedKeysFromKeybox(keybox_); + } + s.GenerateNonce(); + ProvisioningRoundTrip provisioning_messages(&s, encoded_rsa_key_); + ASSERT_NO_FATAL_FAILURE(provisioning_messages.SignAndVerifyRequest()); +} + +TEST_F(OEMCryptoLoadsCertificate, SignLargeProvisioningRequest) { + Session s; + ASSERT_NO_FATAL_FAILURE(s.open()); + if (global_features.provisioning_method == OEMCrypto_OEMCertificate) { + s.LoadOEMCert(true); + } else { + EXPECT_EQ(global_features.provisioning_method, OEMCrypto_Keybox); + s.GenerateDerivedKeysFromKeybox(keybox_); + } + s.GenerateNonce(); + ProvisioningRoundTrip provisioning_messages(&s, encoded_rsa_key_); + provisioning_messages.set_message_size(kMaxMessageSize); + ASSERT_NO_FATAL_FAILURE(provisioning_messages.SignAndVerifyRequest()); +} + // This creates a wrapped RSA key, and then does the sanity check that the // unencrypted key is not found in the wrapped key. The wrapped key should be // encrypted. TEST_F(OEMCryptoLoadsCertificate, CertificateProvision) { - CreateWrappedRSAKey(kSign_RSASSA_PSS, true); + Session s; + ProvisioningRoundTrip provisioning_messages(&s, encoded_rsa_key_); + provisioning_messages.PrepareSession(keybox_); + ASSERT_NO_FATAL_FAILURE(provisioning_messages.SignAndVerifyRequest()); + ASSERT_NO_FATAL_FAILURE(provisioning_messages.CreateDefaultResponse()); + ASSERT_NO_FATAL_FAILURE(provisioning_messages.EncryptAndSignResponse()); + ASSERT_EQ(OEMCrypto_SUCCESS, provisioning_messages.LoadResponse()); // We should not be able to find the rsa key in the wrapped key. It should // be encrypted. - ASSERT_EQ(nullptr, find(wrapped_rsa_key_, encoded_rsa_key_)); + EXPECT_EQ(nullptr, find(provisioning_messages.wrapped_rsa_key(), + provisioning_messages.encoded_rsa_key())); } // Verify that RewrapDeviceRSAKey checks pointers are within the provisioning // message. -TEST_F(OEMCryptoLoadsCertificate, CertificateProvisionBadRange1KeyboxTest) { +TEST_F(OEMCryptoLoadsCertificate, CertificateProvisionBadRange1) { Session s; - ASSERT_NO_FATAL_FAILURE(s.open()); - ASSERT_NO_FATAL_FAILURE(s.GenerateDerivedKeysFromKeybox(keybox_)); - ASSERT_NO_FATAL_FAILURE(s.VerifyProvisioningRequestSignature()); - struct RSAPrivateKeyMessage encrypted; - std::vector signature; - ASSERT_NO_FATAL_FAILURE(s.MakeRSACertificate(&encrypted, sizeof(encrypted), - &signature, kSign_RSASSA_PSS, - encoded_rsa_key_)); - vector wrapped_key; - const uint8_t* message_ptr = reinterpret_cast(&encrypted); - size_t wrapped_key_length = 0; - ASSERT_EQ(OEMCrypto_ERROR_SHORT_BUFFER, - OEMCrypto_RewrapDeviceRSAKey( - s.session_id(), message_ptr, sizeof(encrypted), - signature.data(), signature.size(), &encrypted.nonce, - encrypted.rsa_key, encrypted.rsa_key_length, - encrypted.rsa_key_iv, nullptr, &wrapped_key_length)); - wrapped_key.clear(); - wrapped_key.assign(wrapped_key_length, 0); - uint32_t nonce = encrypted.nonce; - ASSERT_NE( - OEMCrypto_SUCCESS, - OEMCrypto_RewrapDeviceRSAKey( - s.session_id(), message_ptr, sizeof(encrypted), signature.data(), - signature.size(), &nonce, encrypted.rsa_key, encrypted.rsa_key_length, - encrypted.rsa_key_iv, &(wrapped_key.front()), &wrapped_key_length)); + ProvisioningRoundTrip provisioning_messages(&s, encoded_rsa_key_); + provisioning_messages.PrepareSession(keybox_); + ASSERT_NO_FATAL_FAILURE(provisioning_messages.SignAndVerifyRequest()); + ASSERT_NO_FATAL_FAILURE(provisioning_messages.CreateDefaultResponse()); + provisioning_messages.core_response().enc_private_key.offset = + provisioning_messages.message_size() + 1; + ASSERT_NO_FATAL_FAILURE(provisioning_messages.EncryptAndSignResponse()); + ASSERT_NE(OEMCrypto_SUCCESS, provisioning_messages.LoadResponse()); + provisioning_messages.VerifyLoadFailed(); } // Verify that RewrapDeviceRSAKey checks pointers are within the provisioning // message. -TEST_F(OEMCryptoLoadsCertificate, CertificateProvisionBadRange2KeyboxTest) { +TEST_F(OEMCryptoLoadsCertificate, CertificateProvisionBadRange2) { Session s; - ASSERT_NO_FATAL_FAILURE(s.open()); - ASSERT_NO_FATAL_FAILURE(s.GenerateDerivedKeysFromKeybox(keybox_)); - // Provisioning request would be signed by client and verified by server. - ASSERT_NO_FATAL_FAILURE(s.VerifyProvisioningRequestSignature()); - struct RSAPrivateKeyMessage encrypted; - std::vector signature; - s.MakeRSACertificate(&encrypted, sizeof(encrypted), &signature, - kSign_RSASSA_PSS, encoded_rsa_key_); - vector wrapped_key; - const uint8_t* message_ptr = reinterpret_cast(&encrypted); - size_t wrapped_key_length = 0; - ASSERT_EQ(OEMCrypto_ERROR_SHORT_BUFFER, - OEMCrypto_RewrapDeviceRSAKey( - s.session_id(), message_ptr, sizeof(encrypted), - signature.data(), signature.size(), &encrypted.nonce, - encrypted.rsa_key, encrypted.rsa_key_length, - encrypted.rsa_key_iv, nullptr, &wrapped_key_length)); - wrapped_key.clear(); - wrapped_key.assign(wrapped_key_length, 0); - vector bad_buffer(encrypted.rsa_key, - encrypted.rsa_key + sizeof(encrypted.rsa_key)); - - ASSERT_NE(OEMCrypto_SUCCESS, - OEMCrypto_RewrapDeviceRSAKey( - s.session_id(), message_ptr, sizeof(encrypted), - signature.data(), signature.size(), &encrypted.nonce, - bad_buffer.data(), encrypted.rsa_key_length, - encrypted.rsa_key_iv, wrapped_key.data(), &wrapped_key_length)); + ProvisioningRoundTrip provisioning_messages(&s, encoded_rsa_key_); + provisioning_messages.PrepareSession(keybox_); + ASSERT_NO_FATAL_FAILURE(provisioning_messages.SignAndVerifyRequest()); + ASSERT_NO_FATAL_FAILURE(provisioning_messages.CreateDefaultResponse()); + provisioning_messages.core_response().enc_private_key_iv.offset = + provisioning_messages.message_size() + 1; + ASSERT_NO_FATAL_FAILURE(provisioning_messages.EncryptAndSignResponse()); + ASSERT_NE(OEMCrypto_SUCCESS, provisioning_messages.LoadResponse()); + provisioning_messages.VerifyLoadFailed(); } // Verify that RewrapDeviceRSAKey checks pointers are within the provisioning // message. -TEST_F(OEMCryptoLoadsCertificate, CertificateProvisionBadRange3KeyboxTest) { +TEST_F(OEMCryptoLoadsCertificate, CertificateProvisionBadRange3) { Session s; - ASSERT_NO_FATAL_FAILURE(s.open()); - ASSERT_NO_FATAL_FAILURE(s.GenerateDerivedKeysFromKeybox(keybox_)); - // Provisioning request would be signed by client and verified by server. - ASSERT_NO_FATAL_FAILURE(s.VerifyProvisioningRequestSignature()); - struct RSAPrivateKeyMessage encrypted; - std::vector signature; - s.MakeRSACertificate(&encrypted, sizeof(encrypted), &signature, - kSign_RSASSA_PSS, encoded_rsa_key_); - const uint8_t* message_ptr = reinterpret_cast(&encrypted); - vector wrapped_key; + ProvisioningRoundTrip provisioning_messages(&s, encoded_rsa_key_); + provisioning_messages.PrepareSession(keybox_); + ASSERT_NO_FATAL_FAILURE(provisioning_messages.SignAndVerifyRequest()); + ASSERT_NO_FATAL_FAILURE(provisioning_messages.CreateDefaultResponse()); + // If the offset is before the end, but the offset+length is bigger, then + // the message should be rejected. + provisioning_messages.core_response().enc_private_key.offset = + provisioning_messages.message_size() - 5; + ASSERT_NO_FATAL_FAILURE(provisioning_messages.EncryptAndSignResponse()); + ASSERT_NE(OEMCrypto_SUCCESS, provisioning_messages.LoadResponse()); + provisioning_messages.VerifyLoadFailed(); +} - size_t wrapped_key_length = 0; - ASSERT_EQ(OEMCrypto_ERROR_SHORT_BUFFER, - OEMCrypto_RewrapDeviceRSAKey( - s.session_id(), message_ptr, sizeof(encrypted), - signature.data(), signature.size(), &encrypted.nonce, - encrypted.rsa_key, encrypted.rsa_key_length, - encrypted.rsa_key_iv, nullptr, &wrapped_key_length)); - wrapped_key.clear(); - wrapped_key.assign(wrapped_key_length, 0); - vector bad_buffer(encrypted.rsa_key, - encrypted.rsa_key + sizeof(encrypted.rsa_key)); +// Verify that RewrapDeviceRSAKey checks pointers are within the provisioning +// message. +TEST_F(OEMCryptoLoadsCertificate, CertificateProvisionBadRange4) { + Session s; + ProvisioningRoundTrip provisioning_messages(&s, encoded_rsa_key_); + provisioning_messages.PrepareSession(keybox_); + ASSERT_NO_FATAL_FAILURE(provisioning_messages.SignAndVerifyRequest()); + ASSERT_NO_FATAL_FAILURE(provisioning_messages.CreateDefaultResponse()); + // If the offset is before the end, but the offset+length is bigger, then + // the message should be rejected. + provisioning_messages.core_response().enc_private_key_iv.offset = + provisioning_messages.message_size() - 5; + ASSERT_NO_FATAL_FAILURE(provisioning_messages.EncryptAndSignResponse()); + ASSERT_NE(OEMCrypto_SUCCESS, provisioning_messages.LoadResponse()); + provisioning_messages.VerifyLoadFailed(); +} - ASSERT_NE(OEMCrypto_SUCCESS, - OEMCrypto_RewrapDeviceRSAKey( - s.session_id(), message_ptr, sizeof(encrypted), - signature.data(), signature.size(), &encrypted.nonce, - encrypted.rsa_key, encrypted.rsa_key_length, bad_buffer.data(), - &(wrapped_key.front()), &wrapped_key_length)); +// Verify that RewrapDeviceRSAKey checks pointers are within the provisioning +// message. +TEST_F(OEMCryptoLoadsCertificate, CertificateProvisionBadRange5Prov30) { + Session s; + ProvisioningRoundTrip provisioning_messages(&s, encoded_rsa_key_); + provisioning_messages.PrepareSession(keybox_); + ASSERT_NO_FATAL_FAILURE(provisioning_messages.SignAndVerifyRequest()); + ASSERT_NO_FATAL_FAILURE(provisioning_messages.CreateDefaultResponse()); + // If the offset is before the end, but the offset+length is bigger, then + // the message should be rejected. + provisioning_messages.core_response().encrypted_message_key.offset = + provisioning_messages.message_size() + 1; + ASSERT_NO_FATAL_FAILURE(provisioning_messages.EncryptAndSignResponse()); + ASSERT_NE(OEMCrypto_SUCCESS, provisioning_messages.LoadResponse()); + provisioning_messages.VerifyLoadFailed(); } // Test that RewrapDeviceRSAKey verifies the message signature. +// TODO(b/144186970): This test should also run on Prov 3.0 devices. TEST_F(OEMCryptoLoadsCertificate, CertificateProvisionBadSignatureKeyboxTest) { Session s; - ASSERT_NO_FATAL_FAILURE(s.open()); - ASSERT_NO_FATAL_FAILURE(s.GenerateDerivedKeysFromKeybox(keybox_)); - // Provisioning request would be signed by client and verified by server. - ASSERT_NO_FATAL_FAILURE(s.VerifyProvisioningRequestSignature()); - struct RSAPrivateKeyMessage encrypted; - std::vector signature; - s.MakeRSACertificate(&encrypted, sizeof(encrypted), &signature, - kSign_RSASSA_PSS, encoded_rsa_key_); - vector wrapped_key; - const uint8_t* message_ptr = reinterpret_cast(&encrypted); - - size_t wrapped_key_length = 0; - ASSERT_EQ(OEMCrypto_ERROR_SHORT_BUFFER, - OEMCrypto_RewrapDeviceRSAKey( - s.session_id(), message_ptr, sizeof(encrypted), - signature.data(), signature.size(), &encrypted.nonce, - encrypted.rsa_key, encrypted.rsa_key_length, - encrypted.rsa_key_iv, nullptr, &wrapped_key_length)); - wrapped_key.clear(); - wrapped_key.assign(wrapped_key_length, 0); - signature[4] ^= 42; // bad signature. - ASSERT_NE(OEMCrypto_SUCCESS, - OEMCrypto_RewrapDeviceRSAKey( - s.session_id(), message_ptr, sizeof(encrypted), - signature.data(), signature.size(), &encrypted.nonce, - encrypted.rsa_key, encrypted.rsa_key_length, - encrypted.rsa_key_iv, wrapped_key.data(), &wrapped_key_length)); + ProvisioningRoundTrip provisioning_messages(&s, encoded_rsa_key_); + provisioning_messages.PrepareSession(keybox_); + ASSERT_NO_FATAL_FAILURE(provisioning_messages.SignAndVerifyRequest()); + ASSERT_NO_FATAL_FAILURE(provisioning_messages.CreateDefaultResponse()); + ASSERT_NO_FATAL_FAILURE(provisioning_messages.EncryptAndSignResponse()); + provisioning_messages.response_signature()[4] ^= 42; // bad signature. + ASSERT_EQ(OEMCrypto_ERROR_SIGNATURE_FAILURE, + provisioning_messages.LoadResponse()); + provisioning_messages.VerifyLoadFailed(); } // Test that RewrapDeviceRSAKey verifies the nonce is current. -TEST_F(OEMCryptoLoadsCertificate, CertificateProvisionBadNonceKeyboxTest) { +TEST_F(OEMCryptoLoadsCertificate, CertificateProvisionBadNonce) { Session s; - ASSERT_NO_FATAL_FAILURE(s.open()); - ASSERT_NO_FATAL_FAILURE(s.GenerateDerivedKeysFromKeybox(keybox_)); - // Provisioning request would be signed by client and verified by server. - ASSERT_NO_FATAL_FAILURE(s.VerifyProvisioningRequestSignature()); - struct RSAPrivateKeyMessage encrypted; - std::vector signature; - s.MakeRSACertificate(&encrypted, sizeof(encrypted), &signature, - kSign_RSASSA_PSS, encoded_rsa_key_); - vector wrapped_key; - const uint8_t* message_ptr = reinterpret_cast(&encrypted); - - size_t wrapped_key_length = 0; - ASSERT_EQ(OEMCrypto_ERROR_SHORT_BUFFER, - OEMCrypto_RewrapDeviceRSAKey( - s.session_id(), message_ptr, sizeof(encrypted), - signature.data(), signature.size(), &encrypted.nonce, - encrypted.rsa_key, encrypted.rsa_key_length, - encrypted.rsa_key_iv, nullptr, &wrapped_key_length)); - wrapped_key.clear(); - wrapped_key.assign(wrapped_key_length, 0); - encrypted.nonce ^= 42; // Almost surely a bad nonce. + ProvisioningRoundTrip provisioning_messages(&s, encoded_rsa_key_); + provisioning_messages.PrepareSession(keybox_); + ASSERT_NO_FATAL_FAILURE(provisioning_messages.SignAndVerifyRequest()); + provisioning_messages.core_request().nonce ^= 42; // bad nonce. + ASSERT_NO_FATAL_FAILURE(provisioning_messages.CreateDefaultResponse()); + ASSERT_NO_FATAL_FAILURE(provisioning_messages.EncryptAndSignResponse()); ASSERT_EQ(OEMCrypto_ERROR_INVALID_NONCE, - OEMCrypto_RewrapDeviceRSAKey( - s.session_id(), message_ptr, sizeof(encrypted), - signature.data(), signature.size(), &encrypted.nonce, - encrypted.rsa_key, encrypted.rsa_key_length, - encrypted.rsa_key_iv, wrapped_key.data(), &wrapped_key_length)); + provisioning_messages.LoadResponse()); + provisioning_messages.VerifyLoadFailed(); } // Test that RewrapDeviceRSAKey verifies the RSA key is valid. +TEST_F(OEMCryptoLoadsCertificate, CertificateProvisionBadRSAKey) { + Session s; + ProvisioningRoundTrip provisioning_messages(&s, encoded_rsa_key_); + provisioning_messages.PrepareSession(keybox_); + ASSERT_NO_FATAL_FAILURE(provisioning_messages.SignAndVerifyRequest()); + ASSERT_NO_FATAL_FAILURE(provisioning_messages.CreateDefaultResponse()); + provisioning_messages.response_data().rsa_key[4] ^= 42; // bad key. + ASSERT_NO_FATAL_FAILURE(provisioning_messages.EncryptAndSignResponse()); + ASSERT_NE(OEMCrypto_SUCCESS, provisioning_messages.LoadResponse()); + provisioning_messages.VerifyLoadFailed(); +} + +// Test that RewrapDeviceRSAKey verifies the RSA key is valid. +// TODO(b/144186970): This test should also run on Prov 3.0 devices. TEST_F(OEMCryptoLoadsCertificate, CertificateProvisionBadRSAKeyKeyboxTest) { Session s; - ASSERT_NO_FATAL_FAILURE(s.open()); - ASSERT_NO_FATAL_FAILURE(s.GenerateDerivedKeysFromKeybox(keybox_)); - // Provisioning request would be signed by client and verified by server. - ASSERT_NO_FATAL_FAILURE(s.VerifyProvisioningRequestSignature()); - struct RSAPrivateKeyMessage encrypted; - std::vector signature; - s.MakeRSACertificate(&encrypted, sizeof(encrypted), &signature, - kSign_RSASSA_PSS, encoded_rsa_key_); - vector wrapped_key; - const uint8_t* message_ptr = reinterpret_cast(&encrypted); - - size_t wrapped_key_length = 0; - ASSERT_EQ(OEMCrypto_ERROR_SHORT_BUFFER, - OEMCrypto_RewrapDeviceRSAKey( - s.session_id(), message_ptr, sizeof(encrypted), - signature.data(), signature.size(), &encrypted.nonce, - encrypted.rsa_key, encrypted.rsa_key_length, - encrypted.rsa_key_iv, nullptr, &wrapped_key_length)); - wrapped_key.clear(); - wrapped_key.assign(wrapped_key_length, 0); - encrypted.rsa_key[1] ^= 42; // Almost surely a bad key. - ASSERT_NE(OEMCrypto_SUCCESS, - OEMCrypto_RewrapDeviceRSAKey( - s.session_id(), message_ptr, sizeof(encrypted), - signature.data(), signature.size(), &encrypted.nonce, - encrypted.rsa_key, encrypted.rsa_key_length, - encrypted.rsa_key_iv, wrapped_key.data(), &wrapped_key_length)); + ProvisioningRoundTrip provisioning_messages(&s, encoded_rsa_key_); + provisioning_messages.PrepareSession(keybox_); + ASSERT_NO_FATAL_FAILURE(provisioning_messages.SignAndVerifyRequest()); + ASSERT_NO_FATAL_FAILURE(provisioning_messages.CreateDefaultResponse()); + ASSERT_NO_FATAL_FAILURE(provisioning_messages.EncryptAndSignResponse()); + size_t rsa_offset = + provisioning_messages.core_response().enc_private_key.offset; + rsa_offset += 4; // Change the middle of the key. + provisioning_messages.encrypted_response_buffer()[rsa_offset] ^= 42; + ASSERT_EQ(OEMCrypto_ERROR_SIGNATURE_FAILURE, + provisioning_messages.LoadResponse()); + provisioning_messages.VerifyLoadFailed(); } // Test that RewrapDeviceRSAKey accepts the maximum message size. -TEST_F(OEMCryptoLoadsCertificate, CertificateProvisionLargeBufferKeyboxTest) { +TEST_F(OEMCryptoLoadsCertificate, CertificateProvisionLargeBuffer) { Session s; - ASSERT_NO_FATAL_FAILURE(s.open()); - ASSERT_NO_FATAL_FAILURE(s.GenerateDerivedKeysFromKeybox(keybox_)); - // Provisioning request would be signed by client and verified by server. - ASSERT_NO_FATAL_FAILURE(s.VerifyProvisioningRequestSignature()); - struct LargeRSAPrivateKeyMessage : public RSAPrivateKeyMessage { - uint8_t padding[kMaxMessageSize - sizeof(RSAPrivateKeyMessage)]; - } encrypted; - std::vector signature; - s.MakeRSACertificate(&encrypted, sizeof(encrypted), &signature, - kSign_RSASSA_PSS, encoded_rsa_key_); - vector wrapped_key; - ASSERT_NO_FATAL_FAILURE(s.RewrapRSAKey(encrypted, sizeof(encrypted), - signature, &wrapped_key, true)); - // Verify that the clear key is not contained in the wrapped key. - // It should be encrypted. - ASSERT_EQ(nullptr, find(wrapped_key, encoded_rsa_key_)); -} - -// Test that RewrapDeviceRSAKey30 verifies the nonce. -TEST_F(OEMCryptoLoadsCertificate, CertificateProvisionBadNonceProv30Test) { - Session s; - ASSERT_NO_FATAL_FAILURE(s.open()); - ASSERT_NO_FATAL_FAILURE(s.LoadOEMCert()); - struct RSAPrivateKeyMessage encrypted; - std::vector signature; - std::vector message_key; - std::vector encrypted_message_key; - s.GenerateRSASessionKey(&message_key, &encrypted_message_key); - Encryptor encryptor(message_key); - ASSERT_NO_FATAL_FAILURE(s.MakeRSACertificate(&encrypted, sizeof(encrypted), - &signature, kSign_RSASSA_PSS, - encoded_rsa_key_, &encryptor)); - uint32_t bad_nonce = s.nonce() ^ 42; - size_t wrapped_key_length = 0; - ASSERT_EQ(OEMCrypto_ERROR_SHORT_BUFFER, - OEMCrypto_RewrapDeviceRSAKey30( - s.session_id(), &bad_nonce, encrypted_message_key.data(), - encrypted_message_key.size(), encrypted.rsa_key, - encrypted.rsa_key_length, encrypted.rsa_key_iv, nullptr, - &wrapped_key_length)); - vector wrapped_key(wrapped_key_length, 0); - ASSERT_EQ(OEMCrypto_ERROR_INVALID_NONCE, - OEMCrypto_RewrapDeviceRSAKey30( - s.session_id(), &bad_nonce, encrypted_message_key.data(), - encrypted_message_key.size(), encrypted.rsa_key, - encrypted.rsa_key_length, encrypted.rsa_key_iv, - wrapped_key.data(), &wrapped_key_length)); -} - -// Test that RewrapDeviceRSAKey30 verifies that the RSA key is valid. -TEST_F(OEMCryptoLoadsCertificate, CertificateProvisionBadRSAKeyProv30Test) { - Session s; - ASSERT_NO_FATAL_FAILURE(s.open()); - ASSERT_NO_FATAL_FAILURE(s.LoadOEMCert()); - struct RSAPrivateKeyMessage encrypted; - std::vector signature; - std::vector message_key; - std::vector encrypted_message_key; - s.GenerateRSASessionKey(&message_key, &encrypted_message_key); - Encryptor encryptor(message_key); - ASSERT_NO_FATAL_FAILURE(s.MakeRSACertificate(&encrypted, sizeof(encrypted), - &signature, kSign_RSASSA_PSS, - encoded_rsa_key_, &encryptor)); - size_t wrapped_key_length = 0; - uint32_t nonce = s.nonce(); - ASSERT_EQ(OEMCrypto_ERROR_SHORT_BUFFER, - OEMCrypto_RewrapDeviceRSAKey30( - s.session_id(), &nonce, encrypted_message_key.data(), - encrypted_message_key.size(), encrypted.rsa_key, - encrypted.rsa_key_length, encrypted.rsa_key_iv, nullptr, - &wrapped_key_length)); - vector wrapped_key(wrapped_key_length, 0); - encrypted.rsa_key[1] ^= 42; // Almost surely a bad key. - ASSERT_EQ(OEMCrypto_ERROR_INVALID_RSA_KEY, - OEMCrypto_RewrapDeviceRSAKey30( - s.session_id(), &nonce, encrypted_message_key.data(), - encrypted_message_key.size(), encrypted.rsa_key, - encrypted.rsa_key_length, encrypted.rsa_key_iv, - wrapped_key.data(), &wrapped_key_length)); + ProvisioningRoundTrip provisioning_messages(&s, encoded_rsa_key_); + provisioning_messages.set_message_size(kMaxMessageSize); + provisioning_messages.PrepareSession(keybox_); + ASSERT_NO_FATAL_FAILURE(provisioning_messages.SignAndVerifyRequest()); + ASSERT_NO_FATAL_FAILURE(provisioning_messages.CreateDefaultResponse()); + ASSERT_NO_FATAL_FAILURE(provisioning_messages.EncryptAndSignResponse()); + ASSERT_EQ(OEMCrypto_SUCCESS, provisioning_messages.LoadResponse()); + // We should not be able to find the rsa key in the wrapped key. It should + // be encrypted. + EXPECT_EQ(nullptr, find(provisioning_messages.wrapped_rsa_key(), + provisioning_messages.encoded_rsa_key())); } // Test that a wrapped RSA key can be loaded. TEST_F(OEMCryptoLoadsCertificate, LoadWrappedRSAKey) { OEMCryptoResult sts; - CreateWrappedRSAKey(kSign_RSASSA_PSS, true); - + CreateWrappedRSAKey(); Session s; ASSERT_NO_FATAL_FAILURE(s.open()); sts = OEMCrypto_LoadDeviceRSAKey(s.session_id(), wrapped_rsa_key_.data(), @@ -2898,25 +2669,12 @@ TEST_F(OEMCryptoLoadsCertificate, LoadWrappedRSAKey) { ASSERT_EQ(OEMCrypto_SUCCESS, sts); } -// This tests that a device with a keybox can also decrypt with a cert. -// Decrypt for devices that only use a cert are tested in the session tests. -TEST_F(OEMCryptoLoadsCertificate, CertificateDecrypt) { - CreateWrappedRSAKey(kSign_RSASSA_PSS, true); - Session s; - ASSERT_NO_FATAL_FAILURE(s.open()); - 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()); - ASSERT_NO_FATAL_FAILURE(s.TestDecryptCTR()); -} - // Test a 3072 bit RSA key certificate. TEST_F(OEMCryptoLoadsCertificate, TestLargeRSAKey3072) { encoded_rsa_key_.assign(kTestRSAPKCS8PrivateKeyInfo3_3072, kTestRSAPKCS8PrivateKeyInfo3_3072 + sizeof(kTestRSAPKCS8PrivateKeyInfo3_3072)); - CreateWrappedRSAKey(kSign_RSASSA_PSS, true); + CreateWrappedRSAKey(); Session s; ASSERT_NO_FATAL_FAILURE(s.open()); ASSERT_NO_FATAL_FAILURE(s.PreparePublicKey(encoded_rsa_key_.data(), @@ -2934,7 +2692,7 @@ TEST_F(OEMCryptoLoadsCertificate, TestCarmichaelRSAKey) { encoded_rsa_key_.assign( kTestKeyRSACarmichael_2048, kTestKeyRSACarmichael_2048 + sizeof(kTestKeyRSACarmichael_2048)); - CreateWrappedRSAKey(kSign_RSASSA_PSS, true); + CreateWrappedRSAKey(); Session s; ASSERT_NO_FATAL_FAILURE(s.open()); ASSERT_NO_FATAL_FAILURE(s.PreparePublicKey(encoded_rsa_key_.data(), @@ -2948,7 +2706,7 @@ TEST_F(OEMCryptoLoadsCertificate, TestCarmichaelRSAKey) { // This tests that two sessions can use different RSA keys simultaneously. TEST_F(OEMCryptoLoadsCertificate, TestMultipleRSAKeys) { - CreateWrappedRSAKey(kSign_RSASSA_PSS, true); + CreateWrappedRSAKey(); Session s1; // Session s1 loads the default rsa key, but doesn't use it // until after s2 uses its key. ASSERT_NO_FATAL_FAILURE(s1.open()); @@ -2962,7 +2720,7 @@ TEST_F(OEMCryptoLoadsCertificate, TestMultipleRSAKeys) { encoded_rsa_key_.assign(kTestRSAPKCS8PrivateKeyInfo4_2048, kTestRSAPKCS8PrivateKeyInfo4_2048 + sizeof(kTestRSAPKCS8PrivateKeyInfo4_2048)); - CreateWrappedRSAKey(kSign_RSASSA_PSS, true); + CreateWrappedRSAKey(); ASSERT_NO_FATAL_FAILURE(s2.open()); ASSERT_NO_FATAL_FAILURE(s2.PreparePublicKey(encoded_rsa_key_.data(), encoded_rsa_key_.size())); @@ -2995,11 +2753,7 @@ class OEMCryptoUsesCertificate : public OEMCryptoLoadsCertificate { ASSERT_NO_FATAL_FAILURE(session_.open()); if (global_features.derive_key_method != DeviceFeatures::LOAD_TEST_RSA_KEY) { - CreateWrappedRSAKey(kSign_RSASSA_PSS, true); - ASSERT_EQ(OEMCrypto_SUCCESS, - OEMCrypto_LoadDeviceRSAKey(session_.session_id(), - wrapped_rsa_key_.data(), - wrapped_rsa_key_.size())); + InstallTestRSAKey(&session_); } } @@ -3017,19 +2771,19 @@ TEST_F(OEMCryptoLoadsCertificate, RSAPerformance) { const std::chrono::milliseconds kTestDuration(5000); OEMCryptoResult sts; std::chrono::steady_clock clock; - sleep(2); // Make sure are not nonce limited. + wvcdm::TestSleep::Sleep(kShortSleep); // Make sure we are not nonce limited. auto start_time = clock.now(); int count = 15; for (int i = 0; i < count; i++) { // Only 20 nonce available. - CreateWrappedRSAKey(kSign_RSASSA_PSS, true); + CreateWrappedRSAKey(); } auto delta_time = clock.now() - start_time; const double provision_time = delta_time / std::chrono::milliseconds(1) / count; Session session; - CreateWrappedRSAKey(kSign_RSASSA_PSS, true); + CreateWrappedRSAKey(); start_time = clock.now(); count = 0; while (clock.now() - start_time < kTestDuration) { @@ -3111,67 +2865,6 @@ TEST_F(OEMCryptoLoadsCertificate, RSAPerformance) { license_request_time, derive_keys_time); } -// Test that OEMCrypto can compute an RSA signature. -TEST_F(OEMCryptoUsesCertificate, RSASignature) { - OEMCryptoResult sts; - // Sign a Message - vector licenseRequest(500); - GetRandBytes(licenseRequest.data(), licenseRequest.size()); - size_t signature_length = 0; - uint8_t signature[500]; - - sts = OEMCrypto_GenerateRSASignature( - session_.session_id(), licenseRequest.data(), licenseRequest.size(), - signature, &signature_length, kSign_RSASSA_PSS); - - ASSERT_EQ(OEMCrypto_ERROR_SHORT_BUFFER, sts); - ASSERT_NE(static_cast(0), signature_length); - ASSERT_GE(sizeof(signature), signature_length); - - sts = OEMCrypto_GenerateRSASignature( - session_.session_id(), licenseRequest.data(), licenseRequest.size(), - signature, &signature_length, kSign_RSASSA_PSS); - - ASSERT_EQ(OEMCrypto_SUCCESS, sts); - // In the real world, the signature above would just have been used to contact - // the license server to get this response. - ASSERT_NO_FATAL_FAILURE(session_.PreparePublicKey(encoded_rsa_key_.data(), - encoded_rsa_key_.size())); - ASSERT_NO_FATAL_FAILURE(session_.VerifyRSASignature( - licenseRequest, signature, signature_length, kSign_RSASSA_PSS)); -} - -// Test that OEMCrypto can compute an RSA signature of a message with the -// maximum size. -TEST_F(OEMCryptoUsesCertificate, RSASignatureLargeBuffer) { - OEMCryptoResult sts; - // Sign a Message - vector licenseRequest(kMaxMessageSize); - GetRandBytes(licenseRequest.data(), licenseRequest.size()); - size_t signature_length = 0; - uint8_t signature[500]; - - sts = OEMCrypto_GenerateRSASignature( - session_.session_id(), licenseRequest.data(), licenseRequest.size(), - signature, &signature_length, kSign_RSASSA_PSS); - - ASSERT_EQ(OEMCrypto_ERROR_SHORT_BUFFER, sts); - ASSERT_NE(static_cast(0), signature_length); - ASSERT_GE(sizeof(signature), signature_length); - - sts = OEMCrypto_GenerateRSASignature( - session_.session_id(), licenseRequest.data(), licenseRequest.size(), - signature, &signature_length, kSign_RSASSA_PSS); - - ASSERT_EQ(OEMCrypto_SUCCESS, sts); - // In the real world, the signature above would just have been used to contact - // the license server to get this response. - ASSERT_NO_FATAL_FAILURE(session_.PreparePublicKey(encoded_rsa_key_.data(), - encoded_rsa_key_.size())); - ASSERT_NO_FATAL_FAILURE(session_.VerifyRSASignature( - licenseRequest, signature, signature_length, kSign_RSASSA_PSS)); -} - // Test DeriveKeysFromSessionKey using the maximum size for the HMAC context. TEST_F(OEMCryptoUsesCertificate, GenerateDerivedKeysLargeBuffer) { vector session_key; @@ -3289,9 +2982,22 @@ class OEMCryptoLoadsCertificateAlternates : public OEMCryptoLoadsCertificate { // If force is true, we assert that the key loads successfully. void LoadWithAllowedSchemes(uint32_t schemes, bool force) { - CreateWrappedRSAKey(schemes, force); + Session s; + ProvisioningRoundTrip provisioning_messages(&s, encoded_rsa_key_); + provisioning_messages.set_allowed_schemes(schemes); + provisioning_messages.PrepareSession(keybox_); + ASSERT_NO_FATAL_FAILURE(provisioning_messages.SignAndVerifyRequest()); + ASSERT_NO_FATAL_FAILURE(provisioning_messages.CreateDefaultResponse()); + ASSERT_NO_FATAL_FAILURE(provisioning_messages.EncryptAndSignResponse()); + OEMCryptoResult sts = provisioning_messages.LoadResponse(); + encoded_rsa_key_ = provisioning_messages.encoded_rsa_key(); + wrapped_rsa_key_ = provisioning_messages.wrapped_rsa_key(); key_loaded_ = (wrapped_rsa_key_.size() > 0); - if (force) ASSERT_TRUE(key_loaded_); + if (force) { + EXPECT_EQ(OEMCrypto_SUCCESS, sts); + EXPECT_EQ(nullptr, find(wrapped_rsa_key_, encoded_rsa_key_)); + ASSERT_TRUE(key_loaded_); + } } bool key_loaded_; @@ -3329,25 +3035,6 @@ TEST_F(OEMCryptoLoadsCertificateAlternates, TestSignaturePKCS1) { } } -// Try to load an RSA key with alternative padding schemes. This key is allowed -// to sign with either padding scheme. Devices are not required to support both -// padding schemes. -TEST_F(OEMCryptoLoadsCertificateAlternates, TestSignatureBoth) { - LoadWithAllowedSchemes(kSign_RSASSA_PSS | kSign_PKCS1_Block1, false); - // If the device loads this key, it should process it correctly. - if (key_loaded_) { - DisallowDeriveKeys(); - // A signature with padding that is too big should fail. - DisallowForbiddenPadding(kSign_PKCS1_Block1, 84); - if (global_features.cast_receiver) { - TestSignature(kSign_RSASSA_PSS, 200); - // A signature with a valid size should succeed. - TestSignature(kSign_PKCS1_Block1, 83); - TestSignature(kSign_PKCS1_Block1, 50); - } - } -} - // This test verifies RSA signing with the alternate padding scheme used by // Android cast receivers, PKCS1 Block 1. These tests are not required for // other devices, and should be filtered out by DeviceFeatures::Initialize for @@ -4267,7 +3954,7 @@ class OEMCryptoGenericCryptoTest : public OEMCryptoSessionTests { void SetUp() override { OEMCryptoSessionTests::SetUp(); ASSERT_NO_FATAL_FAILURE(session_.open()); - ASSERT_NO_FATAL_FAILURE(InstallTestSessionKeys(&session_)); + ASSERT_NO_FATAL_FAILURE(InstallTestRSAKey(&session_)); ASSERT_NO_FATAL_FAILURE(MakeFourKeys()); } @@ -4726,7 +4413,7 @@ TEST_F(OEMCryptoGenericCryptoTest, KeyDurationEncrypt) { unsigned int key_index = 0; vector encrypted(clear_buffer_.size()); - sleep(kShortSleep); // Should still be valid key. + wvcdm::TestSleep::Sleep(kShortSleep); // Should still be valid key. ASSERT_EQ( OEMCrypto_SUCCESS, @@ -4741,7 +4428,7 @@ TEST_F(OEMCryptoGenericCryptoTest, KeyDurationEncrypt) { encrypted.data())); ASSERT_EQ(expected_encrypted, encrypted); - sleep(kLongSleep); // Should be expired key. + wvcdm::TestSleep::Sleep(kLongSleep); // Should be expired key. encrypted.assign(clear_buffer_.size(), 0); OEMCryptoResult status = OEMCrypto_Generic_Encrypt( @@ -4767,7 +4454,7 @@ TEST_F(OEMCryptoGenericCryptoTest, KeyDurationDecrypt) { session_.license().keys[key_index].key_id_length, OEMCrypto_CipherMode_CTR)); - sleep(kShortSleep); // Should still be valid key. + wvcdm::TestSleep::Sleep(kShortSleep); // Should still be valid key. vector resultant(encrypted.size()); ASSERT_EQ(OEMCrypto_SUCCESS, @@ -4776,7 +4463,7 @@ TEST_F(OEMCryptoGenericCryptoTest, KeyDurationDecrypt) { OEMCrypto_AES_CBC_128_NO_PADDING, resultant.data())); ASSERT_EQ(clear_buffer_, resultant); - sleep(kLongSleep); // Should be expired key. + wvcdm::TestSleep::Sleep(kLongSleep); // Should be expired key. resultant.assign(encrypted.size(), 0); OEMCryptoResult status = OEMCrypto_Generic_Decrypt( @@ -4805,7 +4492,7 @@ TEST_F(OEMCryptoGenericCryptoTest, KeyDurationSign) { session_.license().keys[key_index].key_id_length, OEMCrypto_CipherMode_CTR)); - sleep(kShortSleep); // Should still be valid key. + wvcdm::TestSleep::Sleep(kShortSleep); // Should still be valid key. ASSERT_EQ(OEMCrypto_SUCCESS, OEMCrypto_Generic_Sign(session_.session_id(), clear_buffer_.data(), @@ -4813,7 +4500,7 @@ TEST_F(OEMCryptoGenericCryptoTest, KeyDurationSign) { signature.data(), &signature_length)); ASSERT_EQ(expected_signature, signature); - sleep(kLongSleep); // Should be expired key. + wvcdm::TestSleep::Sleep(kLongSleep); // Should be expired key. signature.assign(SHA256_DIGEST_LENGTH, 0); OEMCryptoResult status = OEMCrypto_Generic_Sign( @@ -4840,7 +4527,7 @@ TEST_F(OEMCryptoGenericCryptoTest, KeyDurationVerify) { session_.license().keys[key_index].key_id_length, OEMCrypto_CipherMode_CTR)); - sleep(kShortSleep); // Should still be valid key. + wvcdm::TestSleep::Sleep(kShortSleep); // Should still be valid key. ASSERT_EQ(OEMCrypto_SUCCESS, OEMCrypto_Generic_Verify( @@ -4848,7 +4535,7 @@ TEST_F(OEMCryptoGenericCryptoTest, KeyDurationVerify) { clear_buffer_.size(), OEMCrypto_HMAC_SHA256, signature.data(), signature.size())); - sleep(kLongSleep); // Should be expired key. + wvcdm::TestSleep::Sleep(kLongSleep); // Should be expired key. OEMCryptoResult status = OEMCrypto_Generic_Verify( session_.session_id(), clear_buffer_.data(), clear_buffer_.size(), @@ -4980,7 +4667,7 @@ class OEMCryptoUsageTableTest : public OEMCryptoGenericCryptoTest { void LoadOfflineLicense(Session& s, const std::string& pst) { ASSERT_NO_FATAL_FAILURE(s.open()); - ASSERT_NO_FATAL_FAILURE(InstallTestSessionKeys(&s)); + ASSERT_NO_FATAL_FAILURE(InstallTestRSAKey(&s)); ASSERT_NO_FATAL_FAILURE(s.GenerateNonce()); ASSERT_NO_FATAL_FAILURE( s.FillSimpleMessage(0, wvoec::kControlNonceOrEntry, s.nonce(), pst)); @@ -4992,13 +4679,13 @@ class OEMCryptoUsageTableTest : public OEMCryptoGenericCryptoTest { ASSERT_NO_FATAL_FAILURE(s.close()); } - void PrintDotsWhileSleep(time_t total_seconds, time_t interval_seconds) { - time_t dot_time = interval_seconds; - time_t elapsed_time = 0; - time_t start_time = time(nullptr); + void PrintDotsWhileSleep(int64_t total_seconds, int64_t interval_seconds) { + int64_t dot_time = interval_seconds; + int64_t elapsed_time = 0; + const int64_t start_time = wvcdm::Clock().GetCurrentTime(); do { - sleep(1); - elapsed_time = time(nullptr) - start_time; + wvcdm::TestSleep::Sleep(1); + elapsed_time = wvcdm::Clock().GetCurrentTime() - start_time; if (elapsed_time >= dot_time) { cout << "."; cout.flush(); @@ -5051,7 +4738,7 @@ TEST_P(OEMCryptoUsageTableTestWithMAC, OnlineLicense) { std::string pst = "my_pst"; Session s; ASSERT_NO_FATAL_FAILURE(s.open()); - ASSERT_NO_FATAL_FAILURE(InstallTestSessionKeys(&s)); + ASSERT_NO_FATAL_FAILURE(InstallTestRSAKey(&s)); ASSERT_NO_FATAL_FAILURE(s.GenerateNonce()); ASSERT_NO_FATAL_FAILURE(s.FillSimpleMessage( 0, wvoec::kControlNonceEnabled | wvoec::kControlNonceRequired, s.nonce(), @@ -5090,7 +4777,7 @@ TEST_P(OEMCryptoUsageTableTestWithMAC, OnlineLicenseUnused) { std::string pst = "my_pst"; Session s; ASSERT_NO_FATAL_FAILURE(s.open()); - ASSERT_NO_FATAL_FAILURE(InstallTestSessionKeys(&s)); + ASSERT_NO_FATAL_FAILURE(InstallTestRSAKey(&s)); ASSERT_NO_FATAL_FAILURE(s.GenerateNonce()); ASSERT_NO_FATAL_FAILURE(s.FillSimpleMessage( 0, wvoec::kControlNonceEnabled | wvoec::kControlNonceRequired, s.nonce(), @@ -5123,7 +4810,7 @@ TEST_P(OEMCryptoUsageTableTestWithMAC, ForbidReportWithNoUpdate) { std::string pst = "my_pst"; Session s; ASSERT_NO_FATAL_FAILURE(s.open()); - ASSERT_NO_FATAL_FAILURE(InstallTestSessionKeys(&s)); + ASSERT_NO_FATAL_FAILURE(InstallTestRSAKey(&s)); ASSERT_NO_FATAL_FAILURE(s.GenerateNonce()); ASSERT_NO_FATAL_FAILURE(s.FillSimpleMessage( 0, wvoec::kControlNonceEnabled | wvoec::kControlNonceRequired, s.nonce(), @@ -5155,7 +4842,7 @@ TEST_P(OEMCryptoUsageTableTestWithMAC, OnlineLicenseWithRefreshAPI16) { std::string pst = "my_pst"; Session s; ASSERT_NO_FATAL_FAILURE(s.open()); - ASSERT_NO_FATAL_FAILURE(InstallTestSessionKeys(&s)); + ASSERT_NO_FATAL_FAILURE(InstallTestRSAKey(&s)); ASSERT_NO_FATAL_FAILURE(s.GenerateNonce()); ASSERT_NO_FATAL_FAILURE(s.FillSimpleMessage( 0, wvoec::kControlNonceEnabled | wvoec::kControlNonceRequired, s.nonce(), @@ -5163,7 +4850,7 @@ TEST_P(OEMCryptoUsageTableTestWithMAC, OnlineLicenseWithRefreshAPI16) { ASSERT_NO_FATAL_FAILURE(s.EncryptAndSign()); ASSERT_NO_FATAL_FAILURE(s.CreateNewUsageEntry()); ASSERT_NO_FATAL_FAILURE(s.LoadTestKeys(pst, new_mac_keys_)); - time_t loaded = time(nullptr); + const int64_t loaded = wvcdm::Clock().GetCurrentTime(); ASSERT_NO_FATAL_FAILURE(s.TestDecryptCTR()); // License renewal message is signed by client and verified by the server. ASSERT_NO_FATAL_FAILURE(s.VerifyRenewalRequestSignature()); @@ -5184,7 +4871,7 @@ TEST_F(OEMCryptoUsageTableTest, RepeatOnlineLicense) { std::string pst = "my_pst"; Session s; ASSERT_NO_FATAL_FAILURE(s.open()); - ASSERT_NO_FATAL_FAILURE(InstallTestSessionKeys(&s)); + ASSERT_NO_FATAL_FAILURE(InstallTestRSAKey(&s)); ASSERT_NO_FATAL_FAILURE(s.GenerateNonce()); ASSERT_NO_FATAL_FAILURE(s.FillSimpleMessage( 0, wvoec::kControlNonceEnabled | wvoec::kControlNonceRequired, s.nonce(), @@ -5196,7 +4883,7 @@ TEST_F(OEMCryptoUsageTableTest, RepeatOnlineLicense) { ASSERT_NO_FATAL_FAILURE(s.close()); Session s2; ASSERT_NO_FATAL_FAILURE(s2.open()); - ASSERT_NO_FATAL_FAILURE(InstallTestSessionKeys(&s2)); + ASSERT_NO_FATAL_FAILURE(InstallTestRSAKey(&s2)); s2.LoadUsageEntry(s); // Use the same entry. // Trying to reuse a PST is bad. We use session ID for s2, everything else // reused from s. @@ -5214,7 +4901,7 @@ TEST_F(OEMCryptoUsageTableTest, RepeatOnlineLicense) { TEST_F(OEMCryptoUsageTableTest, OnlineEmptyPST) { Session s; ASSERT_NO_FATAL_FAILURE(s.open()); - ASSERT_NO_FATAL_FAILURE(InstallTestSessionKeys(&s)); + ASSERT_NO_FATAL_FAILURE(InstallTestRSAKey(&s)); ASSERT_NO_FATAL_FAILURE(s.GenerateNonce()); ASSERT_NO_FATAL_FAILURE(s.FillSimpleMessage( 0, wvoec::kControlNonceEnabled | wvoec::kControlNonceRequired, @@ -5235,7 +4922,7 @@ TEST_F(OEMCryptoUsageTableTest, OnlineMissingEntry) { std::string pst = "my_pst"; Session s; ASSERT_NO_FATAL_FAILURE(s.open()); - ASSERT_NO_FATAL_FAILURE(InstallTestSessionKeys(&s)); + ASSERT_NO_FATAL_FAILURE(InstallTestRSAKey(&s)); ASSERT_NO_FATAL_FAILURE(s.GenerateNonce()); ASSERT_NO_FATAL_FAILURE(s.FillSimpleMessage( 0, wvoec::kControlNonceEnabled | wvoec::kControlNonceRequired, s.nonce(), @@ -5251,6 +4938,44 @@ TEST_F(OEMCryptoUsageTableTest, OnlineMissingEntry) { ASSERT_NO_FATAL_FAILURE(s.close()); } +// Sessions should have at most one entry at a time. This tests different +// orderings of CreateNewUsageEntry and LoadUsageEntry calls. +TEST_F(OEMCryptoUsageTableTest, CreateAndLoadMultipleEntriesAPI16) { + std::string pst = "my_pst"; + Session s; + + ASSERT_NO_FATAL_FAILURE(s.open()); + ASSERT_NO_FATAL_FAILURE(s.CreateNewUsageEntry()); + uint32_t usage_entry_number; + ASSERT_EQ(OEMCrypto_ERROR_MULTIPLE_USAGE_ENTRIES, + OEMCrypto_CreateNewUsageEntry(s.session_id(), &usage_entry_number)); + ASSERT_NO_FATAL_FAILURE(s.close()); + + ASSERT_NO_FATAL_FAILURE(LoadOfflineLicense(s, pst)); + ASSERT_NO_FATAL_FAILURE(s.open()); + ASSERT_NO_FATAL_FAILURE(s.ReloadUsageEntry()); + ASSERT_EQ(OEMCrypto_ERROR_MULTIPLE_USAGE_ENTRIES, + OEMCrypto_CreateNewUsageEntry(s.session_id(), &usage_entry_number)); + ASSERT_NO_FATAL_FAILURE(s.close()); + + ASSERT_NO_FATAL_FAILURE(s.open()); + ASSERT_NO_FATAL_FAILURE(s.CreateNewUsageEntry()); + ASSERT_EQ(OEMCrypto_ERROR_MULTIPLE_USAGE_ENTRIES, + OEMCrypto_LoadUsageEntry(s.session_id(), s.usage_entry_number(), + s.encrypted_usage_entry().data(), + s.encrypted_usage_entry().size())); + ASSERT_NO_FATAL_FAILURE(s.close()); + + ASSERT_NO_FATAL_FAILURE(LoadOfflineLicense(s, pst)); + ASSERT_NO_FATAL_FAILURE(s.open()); + ASSERT_NO_FATAL_FAILURE(s.ReloadUsageEntry()); + ASSERT_EQ(OEMCrypto_ERROR_MULTIPLE_USAGE_ENTRIES, + OEMCrypto_LoadUsageEntry(s.session_id(), s.usage_entry_number(), + s.encrypted_usage_entry().data(), + s.encrypted_usage_entry().size())); + ASSERT_NO_FATAL_FAILURE(s.close()); +} + // Test generic encrypt when the license uses a PST. TEST_P(OEMCryptoUsageTableTestWithMAC, GenericCryptoEncrypt) { std::string pst = "A PST"; @@ -5428,14 +5153,14 @@ TEST_P(OEMCryptoUsageTableTestWithMAC, OfflineLicenseRefresh) { std::string pst = "my_pst"; Session s; ASSERT_NO_FATAL_FAILURE(s.open()); - ASSERT_NO_FATAL_FAILURE(InstallTestSessionKeys(&s)); + ASSERT_NO_FATAL_FAILURE(InstallTestRSAKey(&s)); ASSERT_NO_FATAL_FAILURE(s.GenerateNonce()); ASSERT_NO_FATAL_FAILURE( s.FillSimpleMessage(0, wvoec::kControlNonceOrEntry, s.nonce(), pst)); ASSERT_NO_FATAL_FAILURE(s.EncryptAndSign()); ASSERT_NO_FATAL_FAILURE(s.CreateNewUsageEntry()); ASSERT_NO_FATAL_FAILURE(s.LoadTestKeys(pst, new_mac_keys_)); - time_t loaded = time(nullptr); + const int64_t loaded = wvcdm::Clock().GetCurrentTime(); ASSERT_NO_FATAL_FAILURE(s.TestDecryptCTR()); // License renewal message is signed by client and verified by the server. ASSERT_NO_FATAL_FAILURE(s.VerifyRenewalRequestSignature()); @@ -5461,7 +5186,7 @@ TEST_P(OEMCryptoUsageTableTestWithMAC, ReloadOfflineLicense) { // We will reuse the encrypted and signed message, so we don't call // FillSimpleMessage again. ASSERT_NO_FATAL_FAILURE(s.ReloadUsageEntry()); - ASSERT_NO_FATAL_FAILURE(InstallTestSessionKeys(&s)); + ASSERT_NO_FATAL_FAILURE(InstallTestRSAKey(&s)); ASSERT_NO_FATAL_FAILURE(s.LoadTestKeys(pst, new_mac_keys_)); ASSERT_NO_FATAL_FAILURE(s.UpdateUsageEntry(&encrypted_usage_header_)); ASSERT_NO_FATAL_FAILURE(s.GenerateVerifyReport(pst, kUnused)); @@ -5477,18 +5202,18 @@ TEST_P(OEMCryptoUsageTableTestWithMAC, ReloadOfflineLicenseWithRefresh) { std::string pst = "my_pst"; Session s; ASSERT_NO_FATAL_FAILURE(LoadOfflineLicense(s, pst)); - time_t loaded = time(nullptr); + const int64_t loaded = wvcdm::Clock().GetCurrentTime(); ASSERT_NO_FATAL_FAILURE(s.open()); // We will reuse the encrypted and signed message, so we don't call // FillSimpleMessage again. ASSERT_NO_FATAL_FAILURE(s.ReloadUsageEntry()); - ASSERT_NO_FATAL_FAILURE(InstallTestSessionKeys(&s)); + ASSERT_NO_FATAL_FAILURE(InstallTestRSAKey(&s)); ASSERT_NO_FATAL_FAILURE(s.LoadTestKeys(pst, new_mac_keys_)); ASSERT_NO_FATAL_FAILURE(s.UpdateUsageEntry(&encrypted_usage_header_)); ASSERT_NO_FATAL_FAILURE(s.GenerateVerifyReport(pst, kUnused, loaded, 0, 0)); ASSERT_NO_FATAL_FAILURE(s.TestDecryptCTR()); - time_t decrypt_time = time(nullptr); + const int64_t decrypt_time = wvcdm::Clock().GetCurrentTime(); ASSERT_NO_FATAL_FAILURE(s.UpdateUsageEntry(&encrypted_usage_header_)); ASSERT_NO_FATAL_FAILURE( s.GenerateVerifyReport(pst, kActive, @@ -5524,7 +5249,7 @@ TEST_P(OEMCryptoUsageTableTestWithMAC, ReloadOfflineLicenseWithTerminate) { // We will reuse the encrypted and signed message, so we don't call // FillSimpleMessage again. ASSERT_NO_FATAL_FAILURE(s.ReloadUsageEntry()); - ASSERT_NO_FATAL_FAILURE(InstallTestSessionKeys(&s)); + ASSERT_NO_FATAL_FAILURE(InstallTestRSAKey(&s)); ASSERT_NO_FATAL_FAILURE(s.LoadTestKeys(pst, new_mac_keys_)); ASSERT_NO_FATAL_FAILURE(s.UpdateUsageEntry(&encrypted_usage_header_)); ASSERT_NO_FATAL_FAILURE(s.GenerateVerifyReport(pst, kUnused)); @@ -5541,12 +5266,12 @@ TEST_P(OEMCryptoUsageTableTestWithMAC, BadReloadOfflineLicense) { std::string pst = "my_pst"; Session s; ASSERT_NO_FATAL_FAILURE(LoadOfflineLicense(s, pst)); - time_t loaded = time(nullptr); + const int64_t loaded = wvcdm::Clock().GetCurrentTime(); // Offline license with new mac keys should fail. Session s2; ASSERT_NO_FATAL_FAILURE(s2.open()); - ASSERT_NO_FATAL_FAILURE(InstallTestSessionKeys(&s2)); + ASSERT_NO_FATAL_FAILURE(InstallTestRSAKey(&s2)); ASSERT_NO_FATAL_FAILURE(s2.GenerateNonce()); ASSERT_NO_FATAL_FAILURE( s2.FillSimpleMessage(0, wvoec::kControlNonceOrEntry, s2.nonce(), pst)); @@ -5564,7 +5289,7 @@ TEST_P(OEMCryptoUsageTableTestWithMAC, BadReloadOfflineLicense) { // Offline license with same mac keys should still be OK. ASSERT_NO_FATAL_FAILURE(s.open()); ASSERT_NO_FATAL_FAILURE(s.ReloadUsageEntry()); - ASSERT_NO_FATAL_FAILURE(InstallTestSessionKeys(&s)); + ASSERT_NO_FATAL_FAILURE(InstallTestRSAKey(&s)); ASSERT_NO_FATAL_FAILURE(s.LoadTestKeys(pst, new_mac_keys_)); ASSERT_NO_FATAL_FAILURE(s.UpdateUsageEntry(&encrypted_usage_header_)); ASSERT_NO_FATAL_FAILURE(s.GenerateVerifyReport(pst, kUnused, @@ -5578,7 +5303,7 @@ TEST_P(OEMCryptoUsageTableTestWithMAC, OfflineBadNonce) { std::string pst = "my_pst"; Session s; ASSERT_NO_FATAL_FAILURE(s.open()); - ASSERT_NO_FATAL_FAILURE(InstallTestSessionKeys(&s)); + ASSERT_NO_FATAL_FAILURE(InstallTestRSAKey(&s)); ASSERT_NO_FATAL_FAILURE(s.CreateNewUsageEntry()); ASSERT_NO_FATAL_FAILURE(s.GenerateNonce()); ASSERT_NO_FATAL_FAILURE( @@ -5597,7 +5322,7 @@ TEST_P(OEMCryptoUsageTableTestWithMAC, OfflineBadNonce) { TEST_P(OEMCryptoUsageTableTestWithMAC, OfflineEmptyPST) { Session s; ASSERT_NO_FATAL_FAILURE(s.open()); - ASSERT_NO_FATAL_FAILURE(InstallTestSessionKeys(&s)); + ASSERT_NO_FATAL_FAILURE(InstallTestRSAKey(&s)); ASSERT_NO_FATAL_FAILURE(s.CreateNewUsageEntry()); ASSERT_NO_FATAL_FAILURE(s.GenerateNonce()); ASSERT_NO_FATAL_FAILURE( @@ -5620,7 +5345,7 @@ TEST_P(OEMCryptoUsageTableTestWithMAC, ReloadOfflineWrongPST) { std::string bad_pst = "my_pst2"; ASSERT_NO_FATAL_FAILURE(s.open()); - ASSERT_NO_FATAL_FAILURE(InstallTestSessionKeys(&s)); + ASSERT_NO_FATAL_FAILURE(InstallTestRSAKey(&s)); ASSERT_NO_FATAL_FAILURE(s.ReloadUsageEntry()); memcpy(s.license().pst, bad_pst.c_str(), bad_pst.length()); ASSERT_NO_FATAL_FAILURE(s.EncryptAndSign()); @@ -5641,7 +5366,7 @@ TEST_P(OEMCryptoUsageTableTestWithMAC, DeactivateOfflineLicense) { ASSERT_NO_FATAL_FAILURE(s.open()); ASSERT_NO_FATAL_FAILURE(s.ReloadUsageEntry()); - ASSERT_NO_FATAL_FAILURE(InstallTestSessionKeys(&s)); + ASSERT_NO_FATAL_FAILURE(InstallTestRSAKey(&s)); ASSERT_NO_FATAL_FAILURE( s.LoadTestKeys(pst, new_mac_keys_)); // Reload the license ASSERT_NO_FATAL_FAILURE(s.TestDecryptCTR()); // Should be able to decrypt. @@ -5655,7 +5380,7 @@ TEST_P(OEMCryptoUsageTableTestWithMAC, DeactivateOfflineLicense) { Session s2; ASSERT_NO_FATAL_FAILURE(s2.open()); - ASSERT_NO_FATAL_FAILURE(InstallTestSessionKeys(&s2)); + ASSERT_NO_FATAL_FAILURE(InstallTestRSAKey(&s2)); ASSERT_NO_FATAL_FAILURE(s2.LoadUsageEntry(s)); // Offile license can not be reused if it has been deactivated. EXPECT_NE( @@ -5690,7 +5415,7 @@ TEST_P(OEMCryptoUsageTableTestWithMAC, DeactivateOfflineLicenseUnused) { ASSERT_NO_FATAL_FAILURE(s1.open()); ASSERT_NO_FATAL_FAILURE(s1.ReloadUsageEntry()); - ASSERT_NO_FATAL_FAILURE(InstallTestSessionKeys(&s1)); + ASSERT_NO_FATAL_FAILURE(InstallTestRSAKey(&s1)); ASSERT_NO_FATAL_FAILURE( s1.LoadTestKeys(pst, new_mac_keys_)); // Reload the license // No Decrypt. This license is unused. @@ -5704,7 +5429,7 @@ TEST_P(OEMCryptoUsageTableTestWithMAC, DeactivateOfflineLicenseUnused) { Session s2; ASSERT_NO_FATAL_FAILURE(s2.open()); - ASSERT_NO_FATAL_FAILURE(InstallTestSessionKeys(&s2)); + ASSERT_NO_FATAL_FAILURE(InstallTestRSAKey(&s2)); ASSERT_NO_FATAL_FAILURE(s2.LoadUsageEntry(s1)); // Offline license can not be reused if it has been deactivated. EXPECT_NE( @@ -5736,7 +5461,7 @@ TEST_P(OEMCryptoUsageTableTestWithMAC, BadRange) { std::string pst = "my_pst"; Session s; ASSERT_NO_FATAL_FAILURE(s.open()); - ASSERT_NO_FATAL_FAILURE(InstallTestSessionKeys(&s)); + ASSERT_NO_FATAL_FAILURE(InstallTestRSAKey(&s)); ASSERT_NO_FATAL_FAILURE(session_.CreateNewUsageEntry()); ASSERT_NO_FATAL_FAILURE(s.GenerateNonce()); ASSERT_NO_FATAL_FAILURE( @@ -5760,7 +5485,7 @@ TEST_F(OEMCryptoUsageTableTest, UpdateFailsWithNullPtr) { std::string pst = "my_pst"; Session s; ASSERT_NO_FATAL_FAILURE(s.open()); - ASSERT_NO_FATAL_FAILURE(InstallTestSessionKeys(&s)); + ASSERT_NO_FATAL_FAILURE(InstallTestRSAKey(&s)); ASSERT_NO_FATAL_FAILURE(s.GenerateNonce()); ASSERT_NO_FATAL_FAILURE(s.FillSimpleMessage( 0, wvoec::kControlNonceEnabled | wvoec::kControlNonceRequired, s.nonce(), @@ -5788,7 +5513,7 @@ class OEMCryptoUsageTableDefragTest : public OEMCryptoUsageTableTest { protected: void LoadFirstLicense(Session* s, uint32_t index) { ASSERT_NO_FATAL_FAILURE(s->open()); - ASSERT_NO_FATAL_FAILURE(InstallTestSessionKeys(s)); + ASSERT_NO_FATAL_FAILURE(InstallTestRSAKey(s)); std::string pst = "pst " + std::to_string(index); ASSERT_NO_FATAL_FAILURE(s->GenerateNonce()); ASSERT_NO_FATAL_FAILURE( @@ -5802,10 +5527,10 @@ class OEMCryptoUsageTableDefragTest : public OEMCryptoUsageTableTest { ASSERT_NO_FATAL_FAILURE(s->close()); } - void ReloadLicense(Session* s, time_t start) { + void ReloadLicense(Session* s, int64_t start) { ASSERT_NO_FATAL_FAILURE(s->open()); ASSERT_NO_FATAL_FAILURE(s->ReloadUsageEntry()); - ASSERT_NO_FATAL_FAILURE(InstallTestSessionKeys(s)); + ASSERT_NO_FATAL_FAILURE(InstallTestRSAKey(s)); ASSERT_NO_FATAL_FAILURE(s->LoadTestKeys(s->pst(), new_mac_keys_)); ASSERT_NO_FATAL_FAILURE(s->UpdateUsageEntry(&encrypted_usage_header_)); ASSERT_NO_FATAL_FAILURE(s->TestDecryptCTR()); @@ -5817,7 +5542,7 @@ class OEMCryptoUsageTableDefragTest : public OEMCryptoUsageTableTest { void FailReload(Session* s, OEMCryptoResult expected_result) { ASSERT_NO_FATAL_FAILURE(s->open()); - ASSERT_NO_FATAL_FAILURE(InstallTestSessionKeys(s)); + ASSERT_NO_FATAL_FAILURE(InstallTestRSAKey(s)); ASSERT_EQ(expected_result, OEMCrypto_LoadUsageEntry(s->session_id(), s->usage_entry_number(), s->encrypted_usage_entry().data(), @@ -5857,11 +5582,12 @@ class OEMCryptoUsageTableDefragTest : public OEMCryptoUsageTableTest { TEST_F(OEMCryptoUsageTableDefragTest, MoveUsageEntries) { const size_t ENTRY_COUNT = 10; vector sessions(ENTRY_COUNT); - vector start(ENTRY_COUNT); + vector start(ENTRY_COUNT); for (size_t i = 0; i < ENTRY_COUNT; i++) { ASSERT_NO_FATAL_FAILURE(LoadFirstLicense(&sessions[i], i)) << "On license " << i << " pst=" << sessions[i].pst(); - start[i] = time(nullptr); + wvcdm::TestSleep::SyncFakeClock(); + start[i] = wvcdm::Clock().GetCurrentTime(); } for (size_t i = 0; i < ENTRY_COUNT; i++) { ASSERT_NO_FATAL_FAILURE(ReloadLicense(&sessions[i], start[i])) @@ -5877,6 +5603,7 @@ TEST_F(OEMCryptoUsageTableDefragTest, MoveUsageEntries) { ASSERT_EQ(OEMCrypto_SUCCESS, OEMCrypto_LoadUsageTableHeader(encrypted_usage_header_.data(), encrypted_usage_header_.size())); + wvcdm::TestSleep::SyncFakeClock(); ASSERT_NO_FATAL_FAILURE(ReloadLicense(&sessions[0], start[0])); // Now has index 1. ASSERT_NO_FATAL_FAILURE(ReloadLicense(&sessions[4], start[4])); @@ -5959,7 +5686,7 @@ TEST_F(OEMCryptoUsageTableDefragTest, ReloadUsageEntryBadData) { Session s; LoadFirstLicense(&s, 0); ASSERT_NO_FATAL_FAILURE(s.open()); - ASSERT_NO_FATAL_FAILURE(InstallTestSessionKeys(&s)); + ASSERT_NO_FATAL_FAILURE(InstallTestRSAKey(&s)); vector data = s.encrypted_usage_entry(); ASSERT_LT(0UL, data.size()); data[0] ^= 42; @@ -5984,9 +5711,10 @@ TEST_F(OEMCryptoUsageTableDefragTest, TwoHundredEntries) { vector sessions(ENTRY_COUNT); size_t successful_count = 0; for (size_t i = 0; i < ENTRY_COUNT; i++) { + wvcdm::TestSleep::SyncFakeClock(); if (i % 50 == 0) LOGD("Creating license %zd", i); ASSERT_NO_FATAL_FAILURE(sessions[i].open()); - ASSERT_NO_FATAL_FAILURE(InstallTestSessionKeys(&sessions[i])); + ASSERT_NO_FATAL_FAILURE(InstallTestRSAKey(&sessions[i])); std::string pst = MakePST(i); ASSERT_NO_FATAL_FAILURE(sessions[i].GenerateNonce()); ASSERT_NO_FATAL_FAILURE(sessions[i].FillSimpleMessage( @@ -6012,16 +5740,17 @@ TEST_F(OEMCryptoUsageTableDefragTest, TwoHundredEntries) { } LOGD("successful_count = %d", successful_count); EXPECT_GE(successful_count, 200u); - sleep(kShortSleep); + wvcdm::TestSleep::Sleep(kShortSleep); // Now we will loop through each valid entry, and verify that we can still // reload the license and perform a decrypt. for (size_t i = 0; i < successful_count; i++) { + wvcdm::TestSleep::SyncFakeClock(); if (i % 50 == 0) LOGD("Reloading license %zd", i); ASSERT_NO_FATAL_FAILURE(sessions[i].open()); std::string pst = MakePST(i); // Reuse license message created above. ASSERT_NO_FATAL_FAILURE(sessions[i].ReloadUsageEntry()); - ASSERT_NO_FATAL_FAILURE(InstallTestSessionKeys(&sessions[i])); + ASSERT_NO_FATAL_FAILURE(InstallTestRSAKey(&sessions[i])); ASSERT_NO_FATAL_FAILURE(sessions[i].LoadTestKeys(pst, new_mac_keys_)) << "Failed to reload license " << i << " with pst = " << pst; ASSERT_NO_FATAL_FAILURE( @@ -6039,11 +5768,12 @@ TEST_F(OEMCryptoUsageTableDefragTest, TwoHundredEntries) { size_t smaller_size = 10u; // 10 is smaller than 200. ASSERT_NO_FATAL_FAILURE(ShrinkHeader(smaller_size)); for (size_t i = 0; i < smaller_size; i++) { + wvcdm::TestSleep::SyncFakeClock(); ASSERT_NO_FATAL_FAILURE(sessions[i].open()); std::string pst = MakePST(i); // Reuse license message created above. ASSERT_NO_FATAL_FAILURE(sessions[i].ReloadUsageEntry()); - ASSERT_NO_FATAL_FAILURE(InstallTestSessionKeys(&sessions[i])); + ASSERT_NO_FATAL_FAILURE(InstallTestRSAKey(&sessions[i])); ASSERT_NO_FATAL_FAILURE(sessions[i].LoadTestKeys(pst, new_mac_keys_)) << "Failed to reload license " << i << " with pst = " << pst; ASSERT_NO_FATAL_FAILURE( @@ -6071,7 +5801,7 @@ TEST_F(OEMCryptoUsageTableTest, ReloadUsageTableWithSkew) { // We will reuse the encrypted and signed message, so we don't call // FillSimpleMessage again. ASSERT_NO_FATAL_FAILURE(s.ReloadUsageEntry()); - ASSERT_NO_FATAL_FAILURE(InstallTestSessionKeys(&s)); + ASSERT_NO_FATAL_FAILURE(InstallTestRSAKey(&s)); ASSERT_NO_FATAL_FAILURE(s.LoadTestKeys(pst, new_mac_keys_)); ASSERT_NO_FATAL_FAILURE(s.UpdateUsageEntry(&encrypted_usage_header_)); vector old_usage_header_2_ = encrypted_usage_header_; @@ -6092,7 +5822,7 @@ TEST_F(OEMCryptoUsageTableTest, ReloadUsageTableWithSkew) { OEMCrypto_LoadUsageEntry(s.session_id(), s.usage_entry_number(), s.encrypted_usage_entry().data(), s.encrypted_usage_entry().size())); - ASSERT_NO_FATAL_FAILURE(InstallTestSessionKeys(&s)); + ASSERT_NO_FATAL_FAILURE(InstallTestRSAKey(&s)); ASSERT_NO_FATAL_FAILURE(s.close()); // Modified header generates error. @@ -6108,7 +5838,7 @@ TEST_F(OEMCryptoUsageTableTest, ReloadUsageTableWithSkew) { OEMCrypto_LoadUsageEntry(s.session_id(), s.usage_entry_number(), s.encrypted_usage_entry().data(), s.encrypted_usage_entry().size())); - ASSERT_NO_FATAL_FAILURE(InstallTestSessionKeys(&s)); + ASSERT_NO_FATAL_FAILURE(InstallTestRSAKey(&s)); ASSERT_NO_FATAL_FAILURE(s.close()); // Old by 2 generation numbers is error. @@ -6122,7 +5852,7 @@ TEST_F(OEMCryptoUsageTableTest, ReloadUsageTableWithSkew) { OEMCrypto_LoadUsageEntry(s.session_id(), s.usage_entry_number(), s.encrypted_usage_entry().data(), s.encrypted_usage_entry().size())); - ASSERT_NO_FATAL_FAILURE(InstallTestSessionKeys(&s)); + ASSERT_NO_FATAL_FAILURE(InstallTestRSAKey(&s)); ASSERT_NO_FATAL_FAILURE(s.close()); // Old by 1 generation numbers is just warning. @@ -6136,7 +5866,7 @@ TEST_F(OEMCryptoUsageTableTest, ReloadUsageTableWithSkew) { OEMCrypto_LoadUsageEntry(s.session_id(), s.usage_entry_number(), s.encrypted_usage_entry().data(), s.encrypted_usage_entry().size())); - ASSERT_NO_FATAL_FAILURE(InstallTestSessionKeys(&s)); + ASSERT_NO_FATAL_FAILURE(InstallTestRSAKey(&s)); ASSERT_NO_FATAL_FAILURE(s.LoadTestKeys(pst, new_mac_keys_)); ASSERT_NO_FATAL_FAILURE(s.UpdateUsageEntry(&encrypted_usage_header_)); ASSERT_NO_FATAL_FAILURE(s.close()); @@ -6147,7 +5877,7 @@ TEST_F(OEMCryptoUsageTableTest, GenerateReportWrongPST) { std::string pst = "my_pst"; Session s; ASSERT_NO_FATAL_FAILURE(s.open()); - ASSERT_NO_FATAL_FAILURE(InstallTestSessionKeys(&s)); + ASSERT_NO_FATAL_FAILURE(InstallTestRSAKey(&s)); ASSERT_NO_FATAL_FAILURE(s.GenerateNonce()); ASSERT_NO_FATAL_FAILURE( s.FillSimpleMessage(0, wvoec::kControlNonceOrEntry, s.nonce(), pst)); @@ -6168,54 +5898,54 @@ TEST_F(OEMCryptoUsageTableTest, TimingTest) { Session s2; Session s3; ASSERT_NO_FATAL_FAILURE(LoadOfflineLicense(s1, pst1)); - time_t loaded1 = time(nullptr); + const int64_t loaded1 = wvcdm::Clock().GetCurrentTime(); ASSERT_NO_FATAL_FAILURE(LoadOfflineLicense(s2, pst2)); - time_t loaded2 = time(nullptr); + const int64_t loaded2 = wvcdm::Clock().GetCurrentTime(); ASSERT_NO_FATAL_FAILURE(LoadOfflineLicense(s3, pst3)); - time_t loaded3 = time(nullptr); + const int64_t loaded3 = wvcdm::Clock().GetCurrentTime(); - sleep(kLongSleep); + wvcdm::TestSleep::Sleep(kLongSleep); ASSERT_NO_FATAL_FAILURE(s1.open()); ASSERT_NO_FATAL_FAILURE(s1.ReloadUsageEntry()); - ASSERT_NO_FATAL_FAILURE(InstallTestSessionKeys(&s1)); + ASSERT_NO_FATAL_FAILURE(InstallTestRSAKey(&s1)); ASSERT_NO_FATAL_FAILURE(s1.LoadTestKeys(pst1, new_mac_keys_)); - time_t first_decrypt1 = time(nullptr); + const int64_t first_decrypt1 = wvcdm::Clock().GetCurrentTime(); ASSERT_NO_FATAL_FAILURE(s1.TestDecryptCTR()); ASSERT_NO_FATAL_FAILURE(s2.open()); ASSERT_NO_FATAL_FAILURE(s2.ReloadUsageEntry()); - ASSERT_NO_FATAL_FAILURE(InstallTestSessionKeys(&s2)); + ASSERT_NO_FATAL_FAILURE(InstallTestRSAKey(&s2)); ASSERT_NO_FATAL_FAILURE(s2.LoadTestKeys(pst2, new_mac_keys_)); - time_t first_decrypt2 = time(nullptr); + const int64_t first_decrypt2 = wvcdm::Clock().GetCurrentTime(); ASSERT_NO_FATAL_FAILURE(s2.TestDecryptCTR()); - sleep(kLongSleep); - time_t second_decrypt = time(nullptr); + wvcdm::TestSleep::Sleep(kLongSleep); + const int64_t second_decrypt = wvcdm::Clock().GetCurrentTime(); ASSERT_NO_FATAL_FAILURE(s1.TestDecryptCTR()); ASSERT_NO_FATAL_FAILURE(s2.TestDecryptCTR()); - sleep(kLongSleep); + wvcdm::TestSleep::Sleep(kLongSleep); ASSERT_NO_FATAL_FAILURE(s1.DeactivateUsageEntry(pst1)); ASSERT_NO_FATAL_FAILURE(s1.UpdateUsageEntry(&encrypted_usage_header_)); ASSERT_NO_FATAL_FAILURE(s2.UpdateUsageEntry(&encrypted_usage_header_)); ASSERT_NO_FATAL_FAILURE(s1.close()); ASSERT_NO_FATAL_FAILURE(s2.close()); - sleep(kLongSleep); + wvcdm::TestSleep::Sleep(kLongSleep); // This is as close to reboot as we can simulate in code. ShutDown(); - sleep(kShortSleep); + wvcdm::TestSleep::Sleep(kShortSleep); Restart(); ASSERT_EQ(OEMCrypto_SUCCESS, OEMCrypto_LoadUsageTableHeader(encrypted_usage_header_.data(), encrypted_usage_header_.size())); // After a reboot, we should be able to reload keys, and generate reports. - sleep(kLongSleep); - time_t third_decrypt = time(nullptr); + wvcdm::TestSleep::Sleep(kLongSleep); + const int64_t third_decrypt = wvcdm::Clock().GetCurrentTime(); ASSERT_NO_FATAL_FAILURE(s2.open()); ASSERT_NO_FATAL_FAILURE(s2.ReloadUsageEntry()); - ASSERT_NO_FATAL_FAILURE(InstallTestSessionKeys(&s2)); + ASSERT_NO_FATAL_FAILURE(InstallTestRSAKey(&s2)); ASSERT_NO_FATAL_FAILURE(s2.LoadTestKeys(pst2, new_mac_keys_)); ASSERT_NO_FATAL_FAILURE(s2.TestDecryptCTR()); ASSERT_NO_FATAL_FAILURE(s2.UpdateUsageEntry(&encrypted_usage_header_)); @@ -6227,7 +5957,7 @@ TEST_F(OEMCryptoUsageTableTest, TimingTest) { ASSERT_NO_FATAL_FAILURE(s1.ReloadUsageEntry()); ASSERT_NO_FATAL_FAILURE(s2.ReloadUsageEntry()); ASSERT_NO_FATAL_FAILURE(s3.ReloadUsageEntry()); - sleep(kLongSleep); + wvcdm::TestSleep::Sleep(kLongSleep); ASSERT_NO_FATAL_FAILURE(s1.UpdateUsageEntry(&encrypted_usage_header_)); ASSERT_NO_FATAL_FAILURE(s1.GenerateVerifyReport(pst1, kInactiveUsed, loaded1, @@ -6251,7 +5981,7 @@ TEST_F(OEMCryptoUsageTableTest, VerifyUsageTimes) { std::string pst = "my_pst"; Session s; ASSERT_NO_FATAL_FAILURE(s.open()); - ASSERT_NO_FATAL_FAILURE(InstallTestSessionKeys(&s)); + ASSERT_NO_FATAL_FAILURE(InstallTestRSAKey(&s)); ASSERT_NO_FATAL_FAILURE(s.GenerateNonce()); ASSERT_NO_FATAL_FAILURE(s.FillSimpleMessage( 0, wvoec::kControlNonceEnabled | wvoec::kControlNonceRequired, s.nonce(), @@ -6259,14 +5989,14 @@ TEST_F(OEMCryptoUsageTableTest, VerifyUsageTimes) { ASSERT_NO_FATAL_FAILURE(s.EncryptAndSign()); ASSERT_NO_FATAL_FAILURE(s.CreateNewUsageEntry()); ASSERT_NO_FATAL_FAILURE(s.LoadTestKeys(pst, new_mac_keys_)); - time_t load_time = time(nullptr); + const int64_t load_time = wvcdm::Clock().GetCurrentTime(); ASSERT_NO_FATAL_FAILURE(s.UpdateUsageEntry(&encrypted_usage_header_)); ASSERT_NO_FATAL_FAILURE(s.GenerateVerifyReport(pst, kUnused)); - const time_t kDotIntervalInSeconds = 5; - const time_t kIdleInSeconds = 20; - const time_t kPlaybackLoopInSeconds = 2 * 60; + const int64_t kDotIntervalInSeconds = 5; + const int64_t kIdleInSeconds = 20; + const int64_t kPlaybackLoopInSeconds = 2 * 60; cout << "This test verifies the elapsed time reported in the usage table " "for a 2 minute simulated playback." @@ -6282,9 +6012,9 @@ TEST_F(OEMCryptoUsageTableTest, VerifyUsageTimes) { ASSERT_NO_FATAL_FAILURE(s.GenerateVerifyReport(pst, kUnused, load_time)); cout << "Start simulated playback..." << endl; - time_t dot_time = kDotIntervalInSeconds; - time_t playback_time = 0; - time_t start_time = time(nullptr); + int64_t dot_time = kDotIntervalInSeconds; + int64_t playback_time = 0; + const int64_t start_time = wvcdm::Clock().GetCurrentTime(); do { ASSERT_NO_FATAL_FAILURE(s.TestDecryptCTR()); ASSERT_NO_FATAL_FAILURE(s.UpdateUsageEntry(&encrypted_usage_header_)); @@ -6292,7 +6022,8 @@ TEST_F(OEMCryptoUsageTableTest, VerifyUsageTimes) { load_time, start_time, 0)); // last decrypt = now. - playback_time = time(nullptr) - start_time; + wvcdm::TestSleep::Sleep(kShortSleep); + playback_time = wvcdm::Clock().GetCurrentTime() - start_time; ASSERT_LE(0, playback_time); if (playback_time >= dot_time) { cout << "."; @@ -6357,7 +6088,7 @@ TEST_F(OEMCryptoUsageTableTest, TimeRollbackPrevention) { ASSERT_NO_FATAL_FAILURE(s1.open()); ASSERT_NO_FATAL_FAILURE(s1.ReloadUsageEntry()); - ASSERT_NO_FATAL_FAILURE(InstallTestSessionKeys(&s1)); + ASSERT_NO_FATAL_FAILURE(InstallTestRSAKey(&s1)); ASSERT_NO_FATAL_FAILURE(s1.LoadTestKeys(pst, new_mac_keys_)); const auto first_decrypt = wall_clock.now(); // Monotonic clock can't be changed. We use this since system clock will be @@ -6368,11 +6099,11 @@ TEST_F(OEMCryptoUsageTableTest, TimeRollbackPrevention) { ASSERT_NO_FATAL_FAILURE(s1.close()); // Imitate playback. - sleep(kLongDuration * 2); + wvcdm::TestSleep::Sleep(kLongDuration * 2); ASSERT_NO_FATAL_FAILURE(s1.open()); ASSERT_NO_FATAL_FAILURE(s1.ReloadUsageEntry()); - ASSERT_NO_FATAL_FAILURE(InstallTestSessionKeys(&s1)); + ASSERT_NO_FATAL_FAILURE(InstallTestRSAKey(&s1)); ASSERT_NO_FATAL_FAILURE(s1.LoadTestKeys(pst, new_mac_keys_)); ASSERT_NO_FATAL_FAILURE(s1.TestDecryptCTR()); ASSERT_NO_FATAL_FAILURE(s1.UpdateUsageEntry(&encrypted_usage_header_)); @@ -6386,7 +6117,7 @@ TEST_F(OEMCryptoUsageTableTest, TimeRollbackPrevention) { // Try to playback again. ASSERT_NO_FATAL_FAILURE(s1.open()); ASSERT_NO_FATAL_FAILURE(s1.ReloadUsageEntry()); - ASSERT_NO_FATAL_FAILURE(InstallTestSessionKeys(&s1)); + ASSERT_NO_FATAL_FAILURE(InstallTestRSAKey(&s1)); ASSERT_NO_FATAL_FAILURE(s1.LoadTestKeys(pst, new_mac_keys_)); const auto third_decrypt_monotonic = monotonic_clock.now(); ASSERT_NO_FATAL_FAILURE(s1.TestDecryptCTR()); @@ -6419,7 +6150,7 @@ TEST_F(OEMCryptoUsageTableTest, PSTLargeBuffer) { ASSERT_NO_FATAL_FAILURE(s.open()); ASSERT_NO_FATAL_FAILURE(s.ReloadUsageEntry()); - ASSERT_NO_FATAL_FAILURE(InstallTestSessionKeys(&s)); + ASSERT_NO_FATAL_FAILURE(InstallTestRSAKey(&s)); ASSERT_NO_FATAL_FAILURE( s.LoadTestKeys(pst, new_mac_keys_)); // Reload the license ASSERT_NO_FATAL_FAILURE(s.TestDecryptCTR()); // Should be able to decrypt. @@ -6433,7 +6164,7 @@ TEST_F(OEMCryptoUsageTableTest, PSTLargeBuffer) { Session s2; ASSERT_NO_FATAL_FAILURE(s2.open()); - ASSERT_NO_FATAL_FAILURE(InstallTestSessionKeys(&s2)); + ASSERT_NO_FATAL_FAILURE(InstallTestRSAKey(&s2)); // Offile license can not be reused if it has been deactivated. ASSERT_NO_FATAL_FAILURE(s2.LoadUsageEntry(s)); EXPECT_NE( @@ -6453,6 +6184,7 @@ TEST_F(OEMCryptoUsageTableTest, PSTLargeBuffer) { EXPECT_EQ(kInactiveUsed, s3.pst_report().status()); } -INSTANTIATE_TEST_CASE_P(TestUsageTables, OEMCryptoUsageTableTestWithMAC, +INSTANTIATE_TEST_CASE_P(OEMCryptoTestUsageTables, + OEMCryptoUsageTableTestWithMAC, Values(true, false)); // With and without new_mac_keys. } // namespace wvoec diff --git a/oemcrypto/test/oemcrypto_test_android.cpp b/oemcrypto/test/oemcrypto_test_android.cpp index 1a4de9b..1d05553 100644 --- a/oemcrypto/test/oemcrypto_test_android.cpp +++ b/oemcrypto/test/oemcrypto_test_android.cpp @@ -60,23 +60,9 @@ TEST_F(OEMCryptoAndroidLMPTest, ValidKeyboxTest) { } TEST_F(OEMCryptoAndroidLMPTest, RewrapDeviceRSAKeyImplemented) { - if (OEMCrypto_Keybox == OEMCrypto_GetProvisioningMethod()) { - ASSERT_NE( - OEMCrypto_ERROR_NOT_IMPLEMENTED, - OEMCrypto_RewrapDeviceRSAKey(0, nullptr, 0, nullptr, 0, nullptr, - nullptr, 0, nullptr, nullptr, nullptr)); - } else { - ASSERT_NE(OEMCrypto_ERROR_NOT_IMPLEMENTED, - OEMCrypto_RewrapDeviceRSAKey30(0, nullptr, nullptr, 0, nullptr, 0, - nullptr, nullptr, nullptr)); - } -} - -// This verifies that the device can load a DRM Certificate. -TEST_F(OEMCryptoAndroidLMPTest, RSASignatureImplemented) { ASSERT_NE(OEMCrypto_ERROR_NOT_IMPLEMENTED, - OEMCrypto_GenerateRSASignature(0, nullptr, 0, nullptr, nullptr, - kSign_RSASSA_PSS)); + OEMCrypto_LoadProvisioning(0, nullptr, 0, 0, nullptr, 0, nullptr, + nullptr)); } // The Generic Crypto API functions are required for Android. diff --git a/oemcrypto/test/oemcrypto_test_main.cpp b/oemcrypto/test/oemcrypto_test_main.cpp index 2b5f40c..0f40112 100644 --- a/oemcrypto/test/oemcrypto_test_main.cpp +++ b/oemcrypto/test/oemcrypto_test_main.cpp @@ -4,6 +4,7 @@ #include "OEMCryptoCENC.h" #include "log.h" #include "oec_device_features.h" +#include "test_sleep.h" static void acknowledge_cast() { std::cout @@ -36,6 +37,9 @@ int main(int argc, char** argv) { if (arg == "--no_filter") { filter_tests = false; } + if (arg == "--fake_sleep") { + wvcdm::TestSleep::set_real_sleep(false); + } } wvcdm::g_cutoff = static_cast(verbosity); wvoec::global_features.Initialize(is_cast_receiver, force_load_test_keybox); diff --git a/oemcrypto/test/oemcrypto_unittests.gypi b/oemcrypto/test/oemcrypto_unittests.gypi index 8e28e16..077c5a5 100644 --- a/oemcrypto/test/oemcrypto_unittests.gypi +++ b/oemcrypto/test/oemcrypto_unittests.gypi @@ -15,9 +15,12 @@ ], 'include_dirs': [ '<(util_dir)/include', + '<(util_dir)/test', '<(oemcrypto_dir)/include', '<(oemcrypto_dir)/ref/src', '<(oemcrypto_dir)/test', + '<(oemcrypto_dir)/odk/include', + '<(oemcrypto_dir)/util/include', ], 'defines': [ 'OEMCRYPTO_TESTS', diff --git a/util/test/base64_test.cpp b/util/test/base64_test.cpp new file mode 100644 index 0000000..dd25a20 --- /dev/null +++ b/util/test/base64_test.cpp @@ -0,0 +1,124 @@ +// Copyright 2018 Google LLC. All Rights Reserved. This file and proprietary +// source code may only be used and distributed under the Widevine Master +// License Agreement. + +#include + +#include + +#include "log.h" +#include "string_conversions.h" + +namespace wvcdm { + +namespace { + +// Test vectors as suggested by http://tools.ietf.org/html/rfc4648#section-10 +const std::string kNullString(""); +const std::string kf("f"); +const std::string kfo("fo"); +const std::string kfoo("foo"); +const std::string kfoob("foob"); +const std::string kfooba("fooba"); +const std::string kfoobar("foobar"); +const std::string kfB64("Zg=="); +const std::string kfoB64("Zm8="); +const std::string kfooB64("Zm9v"); +const std::string kfoobB64("Zm9vYg=="); +const std::string kfoobaB64("Zm9vYmE="); +const std::string kfoobarB64("Zm9vYmFy"); + +// Arbitrary clear test vectors +const std::string kMultipleOf24BitsData("Good day!"); +const std::string kOneByteOverData("Hello Friend!"); +const std::string kTwoBytesOverData("Hello Friend!!"); +const std::string kTestData = + "\030\361\\\366\267> \331\210\360\\-\311:\324\256\376" + "\261\234\241\326d\326\177\346\346\223\333Y\305\214\330"; + +// Arbitrary encoded test vectors +const std::string kMultipleOf24BitsB64Data("R29vZCBkYXkh"); +const std::string kOneByteOverB64Data("SGVsbG8gRnJpZW5kIQ=="); +const std::string kTwoBytesOverB64Data("SGVsbG8gRnJpZW5kISE="); +const std::string kB64TestData = "GPFc9rc+INmI8FwtyTrUrv6xnKHWZNZ/5uaT21nFjNg="; + +const std::pair kBase64TestVectors[] = { + make_pair(&kNullString, &kNullString), + make_pair(&kf, &kfB64), + make_pair(&kfo, &kfoB64), + make_pair(&kfoo, &kfooB64), + make_pair(&kfoob, &kfoobB64), + make_pair(&kfooba, &kfoobaB64), + make_pair(&kfoobar, &kfoobarB64), + make_pair(&kMultipleOf24BitsData, &kMultipleOf24BitsB64Data), + make_pair(&kOneByteOverData, &kOneByteOverB64Data), + make_pair(&kTwoBytesOverData, &kTwoBytesOverB64Data), + make_pair(&kTestData, &kB64TestData)}; + +const std::string kBase64ErrorVectors[] = {"Foo$sa", "Foo\x99\x23\xfa\02", + "Foo==Foo", "FooBa"}; + +std::string ConvertToBase64WebSafe(const std::string& std_base64_string) { + std::string str(std_base64_string); + for (size_t i = 0; i < str.size(); ++i) { + if (str[i] == '+') + str[i] = '-'; + else if (str[i] == '/') + str[i] = '_'; + } + return str; +} + +} // namespace + +class Base64EncodeDecodeTest + : public ::testing::TestWithParam< + std::pair > {}; + +TEST_P(Base64EncodeDecodeTest, EncodeDecodeTest) { + std::pair values = GetParam(); + std::vector decoded_vector = Base64Decode(values.second->data()); + std::string decoded_string(decoded_vector.begin(), decoded_vector.end()); + EXPECT_STREQ(values.first->data(), decoded_string.data()); + std::string b64_string = Base64Encode(decoded_vector); + EXPECT_STREQ(values.second->data(), b64_string.data()); +} + +TEST_P(Base64EncodeDecodeTest, WebSafeEncodeDecodeTest) { + std::pair values = GetParam(); + std::string encoded_string = ConvertToBase64WebSafe(*(values.second)); + std::vector decoded_vector = Base64SafeDecode(encoded_string); + std::string decoded_string(decoded_vector.begin(), decoded_vector.end()); + EXPECT_STREQ(values.first->data(), decoded_string.data()); + std::string b64_string = Base64SafeEncode(decoded_vector); + EXPECT_STREQ(encoded_string.data(), b64_string.data()); +} + +class Base64ErrorDecodeTest : public ::testing::TestWithParam {}; + +TEST_P(Base64ErrorDecodeTest, EncoderErrors) { + std::vector result = Base64Decode(GetParam()); + EXPECT_EQ(0u, result.size()); +} + +INSTANTIATE_TEST_CASE_P(ExecutesBase64Test, Base64EncodeDecodeTest, + ::testing::ValuesIn(kBase64TestVectors)); + +INSTANTIATE_TEST_CASE_P(ExecutesBase64Test, Base64ErrorDecodeTest, + ::testing::ValuesIn(kBase64ErrorVectors)); + +class HtoNLL64Test : public ::testing::Test {}; + +TEST_F(HtoNLL64Test, PositiveNumber) { + uint8_t data[8] = {1, 2, 3, 4, 5, 6, 7, 8}; + int64_t* network_byte_order = reinterpret_cast(data); + int64_t host_byte_order = htonll64(*network_byte_order); + EXPECT_EQ(0x0102030405060708, host_byte_order); +} +TEST_F(HtoNLL64Test, NegativeNumber) { + uint8_t data[8] = {0xfe, 2, 3, 4, 5, 6, 7, 8}; + int64_t* network_byte_order = reinterpret_cast(data); + int64_t host_byte_order = htonll64(*network_byte_order); + EXPECT_EQ(-0x01FdFcFbFaF9F8F8, host_byte_order); +} +} // namespace wvcdm diff --git a/util/test/file_store_unittest.cpp b/util/test/file_store_unittest.cpp new file mode 100644 index 0000000..5140b88 --- /dev/null +++ b/util/test/file_store_unittest.cpp @@ -0,0 +1,178 @@ +// Copyright 2018 Google LLC. All Rights Reserved. This file and proprietary +// source code may only be used and distributed under the Widevine Master +// License Agreement. + +#include +#include + +#include "cdm_random.h" +#include "file_store.h" +#include "test_vectors.h" + +namespace wvcdm { + +namespace { +const std::string kTestDirName = "test_dir"; +const std::string kTestFileName = "test.txt"; +const std::string kTestFileName2 = "test2.txt"; +const std::string kTestFileName3 = "test3.other"; +const std::string kTestFileNameExt = ".txt"; +const std::string kTestFileNameExt3 = ".other"; +const std::string kWildcard = "*"; +} // namespace + +class FileTest : public testing::Test { + protected: + FileTest() {} + + void TearDown() override { RemoveTestDir(); } + + void RemoveTestDir() { + EXPECT_TRUE(file_system.Remove(test_vectors::kTestDir)); + } + + FileSystem file_system; +}; + +TEST_F(FileTest, FileExists) { + EXPECT_TRUE(file_system.Exists(test_vectors::kExistentFile)); + EXPECT_TRUE(file_system.Exists(test_vectors::kExistentDir)); + EXPECT_FALSE(file_system.Exists(test_vectors::kNonExistentFile)); + EXPECT_FALSE(file_system.Exists(test_vectors::kNonExistentDir)); +} + +TEST_F(FileTest, RemoveDir) { + EXPECT_TRUE(file_system.Remove(test_vectors::kTestDir)); + EXPECT_FALSE(file_system.Exists(test_vectors::kTestDir)); +} + +TEST_F(FileTest, OpenFile) { + std::string path = test_vectors::kTestDir + kTestFileName; + EXPECT_TRUE(file_system.Remove(path)); + + std::unique_ptr file = file_system.Open(path, FileSystem::kCreate); + ASSERT_TRUE(file); + + EXPECT_TRUE(file_system.Exists(path)); +} + +TEST_F(FileTest, RemoveDirAndFile) { + std::string path = test_vectors::kTestDir + kTestFileName; + + std::unique_ptr file = file_system.Open(path, FileSystem::kCreate); + ASSERT_TRUE(file); + + EXPECT_TRUE(file_system.Exists(path)); + EXPECT_TRUE(file_system.Remove(path)); + EXPECT_FALSE(file_system.Exists(path)); + + file = file_system.Open(path, FileSystem::kCreate); + ASSERT_TRUE(file); + + EXPECT_TRUE(file_system.Exists(path)); + RemoveTestDir(); + EXPECT_FALSE(file_system.Exists(test_vectors::kTestDir)); + EXPECT_FALSE(file_system.Exists(path)); +} + +TEST_F(FileTest, RemoveWildcardFiles) { + std::string path1 = test_vectors::kTestDir + kTestFileName; + std::string path2 = test_vectors::kTestDir + kTestFileName2; + std::string wildcard_path = + test_vectors::kTestDir + kWildcard + kTestFileNameExt; + + std::unique_ptr file = file_system.Open(path1, FileSystem::kCreate); + ASSERT_TRUE(file); + file = file_system.Open(path2, FileSystem::kCreate); + ASSERT_TRUE(file); + + EXPECT_TRUE(file_system.Exists(path1)); + EXPECT_TRUE(file_system.Exists(path2)); + EXPECT_TRUE(file_system.Remove(wildcard_path)); + EXPECT_FALSE(file_system.Exists(path1)); + EXPECT_FALSE(file_system.Exists(path2)); +} + +TEST_F(FileTest, FileSize) { + std::string path = test_vectors::kTestDir + kTestFileName; + file_system.Remove(path); + + std::string write_data = CdmRandom::RandomData(600); + size_t write_data_size = write_data.size(); + std::unique_ptr file = file_system.Open(path, FileSystem::kCreate); + ASSERT_TRUE(file); + EXPECT_EQ(file->Write(write_data.data(), write_data_size), write_data_size); + EXPECT_TRUE(file_system.Exists(path)); + + EXPECT_EQ(static_cast(write_data_size), file_system.FileSize(path)); +} + +TEST_F(FileTest, WriteReadBinaryFile) { + std::string path = test_vectors::kTestDir + kTestFileName; + file_system.Remove(path); + + std::string write_data = CdmRandom::RandomData(600); + size_t write_data_size = write_data.size(); + std::unique_ptr file = file_system.Open(path, FileSystem::kCreate); + ASSERT_TRUE(file); + EXPECT_EQ(file->Write(write_data.data(), write_data_size), write_data_size); + EXPECT_TRUE(file_system.Exists(path)); + + std::string read_data; + read_data.resize(file_system.FileSize(path)); + size_t read_data_size = read_data.size(); + file = file_system.Open(path, FileSystem::kReadOnly); + ASSERT_TRUE(file); + EXPECT_EQ(file->Read(&read_data[0], read_data_size), read_data_size); + EXPECT_EQ(write_data, read_data); +} + +TEST_F(FileTest, ListFiles) { + std::vector names; + + std::string not_path("zzz"); + std::string path1 = test_vectors::kTestDir + kTestFileName; + std::string path2 = test_vectors::kTestDir + kTestFileName2; + std::string path3 = test_vectors::kTestDir + kTestFileName3; + std::string path_dir = test_vectors::kTestDir; + + std::unique_ptr file = file_system.Open(path1, FileSystem::kCreate); + ASSERT_TRUE(file); + file = file_system.Open(path2, FileSystem::kCreate); + ASSERT_TRUE(file); + file = file_system.Open(path3, FileSystem::kCreate); + ASSERT_TRUE(file); + + EXPECT_TRUE(file_system.Exists(path1)); + EXPECT_TRUE(file_system.Exists(path2)); + EXPECT_TRUE(file_system.Exists(path3)); + + // Ask for non-existent path. + EXPECT_FALSE(file_system.List(not_path, &names)); + + // Valid path, but no way to return names. + EXPECT_FALSE(file_system.List(path_dir, nullptr)); + + // Valid path, valid return. + EXPECT_TRUE(file_system.List(path_dir, &names)); + + // Should find three files. Order not important. + EXPECT_EQ(3u, names.size()); + EXPECT_THAT(names, ::testing::UnorderedElementsAre( + kTestFileName, kTestFileName2, kTestFileName3)); + + std::string wild_card_path = path_dir + kWildcard + kTestFileNameExt; + EXPECT_TRUE(file_system.Remove(wild_card_path)); + EXPECT_TRUE(file_system.List(path_dir, &names)); + + EXPECT_EQ(1u, names.size()); + EXPECT_TRUE(names[0].compare(kTestFileName3) == 0); + + std::string wild_card_path2 = path_dir + kWildcard + kTestFileNameExt3; + EXPECT_TRUE(file_system.Remove(wild_card_path2)); + EXPECT_TRUE(file_system.List(path_dir, &names)); + + EXPECT_EQ(0u, names.size()); +} + +} // namespace wvcdm diff --git a/util/test/test_clock.cpp b/util/test/test_clock.cpp new file mode 100644 index 0000000..86e9017 --- /dev/null +++ b/util/test/test_clock.cpp @@ -0,0 +1,40 @@ +// Copyright 2013 Google Inc. All Rights Reserved. +// +// Clock - A fake clock just for running tests. + +#include "clock.h" + +#include + +#include "test_sleep.h" + +namespace wvcdm { + +namespace { +// A fake clock that only advances when TestSleep::Sleep is called. +class FakeClock : public wvcdm::TestSleep::CallBack { + public: + FakeClock() { + auto now = std::chrono::steady_clock().now(); + now_ = now.time_since_epoch() / std::chrono::milliseconds(1); + TestSleep::set_callback(this); + } + ~FakeClock() { TestSleep::set_callback(nullptr); } + void ElapseTime(int64_t milliseconds) { now_ += milliseconds; } + + int64_t now() const { return now_; } + + private: + int64_t now_; +}; + +FakeClock* g_fake_clock = nullptr; +} // namespace + +// On devices running a fake OEMCrypto, we can use a fake sleep and fake time. +int64_t Clock::GetCurrentTime() { + if (g_fake_clock == nullptr) g_fake_clock = new FakeClock(); + return g_fake_clock->now() / 1000; +} + +} // namespace wvcdm diff --git a/util/test/test_sleep.cpp b/util/test/test_sleep.cpp new file mode 100644 index 0000000..2140c9e --- /dev/null +++ b/util/test/test_sleep.cpp @@ -0,0 +1,44 @@ +// Copyright 2019 Google LLC. All Rights Reserved. This file and proprietary +// source code may only be used and distributed under the Widevine Master +// License Agreement. + +#include "test_sleep.h" + +#include + +#include + +#include "clock.h" + +namespace wvcdm { + +bool TestSleep::real_sleep_ = true; +TestSleep::CallBack* TestSleep::callback_ = nullptr; + +void TestSleep::Sleep(unsigned int seconds) { + int64_t milliseconds = 1000 * seconds; + if (real_sleep_) { + // This next bit of logic is to avoid slow drift apart of the real clock and + // the fake clock. We compute how far from the real clock has advanced in + // total since the start, and then compare to a running total of sleep + // calls. We sleep for approximately x second, and then advance the clock by + // the amount of time that has actually passed. + static auto start_real = std::chrono::steady_clock().now(); + static int64_t fake_clock = 0; + sleep(seconds); + auto now_real = std::chrono::steady_clock().now(); + int64_t total_real = (now_real - start_real) / std::chrono::milliseconds(1); + // We want to advance the fake clock by the difference between the real + // clock, and the previous value on the fake clock. + milliseconds = total_real - fake_clock; + fake_clock += milliseconds; + } + if (callback_ != nullptr) callback_->ElapseTime(milliseconds); +} + +void TestSleep::SyncFakeClock() { + // Syncing can be done by sleeping 0 seconds. + Sleep(0); +} + +} // namespace wvcdm diff --git a/util/test/test_sleep.h b/util/test/test_sleep.h new file mode 100644 index 0000000..4f2dcf5 --- /dev/null +++ b/util/test/test_sleep.h @@ -0,0 +1,49 @@ +// Copyright 2018 Google LLC. All Rights Reserved. This file and proprietary +// source code may only be used and distributed under the Widevine Master +// License Agreement. +// +// TestSleep - Controls sleep and clock adjustment during tests. +// +#ifndef WVCDM_UTIL_TEST_SLEEP_H_ +#define WVCDM_UTIL_TEST_SLEEP_H_ + +#include + +namespace wvcdm { + +class TestSleep { + public: + // The callback is called when the clock should be advanced. + class CallBack { + public: + virtual void ElapseTime(int64_t milliseconds) = 0; + + protected: + virtual ~CallBack(){}; + }; + + // If real_sleep_ is true, then this sleeps for |seconds| of time. + // If the callback exists, this calls the callback. + static void Sleep(unsigned int seconds); + + // If we are using a real clock and a fake clock, then the real clock advances + // a little while we are doing work, but the fake one only advances when we + // sleep. This function advances the fake clock to be in sync with the real + // clock. This function should be called to prevent a slow flaky test from + // failing due to this drift. + static void SyncFakeClock(); + + static void set_real_sleep(bool real_sleep) { real_sleep_ = real_sleep; } + + static void set_callback(CallBack* callback) { callback_ = callback; } + + private: + // Controls if the test sleep should use real sleep. + static bool real_sleep_; + // Called when the clock should advance. + static CallBack* callback_; +}; + +} // namespace wvcdm + +#endif // WVCDM_UTIL_TEST_SLEEP_H_