diff --git a/docs/WidevineModularDRMSecurityIntegrationGuideforCENC_v13.pdf b/docs/WidevineModularDRMSecurityIntegrationGuideforCENC_v13.pdf new file mode 100644 index 0000000..2b73490 Binary files /dev/null and b/docs/WidevineModularDRMSecurityIntegrationGuideforCENC_v13.pdf differ diff --git a/docs/WidevineModularDRMSecurityIntegrationGuideforCENC_v14.pdf b/docs/WidevineModularDRMSecurityIntegrationGuideforCENC_v14.pdf new file mode 100644 index 0000000..543b4b1 Binary files /dev/null and b/docs/WidevineModularDRMSecurityIntegrationGuideforCENC_v14.pdf differ diff --git a/docs/WidevineSecurityIntegrationGuideforCENCAndroidSupplement_v13.pdf b/docs/WidevineSecurityIntegrationGuideforCENCAndroidSupplement_v13.pdf new file mode 100644 index 0000000..02c718f Binary files /dev/null and b/docs/WidevineSecurityIntegrationGuideforCENCAndroidSupplement_v13.pdf differ diff --git a/docs/Widevine_Level1_Provisioning_Models.pdf b/docs/Widevine_Level1_Provisioning_Models.pdf new file mode 100644 index 0000000..637c338 Binary files /dev/null and b/docs/Widevine_Level1_Provisioning_Models.pdf differ diff --git a/docs/Widevine_Modular_DRM_Version_10_Delta.pdf b/docs/Widevine_Modular_DRM_Version_10_Delta.pdf new file mode 100644 index 0000000..908e8bf Binary files /dev/null and b/docs/Widevine_Modular_DRM_Version_10_Delta.pdf differ diff --git a/docs/Widevine_Modular_DRM_Version_11_Delta.pdf b/docs/Widevine_Modular_DRM_Version_11_Delta.pdf new file mode 100644 index 0000000..fbed7b0 Binary files /dev/null and b/docs/Widevine_Modular_DRM_Version_11_Delta.pdf differ diff --git a/docs/Widevine_Modular_DRM_Version_12_Delta.pdf b/docs/Widevine_Modular_DRM_Version_12_Delta.pdf new file mode 100644 index 0000000..7568643 Binary files /dev/null and b/docs/Widevine_Modular_DRM_Version_12_Delta.pdf differ diff --git a/docs/Widevine_Modular_DRM_Version_13_Delta.pdf b/docs/Widevine_Modular_DRM_Version_13_Delta.pdf new file mode 100644 index 0000000..941ae2b Binary files /dev/null and b/docs/Widevine_Modular_DRM_Version_13_Delta.pdf differ diff --git a/docs/Widevine_Modular_DRM_Version_14_Delta.pdf b/docs/Widevine_Modular_DRM_Version_14_Delta.pdf index edbc2ba..82151ee 100644 Binary files a/docs/Widevine_Modular_DRM_Version_14_Delta.pdf and b/docs/Widevine_Modular_DRM_Version_14_Delta.pdf differ diff --git a/include/OEMCryptoCENC.h b/include/OEMCryptoCENC.h new file mode 100644 index 0000000..96d1bf3 --- /dev/null +++ b/include/OEMCryptoCENC.h @@ -0,0 +1,3462 @@ +// Copyright 2013 Google Inc. All Rights Reserved. + +/********************************************************************* + * OEMCryptoCENC.h + * + * Reference APIs needed to support Widevine's crypto algorithms. + * + * See the document "WV Modular DRM Security Integration Guide for Common + * Encryption (CENC) -- version 14" for a description of this API. You + * can find this document in the widevine repository as + * docs/WidevineModularDRMSecurityIntegrationGuideforCENC_v14.pdf + * Changes between different versions of this API are documented in the files + * docs/Widevine_Modular_DRM_Version_*_Delta.pdf + * + *********************************************************************/ + +#ifndef OEMCRYPTO_CENC_H_ +#define OEMCRYPTO_CENC_H_ + +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +typedef uint32_t OEMCrypto_SESSION; + +typedef enum OEMCryptoResult { + OEMCrypto_SUCCESS = 0, + OEMCrypto_ERROR_INIT_FAILED = 1, + OEMCrypto_ERROR_TERMINATE_FAILED = 2, + OEMCrypto_ERROR_OPEN_FAILURE = 3, + OEMCrypto_ERROR_CLOSE_FAILURE = 4, + OEMCrypto_ERROR_ENTER_SECURE_PLAYBACK_FAILED = 5, // deprecated + OEMCrypto_ERROR_EXIT_SECURE_PLAYBACK_FAILED = 6, // deprecated + OEMCrypto_ERROR_SHORT_BUFFER = 7, + OEMCrypto_ERROR_NO_DEVICE_KEY = 8, // no keybox device key. + OEMCrypto_ERROR_NO_ASSET_KEY = 9, + OEMCrypto_ERROR_KEYBOX_INVALID = 10, + OEMCrypto_ERROR_NO_KEYDATA = 11, + OEMCrypto_ERROR_NO_CW = 12, + OEMCrypto_ERROR_DECRYPT_FAILED = 13, + OEMCrypto_ERROR_WRITE_KEYBOX = 14, + OEMCrypto_ERROR_WRAP_KEYBOX = 15, + OEMCrypto_ERROR_BAD_MAGIC = 16, + OEMCrypto_ERROR_BAD_CRC = 17, + OEMCrypto_ERROR_NO_DEVICEID = 18, + OEMCrypto_ERROR_RNG_FAILED = 19, + OEMCrypto_ERROR_RNG_NOT_SUPPORTED = 20, + OEMCrypto_ERROR_SETUP = 21, + OEMCrypto_ERROR_OPEN_SESSION_FAILED = 22, + OEMCrypto_ERROR_CLOSE_SESSION_FAILED = 23, + OEMCrypto_ERROR_INVALID_SESSION = 24, + OEMCrypto_ERROR_NOT_IMPLEMENTED = 25, + OEMCrypto_ERROR_NO_CONTENT_KEY = 26, + OEMCrypto_ERROR_CONTROL_INVALID = 27, + OEMCrypto_ERROR_UNKNOWN_FAILURE = 28, + OEMCrypto_ERROR_INVALID_CONTEXT = 29, + OEMCrypto_ERROR_SIGNATURE_FAILURE = 30, + OEMCrypto_ERROR_TOO_MANY_SESSIONS = 31, + OEMCrypto_ERROR_INVALID_NONCE = 32, + OEMCrypto_ERROR_TOO_MANY_KEYS = 33, + OEMCrypto_ERROR_DEVICE_NOT_RSA_PROVISIONED = 34, + OEMCrypto_ERROR_INVALID_RSA_KEY = 35, + OEMCrypto_ERROR_KEY_EXPIRED = 36, + OEMCrypto_ERROR_INSUFFICIENT_RESOURCES = 37, + OEMCrypto_ERROR_INSUFFICIENT_HDCP = 38, + OEMCrypto_ERROR_BUFFER_TOO_LARGE = 39, + OEMCrypto_WARNING_GENERATION_SKEW = 40, // Warning, not an error. + OEMCrypto_ERROR_GENERATION_SKEW = 41, + OEMCrypto_LOCAL_DISPLAY_ONLY = 42, // Info, not an error. + OEMCrypto_ERROR_ANALOG_OUTPUT = 43, + OEMCrypto_ERROR_WRONG_PST = 44, + OEMCrypto_ERROR_WRONG_KEYS = 45, + OEMCrypto_ERROR_MISSING_MASTER = 46, + OEMCrypto_ERROR_LICENSE_INACTIVE = 47, + OEMCrypto_ERROR_ENTRY_NEEDS_UPDATE = 48, + OEMCrypto_ERROR_ENTRY_IN_USE = 49, + OEMCrypto_ERROR_USAGE_TABLE_UNRECOVERABLE = 50, // Reserved. Do not use. + OEMCrypto_KEY_NOT_LOADED = 51, + OEMCrypto_KEY_NOT_ENTITLED = 52, +} OEMCryptoResult; + +/* + * OEMCrypto_DestBufferDesc + * Describes the type and access information for the memory to receive + * decrypted data. + * + * 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. + * + * Specific fields are as follows: + * + * (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. + */ +typedef enum OEMCryptoBufferType { + OEMCrypto_BufferType_Clear, + OEMCrypto_BufferType_Secure, + OEMCrypto_BufferType_Direct +} OEMCryptoBufferType; + +typedef struct { + OEMCryptoBufferType type; + union { + struct { // type == OEMCrypto_BufferType_Clear + uint8_t* address; + size_t max_length; + } clear; + struct { // type == OEMCrypto_BufferType_Secure + void* handle; + size_t max_length; + size_t offset; + } secure; + struct { // type == OEMCrypto_BufferType_Direct + bool is_video; + } direct; + } buffer; +} OEMCrypto_DestBufferDesc; + +/** OEMCryptoCipherMode is used in SelectKey to prepare a key for either CTR + * decryption or CBC decryption. + */ +typedef enum OEMCryptoCipherMode { + OEMCrypto_CipherMode_CTR, + OEMCrypto_CipherMode_CBC, +} OEMCryptoCipherMode; + +/** OEMCrypto_LicenseType is used in LoadKeys to indicate if the key objects + * are for content keys, or for entitlement keys. + */ +typedef enum OEMCrypto_LicenseType { + OEMCrypto_ContentLicense = 0, + OEMCrypto_EntitlementLicense = 1 +} OEMCrypto_LicenseType; + +/* + * OEMCrypto_KeyObject + * Points to the relevant fields for a content key. The fields are extracted + * from the License Response message offered to OEMCrypto_LoadKeys(). Each + * field points to one of the components of the key. Key data, key control, + * and both IV fields are 128 bits (16 bytes): + * key_id - the unique id of this key. + * key_id_length - the size of key_id. OEMCrypto may assume this is at + * most 16. However, OEMCrypto shall correctly handle key id lengths + * from 1 to 16 bytes. + * key_data_iv - the IV for performing AES-128-CBC decryption of the + * key_data field. + * key_data - the key data. It is encrypted (AES-128-CBC) with the + * session's derived encrypt key and the key_data_iv. + * key_control_iv - the IV for performing AES-128-CBC decryption of the + * key_control field. + * key_control - the key control block. It is encrypted (AES-128-CBC) with + * the content key from the key_data field. + * + * The memory for the OEMCrypto_KeyObject fields is allocated and freed + * by the caller of OEMCrypto_LoadKeys(). + */ +typedef struct { + const uint8_t* key_id; + size_t key_id_length; + const uint8_t* key_data_iv; + const uint8_t* key_data; + size_t key_data_length; + const uint8_t* key_control_iv; + const uint8_t* key_control; +} OEMCrypto_KeyObject; + +/* + * OEMCrypto_EntitledContentKeyObject + * Contains encrypted content key data for loading into the sessions keytable. + * The content key data is encrypted using AES-256-CBC encryption, with PKCS#7 + * padding. + * entitlement_key_id - entitlement key id to be matched to key table. + * entitlement_key_id_length - length of entitlment_key_id in bytes (1 to 16). + * content_key_id - content key id to be loaded into key table. + * content_key_id_length - length of content key id in bytes (1 to 16). + * key_data_iv - the IV for performing AES-256-CBC decryption of the key data. + * key_data - encrypted content key data. + * key_data_length - length of key_data - 16 or 32 depending on intended use. + */ +typedef struct { + const uint8_t* entitlement_key_id; + size_t entitlement_key_id_length; + const uint8_t* content_key_id; + size_t content_key_id_length; + const uint8_t* content_key_data_iv; + const uint8_t* content_key_data; + size_t content_key_data_length; +} 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 { + const uint8_t* key_id; + size_t key_id_length; + const uint8_t* key_control_iv; + const uint8_t* key_control; +} OEMCrypto_KeyRefreshObject; + +/* + * OEMCrypto_Algorithm + * This is a list of valid algorithms for OEMCrypto_Generic_* functions. + * Some are valid for encryption/decryption, and some for signing/verifying. + */ +typedef enum OEMCrypto_Algorithm { + OEMCrypto_AES_CBC_128_NO_PADDING = 0, + 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. + */ +typedef enum OEMCrypto_Usage_Entry_Status { + kUnused = 0, + kActive = 1, + kInactive = 2, // Deprecated. Used kInactiveUsed or kInactiveUnused. + kInactiveUsed = 3, + kInactiveUnused = 4, +} OEMCrypto_Usage_Entry_Status; + +/* + * OEMCrypto_PST_Report is used to report an entry from the Usage Table. + * + * Platforms that have compilers that support packed structures, may use the + * following definition. Other platforms may use the header pst_report.h which + * defines a wrapper class. + */ +#if 0 // If your compiler supports __attribute__((packed)). +typedef struct { + uint8_t signature[20]; // -- HMAC SHA1 of the rest of the report. + uint8_t status; // current status of entry. (OEMCrypto_Usage_Entry_Status) + uint8_t clock_security_level; + uint8_t pst_length; + uint8_t padding; // make int64's word aligned. + int64_t seconds_since_license_received; // now - time_of_license_received + int64_t seconds_since_first_decrypt; // now - time_of_first_decrypt + int64_t seconds_since_last_decrypt; // now - time_of_last_decrypt + uint8_t pst[]; +} __attribute__((packed)) OEMCrypto_PST_Report; +#endif + +/* + * OEMCrypto_Clock_Security_Level. + * Valid values for clock_security_level in OEMCrypto_PST_Report. + */ +typedef enum OEMCrypto_Clock_Security_Level { + kInsecureClock = 0, + kSecureTimer = 1, + kSecureClock = 2, + kHardwareSecureClock = 3 +} OEMCrypto_Clock_Security_Level; + +typedef enum RSA_Padding_Scheme { + kSign_RSASSA_PSS = 0x1, // RSASSA-PSS with SHA1. + kSign_PKCS1_Block1 = 0x2, // PKCS1 with block type 1 padding (only). +} RSA_Padding_Scheme; + +/* + * OEMCrypto_HDCP_Capability is used in the key control block to enforce HDCP + * level, and in GetHDCPCapability for reporting. + */ +typedef enum OEMCrypto_HDCP_Capability { + HDCP_NONE = 0, // No HDCP supported, no secure data path. + HDCP_V1 = 1, // HDCP version 1.0 + HDCP_V2 = 2, // HDCP version 2.0 Type 1. + HDCP_V2_1 = 3, // HDCP version 2.1 Type 1. + HDCP_V2_2 = 4, // HDCP version 2.2 Type 1. + HDCP_NO_DIGITAL_OUTPUT = 0xff // No digital output. +} OEMCrypto_HDCP_Capability; + +/* Return value for OEMCrypto_GetProvisioningMethod(). */ +typedef enum OEMCrypto_ProvisioningMethod { + OEMCrypto_ProvisioningError = 0, // Device cannot be provisioned. + OEMCrypto_DrmCertificate = 1, // Device has baked in DRM certificate + // (level 3 only) + OEMCrypto_Keybox = 2, // Device has factory installed unique keybox. + OEMCrypto_OEMCertificate = 3 // Device has factory installed OEM certificate. +} OEMCrypto_ProvisioningMethod; + +/* + * Flags indicating RSA keys supported. + */ +#define OEMCrypto_Supports_RSA_2048bit 0x1 +#define OEMCrypto_Supports_RSA_3072bit 0x2 +#define OEMCrypto_Supports_RSA_CAST 0x10 + +/* + * Flags indicating full decrypt path hash supported. + */ +#define OEMCrypto_Hash_Not_Supported 0 +#define OEMCrypto_HMAC_Clear_Buffer 1 + +/* + * Return values from OEMCrypto_GetAnalogOutputFlags. + */ +#define OEMCrypto_No_Analog_Output 0x0 +#define OEMCrypto_Supports_Analog_Output 0x1 +#define OEMCrypto_Can_Disable_Analog_Ouptput 0x2 +#define OEMCrypto_Supports_CGMS_A 0x4 +// Unknown_Analog_Output is used only for backwards compatibility. +#define OEMCrypto_Unknown_Analog_Output (1<<31) + +/* + * Obfuscation Renames. + */ +#define OEMCrypto_Initialize _oecc01 +#define OEMCrypto_Terminate _oecc02 +#define OEMCrypto_InstallKeybox _oecc03 +#define OEMCrypto_GetKeyData _oecc04 +#define OEMCrypto_IsKeyboxValid _oecc05 +#define OEMCrypto_GetRandom _oecc06 +#define OEMCrypto_GetDeviceID _oecc07 +#define OEMCrypto_WrapKeybox _oecc08 +#define OEMCrypto_OpenSession _oecc09 +#define OEMCrypto_CloseSession _oecc10 +#define OEMCrypto_DecryptCTR_V10 _oecc11 +#define OEMCrypto_GenerateDerivedKeys _oecc12 +#define OEMCrypto_GenerateSignature _oecc13 +#define OEMCrypto_GenerateNonce _oecc14 +#define OEMCrypto_LoadKeys_V8 _oecc15 +#define OEMCrypto_RefreshKeys _oecc16 +#define OEMCrypto_SelectKey_V13 _oecc17 +#define OEMCrypto_RewrapDeviceRSAKey _oecc18 +#define OEMCrypto_LoadDeviceRSAKey _oecc19 +#define OEMCrypto_GenerateRSASignature_V8 _oecc20 +#define OEMCrypto_DeriveKeysFromSessionKey _oecc21 +#define OEMCrypto_APIVersion _oecc22 +#define OEMCrypto_SecurityLevel _oecc23 +#define OEMCrypto_Generic_Encrypt _oecc24 +#define OEMCrypto_Generic_Decrypt _oecc25 +#define OEMCrypto_Generic_Sign _oecc26 +#define OEMCrypto_Generic_Verify _oecc27 +#define OEMCrypto_GetHDCPCapability_V9 _oecc28 +#define OEMCrypto_SupportsUsageTable _oecc29 +#define OEMCrypto_UpdateUsageTable _oecc30 +#define OEMCrypto_DeactivateUsageEntry_V12 _oecc31 +#define OEMCrypto_ReportUsage _oecc32 +#define OEMCrypto_DeleteUsageEntry _oecc33 +#define OEMCrypto_DeleteOldUsageTable _oecc34 +#define OEMCrypto_LoadKeys_V9_or_V10 _oecc35 +#define OEMCrypto_GenerateRSASignature _oecc36 +#define OEMCrypto_GetMaxNumberOfSessions _oecc37 +#define OEMCrypto_GetNumberOfOpenSessions _oecc38 +#define OEMCrypto_IsAntiRollbackHwPresent _oecc39 +#define OEMCrypto_CopyBuffer _oecc40 +#define OEMCrypto_QueryKeyControl _oecc41 +#define OEMCrypto_LoadTestKeybox_V13 _oecc42 +#define OEMCrypto_ForceDeleteUsageEntry _oecc43 +#define OEMCrypto_GetHDCPCapability _oecc44 +#define OEMCrypto_LoadTestRSAKey _oecc45 +#define OEMCrypto_Security_Patch_Level _oecc46 +#define OEMCrypto_LoadKeys_V11_or_V12 _oecc47 +#define OEMCrypto_DecryptCENC _oecc48 +#define OEMCrypto_GetProvisioningMethod _oecc49 +#define OEMCrypto_GetOEMPublicCertificate _oecc50 +#define OEMCrypto_RewrapDeviceRSAKey30 _oecc51 +#define OEMCrypto_SupportedCertificates _oecc52 +#define OEMCrypto_IsSRMUpdateSupported _oecc53 +#define OEMCrypto_GetCurrentSRMVersion _oecc54 +#define OEMCrypto_LoadSRM _oecc55 +#define OEMCrypto_LoadKeys_v13 _oecc56 +#define OEMCrypto_RemoveSRM _oecc57 +#define OEMCrypto_CreateUsageTableHeader _oecc61 +#define OEMCrypto_LoadUsageTableHeader _oecc62 +#define OEMCrypto_CreateNewUsageEntry _oecc63 +#define OEMCrypto_LoadUsageEntry _oecc64 +#define OEMCrypto_UpdateUsageEntry _oecc65 +#define OEMCrypto_DeactivateUsageEntry _oecc66 +#define OEMCrypto_ShrinkUsageTableHeader _oecc67 +#define OEMCrypto_MoveEntry _oecc68 +#define OEMCrypto_CopyOldUsageEntry _oecc69 +#define OEMCrypto_CreateOldUsageEntry _oecc70 +#define OEMCrypto_GetAnalogOutputFlags _oecc71 +#define OEMCrypto_LoadTestKeybox _oecc78 +#define OEMCrypto_LoadEntitledContentKeys _oecc79 +#define OEMCrypto_SelectKey _oecc81 +#define OEMCrypto_LoadKeys _oecc82 + +/* + * OEMCrypto_Initialize + * + * Description: + * Initialize the crypto firmware/hardware. + * + * Parameters: + * N/A + * + * Threading: + * No other function calls will be made while this function is running. This + * function will not be called again before OEMCrypto_Terminate. + * + * Returns: + * OEMCrypto_SUCCESS success + * OEMCrypto_ERROR_INIT_FAILED failed to initialize crypto hardware + * + * Version: + * This method is supported by all API versions. + */ +OEMCryptoResult OEMCrypto_Initialize(void); + +/* + * OEMCrypto_Terminate + * + * Description: + * Closes the crypto operation and releases all related resources. + * + * Parameters: + * N/A + * + * Threading: + * No other OEMCrypto calls are made while this function is running. After + * this function is called, no other OEMCrypto calls will be made until + * another call to OEMCrypto_Initialize is made. + * + * Returns: + * OEMCrypto_SUCCESS success + * OEMCrypto_ERROR_TERMINATE_FAILED failed to de-initialize crypto hardware + * + * Version: + * This method is supported by all API versions. + */ +OEMCryptoResult OEMCrypto_Terminate(void); + +/* + * OEMCrypto_OpenSession + * + * Description: + * Open a new crypto security engine context. The security engine hardware and + * firmware shall acquire resources that are needed to support the session, + * and return a session handle that identifies that session in future calls. + * + * Parameters: + * session (out) - an opaque handle that the crypto firmware uses to identify + * the session. + * + * Threading: + * No other Open/Close session calls will be made while this function is + * running. Functions on other existing sessions may be called while this + * function is active. + * + * Returns: + * OEMCrypto_SUCCESS success + * OEMCrypto_ERROR_TOO_MANY_SESSIONS failed because too many sessions are open + * OEMCrypto_ERROR_OPEN_SESSION_FAILED failed to initialize the crypto session + * + * Version: + * This method changed in API version 5. + */ +OEMCryptoResult OEMCrypto_OpenSession(OEMCrypto_SESSION* session); + +/* + * OEMCrypto_CloseSession + * + * Description: + * Closes the crypto security engine session and frees any associated + * resources. If this session is associated with a Usage Entry, all resident + * memory associated with it will be freed. It is the CDM layer’s + * responsibility to call OEMCrypto_UpdateUsageEntry before closing the + * session. + * + * Parameters: + * session (in) - handle for the session to be closed. + * + * Threading: + * No other Open/Close session calls will be made while this function is + * running. Functions on other existing sessions may be called while this + * function is active. + * + * Returns: + * OEMCrypto_SUCCESS success + * OEMCrypto_ERROR_INVALID_SESSION no open session with that id. + * OEMCrypto_ERROR_CLOSE_SESSION_FAILED failed to terminate the crypto session + * + * Version: + * This method changed in API version 5. + */ +OEMCryptoResult OEMCrypto_CloseSession(OEMCrypto_SESSION session); + +/* + * OEMCrypto_GenerateDerivedKeys + * + * Description: + * Generates three secondary keys -- mac_key_server, mac_key_client, and + * encrypt_key -- for handling signing and content key decryption under the + * license server protocol for AES CTR mode. + * + * Refer to document "Widevine Modular DRM Security Integration Guide for + * CENC" for details. This function computes the AES-128-CMAC of the + * enc_key_context and stores it in secure memory as the encrypt_key. It + * then computes four cycles of AES-128-CMAC of the mac_key_context and + * stores it in the mac_keys. The first two cycles are used for + * mac_key_server and the second two cycles are used for mac_key_client. + * These three keys will be stored until the next call to LoadKeys. + * + * Parameters: + * session (in) - handle for the session to be used. + * mac_key_context (in) - pointer to memory containing context data for + * computing the HMAC generation key. + * mac_key_context_length (in) - length of the HMAC key context data, in + * bytes. + * enc_key_context (in) - pointer to memory containing context data for + * computing the encryption key. + * enc_key_context_length (in) - length of the encryption key context data, in + * bytes. + * + * + * Results: + * mac_key_server: the 256 bit mac key is generated and stored in secure + * memory. + * mac_key_client: the 256 bit mac key is generated and stored in secure + * memory. + * enc_key: the 128 bit encryption key is generated and stored in secure + * memory. + * + * Threading: + * This function may be called simultaneously with functions on other + * sessions, but not with other functions on this session. + * + * Returns: + * OEMCrypto_SUCCESS success + * OEMCrypto_ERROR_NO_DEVICE_KEY + * OEMCrypto_ERROR_INVALID_SESSION + * OEMCrypto_ERROR_INVALID_CONTEXT + * OEMCrypto_ERROR_INSUFFICIENT_RESOURCES + * OEMCrypto_ERROR_UNKNOWN_FAILURE + * OEMCrypto_ERROR_BUFFER_TOO_LARGE + * + * 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. + * + * Version: + * This method changed in API version 8. + */ +OEMCryptoResult OEMCrypto_GenerateDerivedKeys(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); + +/* + * OEMCrypto_GenerateNonce + * + * 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. + * + * Because the nonce will be used to prevent replay attacks, it is desirable + * that a rogue application cannot rapidly call this function until a + * repeated nonce is created randomly. With this in mind, if more than 20 + * nonces are requested within one second, OEMCrypto will return an error + * after the 20th and not generate any more nonces for the rest of the + * second. After an error, if the application waits at least one second + * before requesting more nonces, then OEMCrypto will reset the error + * condition and generate valid nonces again. + * + * Parameters: + * session (in) - crypto session identifier. + * nonce (out) - pointer to memory to received the computed nonce. + * + * Results: + * nonce: the nonce is also stored in secure memory. At least 4 nonces should + * be stored for each session. + * + * Threading: + * This function may be called simultaneously with functions on other + * sessions, but not with other functions on this session. + * + * Returns: + * OEMCrypto_SUCCESS success + * OEMCrypto_ERROR_INVALID_SESSION + * OEMCrypto_ERROR_INSUFFICIENT_RESOURCES + * OEMCrypto_ERROR_UNKNOWN_FAILURE + * + * Version: + * This method changed in API version 5. + */ +OEMCryptoResult OEMCrypto_GenerateNonce(OEMCrypto_SESSION session, + uint32_t* nonce); + +/* + * OEMCrypto_GenerateSignature + * + * Description: + * Generates a HMAC-SHA256 signature for license request signing under the + * license server protocol for AES CTR mode. This uses the key mac_key_client. + * + * NOTE: OEMCrypto_GenerateDerivedKeys() must be called first to establish the + * mac_key_client. + * + * Refer to document "Widevine Modular DRM Security Integration Guide for + * CENC" for details. + * + * NOTE: if signature pointer is null and/or input signature_length set to + * zero, this function returns OEMCrypto_ERROR_SHORT_BUFFER and sets output + * signature_length to the size needed to receive the output signature. + * + * Parameters: + * session (in) - crypto session identifier. + * message (in) - pointer to memory containing message to be signed. + * message_length (in) - length of the message, in bytes. + * signature (out) - pointer to memory to received the computed signature. May + * be null (see note above). + * signature_length (in/out) - (in) length of the signature buffer, in bytes. + * (out) actual length of the signature, in bytes. + * + * Threading: + * This function may be called simultaneously with functions on other + * sessions, but not with other functions on this session. + * + * Returns: + * OEMCrypto_SUCCESS success + * OEMCrypto_ERROR_INVALID_SESSION + * OEMCrypto_ERROR_SHORT_BUFFER if signature buffer is not large enough to + * hold the buffer. + * OEMCrypto_ERROR_INSUFFICIENT_RESOURCES + * OEMCrypto_ERROR_UNKNOWN_FAILURE + * OEMCrypto_ERROR_BUFFER_TOO_LARGE + * + * 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. + * + * Version: + * This method changed in API version 5. + */ +OEMCryptoResult OEMCrypto_GenerateSignature(OEMCrypto_SESSION session, + const uint8_t* message, + size_t message_length, + uint8_t* signature, + size_t* signature_length); + +/* + * OEMCrypto_LoadKeys + * + * Description: + * Installs a set of keys for performing decryption in the current session. + * + * The relevant fields have been extracted from the License Response protocol + * message, but the entire message and associated signature are provided so + * the message can be verified (using HMAC-SHA256 with the derived + * mac_key_server). If the signature verification fails, ignore all other + * arguments and return OEMCrypto_ERROR_SIGNATURE_FAILURE. Otherwise, add the + * keys to the session context. + * + * 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 it is not null, 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 calls to OEMCrypto_RefreshKeys(). The first + * 256 bits of the mac_keys become the mac_key[server] and the following 256 + * bits of the mac_keys become the mac_key[client]. If enc_mac_keys is null, + * then there will not be a call to OEMCrypto_RefreshKeys for this session and + * the current mac_keys should remain unchanged. + * + * The mac_key and encrypt_key were generated and stored by the previous call + * to OEMCrypto_GenerateDerivedKeys(). The nonce was generated and stored by + * the previous call to OEMCrypto_GenerateNonce(). + * + * This session’s elapsed time clock is started at 0. The clock will be used + * 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(). + * + * Refer to document "Widevine Modular DRM Security Integration Guide for + * CENC" for details. + * + * If the parameter 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 parameter 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 at least 20 keys per session. This allows a + * single license to contain separate keys for 3 key rotations (previous + * interval, current interval, next interval) times 4 content keys (audio, + * SD, HD, UHD) plus up to 8 keys for watermarks. + * + * 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 pointer must be either null, or point inside the + * message. If the pointer enc_mac_keys is not null, the API shall verify + * that the two pointers enc_mac_keys_iv and enc_mac_keys point to locations + * in the message. I.e. (message <= p && p < message+message_length)for p in + * each of enc_mac_keys_iv, enc_mac_keys. If not, return + * OEMCrypto_ERROR_INVALID_CONTEXT. + * + * 3. The API shall verify that each pointer in each KeyObject points to a + * location in the message. I.e. (message <= p && p < message+message_length) + * for p in 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 + * verification field. If not, return OEMCrypto_ERROR_INVALID_CONTEXT. + * + * 5. If any key control block has the Nonce_Enabled bit set, that key’s Nonce + * field shall match the nonce generated by the current nonce. 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 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 + * verification described below is also performed. + * + * 8. If num_keys == 0, then 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. In this case, the following + * additional checks are performed. + * + * The pointer pst must not be null, and must point to a location in the + * message. If not, return OEMCrypto_ERROR_INVALID_CONTEXT. + * + * - If Replay_Control is 1 = Nonce_Required, then OEMCrypto will perform a + * nonce check as described above. OEMCrypto will verify that the table + * does not already have an entry for the value of pst passed in as a + * parameter. If an entry already exists, an error + * OEMCrypto_ERROR_INVALID_CONTEXT is returned and no keys are + * loaded. OEMCrypto will then create a new entry in the table, and mark + * this session as using this new entry. This 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 check the Session Usage table for an existing + * entry with the same pst. + * + * --- If the pst is not in the table yet, a new entry will be created in the + * table and this session shall use the new entry. In that case, the nonce + * will be verified for each key. + * + * --- If an existing usage table entry is found, then this session will use + * that entry. In that case, the nonce will not be verified for each key. + * 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 an existing entry is not found, the return + * error is OEMCrypto_ERROR_INVALID_NONCE. + * + * Note: If LoadKeys updates the mac keys, then the new updated mac keys will + * be used in the Usage Table. If LoadKeys does not update the mac keys, the + * existing session mac keys are stored in the usage table. + * + * 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. + * + * Note: If LoadKeys creates a new entry in the usage table, OEMCrypto will + * increment the Usage Table’s generation number, and then sign, encrypt, and + * save the Usage Table. + * + * Parameters: + * session (in) - crypto session identifier. + * message (in) - pointer to memory containing message to be verified. + * message_length (in) - length of the message, in bytes. + * signature (in) - pointer to memory containing the signature. + * signature_length (in) - length of the signature, in bytes. + * enc_mac_keys_iv (in) - IV for decrypting new mac_key. Size is 128 bits. + * enc_mac_keys (in) - encrypted mac_keys for generating new mac_keys. Size is + * 512 bits. + * num_keys (in) - number of keys present. + * key_array (in) - set of keys to be installed. + * pst (in) - the Provider Session Token. + * pst_length (in) - the length of pst. + * srm_restriction_data (in) - optional data specifying the minimum SRM + * version. + * license_type (in) - specifies if the license contains content keys or + * entitlement keys. + + * + * Threading: + * This function may be called simultaneously with functions on other + * sessions, but not with other functions on this session. + * + * Returns: + * OEMCrypto_SUCCESS success + * OEMCrypto_ERROR_NO_DEVICE_KEY + * OEMCrypto_ERROR_INVALID_SESSION + * OEMCrypto_ERROR_INVALID_CONTEXT + * OEMCrypto_ERROR_SIGNATURE_FAILURE + * OEMCrypto_ERROR_INVALID_NONCE + * OEMCrypto_ERROR_TOO_MANY_KEYS + * OEMCrypto_ERROR_UNKNOWN_FAILURE + * OEMCrypto_ERROR_BUFFER_TOO_LARGE + * + * 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. + * + * Version: + * This method changed in API version 14. + */ +OEMCryptoResult OEMCrypto_LoadKeys( + OEMCrypto_SESSION session, const uint8_t* message, size_t message_length, + const uint8_t* signature, size_t signature_length, + const uint8_t* enc_mac_keys_iv, const uint8_t* enc_mac_keys, + size_t num_keys, const OEMCrypto_KeyObject* key_array, const uint8_t* pst, + size_t pst_length, const uint8_t* srm_requirement, + OEMCrypto_LicenseType license_type); + + +/* + * OEMCrypto_LoadEntitledContentKeys + * + * Description: + * Load content keys into a session which already has entitlement + * keys loaded. This function will only be called for a session after a call + * to OEMCrypto_LoadKeys with the parameter type license_type equal to + * OEMCrypto_EntitlementLicense. This function may be called multiple times + * for the same session. + * + * If the session does not have license_type equal to + * OEMCrypto_EntitlementLicense, return OEMCrypto_ERROR_INVALID_CONTEXT and + * perform no work. + * + * For each key object in key_array, OEMCrypto shall look up the entry in the + * key table with the corresponding entitlement_key_id. + * 1) If no entry is found, return OEMCrypto_KEY_NOT_ENTITLED. + * 2) If the entry already has a content_key_id and content_key_data, that id + * and data are erased. + * 3) The content_key_id from the key_array is copied to the entry's + * content_key_id. + * 4) The content_key_data decrypted using the entitlement_key_data as a key + * for AES-256-CBC with an IV of content_key_data_iv, and using PKCS#7 + * padding. Notice that the entitlement key will be an AES 256 bit key. + * The clear content key data will be stored in the entry's + * content_key_data. + * + * Entries in the key table that do not correspond to anything in the + * key_array are not modified or removed. + * + * For devices that use a hardware key ladder, it may be more appropriate to + * store the encrypted content key data in the key table, and defer decrypting + * it until the function SelectKey is called. + * + * Parameters: + * session (in) - handle for the session to be used. + * num_keys (in) - number of keys present. + * key_array (in) - set of key updates. + * + * Returns + * OEMCrypto_SUCCESS success + * OEMCrypto_ERROR_INVALID_SESSION + * OEMCrypto_ERROR_INVALID_CONTEXT + * OEMCrypto_ERROR_INSUFFICIENT_RESOURCES + * OEMCrypto_ERROR_UNKNOWN_FAILURE + * OEMCrypto_KEY_NOT_ENTITLED + * Threading + * + * This function may be called simultaneously with functions on other + * sessions, but not with other functions on this session. + * + * Version + * This method is new in API version 14. + */ +OEMCryptoResult OEMCrypto_LoadEntitledContentKeys( + OEMCrypto_SESSION session, + size_t num_keys, + const OEMCrypto_EntitledContentKeyObject* key_array); + +/* + * OEMCrypto_RefreshKeys + * + * Description: + * Updates an existing set of keys for continuing decryption in 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. + * + * NOTE: OEMCrypto_GenerateDerivedKeys() or OEMCrypto_LoadKeys() must be + * called first to establish the mac_key[server]. + * + * This session’s elapsed time clock is reset to 0 when this function is + * called. The elapsed time clock is used in OEMCrypto_DecryptCENC(). + * + * This function does not add keys to the key table. It is only used to + * update a key control block license duration. Refer to the License Signing + * and Verification section above for more details. This function is used to + * update the duration of a key, only. It is not used to update key control + * bits. + * + * If the KeyRefreshObject’s key_control_iv is null, 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. + * + * If the KeyRefreshObject’s key_id is null, 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 be null 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_KEY_NOT_LOADED. + * + * Aside from the key's duration, no other values in the key control block + * should be updated by this function. + * + * 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 API shall verify that each pointer in each KeyObject points to a + * location in the message, or is null. I.e. (message <= p && p < + * message+message_length) for p in each of key_id,key_control_iv, + * key_control. If not, return OEMCrypto_ERROR_INVALID_CONTEXT. + * + * 3. Each key’s control block shall have a valid verification field. If not, + * return OEMCrypto_ERROR_INVALID_CONTEXT. + * + * 4. If the key control block has the Nonce_Enabled bit set, the Nonce field + * shall match one of the nonces 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. + * + * Parameters: + * session (in) - crypto session identifier. + * message (in) - pointer to memory containing message to be verified. + * message_length (in) - length of the message, in bytes. + * signature (in) - pointer to memory containing the signature. + * signature_length (in) - length of the signature, in bytes. + * num_keys (in) - number of keys present. + * key_array (in) - set of key updates. + * + * Threading: + * This function may be called simultaneously with functions on other + * sessions, but not with other functions on this session. + * + * Returns: + * OEMCrypto_SUCCESS success + * OEMCrypto_ERROR_NO_DEVICE_KEY + * OEMCrypto_ERROR_INVALID_SESSION + * OEMCrypto_ERROR_INVALID_CONTEXT + * OEMCrypto_ERROR_INVALID_NONCE + * OEMCrypto_ERROR_SIGNATURE_FAILURE + * OEMCrypto_ERROR_INSUFFICIENT_RESOURCES + * OEMCrypto_ERROR_UNKNOWN_FAILURE + * OEMCrypto_ERROR_BUFFER_TOO_LARGE + * OEMCrypto_KEY_NOT_LOADED + * + * 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. + * + * Version: + * This method changed in API version 8. + */ +OEMCryptoResult OEMCrypto_RefreshKeys( + OEMCrypto_SESSION session, const uint8_t* message, size_t message_length, + const uint8_t* signature, size_t signature_length, size_t num_keys, + const OEMCrypto_KeyRefreshObject* key_array); + +/* + * OEMCrypto_QueryKeyControl + * + * Description: + * Returns the decrypted key control block for the given key_id. This function + * is for application developers to debug license server and key timelines. + * It only returns a key control block if LoadKeys was successful, otherwise + * it returns OEMCrypto_ERROR_NO_CONTENT_KEY. The developer of the OEMCrypto + * library must be careful that the keys themselves are not accidentally + * revealed. + * + * Note: + * Returns control block in original, network byte order. If OEMCrypto + * converts fields to host byte order internally for storage, it should + * convert them back. Since OEMCrypto might not store the nonce or validation + * fields, values of 0 may be used instead. + * + * Verification + * The following checks should be performed. + * 1) If key_id is null, return OEMCrypto_ERROR_INVALID_CONTEXT. + * 2) If key_control_block_length is null, return + * OEMCrypto_ERROR_INVALID_CONTEXT. + * 3) If *key_control_block_length is less than the length of a key control + * block, set it to the correct value, and return + * OEMCrypto_ERROR_SHORT_BUFFER. + * 4) If key_control_block is null, return OEMCrypto_ERROR_INVALID_CONTEXT. + * 5) If the specified key has not been loaded, return + * OEMCrypto_ERROR_NO_CONTENT_KEY. + * + * Parameters + * content_key_id (in) - The unique id of the content key of interest. + * content_key_id_length (in) - The length of key_id, in bytes. From 1 to 16 + * inclusive. + * key_control_block(out) - A caller-owned buffer. + * key_control_block_length (in/out) - The length of key_control_block buffer. + * + * Returns + * OEMCrypto_SUCCESS + * OEMCrypto_ERROR_INVALID_CONTEXT + * OEMCrypto_ERROR_SHORT_BUFFER + * OEMCrypto_ERROR_NO_CONTENT_KEY + * OEMCrypto_ERROR_UNKNOWN_FAILURE + * OEMCrypto_ERROR_BUFFER_TOO_LARGE + * + * 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 function may be called simultaneously with functions on other + * sessions, but not with other functions on this session. + * + * Version + * This method is added in API version 10. + */ +OEMCryptoResult OEMCrypto_QueryKeyControl(OEMCrypto_SESSION session, + const uint8_t* content_key_id, + size_t content_key_id_length, + uint8_t* key_control_block, + size_t* key_control_block_length); + +/* + * OEMCrypto_SelectKey + * + * Description: + * Select a content key and install it in the hardware key ladder for + * subsequent decryption operations (OEMCrypto_DecryptCENC()) for this + * session. The specified key must have been previously "installed" via + * OEMCrypto_LoadKeys() or OEMCrypto_RefreshKeys(). + * + * A key control block is associated with the key and the session, and is used + * to configure the session context. The Key Control data is documented in + * "Key Control Block Definition". + * + * Step 1: Lookup the content key data via the offered key_id. The key data + * 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. + * + * Step 3: use the latched content key to decrypt (AES-128-CTR) buffers + * passed in via OEMCrypto_DecryptCENC(). If the key is 256 bits it + * will be used for OEMCrypto_Generic_Sign or + * OEMCrypto_Generic_Verify as specified in the key control block. + * If the key will be used with OEMCrypto_Generic_Encrypt or + * OEMCrypto_Generic_Decrypt, the cipher mode will always be + * OEMCrypto_CipherMode_CBC. Continue to use this key until + * OEMCrypto_SelectKey() is called again, or until + * OEMCrypto_CloseSession() is called. + * + * 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 key id is not found in the keytable for this session, then the + * key state is not changed and OEMCrypto shall return + * OEMCrypto_KEY_NOT_LOADED. + * + * 2. If the current key's control block has a nonzero Duration field, then + * the API shall verify that the duration is greater than the session's + * elapsed time clock before the key is used. OEMCrypto may return + * OEMCrypto_ERROR_KEY_EXPIRED from OEMCrypto_SelectKey, or SelectKey may + * return success from select key and the decrypt or generic crypto call will + * return OEMCrypto_ERROR_KEY_EXPIRED. + * + * 3. If the key control block has the bit Disable_Analog_Output set, then + * the device should disable analog video output. If the device has analog + * output that cannot be disabled, then the key is not selected, and + * OEMCrypto_ERROR_ANALOG_OUTPUT is returned. + * + * 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. + * + * 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. + * + * Parameters: + * session (in) - crypto session identifier + * content_key_id (in) - pointer to the Content Key ID + * content_key_id_length (in) - length of the Key ID in bytes. From 1 to 16 + * inclusive. + * cipher_mode (in) - whether the key should be prepared for CTR mode or CBC + * mode when used in later calls to DecryptCENC. This + * should be ignored when the key is used for Generic Crypto calls. + * + * Threading: + * This function may be called simultaneously with functions on other + * sessions, but not with other functions on this session. + * + * Returns: + * OEMCrypto_SUCCESS success + * OEMCrypto_ERROR_INVALID_SESSION crypto session ID invalid or not open + * OEMCrypto_ERROR_NO_DEVICE_KEY failed to decrypt device key + * OEMCrypto_ERROR_NO_CONTENT_KEY failed to decrypt content key + * OEMCrypto_ERROR_CONTROL_INVALID invalid or unsupported control input + * OEMCrypto_ERROR_KEYBOX_INVALID cannot decrypt and read from Keybox + * OEMCrypto_ERROR_KEY_EXPIRED + * OEMCrypto_ERROR_INSUFFICIENT_RESOURCES + * OEMCrypto_ERROR_UNKNOWN_FAILURE + * OEMCrypto_ERROR_KEY_EXPIRED + * OEMCrypto_ERROR_ANALOG_OUTPUT + * OEMCrypto_ERROR_INSUFFICIENT_HDCP + * OEMCrypto_KEY_NOT_LOADED + * + * Version: + * This method changed in API version 14. + */ +OEMCryptoResult OEMCrypto_SelectKey(OEMCrypto_SESSION session, + const uint8_t* content_key_id, + size_t content_key_id_length, + OEMCryptoCipherMode cipher_mode); + +/* + * 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 set in + * the OEMCrypto_KeyObject passed in to OEMCrypto_LoadKeys. 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. + * + * After decryption, the data_length bytes are copied to the location + * described by out_buffer. This could be one of + * + * 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 rendered. + * + * 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 initialization 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. + * + * If the session has an entry in the Usage Table, then OEMCrypto will 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". + * + * 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 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 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. + * + * 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. + * + * 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. + * + * 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, 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 set, + * then the device should disable analog video output. If the device has + * analog output that cannot be disabled, then the key is not selected, and + * OEMCrypto_ERROR_ANALOG_OUTPUT is returned. + * + * 4. If the current key’s control block has the HDCP bit set, then the API + * shall verify that the buffer will be output using HDCP only. If not, + * return OEMCrypto_ERROR_INSUFFICIENT_HDCP. + * + * 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. + * + * 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. + * + * 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. + * + * Parameters: + * session (in) - crypto session identifier. + * data_addr (in) - An unaligned pointer to this segment of the stream. + * data_length (in) - The length of this segment of the stream, in bytes. + * is_encrypted (in) - 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. + * iv (in) - The initial value block to be used for content decryption. + * block_offset (in) - 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. This paramater is only used for CTR mode. + * out_buffer (in) - A caller-owned descriptor that specifies the handling of + * the decrypted byte stream. See OEMCrypto_DestbufferDesc + * for details. + * pattern (in) - A caller-owned structure indicating the encrypt/skip + * pattern as specified in the CENC standard. + * subsample_flags (in) - bitwise flags indicating if this is the first, + * middle, or last subsample in a chunk of data. + * 0 = neither first nor last subsample, + * 1 = first subsample, + * 2 = last subsample, + * 3 = both first and last subsample. + * + * Threading: + * This function may be called simultaneously with functions on other + * sessions, but not with other functions on this session. + * + * Returns: + * OEMCrypto_SUCCESS + * OEMCrypto_ERROR_NO_DEVICE_KEY + * OEMCrypto_ERROR_INVALID_SESSION + * OEMCrypto_ERROR_INVALID_CONTEXT + * OEMCrypto_ERROR_DECRYPT_FAILED + * OEMCrypto_ERROR_KEY_EXPIRED + * OEMCrypto_ERROR_INSUFFICIENT_HDCP + * OEMCrypto_ERROR_ANALOG_OUTPUT + * OEMCrypto_ERROR_INSUFFICIENT_RESOURCES + * OEMCrypto_ERROR_UNKNOWN_FAILURE + * OEMCrypto_ERROR_BUFFER_TOO_LARGE + * OEMCrypto_ERROR_LICENSE_INACTIVE + * + * Buffer Sizes + * OEMCrypto shall support subsample sizes (i.e. data_length) of at least + * 100 KiB. + * 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 to not artificially + * restrict the maximum buffer size. + * + * Version: + * This method changed in API version 13. + * 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, + OEMCrypto_DestBufferDesc* out_buffer, + const OEMCrypto_CENCEncryptPatternDesc* pattern, uint8_t subsample_flags); + +/* + * 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 + * copied. The definition of OEMCrypto_DestBufferDesc and subsample_flags are + * the same as in OEMCrypto_DecryptCENC, above. + * + * The main difference between this and DecryptCENC is that this function + * does not need an open session, and it may be called concurrently with + * other session functions on a multithreaded system. In particular, an + * application will use this to copy the clear leader of a video to a secure + * buffer while the license request is being generated, sent to the server, + * and the response is being processed. This functionality is needed because + * an application may not have read or write access to a secure destination + * buffer. + * + * NOTES: + * + * This method may be called several times before the data is used. 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 data will not be + * used until after OEMCrypto_LastSubsample has been set. If an + * implementation copies data immediately, it may ignore subsample_flags. + * + * If the destination buffer is secure, an offset may be specified. + * CopyBuffer begins storing data out_buffer->secure.offset bytes after the + * beginning of the secure buffer. + * + * Verification + * The following checks should be performed. + * 1. If either data_addr or out_buffer is null, return + * OEMCrypto_ERROR_INVALID_CONTEXT. + * + * Parameters + * data_addr (in) - An unaligned pointer to the buffer to be copied. + * data_length (in) - The length of the buffer, in bytes. + * out_buffer (out) - A caller-owned descriptor that specifies the handling + * of the byte stream. See OEMCrypto_DestbufferDesc for + * details. + * subsample_flags (in) - bitwise flags indicating if this is the first, + * middle, or last subsample in a chunk of data. + * 0 = neither first nor last subsample, + * 1 = first subsample, + * 2 = last subsample, + * 3 = both first and last subsample. + * + * Returns + * OEMCrypto_SUCCESS + * OEMCrypto_ERROR_INVALID_CONTEXT + * OEMCrypto_ERROR_UNKNOWN_FAILURE + * OEMCrypto_ERROR_BUFFER_TOO_LARGE + * + * Buffer Sizes + * OEMCrypto shall support subsample sizes (i.e. data_length) of at least + * 100 KiB. + * 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 to not artificially + * restrict the maximum buffer size. + * + * Threading + * This function may be called simultaneously with any other functions. + * Version + * This method changed in API version 12. + */ +OEMCryptoResult OEMCrypto_CopyBuffer(const uint8_t* data_addr, + size_t data_length, + OEMCrypto_DestBufferDesc* out_buffer, + uint8_t subsample_flags); + +/* + * OEMCrypto_WrapKeybox + * + * Description: + * During manufacturing, the keybox should be encrypted with the OEM root key + * and stored on the file system in a region that will not be erased during + * factory reset. As described in section 5.5.4, the keybox may be directly + * encrypted and stored on the device in a single step, or it may use the + * two-step WrapKeybox/InstallKeybox approach. When the Widevine DRM plugin + * initializes, it will look for a wrapped keybox in the file + * /factory/wv.keys and install it into the security processor by calling + * OEMCrypto_InstallKeybox(). + * + * OEMCrypto_WrapKeybox() is used to generate an OEM-encrypted keybox that + * may be passed to OEMCrypto_InstallKeybox() for provisioning. The keybox + * may be either passed in the clear or previously encrypted with a transport + * key. If a transport key is supplied, the keybox is first decrypted with + * the transport key before being wrapped with the OEM root key. This + * function is only needed if the provisioning method involves saving the + * keybox to the file system. + * + * Parameters: + * keybox (in) - pointer to Keybox data to encrypt. May be NULL on the first + * call to test size of wrapped keybox. The keybox may either be clear or + * previously encrypted. + * keyboxLength (in) - length the keybox data in bytes + * wrappedKeybox (out) – Pointer to wrapped keybox + * wrappedKeyboxLength (out) – Pointer to the length of the wrapped keybox in + * bytes + * transportKey (in) – Optional. AES transport key. If provided, the keybox + * parameter was previously encrypted with this key. The keybox will be + * decrypted with the transport key using AES-CBC and a null IV. + * transportKeyLength (in) – Optional. Number of bytes in the transportKey, if + * used. + * + * Threading: + * This function is not called simultaneously with any other functions + * + * Returns: + * OEMCrypto_SUCCESS success + * OEMCrypto_ERROR_WRAP_KEYBOX failed to wrap Keybox + * OEMCrypto_ERROR_SHORT_BUFFER if keybox is provided as NULL, to determine + * the size of the wrapped keybox + * OEMCrypto_ERROR_INSUFFICIENT_RESOURCES + * OEMCrypto_ERROR_NOT_IMPLEMENTED + * + * Version: + * This method is supported by all API versions. + */ +OEMCryptoResult OEMCrypto_WrapKeybox(const uint8_t* keybox, size_t keyBoxLength, + uint8_t* wrappedKeybox, + size_t* wrappedKeyBoxLength, + const uint8_t* transportKey, + size_t transportKeyLength); + +/* + * OEMCrypto_InstallKeybox + * + * Description: + * Decrypts a wrapped keybox and installs it in the security processor. The + * keybox is unwrapped then encrypted with the OEM root key. This function is + * called from the Widevine DRM plugin at initialization time if there is no + * valid keybox installed. It looks for a wrapped keybox in the file + * /factory/wv.keys and if it is present, will read the file and call + * OEMCrypto_InstallKeybox() with the contents of the file. + * + * Parameters: + * keybox (in) - pointer to encrypted Keybox data as input + * keyboxLength (in) - length of the keybox data in bytes + * + * Threading: + * This function is not called simultaneously with any other functions. + * + * Returns: + * OEMCrypto_SUCCESS success + * OEMCrypto_ERROR_BAD_MAGIC + * OEMCrypto_ERROR_BAD_CRC + * OEMCrypto_ERROR_INSUFFICIENT_RESOURCES + * OEMCrypto_ERROR_WRITE_KEYBOX failed to handle and store Keybox + * + * Version: + * This method is supported in all API versions. + */ +OEMCryptoResult OEMCrypto_InstallKeybox(const uint8_t* keybox, + size_t keyBoxLength); + +/* + * OEMCrypto_GetProvisioningMethod + * + * Description: + * This function is for OEMCrypto to tell the layer above what provisioning + * method it uses: keybox or OEM certificate. + * + * Parameters: + * none + * + * Returns: + * OEMCrypto_DrmCertificate - means the device has a DRM certificate built + * into the system. This cannot be used by level 1 devices. + * OEMCrypto_Keybox - means the device has a unique keybox. For level 1 + * devices this keybox must be securely installed by the device manufacturer. + * OEMCrypto_OEMCertificate - means the device has a factory installed OEM + * certificate. This is also called Provisioning 3.0. + * + * Threading: + * This function may be called simultaneously with any session functions. + * + * Version: + * This method is new API version 12. + */ +OEMCrypto_ProvisioningMethod OEMCrypto_GetProvisioningMethod(); + +/* + * 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 discussion of + * Provisioning 3.0 in the document "WV Modular DRM Security Itegration Guide + * for Common Encryption (CENC)". + * + * If the buffer is not large enough, OEMCrypto should update + * public_cert_length and return OEMCrypto_ERROR_SHORT_BUFFER. + * + * Parameters: + * session (in) - this function affects the specified session only. + * public_cert (out) - the buffer where the public certificate is stored. + * public_cert_length (in/out) - on input, this is the available size of the + * buffer. On output, this is the number of bytes needed for the + * certificate. + * + * Returns: + * OEMCrypto_SUCCESS + * OEMCrypto_ERROR_NOT_IMPLEMENTED + * OEMCrypto_ERROR_SHORT_BUFFER + * + * Threading: + * This function may be called simultaneously with functions on other + * sessions, but not with other functions on this session. + * + * Version: + * This method is new API version 12. + */ +OEMCryptoResult OEMCrypto_GetOEMPublicCertificate(OEMCrypto_SESSION session, + uint8_t* public_cert, + size_t* public_cert_length); + +/* + * OEMCrypto_LoadTestKeybox + * + * Description: + * Temporarily use the standard test keybox in place of the factory + * provisioned keybox for all functions that use keybox keys or data. This + * allows a standard suite of unit tests to be run on a production device + * without permanently changing the keybox. This keybox will persist until + * the next call to OEMCrypto_Terminate or OEMCrypto_Initialize. Upon + * initialization, revert to using the factory provisioned keybox. + * + * The test keybox can be found in the reference implementation. + * + * Parameters + * buffer (in) - pointer to memory containing test keybox, in binary form. + * length (in) - length of the buffer, in bytes. + * + * Returns + * OEMCrypto_SUCCESS success + * OEMCrypto_ERROR_UNKNOWN_FAILURE + * + * Threading + * This function is not called simultaneously with any other functions. + * It will be called just after OEMCrypto_Initialize(). + * + * Version + * This method is added in API version 10. + */ +OEMCryptoResult OEMCrypto_LoadTestKeybox(const uint8_t *buffer, size_t length); + +/* + * OEMCrypto_IsKeyboxValid + * + * Description: + * Validate the Widevine Keybox stored on the device. + * + * The API performs two verification steps on the Keybox. It first verifies + * the MAGIC field contains a valid signature (must be 'kbox'). The API then + * computes the CRC using CRC-32 (Posix 1003.2 standard) and compares the + * checksum to the CRC stored in the Keybox. The CRC is computed over the + * entire Keybox excluding the 4 CRC bytes (i.e. Keybox[0..123]). + * + * Parameters: + * none + * + * Threading: + * This function may be called simultaneously with any session functions. + * + * Returns: + * OEMCrypto_SUCCESS + * OEMCrypto_ERROR_BAD_MAGIC + * OEMCrypto_ERROR_BAD_CRC + * + * Version: + * This method is supported by all API versions. + */ +OEMCryptoResult OEMCrypto_IsKeyboxValid(void); + +/* + * OEMCrypto_GetDeviceID + * + * Description: + * Retrieve the device's unique identifier from the Keybox. + * + * Parameters: + * deviceId (out) - pointer to the buffer that receives the Device ID + * idLength (in/out) - on input, size of the caller's device ID buffer. + * On output, the number of bytes written into the buffer. + * + * Threading: + * This function may be called simultaneously with any session functions. + * + * Returns: + * OEMCrypto_SUCCESS success + * OEMCrypto_ERROR_SHORT_BUFFER buffer is too small to return the device ID + * OEMCrypto_ERROR_NO_DEVICEID failed to return Device Id + * + * Version: + * This method is supported by all API versions. + */ +OEMCryptoResult OEMCrypto_GetDeviceID(uint8_t* deviceID, size_t* idLength); + +/* + * OEMCrypto_GetKeyData + * + * Description: + * Returns the Key Data field from the Keybox. The Key Data field does not + * need to be encrypted by an OEM root key, but may be if desired. + * + * If the Key Data field was encrypted with an OEM root key when the Keybox + * was stored on the device, then this function should decrypt it and return + * the clear Key Data. If the Key Data was not encrypted, then this function + * should just access and return the clear Key data. + * + * Parameters: + * keyData (out) - pointer to a caller-managed buffer to hold the Key Data + * field from the Keybox + * dataLength (in/out) - on input, the allocated buffer size. On output, + * the number of bytes in KeyData. + * + * Threading: + * This function may be called simultaneously with any session functions. + * + * Returns: + * OEMCrypto_SUCCESS success + * OEMCrypto_ERROR_SHORT_BUFFER the buffer is too small to return the KeyData + * OEMCrypto_ERROR_NO_KEYDATA failed to return KeyData + * OEMCrypto_ERROR_NOT_IMPLEMENTED - device does not use keybox + * + * Version: + * This method is supported by all API versions. + */ +OEMCryptoResult OEMCrypto_GetKeyData(uint8_t* keyData, size_t* keyDataLength); + +/* + * OEMCrypto_GetRandom + * + * Description: + * Return a buffer filled with hardware-generated random bytes. If the + * hardware feature does not exist, return OEMCrypto_ERROR_RNG_NOT_SUPPORTED. + * + * Parameters: + * randomData (out) - Pointer to caller-manager buffer that will receive the + * random data. + * dataLength (in) - Length of the random data buffer in bytes. + * + * Threading: + * This function may be called simultaneously with any session functions. + * + * Returns: + * OEMCrypto_SUCCESS success + * OEMCrypto_ERROR_RNG_FAILED failed to generate random number + * OEMCrypto_ERROR_RNG_NOT_SUPPORTED function not supported + * OEMCrypto_ERROR_BUFFER_TOO_LARGE + * + * Buffer Sizes + * OEMCrypto shall support dataLength of at least 32 bytes for random number + * generation. + * OEMCrypto shall return OEMCrypto_ERROR_BUFFER_TOO_LARGE if the buffer is + * too large. + * + * Version: + * This method is supported by all API versions. + */ +OEMCryptoResult OEMCrypto_GetRandom(uint8_t* randomData, size_t dataLength); + +/* + * OEMCrypto_RewrapDeviceRSAKey30 + * + * 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. + * + * 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. 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. + * 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: + * session (in) - crypto session identifier. + * unaligned_nonce (in) - The nonce provided in the provisioning + * - response. This points to an uint32_t that might + * - not be aligned to a word boundary. + * encrypted_message_key (in) - message_key encrypted by private key + * - from OEM cert. + * encrypted_message_key_length (in) - length of encrypted_message_key in + * - bytes. + * enc_rsa_key (in) - Encrypted device private RSA key received from + * - the provisioning server. Format is PKCS#8 + * - binary DER encoded, encrypted with message_key, + * - using AES-128-CBC with PKCS#5 + * - padding. Encrypted by message_key. + * enc_rsa_key_length (in) - length of the encrypted RSA key, in bytes. + * enc_rsa_key_iv (in) - IV for decrypting RSA key. Size is 128 bits. + * wrapped_rsa_key (out) - 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. + * wrapped_rsa_key_length (in/out) - length of the encrypted RSA key, in + * - bytes. + * + * Threading: + * This function may be called simultaneously with functions on other + * sessions, but not with other functions on this session. + * + * 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 + * + * 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. + * + * Version: + * This method is new in API version 12. + */ +OEMCryptoResult OEMCrypto_RewrapDeviceRSAKey30( + OEMCrypto_SESSION session, const uint32_t* unaligned_nonce, + const uint8_t* encrypted_message_key, size_t encrypted_message_key_length, + const uint8_t* enc_rsa_key, size_t enc_rsa_key_length, + const uint8_t* enc_rsa_key_iv, uint8_t* wrapped_rsa_key, + size_t* wrapped_rsa_key_length); + +/* + * 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 + * (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. + * 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 “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. + * 7. 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. + * 8. 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. + * 9. 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. + * 10. 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: + * session (in) - crypto session identifier. + * message (in) - pointer to memory containing message to be + * - verified. + * message_length (in) - length of the message, in bytes. + * signature (in) - pointer to memory containing the HMAC-SHA256 + * - signature for message, received from the + * - provisioning server. + * signature_length (in) - length of the signature, in bytes. + * unaligned_nonce (in) - The nonce provided in the provisioning + * - response. This points to an uint32_t that might + * - not be aligned to a word boundary. + * enc_rsa_key (in) - Encrypted device private RSA key received from + * - the provisioning server. Format is PKCS#8 + * - binary DER encoded, encrypted with the derived + * - encryption key, using AES-128-CBC with PKCS#5 + * - padding. + * enc_rsa_key_length (in) - length of the encrypted RSA key, in bytes. + * enc_rsa_key_iv (in) - IV for decrypting RSA key. Size is 128 bits. + * wrapped_rsa_key (out) - 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. + * wrapped_rsa_key_length (in/out) - length of the encrypted RSA key, in + * - bytes. + * + * Threading: + * This function may be called simultaneously with functions on other + * sessions, but not with other functions on this session. + * + * 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 + * + * 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. + * + * Version: + * This method changed in API version 9. + */ + +OEMCryptoResult OEMCrypto_RewrapDeviceRSAKey( + OEMCrypto_SESSION session, const uint8_t* message, size_t message_length, + const uint8_t* signature, size_t signature_length, + const uint32_t* unaligned_nonce, const uint8_t* enc_rsa_key, + size_t enc_rsa_key_length, const uint8_t* enc_rsa_key_iv, + uint8_t* wrapped_rsa_key, size_t* wrapped_rsa_key_length); + +/* + * OEMCrypto_LoadDeviceRSAKey + * + * 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. + * + * 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. + * + * Verification: + * The following checks should be performed. If any check fails, an error is + * returned, and the RSA key is not loaded. + * 1. The wrapped key has a valid signature, as described in + * RewrapDeviceRSAKey. + * 2. The decrypted key is a valid private RSA key. + * 3. If a value for allowed_schemes is included with the key, it is a valid + * value. + * + * Parameters: + * session (in) - crypto session identifier. + * wrapped_rsa_key (in) - 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 is the wrapped key + * - generated by OEMCrypto_RewrapDeviceRSAKey. + * wrapped_rsa_key_length (in) - length of the wrapped key buffer, in bytes. + * + * Threading: + * This function may be called simultaneously with functions on other + * sessions, but not with other functions on this session. + * + * Returns: + * OEMCrypto_SUCCESS success + * OEMCrypto_ERROR_NO_DEVICE_KEY + * OEMCrypto_ERROR_INVALID_SESSION + * OEMCrypto_ERROR_INVALID_RSA_KEY + * OEMCrypto_ERROR_INSUFFICIENT_RESOURCES + * OEMCrypto_ERROR_UNKNOWN_FAILURE + * + * Version: + * This method changed in API version 9. + */ +OEMCryptoResult OEMCrypto_LoadDeviceRSAKey(OEMCrypto_SESSION session, + const uint8_t* wrapped_rsa_key, + size_t wrapped_rsa_key_length); + +/* + * OEMCrypto_LoadTestRSAKey + * + * Description: + + * Temporarily use the standard test RSA key. This function is only required + * for platforms that do not use a keybox, but have an RSA certificate baked + * in. This allows a standard suite of unit tests to be run on a production + * device without permanently changing the certificate. This RSA key will + * persist until the next call to OEMCrypto_Terminate or + * OEMCrypto_Initialize. + * + * The test RSA key can be found in the reference implementation. + * + * Parameters + * none + * + * Returns + * OEMCrypto_SUCCESS success + * OEMCrypto_ERROR_UNKNOWN_FAILURE + * + * Threading + * This function is not called simultaneously with any other functions. + * It will be called just after OEMCrypto_OpenSession(). + * + * Version + * This method is added in API version 10. + */ +OEMCryptoResult OEMCrypto_LoadTestRSAKey(); + +/* + * 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. Refer to the License Request Signed by RSA Certificate + * 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: + * 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 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. + * + * 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. + * + * Parameters: + * session (in) - crypto session identifier. + * message (in) - pointer to memory containing message to be + * - signed. + * message_length (in) - length of the message, in bytes. + * signature (out) - buffer to hold the message signature. On + * - return, it will contain the message signature + * - generated with the device private RSA key using + * - RSASSA-PSS. + * signature_length (in/out) - (in) length of the signature buffer, in bytes. + * - (out) actual length of the signature + * padding_scheme (in) - specify which scheme to use for the signature. + * + * Threading: + * This function may be called simultaneously with functions on other + * sessions, but not with other functions on this session. + * + * Returns: + * OEMCrypto_SUCCESS success + * OEMCrypto_ERROR_INVALID_SESSION + * OEMCrypto_ERROR_INVALID_CONTEXT + * OEMCrypto_ERROR_SHORT_BUFFER if the signature buffer is too small. + * OEMCrypto_ERROR_INVALID_RSA_KEY + * OEMCrypto_ERROR_INSUFFICIENT_RESOURCES + * OEMCrypto_ERROR_UNKNOWN_FAILURE + * OEMCrypto_ERROR_NOT_IMPLEMENTED - if algorithm > 0, and the device does not + * support that algorithm. + * OEMCrypto_ERROR_BUFFER_TOO_LARGE + * + * 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. + * + * Version: + * This method changed in API version 9. + */ +OEMCryptoResult OEMCrypto_GenerateRSASignature( + OEMCrypto_SESSION session, const uint8_t* message, size_t message_length, + uint8_t* signature, size_t* signature_length, + RSA_Padding_Scheme padding_scheme); + +/* + * OEMCrypto_DeriveKeysFromSessionKey + * + * Description: + * Generates three secondary keys -- mac_key_server, mac_key_client, and + * encrypt_key -- for handling signing and content key decryption under the + * license server protocol for AES CTR mode. + * + * 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 two 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. Once the enc_key and mac_keys have been generated, all calls + * to LoadKeys and RefreshKeys proceed in the same manner for license + * requests using RSA or using a Widevine keybox token. + * + * Verification: + * If the RSA key’s allowed_schemes is not kSign_RSASSA_PSS, then no keys are + * derived and the error OEMCrypto_ERROR_INVALID_RSA_KEY is returned. An RSA + * key cannot be used for both deriving session keys and also for PKCS1 + * signatures. + * + * Parameters: + * session (in) - crypto session identifier. + * enc_session_key (in) - session key, encrypted with the device RSA + * - key (from the device certifcate) using + * - RSA-OAEP. + * enc_session_key_length (in) - length of session_key, in bytes. + * mac_key_context (in) - pointer to memory containing context data for + * - computing the HMAC generation key. + * mac_key_context_length (in) - length of the HMAC key context data, in + * - bytes. + * enc_key_context (in) - pointer to memory containing context data for + * - computing the encryption key. + * enc_key_context_length (in) - length of the encryption key context data, in + * - bytes. + * + * Threading: + * This function may be called simultaneously with functions on other + * sessions, but not with other functions on this session. + * + * Returns: + * OEMCrypto_SUCCESS success + * OEMCrypto_ERROR_DEVICE_NOT_RSA_PROVISIONED + * OEMCrypto_ERROR_INVALID_SESSION + * OEMCrypto_ERROR_INVALID_CONTEXT + * OEMCrypto_ERROR_INSUFFICIENT_RESOURCES + * OEMCrypto_ERROR_UNKNOWN_FAILURE + * OEMCrypto_ERROR_BUFFER_TOO_LARGE + * + * 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. + * + * Version: + * This method changed in API version 9. + */ +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, + size_t enc_key_context_length); + +/* + * OEMCrypto_APIVersion() + * + * Description: + * This function returns the current API version number. Because this + * API is part of a shared library, the version number allows the calling + * application to avoid version mis-match errors. + * + * There is a possibility that some API methods will be backwards compatible, + * or backwards compatible at a reduced security level. + * + * There is no plan to introduce forward-compatibility. Applications will + * reject a library with a newer version of the API. + * + * Parameters: + * none + * + * Threading: + * This function may be called simultaneously with any other functions. + * + * Returns: + * The supported API, as specified in the header file OEMCryptoCENC.h. + * + * Version: + * This method should change in all API versions. + */ +uint32_t OEMCrypto_APIVersion(); + +/** + * OEMCrypto_Security_Patch_Level() + * + * Description: + * This function returns the current patch level of the software running in + * the trusted environment. The patch level is defined by the OEM, and is + * only incremented when a security update has been added. + * + * Parameters: + * none + * + * Returns: + * The OEM defined version number. + * + * Threading: + * This function may be called simultaneously with any other functions. + * + * Version: + * This method was introduced in API version 11. + */ +uint8_t OEMCrypto_Security_Patch_Level(); + +/* + * OEMCrypto_SecurityLevel() + * + * Description: + * This function returns the security level of the OEMCrypto library. + * + * Since this function is spoofable, it is not relied on for security + * purposes. It is for information only. + * + * Returns: + * A null terminated string. Useful values are "L1", "L2" or "L3". + * + * Threading: + * This function may be called simultaneously with any other functions. + * + * Version: + * This method changed in API version 6. + */ +const char* OEMCrypto_SecurityLevel(); + +/* + * OEMCrypto_GetHDCPCapability() + * + * Description: + * Returns the maximum HDCP version supported by the device, and the HDCP + * version supported by the device and any connected display. + * + * Parameters: + * current (out) - this is the current HDCP version, based on the device + * itself, and the display to which it is connected. + * maximum (out) - this is the maximum supported HDCP version for the device, + * ignoring any attached device. + * + * Threading: + * This function may be called simultaneously with any other functions. + * + * Returns: + * OEMCrypto_SUCCESS + * OEMCrypto_ERROR_UNKNOWN_FAILURE + * + * Version: + * This method changed in API version 10. + */ +OEMCryptoResult OEMCrypto_GetHDCPCapability(OEMCrypto_HDCP_Capability* current, + OEMCrypto_HDCP_Capability* maximum); + +/* + * OEMCrypto_SupportsUsageTable() + * + * Description: + * This is used to determine if the device can support a usage table. Since + * this function is spoofable, it is not relied on for security purposes. It + * is for information only. The usage table is described in the section + * above. + * + * Parameters: + * none + * + * Threading: + * This function may be called simultaneously with any other functions. + * + * Returns: + * Returns true if the device can maintain a usage table. Returns false + * otherwise. + * + * Version: + * This method changed in API version 9. + */ +bool OEMCrypto_SupportsUsageTable(); + +/* + * OEMCrypto_IsAntiRollbackHwPresent() + * + * Description: + + * Indicate whether there is hardware protection to prevent the rollback of + * the usage table. For example, this is true if the usage table is stored + * entirely on a secure file system that the user cannot read or write to. + * Another example is if the usage table has a generation number and the + * generation number is stored in secure memory that is not user accessible. + * + * Parameters: + * none. + * + * Threading: + * This function may be called simultaneously with any other functions. + * + * Returns: + * Returns true if oemcrypto uses anti-rollback hardware. Returns false + * otherwise. + * + * Version: + * This method is added in API version 10. + */ +bool OEMCrypto_IsAntiRollbackHwPresent(); + +/* + * OEMCrypto_GetNumberOfOpenSessions() + * + * Description: + * Returns the current number of open OEMCrypto sessions. The CDM and + * OEMCrypto consumers can query this value so they can use resources more + * effectively. + * + * Parameters: + * count (out) - the current number of OEMCrypto sessions. + * + * Threading: + * This function may be called simultaneously with any other functions. + * + * Returns: + * OEMCrypto_SUCCESS + * OEMCrypto_ERROR_UNKNOWN_FAILURE + * + * Version: + * This method is added in API version 10. + */ +OEMCryptoResult OEMCrypto_GetNumberOfOpenSessions(size_t* count); + +/* + * OEMCrypto_GetMaxNumberOfSessions() + * + * Description: + * Returns the maximum number of concurrent OEMCrypto sessions supported by + * the device. The CDM and OEMCrypto consumers can query this value so they + * can use resources more effectively. If the maximum number of sessions + * depends on a dynamically allocated shared resource, the returned value + * should be a best estimate of the maximum number of sessions. + * + * OEMCrypto shall support a minimum of 10 sessions. Some applications use + * multiple sessions to pre-fetch licenses, so high end devices should + * support more sessions -- we recommend a minimum of 50 sessions. + * + * Parameters: + * maximum (out) - the maximum number of OEMCrypto sessions supported by the + * device. + * + * Threading: + * This function may be called simultaneously with any other functions. + * + * Returns: + * OEMCrypto_SUCCESS + * OEMCrypto_ERROR_UNKNOWN_FAILURE + * + * Version: + * This method is added in API version 10. + */ +OEMCryptoResult OEMCrypto_GetMaxNumberOfSessions(size_t* max); + +/* + * OEMCrypto_SupportedCertificates() + * + * Description: + * Returns the type of certificates keys that this device supports. With very + * few exceptions, all devices should support at least 2048 bit RSA keys. + * High end devices should also support 3072 bit RSA keys. Devices that are + * cast receivers should also support RSA cast receiver certificates. + * + * Beginning with OEMCrypto v14, the provisioning server may deliver to the + * device an RSA key that uses the Carmichael totient. This does not change + * the RSA algorithm -- however the product of the private and public keys is + * not necessarily the Euler number phi. OEMCrypto should not reject such + * keys. + * + * Parameters: none + * + * Threading: + * This function may be called simultaneously with any other functions. + * + * Returns: + * Returns the bitwise or of the following flags. It is likely that high + * end devices will support both 2048 and 3072 bit keys while the widevine + * servers transition to new key sizes. + * + * 0x1 = OEMCrypto_Supports_RSA_2048bit - the device can load a DRM + * certificate with a 2048 bit RSA key. + * + * 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 + * OEMCrypto_GenerateRSASignature with padding type set to 0x2, PKCS1 with + * block type 1 padding. + * + * Version: + * This method is added in API version 13. + */ +uint32_t OEMCrypto_SupportedCertificates(); + +/* + * OEMCrypto_Generic_Encrypt + * + * Description: + * This function encrypts a generic buffer of data using the current key. + * + * If the session has an entry in the Usage Table, then OEMCrypto will 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. + * + * Verification: + * The following checks should be performed. If any check fails, an error is + * 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. + * 3. If the current session has an entry in the Usage Table, and the status + * of that entry is "inactive", then return OEMCrypto_ERROR_INVALID_SESSION. + * + * Parameters: + * session (in) - crypto session identifier. + * in_buffer (in) - pointer to memory containing data to be encrypted. + * buffer_length (in) - length of the buffer, in bytes. + * iv (in) - IV for encrypting data. Size is specified by the algorithm. + * algorithm (in) - Specifies which encryption algorithm to use. See + * OEMCrypto_Algorithm for valid values. + * out_buffer (out) - pointer to buffer in which encrypted data should be + * stored. + * + * Returns: + * OEMCrypto_SUCCESS success + * OEMCrypto_ERROR_KEY_EXPIRED + * OEMCrypto_ERROR_NO_DEVICE_KEY + * OEMCrypto_ERROR_INVALID_SESSION + * OEMCrypto_ERROR_INSUFFICIENT_RESOURCES + * OEMCrypto_ERROR_UNKNOWN_FAILURE + * OEMCrypto_ERROR_BUFFER_TOO_LARGE + * + * Buffer Sizes + * OEMCrypto shall support buffer sizes of at least 100 KiB for generic + * crypto operations. + * OEMCrypto shall return OEMCrypto_ERROR_BUFFER_TOO_LARGE if the buffer is + * larger than the supported size. + * + * Threading: + * This function may be called simultaneously with functions on other sessions, + * but not with other functions on this session. + * + * Version: + * This method changed in API version 9. + */ +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_Generic_Decrypt + * + * Description: + * This function decrypts a generic buffer of data using the current key. + * + * If the session has an entry in the Usage Table, then OEMCrypto will 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. + * + * Verification: + * The following checks should be performed. If any check fails, an error is + * returned, and the data is not decrypted. + * 1. The control bit for the current key shall have the Allow_Decrypt set. 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. + * 4. If the current session has an entry in the Usage Table, and the status + * of that entry is "inactive", then return OEMCrypto_ERROR_INVALID_SESSION. + * + * Parameters: + * session (in) - crypto session identifier. + * in_buffer (in) - pointer to memory containing data to be encrypted. + * buffer_length (in) - length of the buffer, in bytes. The algorithm may + * restrict buffer_length to be a multiple of block size. + * iv (in) - IV for encrypting data. Size is 128 bits. + * algorithm (in) - Specifies which encryption algorithm to use. + * out_buffer (out) - pointer to buffer in which decrypted data should be + * stored. + * + * Threading: + * This function may be called simultaneously with functions on other + * sessions, but not with other functions on this session. + * + * Returns: + * OEMCrypto_SUCCESS success + * OEMCrypto_ERROR_KEY_EXPIRED + * OEMCrypto_ERROR_DECRYPT_FAILED + * OEMCrypto_ERROR_NO_DEVICE_KEY + * OEMCrypto_ERROR_INVALID_SESSION + * OEMCrypto_ERROR_INSUFFICIENT_RESOURCES + * OEMCrypto_ERROR_UNKNOWN_FAILURE + * OEMCrypto_ERROR_BUFFER_TOO_LARGE + * + * Buffer Sizes + * OEMCrypto shall support buffer sizes of at least 100 KiB for generic + * crypto operations. + * OEMCrypto shall return OEMCrypto_ERROR_BUFFER_TOO_LARGE if the buffer is + * larger than the supported size. + * + * Version: + * This method changed in API version 9. + */ +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_Generic_Sign + * + * Description: + * This function signs a generic buffer of data using the current key. + * + * If the session has an entry in the Usage Table, then OEMCrypto will 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. + * + * Verification: + * 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. + * 3. If the current session has an entry in the Usage Table, and the status of + * that entry is "inactive", then return OEMCrypto_ERROR_INVALID_SESSION. + * + * Parameters: + * session (in) - crypto session identifier. + * in_buffer (in) - pointer to memory containing data to be encrypted. + * buffer_length (in) - length of the buffer, in bytes. + * algorithm (in) - Specifies which algorithm to use. + * signature (out) - pointer to buffer in which signature should be + * stored. May be null on the first call in order to find required buffer + * size. + * signature_length (in/out) - (in) length of the signature buffer, in bytes. + * (out) actual length of the signature + * + * + * + * Threading: + * This function may be called simultaneously with functions on other + * sessions, but not with other functions on this session. + * + * Returns: + * OEMCrypto_SUCCESS success + * OEMCrypto_ERROR_KEY_EXPIRED + * OEMCrypto_ERROR_SHORT_BUFFER if signature buffer is not large enough to + * hold the output signature. + * OEMCrypto_ERROR_NO_DEVICE_KEY + * OEMCrypto_ERROR_INVALID_SESSION + * OEMCrypto_ERROR_INSUFFICIENT_RESOURCES + * OEMCrypto_ERROR_UNKNOWN_FAILURE + * OEMCrypto_ERROR_BUFFER_TOO_LARGE + * OEMCrypto_ERROR_NOT_IMPLEMENTED + * + * Buffer Sizes + * OEMCrypto shall support buffer sizes of at least 100 KiB for generic + * crypto operations. + * OEMCrypto shall return OEMCrypto_ERROR_BUFFER_TOO_LARGE if the buffer is + * larger than the supported size. + * + * Version: + * This method changed in API version 9. + */ +OEMCryptoResult OEMCrypto_Generic_Sign(OEMCrypto_SESSION session, + const uint8_t* in_buffer, + size_t buffer_length, + OEMCrypto_Algorithm algorithm, + uint8_t* signature, + size_t* signature_length); + +/* + * OEMCrypto_Generic_Verify + * + * Description: + * This function verifies the signature of a generic buffer of data using the + * current key. + * + * If the session has an entry in the Usage Table, then OEMCrypto will 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. + * + * Verification: + * The following checks should be performed. If any check fails, an error is + * returned. + * 1. The control bit for the current key shall have the Allow_Verify set. + * 2. 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. + * 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. + * 5. If the current session has an entry in the Usage Table, and the status + * of that entry is "inactive", then return OEMCrypto_ERROR_INVALID_SESSION. + * + * Parameters: + * session (in) - crypto session identifier. + * in_buffer (in) - pointer to memory containing data to be encrypted. + * buffer_length (in) - length of the buffer, in bytes. + * algorithm (in) - Specifies which algorithm to use. + * signature (in) - pointer to buffer in which signature resides. + * signature_length (in) - length of the signature buffer, in bytes. + * + * Threading: + * This function may be called simultaneously with functions on other + * sessions, but not with other functions on this session. + * + * Returns: + * OEMCrypto_SUCCESS success + * OEMCrypto_ERROR_KEY_EXPIRED + * OEMCrypto_ERROR_SIGNATURE_FAILURE + * OEMCrypto_ERROR_NO_DEVICE_KEY + * OEMCrypto_ERROR_INVALID_SESSION + * OEMCrypto_ERROR_INSUFFICIENT_RESOURCES + * OEMCrypto_ERROR_UNKNOWN_FAILURE + * OEMCrypto_ERROR_BUFFER_TOO_LARGE + * OEMCrypto_ERROR_NOT_IMPLEMENTED + * + * Buffer Sizes + * OEMCrypto shall support buffer sizes of at least 100 KiB for generic + * crypto operations. + * OEMCrypto shall return OEMCrypto_ERROR_BUFFER_TOO_LARGE if the buffer is + * larger than the supported size. + * + * Version: + * This method changed in API version 9. + */ +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); + +/* + * 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 + * was Active, or InactiveUnused if it was Unused. This also increments the + * entry's generation number, and the header's master generation number. The + * entry's flag ForbidReport will be set. This flag prevents an application + * from generating a report of a deactivated license without first saving the + * entry. + * + * Devices that do not implement a Session Usage Table may return + * OEMCrypto_ERROR_NOT_IMPLEMENTED. + * + * Parameters: + * session (in): handle for the session to be used. + * pst (in) - pointer to memory containing Provider Session Token. + * pst_length (in) - length of the pst, in bytes. + * + * Threading: + * This function will not be called simultaneously with any session functions. + * + * Returns: + * OEMCrypto_SUCCESS success + * OEMCrypto_ERROR_INVALID_CONTEXT - no entry has matching PST. + * OEMCrypto_ERROR_NOT_IMPLEMENTED + * OEMCrypto_ERROR_UNKNOWN_FAILURE + * OEMCrypto_ERROR_BUFFER_TOO_LARGE + * + * Buffer Sizes + * OEMCrypto shall support pst sizes of at least 255 bytes. + * OEMCrypto shall return OEMCrypto_ERROR_BUFFER_TOO_LARGE if the buffer is + * too large. + * + * Version: + * This method changed in API version 13. + * + */ +OEMCryptoResult OEMCrypto_DeactivateUsageEntry(OEMCrypto_SESSION session, + const uint8_t* pst, + size_t pst_length); + +/* + * OEMCrypto_ReportUsage + * + * Description: + * 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 the usage entry’s flag ForbidReport is set, indicating the entry has + * not been saved since the entry was deactivated, then the error + * OEMCrypto_ERROR_ENTRY_NEEDS_UPDATE is returned and a report is not + * generated. Similarly, if any key in the session has been used since the + * last call to OEMCrypto_UpdateUsageEntry, then the report is not generated, + * 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. + * + * Valid values for status are: + * 0 = kUnused -- the keys have not been used to decrypt. + * 1 = kActive -- the keys have been used, and have not been deactivated. + * 2 = kInactive -- deprecated. Use kInactiveUsed or kInactiveUnused. + * 3 = kInactiveUsed -- the keys have been marked inactive after a decrypt. + * 4 = kInactiveUnused -- the keys have been marked inactive, no decrypt. + * + * The clock_security_level is reported as follows: + * 0 = Insecure Clock - clock just uses system time. + * 1 = Secure Timer - clock uses secure timer, which cannot be modified by + * user software, when OEMCrypto is active and the system time when + * OEMCrypto is inactive. + * 2 = Software Secure Clock - clock cannot be modified by user software + * when OEMCrypto is active or inactive. + * 3 = Hardware Secure Clock - clock 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. + * + * 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 client_mac_key which is stored in the usage table. The + * HMAC SHA1 signature is used to prevent a rogue application from using + * OMECrypto_GenerateSignature to forge a Usage Report. + * + * This function also copies the client_mac_key and server_mac_key from the + * Usage Table entry to the session. They will be used to verify a signature + * in OEMCrypto_DeleteUsageEntry below. This session will be associated with + * the entry in the Usage Table. + * + * Devices that do not implement a Session Usage Table may return + * OEMCrypto_ERROR_NOT_IMPLEMENTED. + * + * Parameters: + * session (in) - handle for the session to be used. + * pst (in) - pointer to memory containing Provider Session Token. + * pst_length (in) - length of the pst, in bytes. + * buffer (out) - pointer to buffer in which usage report should be + * stored. May be null on the first call in order to find required buffer + * size. + * buffer_length (in/out) - (in) length of the report buffer, in bytes. + * (out) actual length of the report + * + * Threading: + * This function will not be called simultaneously with any session functions. + * + * Returns: + * OEMCrypto_SUCCESS success + * OEMCrypto_ERROR_SHORT_BUFFER if report buffer is not large enough to hold + * the output report. + * OEMCrypto_ERROR_INVALID_SESSION no open session with that id. + * OEMCrypto_ERROR_NOT_IMPLEMENTED + * OEMCrypto_ERROR_INVALID_CONTEXT + * OEMCrypto_ERROR_ENTRY_NEEDS_UPDATE - if no call to UpdateUsageEntry since + * last call to Deactivate or since key use. + * OEMCrypto_ERROR_BUFFER_TOO_LARGE + * OEMCrypto_ERROR_WRONG_PST - report asked for wrong pst. + * + * Buffer Sizes + * OEMCrypto shall support pst sizes of at least 255 bytes. + * OEMCrypto shall return OEMCrypto_ERROR_BUFFER_TOO_LARGE if the buffer is + * too large. + * + * Version: + * This method changed in API version 13. + */ +OEMCryptoResult OEMCrypto_ReportUsage(OEMCrypto_SESSION session, + const uint8_t* pst, size_t pst_length, + uint8_t* buffer, + size_t* buffer_length); + +/* + * 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 previous version + * of OEMCrypto to v13. 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 + * + * Threading: + * This function will not be called simultaneously with any session functions. + * + * Returns: + * OEMCrypto_SUCCESS success + * OEMCrypto_ERROR_NOT_IMPLEMENTED + * OEMCrypto_ERROR_UNKNOWN_FAILURE + * + * Version: + * This method changed in API version 13. + */ +OEMCryptoResult OEMCrypto_DeleteOldUsageTable(); + +/* + * 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 have do not support usage tables, or devices that are will + * not be field upgraded to OEMCrypto v13 may return + * OEMCrypto_ERROR_NOT_IMPLEMENTED. + * + * Threading: + * This function will not be called simultaneously with any session functions. + * + * Returns: + * OEMCrypto_SUCCESS success + * OEMCrypto_ERROR_NOT_IMPLEMENTED + * OEMCrypto_ERROR_UNKNOWN_FAILURE + * + * Version: + * This method changed in API version 13. + */ +OEMCryptoResult OEMCrypto_CreateOldUsageEntry(uint64_t time_since_license_received, + uint64_t time_since_first_decrypt, + uint64_t time_since_last_decrypt, + OEMCrypto_Usage_Entry_Status status, + uint8_t *server_mac_key, + uint8_t *client_mac_key, + const uint8_t* pst, + size_t pst_length); + +/* + * OEMCrypto_IsSRMUpdateSupported + * + * Description: + * Returns true if the device supports SRM files and the file can be updated + * via the function OEMCrypto_LoadSRM. This also returns false for devices + * that do not support an SRM file, devices that do not support HDCP, and + * devices that have no external display support. + * + * Parameters: + * none + * + * Threading: + * This function will not be called simultaneously with any session functions. + * + * Returns: + * true - if LoadSRM is supported. + * false - otherwise. + * + * Version: + * This method is new in API version 13. + */ +bool OEMCrypto_IsSRMUpdateSupported(); + +/* + * OEMCrypto_GetCurrentSRMVersion + * + * Description: + * Returns the version number of the current SRM file. If the device does + * not support SRM files, this will return OEMCrypto_ERROR_NOT_IMPLEMENTED. + * If the device only supports local displays, it would return + * OEMCrypto_LOCAL_DISPLAY_ONLY. If the device has an SRM, but cannot use + * OEMCrypto to update the SRM, then this function would set version to be + * the current version number, and return OEMCrypto_SUCCESS, but it would + * return false from OEMCrypto_IsSRMUpdateSupported. + * + * Parameters: + * version (out): current SRM version number. + * + * Threading: + * This function will not be called simultaneously with any session functions. + * + * Returns: + * OEMCrypto_ERROR_NOT_IMPLEMENTED + * OEMCrypto_SUCCESS + * OEMCrypto_LOCAL_DISPLAY_ONLY - to indicate version was not set, and is not + * needed. + * + * Version: + * This method is new in API version 13. + */ +OEMCryptoResult OEMCrypto_GetCurrentSRMVersion(uint16_t* version); + + +/* + * OEMCrypto_LoadSRM + * + * Description: + * Verify and install a new SRM file. The device shall install the new file + * only if verification passes. If verification fails, the existing SRM will + * be left in place. Verification is defined by DCP, and includes + * verification of the SRM’s signature and verification that the SRM version + * number will not be decreased. See the section HDCP SRM Update above for + * more details about the SRM. This function is for devices that support HDCP + * v2.2 or higher and wish to receive 4k content. + * + * Parameters: + * bufer (in): buffer containing the SRM + * buffer_length (in): length of the SRM, in bytes. + * + * Threading: + * This function will not be called simultaneously with any session functions. + * + * Returns: + * OEMCrypto_SUCCESS - if the file was valid and was installed. + * OEMCrypto_ERROR_INVALID_CONTEXT - if the SRM version is too low, or the + * file is corrupted. + * OEMCrypto_ERROR_SIGNATURE_FAILURE - If the signature is invalid. + * OEMCrypto_ERROR_BUFFER_TOO_LARGE - if the buffer is too large for the device. + * OEMCrypto_ERROR_NOT_IMPLEMENTED + * + * Version: + * This method is new in API version 13. + */ +OEMCryptoResult OEMCrypto_LoadSRM(const uint8_t* buffer, + size_t buffer_length); + +/* + * OEMCrypto_RemoveSRM + * + * Description: + * Delete the current SRM. Any valid SRM, regardless of version number, will + * be installable after this via OEMCrypto_LoadSRM. + * + * This function should not be implemented on production devices, and will + * only be used to verify unit tests on a test device. + * + * Parameters: + * none + * + * Threading: + * This function will not be called simultaneously with any session functions. + * + * Returns: + * OEMCrypto_ERROR_NOT_IMPLEMENTED + * OEMCrypto_SUCCESS + * OEMCrypto_ERROR_UNKNOWN_FAILURE + * + * Version: + * This method is new in API version 13. + */ +OEMCryptoResult OEMCrypto_RemoveSRM(); + +/* + * OEMCrypto_CreateUsageTableHeader + * + * Description: + * This creates a new Usage Table Header with no entries. If there is + * already a generation number stored in secure storage, it will be + * incremented by 1 and used as the new Master Generation Number. This will + * only be called if the CDM layer finds no existing usage table on the file + * system. OEMCrypto will encrypt and sign the new, empty, header and return + * it in the provided buffer. + * + * Devices that do not implement a Session Usage Table may return + * OEMCrypto_ERROR_NOT_IMPLEMENTED. + * + * Parameters: + * [out] header_buffer: pointer to memory where encrypted usage table header + * is written. + * [in/out] header_buffer_length: (in) length of the header_buffer, in bytes. + * (out) actual length of the header_buffer + * + * Threading: + * This function will not be called simultaneously with any session functions. + * + * Returns: + * OEMCrypto_SUCCESS success + * OEMCrypto_ERROR_SHORT_BUFFER - if header_buffer_length is too small. + * OEMCrypto_ERROR_NOT_IMPLEMENTED + * OEMCrypto_ERROR_UNKNOWN_FAILURE + * + * Version: + * This method is new in API version 13. + */ +OEMCryptoResult OEMCrypto_CreateUsageTableHeader(uint8_t* header_buffer, + size_t* header_buffer_length); + +/* + * OEMCrypto_LoadUsageTableHeader + * + * Description: + * This loads the Usage Table Header. The buffer’s signature is verified and + * the buffer is decrypted. OEMCrypto will verify the verification + * string. If the Master Generation Number is more than 1 off, the table is + * considered bad, the headers are NOT loaded, and the error + * OEMCrypto_ERROR_GENERATION_SKEW is returned. If the generation number is + * off by 1, the warning OEMCrypto_WARNING_GENERATION_SKEW is returned but + * the header is still loaded. This warning may be logged by the CDM layer. + * + * + * Parameters: + * [in] buffer: pointer to memory containing encrypted usage table header. + * [in] buffert_length: length of the buffer, in bytes. + * + * Threading: + * This function will not be called simultaneously with any session functions. + * + * Returns: + * OEMCrypto_SUCCESS success + * OEMCrypto_ERROR_SHORT_BUFFER + * OEMCrypto_ERROR_NOT_IMPLEMENTED - some devices do not implement usage + * tables. + * OEMCrypto_ERROR_UNKNOWN_FAILURE + * OEMCrypto_WARNING_GENERATION_SKEW - if the generation number is off by + * exactly 1. + * OEMCrypto_ERROR_GENERATION_SKEW - if the generation number is off by more + * than 1. + * OEMCrypto_ERROR_SIGNATURE_FAILURE - if the signature failed. + * OEMCrypto_ERROR_BAD_MAGIC - verification string does not match. + * + * Version: + * This method is new in API version 13. + */ +OEMCryptoResult OEMCrypto_LoadUsageTableHeader(const uint8_t* buffer, + size_t buffer_length); + +/* + * OEMCrypto_CreateNewUsageEntry + * + * Description: + * This creates a new usage entry. The size of the header will be increased + * by 8 bytes, and secure volatile memory will be allocated for it. The new + * entry will be associated with the given session. The status of the new + * entry will be set to “unused”. OEMCrypto will set *usage_entry_number to + * be the index of the new entry. The first entry created will have index 0. + * The new entry will be initialized with a generation number equal to the + * master generation number, which will also be stored in the header’s new + * slot. Then the master generation number will be incremented. Since each + * entry’s generation number is less than the master generation number, the + * new entry will have a generation number that is larger than all other + * entries and larger than all previously deleted entries. This helps + * prevent a rogue application from deleting an entry and then loading an old + * version of it. + * + * Parameters: + * [in] session: handle for the session to be used. + * [out] usage_entry_number: index of new usage entry. + * + * Threading: + * This function may be called simultaneously with functions on other + * sessions, but not with other functions on this session. + * + * Returns: + * OEMCrypto_SUCCESS success + * OEMCrypto_ERROR_NOT_IMPLEMENTED - some devices do not implement usage tables. + * OEMCrypto_ERROR_INSUFFICIENT_RESOURCES - if there is no room in memory to + * increase the size of the usage table header. The CDM layer can delete some + * entries and then try again, or it can pass the error up to the + * application. + * OEMCrypto_ERROR_UNKNOWN_FAILURE + * + * Version: + * This method is new in API version 13. + */ +OEMCryptoResult OEMCrypto_CreateNewUsageEntry(OEMCrypto_SESSION session, + uint32_t* usage_entry_number); + +/* + * OEMCrypto_LoadUsageEntry + * + * Description: + * This loads a usage table saved previously by UpdateUsageEntry. The + * signature at the beginning of the buffer is verified and the buffer will + * be decrypted. Then the verification field in the entry will be verified. + * The index in the entry must match the index passed in. The generation + * 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. + * + * If the entry is already loaded into another session, then this fails and + * returns OEMCrypto_ERROR_INVALID_SESSION. + * + * Parameters: + * [in] session: handle for the session to be used. + * [in] usage_entry_number: index of existing usage entry. + * [in] buffer: pointer to memory containing encrypted usage table entry. + * [in] buffer_length: length of the buffer, in bytes. + * + * Threading: + * This function may be called simultaneously with functions on other + * sessions, but not with other functions on this session. + * + * Returns: + * OEMCrypto_SUCCESS success + * OEMCrypto_ERROR_SHORT_BUFFER + * OEMCrypto_ERROR_NOT_IMPLEMENTED - some devices do not implement usage + * tables. + * OEMCrypto_ERROR_UNKNOWN_FAILURE - index beyond end of table. + * OEMCrypto_ERROR_INVALID_SESSION - entry associated with another session or + * the index is wrong. + * OEMCrypto_WARNING_GENERATION_SKEW - if the generation number is off by + * exactly 1. + * OEMCrypto_ERROR_GENERATION_SKEW - if the generation number is off by more + * than 1. + * OEMCrypto_ERROR_SIGNATURE_FAILURE - if the signature failed. + * OEMCrypto_ERROR_BAD_MAGIC - verification string does not match. + * + * Version: + * This method is new in API version 13. + */ +OEMCryptoResult OEMCrypto_LoadUsageEntry(OEMCrypto_SESSION session, + uint32_t index, + const uint8_t* buffer, + size_t buffer_size); + +/* + * OEMCrypto_UpdateUsageEntry + * + * 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". + * + * 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 + * and save the usage table before the next call to ReportUsage and before + * 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. + * + * Parameters: + * [in] session: handle for the session to be used. + * [out] header_buffer: pointer to memory where encrypted usage table header + * is written. + * [in/out] header_buffer_length: (in) length of the header_buffer, in bytes. + * (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. + * (out) actual length of the entry_buffer + * + * Threading: + * This function may be called simultaneously with functions on other + * sessions, but not with other functions on this session. + * + * Returns: + * OEMCrypto_SUCCESS success + * OEMCrypto_ERROR_SHORT_BUFFER + * OEMCrypto_ERROR_NOT_IMPLEMENTED - some devices do not implement usage tables. + * OEMCrypto_ERROR_UNKNOWN_FAILURE + * + * Version: + * This method is new in API version 13. + */ +OEMCryptoResult OEMCrypto_UpdateUsageEntry(OEMCrypto_SESSION session, + uint8_t* header_buffer, + size_t* header_buffer_length, + uint8_t* entry_buffer, + size_t* entry_buffer_length); + +/* + * OEMCrypto_ShrinkUsageTableHeader + * + * Description: + * This shrinks the usage table and the header. This function is used by the + * 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. 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 + * OEMCrypto_CreateNewUsageEntry is called, the new entry will have an index + * of new_table_size. + * + * Devices that do not implement a Session Usage Table may return + * OEMCrypto_ERROR_NOT_IMPLEMENTED. + * + * If header_buffer_length is not large enough to hold the new table, it is + * set to the needed value, the generation number is not incremented, and + * OEMCrypto_ERROR_SHORT_BUFFER is returned. + * + * 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 + * is written. + * [in/out] header_buffer_length: (in) length of the header_buffer, in bytes. + * (out) actual length of the header_buffer + * + * Threading: + * This function will not be called simultaneously with any session functions. + * + * Returns: + * OEMCrypto_SUCCESS success + * OEMCrypto_ERROR_SHORT_BUFFER + * OEMCrypto_ERROR_NOT_IMPLEMENTED + * OEMCrypto_ERROR_UNKNOWN_FAILURE + * + * Version: + * This method is new in API version 13. + */ +OEMCryptoResult OEMCrypto_ShrinkUsageTableHeader(uint32_t new_entry_count, + uint8_t* header_buffer, + size_t* header_buffer_length); + +/* + * OEMCrypto_MoveEntry + * + * Description: Moves the entry associated with the current session from one + * location in the usage table header to another. This function is used by + * the CDM layer to defragment the usage table. This does not modify any data + * in the entry, except the index and the generation number. The index in + * the session’s usage entry will be changed to new_index. The generation + * number in session’s usage entry and in the header for new_index will be + * increased to the master generation number, and then the master generation + * number is incremented. If there was an existing entry at the new location, + * it will be overwritten. It is an error to call this when the entry that + * was at new_index is associated with a currently open session. In this + * case, the error code OEMCrypto_ERROR_ENTRY_IN_USE is returned. It is the + * CDM layer’s responsibility to call UpdateUsageEntry after moving an entry. + * It is an error for new_index to be beyond the end of the existing usage + * table header. + * + * Devices that do not implement a Session Usage Table may return + * OEMCrypto_ERROR_NOT_IMPLEMENTED. + * + * Parameters: + * [in] session: handle for the session to be used. + * [in] new_index: new index to be used for the session’s usage entry + * + * Threading: + * This function will not be called simultaneously with any session functions. + * + * Returns: + * OEMCrypto_SUCCESS success + * OEMCrypto_ERROR_NOT_IMPLEMENTED + * OEMCrypto_ERROR_UNKNOWN_FAILURE + * OEMCrypto_ERROR_BUFFER_TOO_LARGE + * + * Version: + * This method is new in API version 13. + */ +OEMCryptoResult OEMCrypto_MoveEntry(OEMCrypto_SESSION session, + uint32_t new_index); + +/* + * 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 previous version + * of OEMCrypto to v13. 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. + * + * Threading: + * This function will not be called simultaneously with any session functions. + * + * Returns: + * OEMCrypto_SUCCESS success + * OEMCrypto_ERROR_NOT_IMPLEMENTED + * OEMCrypto_ERROR_UNKNOWN_FAILURE + * + * Version: + * This method is new in API version 13. + */ +OEMCryptoResult OEMCrypto_CopyOldUsageEntry(OEMCrypto_SESSION session, + const uint8_t*pst, + size_t pst_length); + +/* + * OEMCrypto_GetAnalogOutputFlags + * + * Description: + * Returns whether the device supports analog output or not. This + * information will be sent to the license server, and may be used to + * determine the type of license allowed. This function is for reporting + * only. It is paired with the key control block flags Disable_Analog_Output + * and CGMS. + * + * Parameters: + * none. + * + * Threading: + * This function will not be called simultaneously with any session functions. + * + * Returns: + * Returns a bitwise OR of the following flags. + * 0x0 = OEMCrypto_No_Analog_Output -- the device has no analog output. + * 0x1 = OEMCrypto_Supports_Analog_Output - the device does have analog + * output. + * 0x2 = OEMCrypto_Can_Disable_Analog_Ouptput - the device does have analog + * output, but it will disable analog output if required by the key + * control block. + * 0x4 = OEMCrypto_Supports_CGMS_A - the device supports signaling 2-bit + * CGMS-A, if required by the key control block + * + * Version: + * This method is new in API version 14. + */ +uint32_t OEMCrypto_GetAnalogOutputFlags(); + +#ifdef __cplusplus +} +#endif + +#endif // OEMCRYPTO_CENC_H_ diff --git a/include/level3.h b/include/level3.h new file mode 100644 index 0000000..6fd157b --- /dev/null +++ b/include/level3.h @@ -0,0 +1,402 @@ +// Copyright 2013 Google Inc. All Rights Reserved. + +/********************************************************************* + * level3.h + * + * Reference APIs needed to support Widevine's crypto algorithms. + *********************************************************************/ + +#ifndef LEVEL3_OEMCRYPTO_H_ +#define LEVEL3_OEMCRYPTO_H_ + +#include +#include + +#include "level3_file_system.h" +#include "OEMCryptoCENC.h" + +namespace wvoec3 { + +#ifdef DYNAMIC_ADAPTER +#define Level3_IsInApp _lcc00 +#define Level3_Initialize _lcc01 +#define Level3_Terminate _lcc02 +#define Level3_InstallKeybox _lcc03 +#define Level3_GetKeyData _lcc04 +#define Level3_IsKeyboxValid _lcc05 +#define Level3_GetRandom _lcc06 +#define Level3_GetDeviceID _lcc07 +#define Level3_WrapKeybox _lcc08 +#define Level3_OpenSession _lcc09 +#define Level3_CloseSession _lcc10 +#define Level3_DecryptCENC _lcc11 +#define Level3_GenerateDerivedKeys _lcc12 +#define Level3_GenerateSignature _lcc13 +#define Level3_GenerateNonce _lcc14 +#define Level3_RefreshKeys _lcc16 +#define Level3_SelectKey _lcc17 +#define Level3_RewrapDeviceRSAKey _lcc18 +#define Level3_LoadDeviceRSAKey _lcc19 +#define Level3_GenerateRSASignature _lcc20 +#define Level3_DeriveKeysFromSessionKey _lcc21 +#define Level3_APIVersion _lcc22 +#define Level3_SecurityLevel _lcc23 +#define Level3_Generic_Encrypt _lcc24 +#define Level3_Generic_Decrypt _lcc25 +#define Level3_Generic_Sign _lcc26 +#define Level3_Generic_Verify _lcc27 +#define Level3_GetHDCPCapability _lcc28 +#define Level3_SupportsUsageTable _lcc29 +#define Level3_UpdateUsageTable _lcc30 +#define Level3_DeactivateUsageEntry _lcc31 +#define Level3_ReportUsage _lcc32 +#define Level3_DeleteUsageEntry _lcc33 +#define Level3_DeleteOldUsageTable _lcc34 +#define Level3_GetMaxNumberOfSessions _lcc37 +#define Level3_GetNumberOfOpenSessions _lcc38 +#define Level3_IsAntiRollbackHwPresent _lcc39 +#define Level3_CopyBuffer _lcc40 +#define Level3_QueryKeyControl _lcc41 +#define Level3_LoadTestKeybox _lcc42 +#define Level3_ForceDeleteUsageEntry _lcc43 +#define Level3_LoadTestRSAKey _lcc45 +#define Level3_SecurityPatchLevel _lcc46 +#define Level3_GetProvisioningMethod _lcc49 +#define Level3_GetOEMPublicCertificate _lcc50 +#define Level3_RewrapDeviceRSAKey30 _lcc51 +#define Level3_SupportedCertificates _lcc52 +#define Level3_IsSRMUpdateSupported _lcc53 +#define Level3_GetCurrentSRMVersion _lcc54 +#define Level3_LoadSRM _lcc55 +#define Level3_LoadKeys _lcc56 +#define Level3_RemoveSRM _lcc57 +#define Level3_CreateUsageTableHeader _lcc61 +#define Level3_LoadUsageTableHeader _lcc62 +#define Level3_CreateNewUsageEntry _lcc63 +#define Level3_LoadUsageEntry _lcc64 +#define Level3_UpdateUsageEntry _lcc65 +#define Level3_ShrinkUsageTableHeader _lcc67 +#define Level3_MoveEntry _lcc68 +#define Level3_CopyOldUsageEntry _lcc69 +#define Level3_CreateOldUsageEntry _lcc70 +#else +#define Level3_Initialize _oecc01 +#define Level3_Terminate _oecc02 +#define Level3_InstallKeybox _oecc03 +#define Level3_GetKeyData _oecc04 +#define Level3_IsKeyboxValid _oecc05 +#define Level3_GetRandom _oecc06 +#define Level3_GetDeviceID _oecc07 +#define Level3_WrapKeybox _oecc08 +#define Level3_OpenSession _oecc09 +#define Level3_CloseSession _oecc10 +#define Level3_GenerateDerivedKeys _oecc12 +#define Level3_GenerateSignature _oecc13 +#define Level3_GenerateNonce _oecc14 +#define Level3_RefreshKeys _oecc16 +#define Level3_SelectKey _oecc17 +#define Level3_RewrapDeviceRSAKey _oecc18 +#define Level3_LoadDeviceRSAKey _oecc19 +#define Level3_DeriveKeysFromSessionKey _oecc21 +#define Level3_APIVersion _oecc22 +#define Level3_SecurityLevel _oecc23 +#define Level3_Generic_Encrypt _oecc24 +#define Level3_Generic_Decrypt _oecc25 +#define Level3_Generic_Sign _oecc26 +#define Level3_Generic_Verify _oecc27 +#define Level3_SupportsUsageTable _oecc29 +#define Level3_UpdateUsageTable _oecc30 +#define Level3_ReportUsage _oecc32 +#define Level3_DeleteUsageEntry _oecc33 +#define Level3_DeleteOldUsageTable _oecc34 +#define Level3_GenerateRSASignature _oecc36 +#define Level3_GetMaxNumberOfSessions _oecc37 +#define Level3_GetNumberOfOpenSessions _oecc38 +#define Level3_IsAntiRollbackHwPresent _oecc39 +#define Level3_CopyBuffer _oecc40 +#define Level3_QueryKeyControl _oecc41 +#define Level3_LoadTestKeybox _oecc42 +#define Level3_ForceDeleteUsageEntry _oecc43 +#define Level3_GetHDCPCapability _oecc44 +#define Level3_LoadTestRSAKey _oecc45 +#define Level3_SecurityPatchLevel _oecc46 +#define Level3_DecryptCENC _oecc48 +#define Level3_GetProvisioningMethod _oecc49 +#define Level3_GetOEMPublicCertificate _oecc50 +#define Level3_RewrapDeviceRSAKey30 _oecc51 +#define Level3_SupportedCertificates _oecc52 +#define Level3_IsSRMUpdateSupported _oecc53 +#define Level3_GetCurrentSRMVersion _oecc54 +#define Level3_LoadSRM _oecc55 +#define Level3_LoadKeys _oecc56 +#define Level3_RemoveSRM _oecc57 +#define Level3_CreateUsageTableHeader _oecc61 +#define Level3_LoadUsageTableHeader _oecc62 +#define Level3_CreateNewUsageEntry _oecc63 +#define Level3_LoadUsageEntry _oecc64 +#define Level3_UpdateUsageEntry _oecc65 +#define Level3_DeactivateUsageEntry _oecc66 +#define Level3_ShrinkUsageTableHeader _oecc67 +#define Level3_MoveEntry _oecc68 +#define Level3_CopyOldUsageEntry _oecc69 +#define Level3_CreateOldUsageEntry _oecc70 +#endif + +#define Level3_GetInitializationState _oecl3o01 + +extern "C" { + +bool Level3_IsInApp(); +OEMCryptoResult Level3_Initialize(void); +OEMCryptoResult Level3_Terminate(void); +OEMCryptoResult Level3_OpenSession(OEMCrypto_SESSION *session); +OEMCryptoResult Level3_CloseSession(OEMCrypto_SESSION session); +OEMCryptoResult Level3_GenerateDerivedKeys(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 Level3_GenerateNonce(OEMCrypto_SESSION session, + uint32_t* nonce); +OEMCryptoResult Level3_GenerateSignature(OEMCrypto_SESSION session, + const uint8_t* message, + size_t message_length, + uint8_t* signature, + size_t* signature_length); +OEMCryptoResult Level3_LoadKeys( + OEMCrypto_SESSION session, const uint8_t* message, size_t message_length, + const uint8_t* signature, size_t signature_length, + const uint8_t* enc_mac_keys_iv, const uint8_t* enc_mac_keys, + size_t num_keys, const OEMCrypto_KeyObject* key_array, const uint8_t* pst, + size_t pst_length, const uint8_t* srm_requirement); +OEMCryptoResult Level3_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 Level3_QueryKeyControl(OEMCrypto_SESSION session, + const uint8_t* key_id, + size_t key_id_length, + uint8_t* key_control_block, + size_t* key_control_block_length); +OEMCryptoResult Level3_SelectKey(const OEMCrypto_SESSION session, + const uint8_t* key_id, + size_t key_id_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_CopyBuffer(const uint8_t *data_addr, + size_t data_length, + OEMCrypto_DestBufferDesc* out_buffer, + uint8_t subsample_flags); +OEMCryptoResult Level3_WrapKeybox(const uint8_t *keybox, + size_t keyBoxLength, + uint8_t *wrappedKeybox, + size_t *wrappedKeyBoxLength, + const uint8_t *transportKey, + size_t transportKeyLength); +OEMCryptoResult Level3_InstallKeybox(const uint8_t *keybox, + size_t keyBoxLength); +OEMCrypto_ProvisioningMethod Level3_GetProvisioningMethod(); +OEMCryptoResult Level3_GetOEMPublicCertificate(OEMCrypto_SESSION session, + uint8_t *public_cert, + size_t *public_cert_length); +OEMCryptoResult Level3_LoadTestKeybox(); +OEMCryptoResult Level3_IsKeyboxValid(void); +OEMCryptoResult Level3_GetDeviceID(uint8_t* deviceID, + size_t *idLength); +OEMCryptoResult Level3_GetKeyData(uint8_t* keyData, + size_t *keyDataLength); +OEMCryptoResult Level3_GetRandom(uint8_t* randomData, + size_t dataLength); +OEMCryptoResult Level3_RewrapDeviceRSAKey30(OEMCrypto_SESSION session, + const uint32_t *nonce, + const uint8_t* encrypted_message_key, + size_t encrypted_message_key_length, + const uint8_t* enc_rsa_key, + size_t enc_rsa_key_length, + const uint8_t* enc_rsa_key_iv, + uint8_t* wrapped_rsa_key, + size_t* wrapped_rsa_key_length); +OEMCryptoResult Level3_RewrapDeviceRSAKey(OEMCrypto_SESSION session, + const uint8_t* message, + size_t message_length, + const uint8_t* signature, + size_t signature_length, + const uint32_t *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 Level3_LoadDeviceRSAKey(OEMCrypto_SESSION session, + const uint8_t* wrapped_rsa_key, + size_t wrapped_rsa_key_length); +OEMCryptoResult Level3_LoadTestRSAKey(); +OEMCryptoResult Level3_GenerateRSASignature(OEMCrypto_SESSION session, + const uint8_t* message, + size_t message_length, + uint8_t* signature, + size_t *signature_length, + RSA_Padding_Scheme padding_scheme); +OEMCryptoResult Level3_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, + size_t enc_key_context_length); +uint32_t Level3_APIVersion(); +uint8_t Level3_SecurityPatchLevel(); +const char* Level3_SecurityLevel(); +OEMCryptoResult Level3_GetHDCPCapability(OEMCrypto_HDCP_Capability* current, + OEMCrypto_HDCP_Capability* maximum); +bool Level3_SupportsUsageTable(); +bool Level3_IsAntiRollbackHwPresent(); +OEMCryptoResult Level3_GetNumberOfOpenSessions(size_t* count); +OEMCryptoResult Level3_GetMaxNumberOfSessions(size_t* maximum); +uint32_t Level3_SupportedCertificates(); +OEMCryptoResult Level3_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); +OEMCryptoResult Level3_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); +OEMCryptoResult Level3_Generic_Sign(OEMCrypto_SESSION session, + const uint8_t* in_buffer, + size_t buffer_length, + OEMCrypto_Algorithm algorithm, + uint8_t* signature, + size_t* signature_length); +OEMCryptoResult Level3_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 Level3_UpdateUsageTable(); +OEMCryptoResult Level3_DeactivateUsageEntry(OEMCrypto_SESSION session, + const uint8_t *pst, + size_t pst_length); +OEMCryptoResult Level3_ReportUsage(OEMCrypto_SESSION session, + const uint8_t *pst, + size_t pst_length, + uint8_t *buffer, + size_t *buffer_length); +OEMCryptoResult Level3_DeleteUsageEntry(OEMCrypto_SESSION session, + const uint8_t* pst, + size_t pst_length, + const uint8_t *message, + size_t message_length, + const uint8_t *signature, + size_t signature_length); +OEMCryptoResult Level3_ForceDeleteUsageEntry(const uint8_t* pst, + size_t pst_length); +OEMCryptoResult Level3_DeleteOldUsageTable(); +bool Level3_IsSRMUpdateSupported(); +OEMCryptoResult Level3_GetCurrentSRMVersion(uint16_t* version); +OEMCryptoResult Level3_LoadSRM(const uint8_t* buffer, + size_t buffer_length); +OEMCryptoResult Level3_RemoveSRM(); +uint32_t Level3_SupportsDecryptHash(); +OEMCryptoResult Level3_SetDecryptHash(OEMCrypto_SESSION session, + const uint8_t* hash, + size_t hash_length); +OEMCryptoResult Level3_VerifyDecryptHash(OEMCrypto_SESSION session, + uint64_t* failure_data); +OEMCryptoResult Level3_CreateUsageTableHeader(uint8_t* header_buffer, + size_t* header_buffer_length); +OEMCryptoResult Level3_LoadUsageTableHeader(const uint8_t* buffer, + size_t buffer_length); +OEMCryptoResult Level3_CreateNewUsageEntry(OEMCrypto_SESSION session, + uint32_t *usage_entry_number); +OEMCryptoResult Level3_LoadUsageEntry(OEMCrypto_SESSION session, + uint32_t index, + const uint8_t *buffer, + size_t buffer_size); +OEMCryptoResult Level3_UpdateUsageEntry(OEMCrypto_SESSION session, + uint8_t* header_buffer, + size_t* header_buffer_length, + uint8_t* entry_buffer, + size_t* entry_buffer_length); +OEMCryptoResult Level3_ShrinkUsageTableHeader(uint32_t new_table_size, + uint8_t* header_buffer, + size_t* header_buffer_length); +OEMCryptoResult Level3_MoveEntry(OEMCrypto_SESSION session, + uint32_t new_index); +OEMCryptoResult Level3_CopyOldUsageEntry(OEMCrypto_SESSION session, + const uint8_t*pst, + size_t pst_length); +OEMCryptoResult Level3_CreateOldUsageEntry(uint64_t time_since_license_received, + uint64_t time_since_first_decrypt, + uint64_t time_since_last_decrypt, + OEMCrypto_Usage_Entry_Status status, + uint8_t *server_mac_key, + uint8_t *client_mac_key, + const uint8_t* pst, + size_t pst_length); + +/* + * Level3_GetInitializationState + * + * Description: + * Return any warning or error condition which occurred during + * initialization. On some platforms, this value will be logged and metrics + * will be gathered on production devices. This is an optional feature, and + * OEMCrypto may always return 0, even if Level3_Initialize failed. This + * function may be called whether Level3_Initialize succeeded or not. + * + * Parameters: + * N/A + * + * Threading: + * No other function calls will be made while this function is running. + * + * Returns: + * 0 - no warnings or errors during initialization + * + * Version: + * This method is new in API version 14. + */ +OEMCryptoResult Level3_GetInitializationState(void); + +} // extern "C" + +// The following are interfaces needed for Level3 OEMCrypto specifically, which +// partners are expected to implement. + +// Returns a stable, unique identifier for the device. This could be a +// serial number or any other character sequence representing that device. +// The parameter |len| needs to be changed to reflect the length of the +// unique identifier. +const char *getUniqueID(size_t *len); + +// Returns a 64-bit unsigned integer to be used as a random seed for RNG. +// If the operation is unsuccessful, this function returns 0. +// We provide a sample implementation under the name generate_entropy_linux.cpp +// which partners should use if they can. +uint64_t generate_entropy(); + +// Creates and returns an OEMCrypto_Level3FileSystem implementation. +OEMCrypto_Level3FileSystem* createLevel3FileSystem(); + +// Deletes the pointer retrieved by the function above. +void deleteLevel3FileSystem(OEMCrypto_Level3FileSystem* file_system); + +} // namespace wvoec3 + +#endif // LEVEL3_OEMCRYPTO_H_ diff --git a/include/level3_file_system.h b/include/level3_file_system.h new file mode 100644 index 0000000..c1474ad --- /dev/null +++ b/include/level3_file_system.h @@ -0,0 +1,29 @@ +// Copyright 2017 Google Inc. All Rights Reserved + +/********************************************************************* + * level3_file_system.h + * + * File system for OEMCrypto Level3 file operations. + *********************************************************************/ + +#ifndef LEVEL3_FILE_SYSTEM_H_ +#define LEVEL3_FILE_SYSTEM_H_ + +#include + +namespace wvoec3 { + +class OEMCrypto_Level3FileSystem { + public: + virtual ~OEMCrypto_Level3FileSystem() {} + virtual ssize_t Read(const char *filename, void *buffer, size_t size) = 0; + virtual ssize_t Write(const char *filename, const void *buffer, + size_t size) = 0; + virtual bool Exists(const char *filename) = 0; + virtual ssize_t FileSize(const char *filename) = 0; + virtual bool Remove(const char *filename) = 0; +}; + +} // namespace wvoec3 + +#endif diff --git a/include/oemcrypto_logging.h b/include/oemcrypto_logging.h new file mode 100644 index 0000000..5413f4b --- /dev/null +++ b/include/oemcrypto_logging.h @@ -0,0 +1,65 @@ +// Copyright 2014 Google Inc. All Rights Reserved. + +#ifndef WVOEC_OEMCRYPTO_LOGGING_H_ +#define WVOEC_OEMCRYPTO_LOGGING_H_ + +#include +#include + +#include "OEMCryptoCENC.h" +#include "log.h" + +namespace wvoec_mock { + +// The constants below represent integers with a single "on" bit that +// represents categories of logging This allows users to specify with +// more precision what they want to log. LogCategoryEnabled(category) +// is used to see if the category passed in the parameters is to +// be logged based on the current settings. Categories can be combines +// using the | (or) bitwise operator. For example +// LogCategoryEnabled(category1 | category2) will return true if +// category1 and/or category2 are set to logging. + +const int kLoggingTraceOEMCryptoCalls = 0x01; // All except decrypt calls. +const int kLoggingDumpContentKeys = 0x02; +const int kLoggingDumpKeyControlBlocks = 0x04; +const int kLoggingDumpDerivedKeys = 0x08; +const int kLoggingTraceNonce = 0x10; +const int kLoggingTraceDecryption = 0x20; +const int kLoggingTraceUsageTable = 0x40; +const int kLoggingTraceDecryptCalls = 0x80; +const int kLoggingDumpTraceAll = 0xFF; + +void SetLoggingSettings(int level, int categories); + +// set level of logging +void SetLoggingLevel(int level); + +void TurnOffLoggingForAllCategories(); + +// Returns true if the category passed is set to logging. +// Returns false otherwise. The category constant declared +// above are passed. +bool LogCategoryEnabled(int category); + +// Turn on logging for the categories passed. +void AddLoggingForCategories(int categories); + +// Turn off logging for the categories passed. +void RemoveLoggingForCategories(int categories); + +void dump_hex_helper(std::string& buffer, std::string name, + const uint8_t* vector, size_t length); + +void dump_hex(std::string name, const uint8_t* vector, size_t length); + +void dump_array_part_helper(std::string& buffer, std::string array, + size_t index, std::string name, + const uint8_t* vector, size_t length); + +void dump_array_part(std::string array, size_t index, std::string name, + const uint8_t* vector, size_t length); + +} // namespace wvoec_mock + +#endif diff --git a/include/pst_report.h b/include/pst_report.h new file mode 100644 index 0000000..cd21e23 --- /dev/null +++ b/include/pst_report.h @@ -0,0 +1,146 @@ +// Copyright 2017 Google Inc. All Rights Reserved. + +/********************************************************************* + * pst_report.h + * + * Reference APIs needed to support Widevine's crypto algorithms. + *********************************************************************/ + +#ifndef PST_REPORT_H_ +#define PST_REPORT_H_ + +#include +#include +#include + +#include "OEMCryptoCENC.h" +#include "string_conversions.h" // needed for htonll64. + +namespace wvcdm { + +class Unpacked_PST_Report { + public: + // This object does not own the buffer, and does not check that buffer + // is not null. + Unpacked_PST_Report(uint8_t *buffer) : buffer_(buffer) {} + + // Copy and move semantics of this class is like that of a pointer. + Unpacked_PST_Report(const Unpacked_PST_Report& other) : + buffer_(other.buffer_) {} + + Unpacked_PST_Report& operator=(const Unpacked_PST_Report& other) { + buffer_ = other.buffer_; + return *this; + } + + size_t report_size() const { + return pst_length() + kraw_pst_report_size; + } + + static size_t report_size(size_t pst_length) { + return pst_length + kraw_pst_report_size; + } + + uint8_t status() const { + return static_cast(* (buffer_ + kstatus_offset)); + } + + void set_status(uint8_t value) { + buffer_[kstatus_offset] = value; + } + + uint8_t* signature() { + return buffer_ + ksignature_offset; + } + + uint8_t clock_security_level() const { + return static_cast(* (buffer_ + kclock_security_level_offset)); + } + + void set_clock_security_level(uint8_t value) { + buffer_[kclock_security_level_offset] = value; + } + + uint8_t pst_length() const { + return static_cast(* (buffer_ + kpst_length_offset)); + } + + void set_pst_length(uint8_t value) { + buffer_[kpst_length_offset] = value; + } + + uint8_t padding() const { + return static_cast(* (buffer_ + kpadding_offset)); + } + + void set_padding(uint8_t value) { + buffer_[kpadding_offset] = value; + } + + // In host byte order. + int64_t seconds_since_license_received() const { + int64_t time; + memcpy(&time, buffer_ + kseconds_since_license_received_offset, + sizeof(int64_t)); + return wvcdm::ntohll64(time); + } + + // Parameter time is in host byte order. + void set_seconds_since_license_received(int64_t time) const { + time = wvcdm::ntohll64(time); + memcpy(buffer_ + kseconds_since_license_received_offset, &time, + sizeof(int64_t)); + } + + // In host byte order. + int64_t seconds_since_first_decrypt() const { + int64_t time; + memcpy(&time, buffer_ + kseconds_since_first_decrypt_offset, + sizeof(int64_t)); + return wvcdm::ntohll64(time); + } + + // Parameter time is in host byte order. + void set_seconds_since_first_decrypt(int64_t time) const { + time = wvcdm::ntohll64(time); + memcpy(buffer_ + kseconds_since_first_decrypt_offset, &time, + sizeof(int64_t)); + } + + // In host byte order. + int64_t seconds_since_last_decrypt() const { + int64_t time; + memcpy(&time, buffer_ + kseconds_since_last_decrypt_offset, + sizeof(int64_t)); + return wvcdm::ntohll64(time); + } + + // Parameter time is in host byte order. + void set_seconds_since_last_decrypt(int64_t time) const { + time = wvcdm::ntohll64(time); + memcpy(buffer_ + kseconds_since_last_decrypt_offset, &time, + sizeof(int64_t)); + } + + uint8_t* pst() { + return (buffer_ + kpst_offset); + } + + private: + uint8_t *buffer_; + + // Size of the PST_Report without the pst string. + static const size_t kraw_pst_report_size = 48; + static const size_t ksignature_offset = 0; + static const size_t kstatus_offset = 20; + static const size_t kclock_security_level_offset = 21; + static const size_t kpst_length_offset = 22; + static const size_t kpadding_offset = 23; + static const size_t kseconds_since_license_received_offset = 24; + static const size_t kseconds_since_first_decrypt_offset = 32; + static const size_t kseconds_since_last_decrypt_offset = 40; + static const size_t kpst_offset = 48; +}; +} // namespace wvcdm + +#endif // PST_REPORT_H_ diff --git a/mock/oec_mock.gyp b/mock/oec_mock.gyp new file mode 100644 index 0000000..b54e11d --- /dev/null +++ b/mock/oec_mock.gyp @@ -0,0 +1,41 @@ +# Copyright 2013 Google Inc. All Rights Reserved. + +{ + 'target_defaults': { + # It seems that if one target uses -fPIC, then all targets will need that + # flag or else there will be linking errors. For generating a shared + # library, we need position independent code. + 'cflags': [ + '-g', + '-fPIC', + '-std=gnu++98', + ], + 'ldflags': [ + '-fPIC', + ], + }, + 'targets': [ + { + 'target_name': 'oec_mock', + 'type': 'static_library', + 'sources': [ + 'src/oemcrypto_keybox_testkey.cpp', + 'src/oemcrypto_engine_device_properties.cpp', + ], + 'variables': { + 'oec_mock_dir': '.', + }, + 'includes': [ + 'oec_mock_kernel.gypi', + ], + }, + { + 'target_name': 'oec_mock_shared', + 'type': 'shared_library', + 'dependencies': [ + 'oec_mock' + # TODO(joeyparrish or fredgc): circular dependencies. 'wvcdm_sysdep' + ], + }, + ], +} diff --git a/mock/oec_mock_kernel.gypi b/mock/oec_mock_kernel.gypi new file mode 100644 index 0000000..1d3024f --- /dev/null +++ b/mock/oec_mock_kernel.gypi @@ -0,0 +1,37 @@ +# Copyright 2013 Google Inc. All Rights Reserved. + +# Define oec_mock_dir and include into your oec target. + +{ + 'include_dirs': [ + '<(oec_mock_dir)/../../core/include', # for lock.h and wvcdm_types.h + '<(oec_mock_dir)/../include', + '<(oec_mock_dir)/src', + ], + 'direct_dependent_settings': { + 'include_dirs': [ + '<(oec_mock_dir)/../../core/include', + '<(oec_mock_dir)/../include', + '<(oec_mock_dir)/src', + ], + }, + 'sources': [ + '<(oec_mock_dir)/src/keys.cpp', + '<(oec_mock_dir)/src/oemcrypto_auth_mock.cpp', + '<(oec_mock_dir)/src/oemcrypto_engine_mock.cpp', + '<(oec_mock_dir)/src/oemcrypto_key_mock.cpp', + '<(oec_mock_dir)/src/oemcrypto_keybox_mock.cpp', + '<(oec_mock_dir)/src/oemcrypto_mock.cpp', + '<(oec_mock_dir)/src/oemcrypto_nonce_table.cpp', + '<(oec_mock_dir)/src/oemcrypto_old_usage_table_mock.cpp', + '<(oec_mock_dir)/src/oemcrypto_rsa_key_shared.cpp', + '<(oec_mock_dir)/src/oemcrypto_session.cpp', + '<(oec_mock_dir)/src/oemcrypto_session_key_table.cpp', + '<(oec_mock_dir)/src/oemcrypto_usage_table_mock.cpp', + '<(oec_mock_dir)/src/wvcrc.cpp', + '<(oec_mock_dir)/src/oemcrypto_logging.cpp', + ], + 'dependencies': [ + '<(oec_mock_dir)/../../third_party/boringssl/boringssl.gyp:crypto', + ], +} diff --git a/mock/src/keys.cpp b/mock/src/keys.cpp new file mode 100644 index 0000000..380e032 --- /dev/null +++ b/mock/src/keys.cpp @@ -0,0 +1,10 @@ +// If you are using a baked-in certificate instead of supporting keyboxes, +// you should have received a keys.cpp from Widevine that replaces this file. +// +// If you are not using a baked-in certificate, then you may ignore this file, +// as it intentionally defines an empty key. + +#include "keys.h" + +const uint8_t kPrivateKey[] = { 0, }; +const size_t kPrivateKeySize = 0; diff --git a/mock/src/keys.h b/mock/src/keys.h new file mode 100644 index 0000000..e6c0145 --- /dev/null +++ b/mock/src/keys.h @@ -0,0 +1,11 @@ +// This header is used to access the baked-in certificate if one is in use. +#ifndef KEYS_H_ +#define KEYS_H_ + +#include +#include + +extern const uint8_t kPrivateKey[]; +extern const size_t kPrivateKeySize; + +#endif // KEYS_H_ diff --git a/mock/src/oem_cert.cpp b/mock/src/oem_cert.cpp new file mode 100644 index 0000000..3df15d6 --- /dev/null +++ b/mock/src/oem_cert.cpp @@ -0,0 +1,392 @@ +// This file contains the test OEM cert. + +#include "oem_cert.h" + +namespace wvoec_mock { + +extern const uint32_t kOEMSystemId_Prod = 7346; + +extern const uint8_t kOEMPrivateKey_Prod[] = { + 0x30, 0x82, 0x06, 0xfe, 0x02, 0x01, 0x00, 0x30, 0x0d, 0x06, 0x09, 0x2a, + 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00, 0x04, 0x82, + 0x06, 0xe8, 0x30, 0x82, 0x06, 0xe4, 0x02, 0x01, 0x00, 0x02, 0x82, 0x01, + 0x81, 0x00, 0xf5, 0x09, 0x64, 0x4a, 0x26, 0xfe, 0xc0, 0x98, 0x55, 0x6a, + 0x1d, 0x5d, 0x1c, 0xc7, 0x38, 0xaf, 0xfd, 0x49, 0x9e, 0x85, 0x3f, 0xd6, + 0x45, 0x0e, 0x99, 0x09, 0x85, 0x69, 0x84, 0x3c, 0xfe, 0x72, 0xa5, 0x56, + 0xfa, 0x11, 0x4f, 0x6b, 0x7d, 0x32, 0x2b, 0x0c, 0xbf, 0x8f, 0xac, 0x47, + 0x96, 0x22, 0x82, 0x3d, 0xf5, 0x64, 0x74, 0x7e, 0x62, 0x68, 0x74, 0xcd, + 0x0a, 0xec, 0x84, 0xc5, 0x15, 0x06, 0x0e, 0x5a, 0x2f, 0x20, 0xe3, 0xc9, + 0x67, 0xcd, 0xdd, 0x01, 0xb8, 0xb3, 0x18, 0x87, 0x8c, 0xa9, 0x58, 0x86, + 0x0f, 0xb6, 0xc3, 0x42, 0x7e, 0x87, 0x48, 0x5e, 0x10, 0x49, 0xc7, 0xd7, + 0xb7, 0xb8, 0xa6, 0x34, 0x08, 0x0c, 0x94, 0xf4, 0xbb, 0x2a, 0x06, 0xa4, + 0x4f, 0xec, 0xbc, 0xc4, 0x37, 0xbe, 0x99, 0x10, 0x23, 0x37, 0x24, 0xb1, + 0xdf, 0xcb, 0xe6, 0x3f, 0xc1, 0xf0, 0x0f, 0x04, 0x03, 0xc8, 0xb0, 0x1e, + 0xd6, 0xb8, 0xae, 0x77, 0xe1, 0x4d, 0x6d, 0x97, 0x69, 0x6d, 0x8a, 0x73, + 0x66, 0x32, 0x57, 0x6f, 0xcf, 0xea, 0x1e, 0x7b, 0x87, 0x03, 0x75, 0xb1, + 0xef, 0x83, 0x64, 0x26, 0xf1, 0x3f, 0xbf, 0xe6, 0x28, 0x03, 0x72, 0x57, + 0xbf, 0x47, 0x29, 0x99, 0x8f, 0x74, 0x1d, 0x01, 0x16, 0xad, 0xb2, 0xdf, + 0x80, 0xa4, 0xd3, 0x8b, 0xeb, 0x61, 0xd1, 0x40, 0x68, 0xb9, 0xa2, 0xa5, + 0xef, 0x2b, 0xe5, 0x78, 0xe8, 0x28, 0x88, 0x87, 0xb7, 0x53, 0x49, 0xbb, + 0xe4, 0xea, 0x0d, 0x5e, 0x96, 0xa5, 0xdd, 0x1f, 0x0b, 0x25, 0x8b, 0xb5, + 0x95, 0x46, 0xe7, 0xba, 0xb8, 0xc4, 0x0a, 0x36, 0xb1, 0x89, 0xeb, 0x27, + 0x5d, 0xd9, 0x97, 0x24, 0x59, 0xa3, 0x9b, 0xb0, 0x23, 0x0b, 0xd2, 0xec, + 0x65, 0x91, 0xf9, 0xf0, 0xa0, 0x74, 0x5f, 0xb4, 0xce, 0x22, 0x27, 0x18, + 0x37, 0xe2, 0x4b, 0xfc, 0x91, 0xf9, 0x09, 0x15, 0xe6, 0xdb, 0x06, 0x9b, + 0x4d, 0x82, 0xdc, 0x36, 0x14, 0x48, 0xc6, 0xd5, 0x87, 0xca, 0xec, 0x5a, + 0xa2, 0x29, 0x33, 0xef, 0x22, 0x0c, 0x4b, 0xbf, 0xe7, 0x2f, 0x95, 0xe1, + 0xd3, 0xa5, 0xd8, 0xaa, 0x44, 0x77, 0x29, 0xa3, 0x20, 0x33, 0xd2, 0x51, + 0xa2, 0xf9, 0x4a, 0x6f, 0xf7, 0x3e, 0xf7, 0x0b, 0x8a, 0xec, 0xc1, 0x99, + 0x1d, 0x47, 0xf3, 0x74, 0x02, 0x04, 0xab, 0x8e, 0x62, 0x4c, 0x9e, 0x00, + 0xc2, 0x84, 0xd7, 0xd0, 0xf8, 0xe4, 0x1c, 0x9d, 0x98, 0x15, 0xa8, 0x8f, + 0x08, 0x98, 0x4e, 0x5a, 0xfa, 0xd6, 0x60, 0x87, 0x12, 0xdc, 0x8e, 0xfd, + 0xcb, 0xb3, 0x13, 0x97, 0x7a, 0xa8, 0x8c, 0x56, 0x2e, 0x49, 0x26, 0x60, + 0xe9, 0x4a, 0xdc, 0xec, 0x3f, 0xf0, 0x94, 0xcd, 0x90, 0x8e, 0x7c, 0x21, + 0x3f, 0x80, 0x14, 0x33, 0xdd, 0xb0, 0x00, 0xe2, 0x09, 0x37, 0x06, 0xdd, + 0x17, 0x69, 0x02, 0x03, 0x01, 0x00, 0x01, 0x02, 0x82, 0x01, 0x81, 0x00, + 0xa1, 0xc9, 0x44, 0xad, 0x6d, 0x17, 0xd1, 0x04, 0x03, 0x89, 0x5f, 0xbf, + 0xe5, 0xcb, 0x68, 0x13, 0x52, 0xf2, 0x33, 0xb7, 0x19, 0x12, 0x19, 0x60, + 0x6d, 0x0d, 0x0b, 0x48, 0x42, 0xe6, 0x9e, 0xbe, 0x05, 0x8a, 0xea, 0xeb, + 0x58, 0xfb, 0xc8, 0x9a, 0xc2, 0x2f, 0xd5, 0x9f, 0x40, 0x09, 0xb8, 0x08, + 0x2a, 0xe4, 0x4b, 0xcc, 0xba, 0xd9, 0xe3, 0x91, 0xc2, 0x64, 0xcb, 0x6c, + 0xa4, 0xb1, 0x17, 0x93, 0x7b, 0x10, 0x72, 0x83, 0x8d, 0xc2, 0xa2, 0x46, + 0x1b, 0x41, 0x12, 0xb9, 0x35, 0x5d, 0xf2, 0x32, 0xb1, 0xdf, 0x3a, 0x2a, + 0xda, 0xbb, 0x61, 0x9d, 0x62, 0xdb, 0xb0, 0x77, 0x76, 0x7a, 0x68, 0xb4, + 0x83, 0x10, 0x61, 0xac, 0x25, 0x01, 0x7d, 0x3e, 0x5f, 0x4a, 0x47, 0xf7, + 0x30, 0x1f, 0x82, 0x0a, 0xd7, 0x36, 0xff, 0x79, 0x5e, 0x42, 0x0f, 0x58, + 0xaa, 0x3a, 0xb8, 0x8b, 0x0e, 0xef, 0x00, 0xac, 0x96, 0x14, 0x96, 0x83, + 0x33, 0xb6, 0xb5, 0x4c, 0x91, 0x2a, 0x62, 0x92, 0xcf, 0xd2, 0x27, 0xcc, + 0xdf, 0x4c, 0x55, 0x03, 0xe8, 0x82, 0x78, 0xff, 0x80, 0xcb, 0x2e, 0x30, + 0x1b, 0x85, 0x56, 0xce, 0x57, 0x9e, 0xd8, 0x16, 0x86, 0x7d, 0x87, 0x2e, + 0xae, 0x39, 0xd4, 0xac, 0xbe, 0xa5, 0xc4, 0x5a, 0x85, 0x7a, 0xea, 0x8e, + 0x69, 0x9a, 0xbd, 0x9c, 0x45, 0x33, 0xf8, 0xb1, 0x70, 0xc1, 0x8c, 0xaa, + 0xad, 0x3d, 0x76, 0x08, 0x5b, 0x7d, 0x12, 0x93, 0x03, 0x70, 0xe6, 0x5f, + 0xb2, 0xac, 0x78, 0xae, 0xbe, 0xc5, 0x31, 0xc8, 0x6d, 0x2c, 0x1c, 0x2f, + 0x41, 0x37, 0xf6, 0x88, 0xd3, 0x80, 0x93, 0xed, 0x55, 0xb1, 0xaa, 0x49, + 0xe8, 0x42, 0xd0, 0x19, 0x20, 0x58, 0xe7, 0x2d, 0x10, 0xf2, 0x69, 0x49, + 0xe9, 0x47, 0x95, 0xbb, 0xce, 0xa7, 0xe9, 0x86, 0x46, 0x1c, 0xd1, 0x1f, + 0xd4, 0xa9, 0xeb, 0x28, 0x57, 0x78, 0x6c, 0xc6, 0x6f, 0x84, 0x3b, 0xb4, + 0x8c, 0xe5, 0xd3, 0x23, 0x8b, 0xbe, 0x83, 0x75, 0x5e, 0xea, 0xcd, 0x93, + 0xd2, 0x42, 0x86, 0xfd, 0x2b, 0x67, 0x72, 0xe0, 0x46, 0x9c, 0xf7, 0xc1, + 0xe9, 0x5d, 0xad, 0xac, 0xcb, 0x57, 0xb4, 0xe7, 0x87, 0x25, 0x7d, 0x5a, + 0x43, 0x90, 0xa3, 0x2e, 0xbf, 0x36, 0xbd, 0x4c, 0xba, 0xec, 0x0f, 0x21, + 0x51, 0xda, 0x66, 0xb8, 0x1e, 0xac, 0x33, 0xeb, 0xa1, 0x3e, 0x72, 0x15, + 0x10, 0x45, 0xc0, 0xe4, 0xdb, 0x04, 0x6d, 0xaf, 0x66, 0xc2, 0xfc, 0x35, + 0x04, 0x60, 0x7d, 0x2f, 0x5e, 0x9e, 0x83, 0xf6, 0x72, 0x92, 0x6a, 0x9f, + 0xba, 0x94, 0x97, 0x33, 0xe1, 0x1d, 0x42, 0xda, 0xad, 0xa5, 0x8b, 0xad, + 0x2f, 0x2f, 0x32, 0x16, 0x88, 0x54, 0x88, 0xb2, 0x85, 0xe2, 0x33, 0x08, + 0x43, 0xc8, 0x68, 0x69, 0xaa, 0xea, 0x9a, 0xbf, 0x41, 0x12, 0xe6, 0xf1, + 0x02, 0x81, 0xc1, 0x00, 0xfe, 0x96, 0xe7, 0xc8, 0x89, 0x61, 0x2b, 0x58, + 0xaa, 0xcd, 0x37, 0x46, 0x13, 0xa1, 0x2a, 0xc8, 0x1b, 0x76, 0xde, 0x4c, + 0xb3, 0x00, 0x4f, 0x6b, 0x02, 0xc0, 0x10, 0xef, 0x87, 0xe2, 0x6d, 0x7f, + 0x10, 0x57, 0xec, 0xde, 0x70, 0x60, 0xb5, 0x8f, 0x6d, 0x17, 0x35, 0xbd, + 0xfd, 0x6a, 0x2c, 0xbb, 0xf0, 0x48, 0x5b, 0x32, 0x41, 0xf6, 0xc0, 0x62, + 0x3a, 0x88, 0xc5, 0x41, 0x83, 0x85, 0x56, 0xa7, 0x11, 0xf4, 0xd2, 0xa9, + 0xb3, 0xa3, 0xcb, 0xae, 0xca, 0xee, 0x1c, 0x52, 0x7f, 0x04, 0x34, 0x61, + 0xb9, 0x8d, 0xa3, 0x26, 0x88, 0xce, 0x3d, 0xdb, 0x9c, 0xbf, 0xcc, 0xc4, + 0x38, 0x8b, 0xf2, 0xe4, 0x7e, 0xcc, 0x46, 0x86, 0x7a, 0x3c, 0xb7, 0x95, + 0x3f, 0xbd, 0x81, 0x45, 0xc3, 0x1d, 0xbb, 0xf8, 0x56, 0x6e, 0xa5, 0x88, + 0x2d, 0xff, 0x78, 0xc0, 0xc2, 0x4a, 0x4f, 0xea, 0xb8, 0x81, 0xf3, 0x96, + 0x5e, 0xd8, 0xcb, 0x96, 0x42, 0xa3, 0x21, 0x03, 0xc8, 0x00, 0xbf, 0x95, + 0xc7, 0x83, 0x80, 0xc6, 0xc2, 0x38, 0xe4, 0xaf, 0xb2, 0x0e, 0x80, 0x92, + 0x97, 0x21, 0x16, 0xce, 0x39, 0xd3, 0x95, 0xb7, 0xc3, 0x78, 0xe1, 0x1c, + 0xc0, 0x5a, 0xbc, 0x9b, 0x68, 0x3f, 0xd6, 0x42, 0xcd, 0xca, 0x0b, 0x6d, + 0x9f, 0xde, 0x6b, 0x98, 0x3b, 0x47, 0x57, 0xb9, 0x2d, 0x92, 0x52, 0x29, + 0xc5, 0xed, 0xb5, 0x0d, 0x02, 0x81, 0xc1, 0x00, 0xf6, 0x64, 0xef, 0xef, + 0x57, 0xdb, 0x06, 0xae, 0x36, 0x86, 0x11, 0xaf, 0x96, 0xb9, 0xb1, 0x29, + 0x53, 0xce, 0x24, 0x60, 0x96, 0x8f, 0xd0, 0xb7, 0x4b, 0x60, 0x1e, 0xb3, + 0x1f, 0xae, 0x15, 0x41, 0xf1, 0x56, 0xd6, 0xf3, 0x07, 0xf8, 0xd7, 0xdd, + 0x1c, 0x82, 0xe8, 0xe0, 0xff, 0xb3, 0x41, 0x0d, 0x41, 0x96, 0x0d, 0xf2, + 0x03, 0xb0, 0x68, 0xed, 0xda, 0x8b, 0x83, 0x18, 0x4d, 0xae, 0xaf, 0x72, + 0xa1, 0x82, 0xe5, 0x5a, 0xdc, 0x2a, 0x5f, 0x93, 0x29, 0xc7, 0x24, 0xa0, + 0x8e, 0x32, 0x4c, 0x0e, 0xca, 0x6d, 0x14, 0x69, 0x5b, 0x61, 0xc5, 0xdc, + 0x0e, 0x50, 0x0c, 0x14, 0x84, 0x8b, 0xee, 0x9e, 0x1e, 0x8c, 0x3d, 0xdb, + 0x24, 0xe7, 0x99, 0x6d, 0x3c, 0xaf, 0xe6, 0x3b, 0xaa, 0xb4, 0xe4, 0x42, + 0x13, 0x53, 0x3e, 0x02, 0x47, 0x0d, 0x3a, 0x2b, 0x97, 0x71, 0x9f, 0x1b, + 0x76, 0x2d, 0xe5, 0x9c, 0x5e, 0x46, 0x5f, 0x36, 0xbf, 0x18, 0xb1, 0x1d, + 0x9a, 0xeb, 0x4d, 0x5e, 0x83, 0xd5, 0x3e, 0x5f, 0xf2, 0x6a, 0x56, 0x79, + 0x0f, 0x54, 0xec, 0x41, 0xc0, 0xdc, 0x29, 0x42, 0x45, 0xae, 0x47, 0x1c, + 0x7a, 0xd5, 0xb7, 0x92, 0x1e, 0x66, 0xb8, 0x1a, 0x2f, 0x53, 0xd2, 0x6d, + 0x42, 0x3c, 0x6c, 0x02, 0x01, 0x03, 0x9e, 0x9a, 0x95, 0x35, 0x81, 0x21, + 0x53, 0x53, 0x16, 0xda, 0x8a, 0x80, 0x8c, 0xcd, 0x02, 0x81, 0xc1, 0x00, + 0xd5, 0xba, 0x05, 0xf7, 0x7a, 0x2d, 0x52, 0xe0, 0x6a, 0xf3, 0x40, 0xd5, + 0xd9, 0xa0, 0xd1, 0x73, 0x90, 0x6a, 0xe8, 0x10, 0x67, 0xad, 0x78, 0xfe, + 0x93, 0x1e, 0x7e, 0x99, 0x37, 0xf0, 0x44, 0x90, 0x09, 0x3e, 0x67, 0x22, + 0x0e, 0x21, 0x82, 0x0a, 0x58, 0x40, 0xc5, 0xe3, 0x2b, 0x9d, 0x38, 0xd4, + 0xc5, 0xd1, 0x58, 0x8e, 0x06, 0x86, 0x89, 0xd7, 0x6c, 0xe0, 0x69, 0x08, + 0xa8, 0xcb, 0x05, 0x85, 0xd8, 0x33, 0x39, 0xaf, 0x31, 0x99, 0xee, 0x62, + 0x5d, 0x06, 0x2c, 0x4c, 0xad, 0x48, 0xf0, 0x58, 0xa2, 0x17, 0x5f, 0xc1, + 0xf7, 0xd3, 0x7c, 0x66, 0xa3, 0x5e, 0xf9, 0x1e, 0x39, 0x82, 0x73, 0x74, + 0x93, 0x66, 0x16, 0x46, 0xca, 0xd3, 0xb2, 0x22, 0xdf, 0x91, 0xcd, 0xb6, + 0xad, 0x28, 0x87, 0x26, 0xe2, 0x18, 0x9d, 0x6a, 0x87, 0x83, 0x12, 0xf2, + 0x6f, 0xa9, 0x47, 0x11, 0xfb, 0xb7, 0x4c, 0xb1, 0x0e, 0x0a, 0xde, 0x4e, + 0xd4, 0xbe, 0x71, 0xf6, 0xe4, 0xae, 0x8c, 0x27, 0xc7, 0x88, 0x84, 0x51, + 0x57, 0xb7, 0xbf, 0x74, 0x27, 0xfc, 0xb8, 0xbf, 0x57, 0x94, 0x75, 0xba, + 0xc7, 0x1c, 0xf3, 0x71, 0x83, 0xee, 0x34, 0xbd, 0x98, 0x56, 0x14, 0x44, + 0x3a, 0xee, 0x6c, 0x87, 0x44, 0x8f, 0xbb, 0xac, 0x5a, 0x2b, 0xb5, 0x13, + 0xe5, 0x9f, 0xec, 0xeb, 0x0e, 0x70, 0xe9, 0xfd, 0x1b, 0xa6, 0x84, 0xf9, + 0x02, 0x81, 0xc0, 0x4e, 0x82, 0x26, 0xf9, 0x6a, 0x52, 0xfd, 0xb3, 0xf0, + 0xe7, 0x93, 0x27, 0x11, 0xad, 0xa5, 0x47, 0x77, 0xce, 0x8d, 0x44, 0xc1, + 0x74, 0x9d, 0x9a, 0x69, 0xc7, 0xfc, 0xc0, 0x32, 0x6d, 0xf3, 0x94, 0x09, + 0x64, 0x14, 0x25, 0x67, 0xfa, 0xe0, 0x3d, 0x31, 0xe2, 0x7c, 0x75, 0x84, + 0xc4, 0x07, 0x0c, 0x44, 0x43, 0x9d, 0xb9, 0xe9, 0x77, 0x02, 0x58, 0x17, + 0x74, 0xb0, 0x96, 0xc3, 0xd9, 0xcf, 0x49, 0x85, 0x31, 0x02, 0x07, 0x8b, + 0x73, 0x6c, 0xf4, 0xa5, 0x31, 0x30, 0xf8, 0x7f, 0x96, 0x83, 0x29, 0x8b, + 0x52, 0x6a, 0x58, 0x8f, 0xa7, 0x7d, 0xb5, 0xfa, 0x51, 0x83, 0x27, 0xde, + 0x7b, 0xff, 0xd2, 0x1e, 0x05, 0xad, 0x87, 0xf0, 0x20, 0x63, 0x80, 0xac, + 0xff, 0x97, 0x2a, 0x97, 0xdf, 0xff, 0x83, 0x16, 0x49, 0x45, 0xce, 0xcf, + 0xf8, 0xe4, 0xfa, 0x12, 0xcd, 0x3f, 0x57, 0x2e, 0xb6, 0xbd, 0x1c, 0xaf, + 0xe5, 0x58, 0x5d, 0x47, 0x52, 0x84, 0xcc, 0xdc, 0x19, 0xf1, 0x93, 0x16, + 0x0a, 0x92, 0x4f, 0x5c, 0x1c, 0x89, 0xe5, 0x14, 0xff, 0x88, 0x30, 0x03, + 0x55, 0xa3, 0x47, 0xdc, 0x90, 0x05, 0x54, 0x8b, 0xc7, 0x21, 0x30, 0xcb, + 0xc3, 0x0b, 0x12, 0x3c, 0xd6, 0x46, 0x8c, 0x4d, 0xb8, 0x96, 0xe9, 0xa4, + 0x8d, 0x14, 0xb2, 0x48, 0xac, 0xbd, 0xb2, 0x72, 0xac, 0x5c, 0xf1, 0xd1, + 0x83, 0xd8, 0x59, 0x02, 0x81, 0xc0, 0x2d, 0x65, 0xc1, 0xc7, 0x0f, 0xca, + 0xbc, 0x5d, 0xa1, 0x30, 0x28, 0x27, 0x51, 0xcc, 0xc9, 0x6c, 0x7b, 0x76, + 0x43, 0xa9, 0x77, 0xd8, 0x29, 0xa3, 0x80, 0x57, 0x3b, 0xe9, 0x72, 0x1c, + 0x51, 0x37, 0xc2, 0x0b, 0x74, 0xb7, 0xaa, 0x63, 0xd5, 0xe2, 0x9b, 0x3a, + 0x3c, 0x78, 0x50, 0xcb, 0x88, 0x1a, 0xd6, 0x59, 0xdc, 0xb3, 0x05, 0xbf, + 0xe5, 0xc5, 0xb5, 0xe3, 0x9b, 0xba, 0xe7, 0xbb, 0x4d, 0x45, 0xb3, 0x4c, + 0xf2, 0x48, 0x65, 0xe7, 0x6b, 0xee, 0x12, 0xc8, 0xd5, 0xea, 0xf0, 0x89, + 0xf9, 0x03, 0xa3, 0xd9, 0x5f, 0x62, 0x2d, 0x1a, 0x55, 0x51, 0x5d, 0xf6, + 0xca, 0x6e, 0x5f, 0xf0, 0x66, 0x02, 0xf4, 0x20, 0xe6, 0xe9, 0xc4, 0xd7, + 0x36, 0x8d, 0x00, 0xce, 0x23, 0xfd, 0x90, 0xd4, 0x19, 0x5c, 0xe9, 0xcb, + 0x23, 0xe2, 0x2c, 0xf5, 0xe1, 0x6f, 0x9d, 0xb5, 0xdf, 0xea, 0x51, 0xdd, + 0x02, 0xd8, 0x40, 0xd6, 0x7f, 0x4b, 0xdb, 0x0e, 0xe2, 0x27, 0x4d, 0x0d, + 0x9b, 0x2e, 0xf7, 0xc8, 0x22, 0xca, 0x63, 0x3c, 0x2c, 0xe5, 0xa8, 0x42, + 0x26, 0xd9, 0xfc, 0xdc, 0x42, 0xc8, 0x07, 0x64, 0xa2, 0x98, 0xae, 0xb4, + 0x57, 0x02, 0x02, 0xb2, 0x2d, 0xc8, 0x59, 0x73, 0x9b, 0xee, 0xc0, 0x9a, + 0x42, 0x9a, 0xef, 0xc2, 0x27, 0x24, 0x2b, 0x20, 0x92, 0xad, 0x50, 0xfe, + 0x2c, 0x2d, 0xb6, 0xb6, 0xb5, 0xba +}; +extern const size_t kOEMPrivateKeySize_Prod = 1794; + +extern const uint8_t kOEMPublicCert_Prod[] = { + 0x30, 0x82, 0x09, 0xf7, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, + 0x01, 0x07, 0x02, 0xa0, 0x82, 0x09, 0xe8, 0x30, 0x82, 0x09, 0xe4, 0x02, + 0x01, 0x01, 0x31, 0x00, 0x30, 0x0f, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, + 0xf7, 0x0d, 0x01, 0x07, 0x01, 0xa0, 0x02, 0x04, 0x00, 0xa0, 0x82, 0x09, + 0xc8, 0x30, 0x82, 0x04, 0x1a, 0x30, 0x82, 0x03, 0x02, 0xa0, 0x03, 0x02, + 0x01, 0x02, 0x02, 0x11, 0x00, 0xf2, 0xa1, 0x08, 0xdf, 0x12, 0x84, 0xb9, + 0x73, 0x6c, 0x23, 0x73, 0xe1, 0x1f, 0xf3, 0xac, 0x7a, 0x30, 0x0d, 0x06, + 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b, 0x05, 0x00, + 0x30, 0x81, 0x8b, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, + 0x13, 0x02, 0x55, 0x53, 0x31, 0x13, 0x30, 0x11, 0x06, 0x03, 0x55, 0x04, + 0x08, 0x0c, 0x0a, 0x57, 0x61, 0x73, 0x68, 0x69, 0x6e, 0x67, 0x74, 0x6f, + 0x6e, 0x31, 0x11, 0x30, 0x0f, 0x06, 0x03, 0x55, 0x04, 0x07, 0x0c, 0x08, + 0x4b, 0x69, 0x72, 0x6b, 0x6c, 0x61, 0x6e, 0x64, 0x31, 0x0f, 0x30, 0x0d, + 0x06, 0x03, 0x55, 0x04, 0x0a, 0x0c, 0x06, 0x47, 0x6f, 0x6f, 0x67, 0x6c, + 0x65, 0x31, 0x11, 0x30, 0x0f, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x0c, 0x08, + 0x57, 0x69, 0x64, 0x65, 0x76, 0x69, 0x6e, 0x65, 0x31, 0x30, 0x30, 0x2e, + 0x06, 0x03, 0x55, 0x04, 0x03, 0x0c, 0x27, 0x47, 0x6f, 0x6f, 0x67, 0x6c, + 0x65, 0x20, 0x4f, 0x45, 0x4d, 0x20, 0x54, 0x65, 0x73, 0x74, 0x20, 0x44, + 0x65, 0x76, 0x69, 0x63, 0x65, 0x3b, 0x20, 0x73, 0x79, 0x73, 0x74, 0x65, + 0x6d, 0x20, 0x69, 0x64, 0x3a, 0x20, 0x37, 0x33, 0x34, 0x36, 0x30, 0x1e, + 0x17, 0x0d, 0x31, 0x37, 0x30, 0x33, 0x31, 0x33, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x5a, 0x17, 0x0d, 0x31, 0x39, 0x31, 0x32, 0x30, 0x38, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x5a, 0x30, 0x6d, 0x31, 0x12, 0x30, 0x10, + 0x06, 0x03, 0x55, 0x04, 0x03, 0x0c, 0x09, 0x37, 0x33, 0x34, 0x36, 0x2d, + 0x6c, 0x65, 0x61, 0x66, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, + 0x06, 0x13, 0x02, 0x55, 0x53, 0x31, 0x13, 0x30, 0x11, 0x06, 0x03, 0x55, + 0x04, 0x08, 0x0c, 0x0a, 0x57, 0x61, 0x73, 0x68, 0x69, 0x6e, 0x67, 0x74, + 0x6f, 0x6e, 0x31, 0x11, 0x30, 0x0f, 0x06, 0x03, 0x55, 0x04, 0x07, 0x0c, + 0x08, 0x4b, 0x69, 0x72, 0x6b, 0x6c, 0x61, 0x6e, 0x64, 0x31, 0x0f, 0x30, + 0x0d, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x0c, 0x06, 0x47, 0x6f, 0x6f, 0x67, + 0x6c, 0x65, 0x31, 0x11, 0x30, 0x0f, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x0c, + 0x08, 0x57, 0x69, 0x64, 0x65, 0x76, 0x69, 0x6e, 0x65, 0x30, 0x82, 0x01, + 0xa2, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, + 0x01, 0x01, 0x05, 0x00, 0x03, 0x82, 0x01, 0x8f, 0x00, 0x30, 0x82, 0x01, + 0x8a, 0x02, 0x82, 0x01, 0x81, 0x00, 0xf5, 0x09, 0x64, 0x4a, 0x26, 0xfe, + 0xc0, 0x98, 0x55, 0x6a, 0x1d, 0x5d, 0x1c, 0xc7, 0x38, 0xaf, 0xfd, 0x49, + 0x9e, 0x85, 0x3f, 0xd6, 0x45, 0x0e, 0x99, 0x09, 0x85, 0x69, 0x84, 0x3c, + 0xfe, 0x72, 0xa5, 0x56, 0xfa, 0x11, 0x4f, 0x6b, 0x7d, 0x32, 0x2b, 0x0c, + 0xbf, 0x8f, 0xac, 0x47, 0x96, 0x22, 0x82, 0x3d, 0xf5, 0x64, 0x74, 0x7e, + 0x62, 0x68, 0x74, 0xcd, 0x0a, 0xec, 0x84, 0xc5, 0x15, 0x06, 0x0e, 0x5a, + 0x2f, 0x20, 0xe3, 0xc9, 0x67, 0xcd, 0xdd, 0x01, 0xb8, 0xb3, 0x18, 0x87, + 0x8c, 0xa9, 0x58, 0x86, 0x0f, 0xb6, 0xc3, 0x42, 0x7e, 0x87, 0x48, 0x5e, + 0x10, 0x49, 0xc7, 0xd7, 0xb7, 0xb8, 0xa6, 0x34, 0x08, 0x0c, 0x94, 0xf4, + 0xbb, 0x2a, 0x06, 0xa4, 0x4f, 0xec, 0xbc, 0xc4, 0x37, 0xbe, 0x99, 0x10, + 0x23, 0x37, 0x24, 0xb1, 0xdf, 0xcb, 0xe6, 0x3f, 0xc1, 0xf0, 0x0f, 0x04, + 0x03, 0xc8, 0xb0, 0x1e, 0xd6, 0xb8, 0xae, 0x77, 0xe1, 0x4d, 0x6d, 0x97, + 0x69, 0x6d, 0x8a, 0x73, 0x66, 0x32, 0x57, 0x6f, 0xcf, 0xea, 0x1e, 0x7b, + 0x87, 0x03, 0x75, 0xb1, 0xef, 0x83, 0x64, 0x26, 0xf1, 0x3f, 0xbf, 0xe6, + 0x28, 0x03, 0x72, 0x57, 0xbf, 0x47, 0x29, 0x99, 0x8f, 0x74, 0x1d, 0x01, + 0x16, 0xad, 0xb2, 0xdf, 0x80, 0xa4, 0xd3, 0x8b, 0xeb, 0x61, 0xd1, 0x40, + 0x68, 0xb9, 0xa2, 0xa5, 0xef, 0x2b, 0xe5, 0x78, 0xe8, 0x28, 0x88, 0x87, + 0xb7, 0x53, 0x49, 0xbb, 0xe4, 0xea, 0x0d, 0x5e, 0x96, 0xa5, 0xdd, 0x1f, + 0x0b, 0x25, 0x8b, 0xb5, 0x95, 0x46, 0xe7, 0xba, 0xb8, 0xc4, 0x0a, 0x36, + 0xb1, 0x89, 0xeb, 0x27, 0x5d, 0xd9, 0x97, 0x24, 0x59, 0xa3, 0x9b, 0xb0, + 0x23, 0x0b, 0xd2, 0xec, 0x65, 0x91, 0xf9, 0xf0, 0xa0, 0x74, 0x5f, 0xb4, + 0xce, 0x22, 0x27, 0x18, 0x37, 0xe2, 0x4b, 0xfc, 0x91, 0xf9, 0x09, 0x15, + 0xe6, 0xdb, 0x06, 0x9b, 0x4d, 0x82, 0xdc, 0x36, 0x14, 0x48, 0xc6, 0xd5, + 0x87, 0xca, 0xec, 0x5a, 0xa2, 0x29, 0x33, 0xef, 0x22, 0x0c, 0x4b, 0xbf, + 0xe7, 0x2f, 0x95, 0xe1, 0xd3, 0xa5, 0xd8, 0xaa, 0x44, 0x77, 0x29, 0xa3, + 0x20, 0x33, 0xd2, 0x51, 0xa2, 0xf9, 0x4a, 0x6f, 0xf7, 0x3e, 0xf7, 0x0b, + 0x8a, 0xec, 0xc1, 0x99, 0x1d, 0x47, 0xf3, 0x74, 0x02, 0x04, 0xab, 0x8e, + 0x62, 0x4c, 0x9e, 0x00, 0xc2, 0x84, 0xd7, 0xd0, 0xf8, 0xe4, 0x1c, 0x9d, + 0x98, 0x15, 0xa8, 0x8f, 0x08, 0x98, 0x4e, 0x5a, 0xfa, 0xd6, 0x60, 0x87, + 0x12, 0xdc, 0x8e, 0xfd, 0xcb, 0xb3, 0x13, 0x97, 0x7a, 0xa8, 0x8c, 0x56, + 0x2e, 0x49, 0x26, 0x60, 0xe9, 0x4a, 0xdc, 0xec, 0x3f, 0xf0, 0x94, 0xcd, + 0x90, 0x8e, 0x7c, 0x21, 0x3f, 0x80, 0x14, 0x33, 0xdd, 0xb0, 0x00, 0xe2, + 0x09, 0x37, 0x06, 0xdd, 0x17, 0x69, 0x02, 0x03, 0x01, 0x00, 0x01, 0xa3, + 0x16, 0x30, 0x14, 0x30, 0x12, 0x06, 0x0a, 0x2b, 0x06, 0x01, 0x04, 0x01, + 0xd6, 0x79, 0x04, 0x01, 0x01, 0x04, 0x04, 0x02, 0x02, 0x1c, 0xb2, 0x30, + 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b, + 0x05, 0x00, 0x03, 0x82, 0x01, 0x01, 0x00, 0x8e, 0x2d, 0x13, 0x1e, 0x60, + 0xaa, 0xda, 0x52, 0x53, 0x55, 0x64, 0x3a, 0xdc, 0xb6, 0x7a, 0xc0, 0xba, + 0xfa, 0xeb, 0x20, 0xab, 0xb6, 0x63, 0xcf, 0xcd, 0x9b, 0xdb, 0x71, 0xf3, + 0xa0, 0xd6, 0x91, 0xbf, 0x0c, 0xc1, 0xae, 0x8f, 0x02, 0x18, 0x00, 0x54, + 0xfb, 0x49, 0x03, 0x34, 0x8d, 0x92, 0x9d, 0x5d, 0x8d, 0xa8, 0x1c, 0x20, + 0x0f, 0x85, 0x60, 0xf9, 0xf6, 0x8b, 0xbb, 0x2b, 0x82, 0xce, 0xb3, 0xe2, + 0x91, 0xe7, 0xbd, 0x91, 0x61, 0x52, 0x36, 0x40, 0x9f, 0x2f, 0x5e, 0xa6, + 0x5d, 0x2f, 0xb3, 0x81, 0xe7, 0xf1, 0x87, 0xbe, 0xc5, 0x9d, 0x67, 0x5a, + 0xf7, 0x41, 0x1e, 0x73, 0xb0, 0x1e, 0xdc, 0x4f, 0x8d, 0x53, 0x21, 0x38, + 0x1b, 0xfd, 0x92, 0x43, 0x68, 0x83, 0x03, 0xd0, 0x9a, 0xca, 0x92, 0x14, + 0x73, 0x04, 0x94, 0x2a, 0x93, 0x22, 0x60, 0x5e, 0xee, 0xb6, 0xec, 0x0f, + 0xb0, 0xc8, 0x92, 0x97, 0xfb, 0x5d, 0xed, 0x1f, 0xa0, 0x5f, 0xe4, 0x98, + 0x2f, 0xf6, 0x13, 0x78, 0x99, 0xec, 0xb3, 0xf1, 0x0d, 0x27, 0xaa, 0x19, + 0x95, 0x39, 0xdb, 0xb0, 0x7b, 0x96, 0x74, 0x03, 0x5e, 0x51, 0xf5, 0x15, + 0x27, 0xce, 0xca, 0x0b, 0x2a, 0x0d, 0x43, 0xb3, 0x68, 0x17, 0x1e, 0x11, + 0x60, 0xd9, 0x84, 0x9b, 0xc3, 0x53, 0xce, 0xbd, 0xf4, 0x61, 0x51, 0x4b, + 0x41, 0x00, 0x7e, 0xe1, 0x5f, 0x69, 0xb3, 0x4a, 0x89, 0x7e, 0x47, 0x67, + 0xfd, 0x76, 0xf8, 0x94, 0x2f, 0x72, 0xb6, 0x14, 0x08, 0x2c, 0x16, 0x4e, + 0x9d, 0x37, 0x62, 0xbf, 0x11, 0x67, 0xc0, 0x70, 0x71, 0xec, 0x55, 0x51, + 0x4e, 0x46, 0x76, 0xb4, 0xc3, 0xeb, 0x52, 0x06, 0x17, 0x06, 0xce, 0x61, + 0x43, 0xce, 0x26, 0x80, 0x68, 0xb6, 0x2d, 0x57, 0xba, 0x8c, 0x7d, 0xb7, + 0xc5, 0x05, 0x2c, 0xf8, 0xa3, 0x69, 0xf8, 0x96, 0xad, 0xac, 0xd1, 0x30, + 0x82, 0x05, 0xa6, 0x30, 0x82, 0x03, 0x8e, 0xa0, 0x03, 0x02, 0x01, 0x02, + 0x02, 0x10, 0x73, 0xd1, 0xe1, 0x1d, 0xa9, 0x75, 0xfd, 0x0c, 0xda, 0x7f, + 0xfa, 0x43, 0x3c, 0x26, 0xbd, 0x3d, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, + 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b, 0x05, 0x00, 0x30, 0x7e, 0x31, + 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x55, 0x53, + 0x31, 0x13, 0x30, 0x11, 0x06, 0x03, 0x55, 0x04, 0x08, 0x0c, 0x0a, 0x57, + 0x61, 0x73, 0x68, 0x69, 0x6e, 0x67, 0x74, 0x6f, 0x6e, 0x31, 0x11, 0x30, + 0x0f, 0x06, 0x03, 0x55, 0x04, 0x07, 0x0c, 0x08, 0x4b, 0x69, 0x72, 0x6b, + 0x6c, 0x61, 0x6e, 0x64, 0x31, 0x0f, 0x30, 0x0d, 0x06, 0x03, 0x55, 0x04, + 0x0a, 0x0c, 0x06, 0x47, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x31, 0x11, 0x30, + 0x0f, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x0c, 0x08, 0x57, 0x69, 0x64, 0x65, + 0x76, 0x69, 0x6e, 0x65, 0x31, 0x23, 0x30, 0x21, 0x06, 0x03, 0x55, 0x04, + 0x03, 0x0c, 0x1a, 0x77, 0x69, 0x64, 0x65, 0x76, 0x69, 0x6e, 0x65, 0x2e, + 0x63, 0x6f, 0x6d, 0x2f, 0x6f, 0x65, 0x6d, 0x2d, 0x72, 0x6f, 0x6f, 0x74, + 0x2d, 0x70, 0x72, 0x6f, 0x64, 0x30, 0x1e, 0x17, 0x0d, 0x31, 0x37, 0x30, + 0x33, 0x31, 0x34, 0x30, 0x33, 0x30, 0x32, 0x34, 0x31, 0x5a, 0x17, 0x0d, + 0x32, 0x37, 0x30, 0x33, 0x31, 0x34, 0x30, 0x33, 0x30, 0x32, 0x34, 0x31, + 0x5a, 0x30, 0x81, 0x8b, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, + 0x06, 0x13, 0x02, 0x55, 0x53, 0x31, 0x13, 0x30, 0x11, 0x06, 0x03, 0x55, + 0x04, 0x08, 0x0c, 0x0a, 0x57, 0x61, 0x73, 0x68, 0x69, 0x6e, 0x67, 0x74, + 0x6f, 0x6e, 0x31, 0x11, 0x30, 0x0f, 0x06, 0x03, 0x55, 0x04, 0x07, 0x0c, + 0x08, 0x4b, 0x69, 0x72, 0x6b, 0x6c, 0x61, 0x6e, 0x64, 0x31, 0x0f, 0x30, + 0x0d, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x0c, 0x06, 0x47, 0x6f, 0x6f, 0x67, + 0x6c, 0x65, 0x31, 0x11, 0x30, 0x0f, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x0c, + 0x08, 0x57, 0x69, 0x64, 0x65, 0x76, 0x69, 0x6e, 0x65, 0x31, 0x30, 0x30, + 0x2e, 0x06, 0x03, 0x55, 0x04, 0x03, 0x0c, 0x27, 0x47, 0x6f, 0x6f, 0x67, + 0x6c, 0x65, 0x20, 0x4f, 0x45, 0x4d, 0x20, 0x54, 0x65, 0x73, 0x74, 0x20, + 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x3b, 0x20, 0x73, 0x79, 0x73, 0x74, + 0x65, 0x6d, 0x20, 0x69, 0x64, 0x3a, 0x20, 0x37, 0x33, 0x34, 0x36, 0x30, + 0x82, 0x01, 0x22, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, + 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00, 0x03, 0x82, 0x01, 0x0f, 0x00, 0x30, + 0x82, 0x01, 0x0a, 0x02, 0x82, 0x01, 0x01, 0x00, 0xa5, 0x45, 0x13, 0xf2, + 0xb2, 0xcb, 0x4b, 0x0f, 0xb4, 0x44, 0x25, 0x9c, 0x8a, 0x68, 0x54, 0xd5, + 0x45, 0x1e, 0x15, 0x89, 0x5b, 0xb8, 0xce, 0xda, 0x5a, 0x42, 0xe6, 0x9a, + 0x8c, 0xc1, 0xcb, 0xe8, 0xc5, 0xf5, 0x8f, 0x49, 0x0e, 0x02, 0xef, 0x5e, + 0x97, 0x1a, 0x91, 0xa4, 0x94, 0xc3, 0x50, 0x13, 0xe5, 0x13, 0xb7, 0x7f, + 0x26, 0x53, 0x19, 0xb0, 0x37, 0xa5, 0xef, 0xe6, 0x2a, 0x39, 0xdc, 0x93, + 0x37, 0xe2, 0x3d, 0x7f, 0xcb, 0x4b, 0x93, 0xa2, 0xc3, 0x69, 0x78, 0xc9, + 0x01, 0xfa, 0x68, 0x3b, 0xe0, 0xe2, 0x22, 0x6c, 0xeb, 0xe4, 0x8a, 0xa8, + 0x3e, 0xf5, 0x20, 0x82, 0xa8, 0x62, 0x68, 0x59, 0x78, 0x24, 0xde, 0xef, + 0x47, 0x43, 0xb1, 0x6c, 0x38, 0x29, 0xd3, 0x69, 0x3f, 0xae, 0x35, 0x57, + 0x75, 0x80, 0xc9, 0x21, 0xe7, 0x01, 0xb9, 0x54, 0x8b, 0x6e, 0x4e, 0x2e, + 0x5a, 0x5b, 0x77, 0xa4, 0x22, 0xc2, 0x7b, 0x95, 0xb9, 0x39, 0x2c, 0xbd, + 0xc2, 0x1e, 0x02, 0xa6, 0xb2, 0xbc, 0x0f, 0x7a, 0xcb, 0xdc, 0xbc, 0xbc, + 0x90, 0x66, 0xe3, 0xca, 0x46, 0x53, 0x3e, 0x98, 0xff, 0x2e, 0x78, 0x9f, + 0xd3, 0xa1, 0x12, 0x93, 0x66, 0x7d, 0xcc, 0x94, 0x6b, 0xec, 0x19, 0x0e, + 0x20, 0x45, 0x22, 0x57, 0x6d, 0x9e, 0xd0, 0x89, 0xf2, 0xa9, 0x34, 0xdc, + 0xab, 0xa5, 0x73, 0x47, 0x38, 0xe3, 0x7f, 0x98, 0x3a, 0x61, 0xae, 0x6c, + 0x4d, 0xf2, 0x31, 0x90, 0xcb, 0x83, 0xc1, 0xee, 0xb4, 0xf2, 0x9a, 0x28, + 0x5f, 0xbb, 0x7d, 0x89, 0xdf, 0xa2, 0x31, 0xb6, 0x1d, 0x39, 0x2b, 0x70, + 0xbf, 0x1e, 0xad, 0xe1, 0x74, 0x94, 0x1d, 0xf8, 0xc5, 0x1a, 0x8d, 0x13, + 0x45, 0xf0, 0x6a, 0x80, 0x0c, 0x5d, 0xbb, 0x46, 0x8a, 0x43, 0xd0, 0xff, + 0x21, 0x39, 0x57, 0x53, 0x5b, 0x51, 0xf8, 0xa2, 0x8f, 0x7f, 0x27, 0xc7, + 0x02, 0x03, 0x01, 0x00, 0x01, 0xa3, 0x82, 0x01, 0x10, 0x30, 0x82, 0x01, + 0x0c, 0x30, 0x12, 0x06, 0x03, 0x55, 0x1d, 0x13, 0x01, 0x01, 0xff, 0x04, + 0x08, 0x30, 0x06, 0x01, 0x01, 0xff, 0x02, 0x01, 0x00, 0x30, 0x0e, 0x06, + 0x03, 0x55, 0x1d, 0x0f, 0x01, 0x01, 0xff, 0x04, 0x04, 0x03, 0x02, 0x02, + 0x04, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x1d, 0x0e, 0x04, 0x16, 0x04, 0x14, + 0xe8, 0xe9, 0xac, 0x16, 0x5c, 0x5e, 0xb2, 0xe8, 0xeb, 0xff, 0x57, 0x27, + 0x20, 0x08, 0x72, 0x63, 0x9b, 0xe5, 0xb5, 0x16, 0x30, 0x81, 0xb2, 0x06, + 0x03, 0x55, 0x1d, 0x23, 0x04, 0x81, 0xaa, 0x30, 0x81, 0xa7, 0x80, 0x14, + 0x04, 0x94, 0x66, 0xaa, 0xf9, 0x61, 0x89, 0xb6, 0xdb, 0xb5, 0xf7, 0x13, + 0x38, 0x3d, 0x62, 0x84, 0xb8, 0x18, 0x0a, 0x8f, 0xa1, 0x81, 0x83, 0xa4, + 0x81, 0x80, 0x30, 0x7e, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, + 0x06, 0x13, 0x02, 0x55, 0x53, 0x31, 0x13, 0x30, 0x11, 0x06, 0x03, 0x55, + 0x04, 0x08, 0x0c, 0x0a, 0x57, 0x61, 0x73, 0x68, 0x69, 0x6e, 0x67, 0x74, + 0x6f, 0x6e, 0x31, 0x11, 0x30, 0x0f, 0x06, 0x03, 0x55, 0x04, 0x07, 0x0c, + 0x08, 0x4b, 0x69, 0x72, 0x6b, 0x6c, 0x61, 0x6e, 0x64, 0x31, 0x0f, 0x30, + 0x0d, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x0c, 0x06, 0x47, 0x6f, 0x6f, 0x67, + 0x6c, 0x65, 0x31, 0x11, 0x30, 0x0f, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x0c, + 0x08, 0x57, 0x69, 0x64, 0x65, 0x76, 0x69, 0x6e, 0x65, 0x31, 0x23, 0x30, + 0x21, 0x06, 0x03, 0x55, 0x04, 0x03, 0x0c, 0x1a, 0x77, 0x69, 0x64, 0x65, + 0x76, 0x69, 0x6e, 0x65, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x6f, 0x65, 0x6d, + 0x2d, 0x72, 0x6f, 0x6f, 0x74, 0x2d, 0x70, 0x72, 0x6f, 0x64, 0x82, 0x09, + 0x00, 0xdf, 0x86, 0x05, 0x31, 0x01, 0xbe, 0x9a, 0x9a, 0x30, 0x12, 0x06, + 0x0a, 0x2b, 0x06, 0x01, 0x04, 0x01, 0xd6, 0x79, 0x04, 0x01, 0x01, 0x04, + 0x04, 0x02, 0x02, 0x1c, 0xb2, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, + 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b, 0x05, 0x00, 0x03, 0x82, 0x02, 0x01, + 0x00, 0x25, 0xce, 0xd2, 0x02, 0x48, 0xbb, 0xbe, 0xfc, 0xb6, 0xa4, 0x87, + 0x87, 0xe0, 0x21, 0x7d, 0xfa, 0x23, 0xc3, 0x0d, 0x73, 0x8f, 0x46, 0xe7, + 0x09, 0x59, 0xda, 0x2e, 0x55, 0x59, 0xff, 0x3c, 0x1b, 0xf6, 0xf8, 0x9a, + 0xc4, 0x1c, 0xf7, 0xac, 0xca, 0xe7, 0x63, 0xf2, 0xc7, 0xd6, 0x0c, 0x2d, + 0xa6, 0xad, 0x55, 0xf4, 0x10, 0x0e, 0xa8, 0x82, 0x0f, 0x88, 0xb5, 0x44, + 0xe8, 0x8e, 0x84, 0x08, 0xf7, 0xdd, 0xe7, 0x10, 0xce, 0x71, 0x56, 0x57, + 0x3f, 0xed, 0x48, 0xee, 0xe2, 0x5d, 0x08, 0x0a, 0x58, 0xe4, 0xfe, 0xbc, + 0x8c, 0x27, 0x1a, 0x46, 0x3f, 0xd5, 0x2d, 0xdb, 0x0b, 0x71, 0x73, 0xd1, + 0x49, 0xf3, 0x5c, 0x86, 0x4d, 0x0a, 0xe1, 0xeb, 0x53, 0x21, 0x38, 0x4f, + 0xec, 0x1e, 0xc2, 0x68, 0x1f, 0x7d, 0xa6, 0x33, 0xe9, 0xa5, 0x37, 0x2a, + 0xef, 0xcd, 0x78, 0x56, 0xb3, 0x39, 0x60, 0xf4, 0xa5, 0xf9, 0x2b, 0x85, + 0xcf, 0xe6, 0x1c, 0x7c, 0x8a, 0x5d, 0xe8, 0x26, 0x02, 0xcf, 0x7a, 0x56, + 0x1f, 0xae, 0x0d, 0x71, 0x20, 0xee, 0xec, 0x3b, 0xae, 0x95, 0x25, 0x15, + 0xc8, 0xf6, 0x92, 0x5d, 0xb8, 0x9b, 0xc2, 0xb4, 0x95, 0x33, 0x13, 0x76, + 0x45, 0xbe, 0x21, 0xe2, 0x3a, 0x69, 0x66, 0xd7, 0xff, 0x22, 0x00, 0x89, + 0xc9, 0x44, 0xb6, 0x54, 0x38, 0x1f, 0x33, 0xe4, 0xda, 0x7b, 0x87, 0xf3, + 0x23, 0xed, 0xf5, 0x16, 0x08, 0xbe, 0x4b, 0xea, 0x91, 0x8f, 0x91, 0x8b, + 0x4e, 0xd1, 0x02, 0x06, 0xa2, 0x77, 0x15, 0x03, 0x46, 0x11, 0x7d, 0x5b, + 0xea, 0x7a, 0xf6, 0x86, 0x7d, 0x96, 0xb7, 0x73, 0x9b, 0x5b, 0x32, 0xc3, + 0xf8, 0x92, 0x36, 0xe3, 0xe3, 0x2f, 0xe8, 0xf1, 0x72, 0xec, 0x0d, 0x50, + 0xd4, 0x86, 0xc5, 0x62, 0x83, 0xf1, 0x2a, 0x4c, 0xd1, 0xbf, 0x76, 0x62, + 0xd4, 0x21, 0x11, 0x68, 0xb2, 0xd6, 0x8d, 0xc4, 0xf8, 0xe4, 0x70, 0x85, + 0x19, 0xa7, 0x82, 0x27, 0x2c, 0x24, 0x21, 0x7a, 0x3b, 0xad, 0x8a, 0xd3, + 0xae, 0xda, 0x78, 0x3c, 0x6c, 0xab, 0xa2, 0xaa, 0x36, 0xf0, 0x1c, 0x58, + 0xd4, 0x72, 0x5e, 0xe8, 0x8b, 0x41, 0x08, 0xf5, 0x85, 0xdd, 0xee, 0x99, + 0x12, 0xf4, 0xd6, 0x41, 0x83, 0x69, 0xe7, 0x79, 0x19, 0xa3, 0x74, 0xc4, + 0x34, 0x2a, 0x8a, 0x7e, 0x4d, 0xbb, 0x2c, 0x49, 0x19, 0xf7, 0x98, 0x98, + 0xfc, 0x81, 0xf7, 0x9b, 0x7f, 0xff, 0xd9, 0x66, 0xf4, 0x51, 0x14, 0x29, + 0x2a, 0x14, 0x1d, 0x4f, 0xbd, 0x91, 0xba, 0x6f, 0x32, 0x34, 0x3c, 0x40, + 0x28, 0x6c, 0x97, 0xf8, 0x6d, 0x38, 0xcd, 0xa3, 0x7b, 0x18, 0xc8, 0x77, + 0x58, 0x4d, 0x53, 0x30, 0x7f, 0x4d, 0x89, 0xca, 0x95, 0x6e, 0xb5, 0xb8, + 0x8e, 0xc8, 0x2d, 0x18, 0x2f, 0x52, 0x2a, 0xde, 0xac, 0x56, 0x8d, 0x8c, + 0x67, 0x14, 0xf6, 0xb9, 0xf1, 0x65, 0xd3, 0x22, 0x43, 0xa3, 0x98, 0x42, + 0x20, 0x43, 0x4c, 0xdf, 0xf2, 0xeb, 0x31, 0x8c, 0x0e, 0x53, 0x5b, 0x99, + 0x82, 0xc3, 0x48, 0x04, 0x53, 0xad, 0x96, 0xb6, 0x9f, 0x52, 0xcc, 0x01, + 0xc8, 0xb3, 0x87, 0x6b, 0x9e, 0xea, 0xa9, 0xeb, 0xda, 0xac, 0xf9, 0x6f, + 0xde, 0xa1, 0x44, 0x32, 0x52, 0x49, 0x47, 0xff, 0x65, 0x79, 0x1e, 0xc5, + 0x73, 0x17, 0xb3, 0x36, 0xfc, 0x45, 0xca, 0x90, 0x37, 0x59, 0x1e, 0x16, + 0xab, 0x09, 0x69, 0xcf, 0xda, 0x56, 0x51, 0xfd, 0xeb, 0xcf, 0xcb, 0x8f, + 0xb1, 0xc3, 0x45, 0x2b, 0x7c, 0x0a, 0xa5, 0x9c, 0x0d, 0x2c, 0xad, 0x1c, + 0xd3, 0x33, 0xdd, 0xfe, 0x93, 0x69, 0xa2, 0x4b, 0x4b, 0xcf, 0x1d, 0x20, + 0x98, 0x4a, 0x4f, 0x5b, 0xe9, 0x24, 0xca, 0xfa, 0x18, 0x11, 0x81, 0x8b, + 0x7a, 0xb4, 0x5a, 0xc8, 0xdf, 0x6f, 0x5f, 0x21, 0x07, 0x31, 0x00 +}; +extern const size_t kOEMPublicCertSize_Prod = 2555; + +// Refer to the following in main modules. +// This level of indirection is present so new OEM Certificates can be +// added and then selected for use at compile time or run time. + +const uint32_t kOEMSystemId = kOEMSystemId_Prod; + +const uint8_t* kOEMPrivateKey = kOEMPrivateKey_Prod; +const uint8_t* kOEMPublicCert = kOEMPublicCert_Prod; + +const size_t kOEMPrivateKeySize = kOEMPrivateKeySize_Prod; +const size_t kOEMPublicCertSize = kOEMPublicCertSize_Prod; + +} // namespace wvoec_mock diff --git a/mock/src/oem_cert.h b/mock/src/oem_cert.h new file mode 100644 index 0000000..70b522f --- /dev/null +++ b/mock/src/oem_cert.h @@ -0,0 +1,21 @@ +// This header is used to access the OEM certificate if one is in use. +#ifndef OEM_CERT_H_ +#define OEM_CERT_H_ + +#include +#include + +namespace wvoec_mock { + +// Refer to the following in main modules +extern const uint32_t kOEMSystemId; + +extern const uint8_t* kOEMPrivateKey; +extern const uint8_t* kOEMPublicCert; + +extern const size_t kOEMPrivateKeySize; +extern const size_t kOEMPublicCertSize; + +} // namespace wvoec_mock + +#endif // OEM_CERT_H_ diff --git a/mock/src/oemcrypto_auth_mock.cpp b/mock/src/oemcrypto_auth_mock.cpp new file mode 100644 index 0000000..1eef560 --- /dev/null +++ b/mock/src/oemcrypto_auth_mock.cpp @@ -0,0 +1,203 @@ +// Copyright 2013 Google Inc. All Rights Reserved. +// +// Mock implementation of OEMCrypto APIs +// +#include "oemcrypto_auth_mock.h" + +#include + +#include "keys.h" +#include "log.h" +#include "oemcrypto_key_mock.h" +#include "oemcrypto_logging.h" +#include "oemcrypto_rsa_key_shared.h" +#include "wv_cdm_constants.h" + +namespace { + +// A 2048 bit RSA key in PKCS#8 PrivateKeyInfo format +// This is the RSA Test Key. +static const uint8_t kTestRSAPKCS8PrivateKeyInfo2_2048[] = { + 0x30, 0x82, 0x04, 0xbc, 0x02, 0x01, 0x00, 0x30, + 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, + 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00, 0x04, 0x82, + 0x04, 0xa6, 0x30, 0x82, 0x04, 0xa2, 0x02, 0x01, + 0x00, 0x02, 0x82, 0x01, 0x01, 0x00, 0xa7, 0x00, + 0x36, 0x60, 0x65, 0xdc, 0xbd, 0x54, 0x5a, 0x2a, + 0x40, 0xb4, 0xe1, 0x15, 0x94, 0x58, 0x11, 0x4f, + 0x94, 0x58, 0xdd, 0xde, 0xa7, 0x1f, 0x3c, 0x2c, + 0xe0, 0x88, 0x09, 0x29, 0x61, 0x57, 0x67, 0x5e, + 0x56, 0x7e, 0xee, 0x27, 0x8f, 0x59, 0x34, 0x9a, + 0x2a, 0xaa, 0x9d, 0xb4, 0x4e, 0xfa, 0xa7, 0x6a, + 0xd4, 0xc9, 0x7a, 0x53, 0xc1, 0x4e, 0x9f, 0xe3, + 0x34, 0xf7, 0x3d, 0xb7, 0xc9, 0x10, 0x47, 0x4f, + 0x28, 0xda, 0x3f, 0xce, 0x31, 0x7b, 0xfd, 0x06, + 0x10, 0xeb, 0xf7, 0xbe, 0x92, 0xf9, 0xaf, 0xfb, + 0x3e, 0x68, 0xda, 0xee, 0x1a, 0x64, 0x4c, 0xf3, + 0x29, 0xf2, 0x73, 0x9e, 0x39, 0xd8, 0xf6, 0x6f, + 0xd8, 0xb2, 0x80, 0x82, 0x71, 0x8e, 0xb5, 0xa4, + 0xf2, 0xc2, 0x3e, 0xcd, 0x0a, 0xca, 0xb6, 0x04, + 0xcd, 0x9a, 0x13, 0x8b, 0x54, 0x73, 0x54, 0x25, + 0x54, 0x8c, 0xbe, 0x98, 0x7a, 0x67, 0xad, 0xda, + 0xb3, 0x4e, 0xb3, 0xfa, 0x82, 0xa8, 0x4a, 0x67, + 0x98, 0x56, 0x57, 0x54, 0x71, 0xcd, 0x12, 0x7f, + 0xed, 0xa3, 0x01, 0xc0, 0x6a, 0x8b, 0x24, 0x03, + 0x96, 0x88, 0xbe, 0x97, 0x66, 0x2a, 0xbc, 0x53, + 0xc9, 0x83, 0x06, 0x51, 0x5a, 0x88, 0x65, 0x13, + 0x18, 0xe4, 0x3a, 0xed, 0x6b, 0xf1, 0x61, 0x5b, + 0x4c, 0xc8, 0x1e, 0xf4, 0xc2, 0xae, 0x08, 0x5e, + 0x2d, 0x5f, 0xf8, 0x12, 0x7f, 0xa2, 0xfc, 0xbb, + 0x21, 0x18, 0x30, 0xda, 0xfe, 0x40, 0xfb, 0x01, + 0xca, 0x2e, 0x37, 0x0e, 0xce, 0xdd, 0x76, 0x87, + 0x82, 0x46, 0x0b, 0x3a, 0x77, 0x8f, 0xc0, 0x72, + 0x07, 0x2c, 0x7f, 0x9d, 0x1e, 0x86, 0x5b, 0xed, + 0x27, 0x29, 0xdf, 0x03, 0x97, 0x62, 0xef, 0x44, + 0xd3, 0x5b, 0x3d, 0xdb, 0x9c, 0x5e, 0x1b, 0x7b, + 0x39, 0xb4, 0x0b, 0x6d, 0x04, 0x6b, 0xbb, 0xbb, + 0x2c, 0x5f, 0xcf, 0xb3, 0x7a, 0x05, 0x02, 0x03, + 0x01, 0x00, 0x01, 0x02, 0x82, 0x01, 0x00, 0x5e, + 0x79, 0x65, 0x49, 0xa5, 0x76, 0x79, 0xf9, 0x05, + 0x45, 0x0f, 0xf4, 0x03, 0xbd, 0xa4, 0x7d, 0x29, + 0xd5, 0xde, 0x33, 0x63, 0xd8, 0xb8, 0xac, 0x97, + 0xeb, 0x3f, 0x5e, 0x55, 0xe8, 0x7d, 0xf3, 0xe7, + 0x3b, 0x5c, 0x2d, 0x54, 0x67, 0x36, 0xd6, 0x1d, + 0x46, 0xf5, 0xca, 0x2d, 0x8b, 0x3a, 0x7e, 0xdc, + 0x45, 0x38, 0x79, 0x7e, 0x65, 0x71, 0x5f, 0x1c, + 0x5e, 0x79, 0xb1, 0x40, 0xcd, 0xfe, 0xc5, 0xe1, + 0xc1, 0x6b, 0x78, 0x04, 0x4e, 0x8e, 0x79, 0xf9, + 0x0a, 0xfc, 0x79, 0xb1, 0x5e, 0xb3, 0x60, 0xe3, + 0x68, 0x7b, 0xc6, 0xef, 0xcb, 0x71, 0x4c, 0xba, + 0xa7, 0x79, 0x5c, 0x7a, 0x81, 0xd1, 0x71, 0xe7, + 0x00, 0x21, 0x13, 0xe2, 0x55, 0x69, 0x0e, 0x75, + 0xbe, 0x09, 0xc3, 0x4f, 0xa9, 0xc9, 0x68, 0x22, + 0x0e, 0x97, 0x8d, 0x89, 0x6e, 0xf1, 0xe8, 0x88, + 0x7a, 0xd1, 0xd9, 0x09, 0x5d, 0xd3, 0x28, 0x78, + 0x25, 0x0b, 0x1c, 0x47, 0x73, 0x25, 0xcc, 0x21, + 0xb6, 0xda, 0xc6, 0x24, 0x5a, 0xd0, 0x37, 0x14, + 0x46, 0xc7, 0x94, 0x69, 0xe4, 0x43, 0x6f, 0x47, + 0xde, 0x00, 0x33, 0x4d, 0x8f, 0x95, 0x72, 0xfa, + 0x68, 0x71, 0x17, 0x66, 0x12, 0x1a, 0x87, 0x27, + 0xf7, 0xef, 0x7e, 0xe0, 0x35, 0x58, 0xf2, 0x4d, + 0x6f, 0x35, 0x01, 0xaa, 0x96, 0xe2, 0x3d, 0x51, + 0x13, 0x86, 0x9c, 0x79, 0xd0, 0xb7, 0xb6, 0x64, + 0xe8, 0x86, 0x65, 0x50, 0xbf, 0xcc, 0x27, 0x53, + 0x1f, 0x51, 0xd4, 0xca, 0xbe, 0xf5, 0xdd, 0x77, + 0x70, 0x98, 0x0f, 0xee, 0xa8, 0x96, 0x07, 0x5f, + 0x45, 0x6a, 0x7a, 0x0d, 0x03, 0x9c, 0x4f, 0x29, + 0xf6, 0x06, 0xf3, 0x5d, 0x58, 0x6c, 0x47, 0xd0, + 0x96, 0xa9, 0x03, 0x17, 0xbb, 0x4e, 0xc9, 0x21, + 0xe0, 0xac, 0xcd, 0x78, 0x78, 0xb2, 0xfe, 0x81, + 0xb2, 0x51, 0x53, 0xa6, 0x1f, 0x98, 0x45, 0x02, + 0x81, 0x81, 0x00, 0xcf, 0x73, 0x8c, 0xbe, 0x6d, + 0x45, 0x2d, 0x0c, 0x0b, 0x5d, 0x5c, 0x6c, 0x75, + 0x78, 0xcc, 0x35, 0x48, 0xb6, 0x98, 0xf1, 0xb9, + 0x64, 0x60, 0x8c, 0x43, 0xeb, 0x85, 0xab, 0x04, + 0xb6, 0x7d, 0x1b, 0x71, 0x75, 0x06, 0xe2, 0xda, + 0x84, 0x68, 0x2e, 0x7f, 0x4c, 0xe3, 0x73, 0xb4, + 0xde, 0x51, 0x4b, 0xb6, 0x51, 0x86, 0x7b, 0xd0, + 0xe6, 0x4d, 0xf3, 0xd1, 0xcf, 0x1a, 0xfe, 0x7f, + 0x3a, 0x83, 0xba, 0xb3, 0xe1, 0xff, 0x54, 0x13, + 0x93, 0xd7, 0x9c, 0x27, 0x80, 0xb7, 0x1e, 0x64, + 0x9e, 0xf7, 0x32, 0x2b, 0x46, 0x29, 0xf7, 0xf8, + 0x18, 0x6c, 0xf7, 0x4a, 0xbe, 0x4b, 0xee, 0x96, + 0x90, 0x8f, 0xa2, 0x16, 0x22, 0x6a, 0xcc, 0x48, + 0x06, 0x74, 0x63, 0x43, 0x7f, 0x27, 0x22, 0x44, + 0x3c, 0x2d, 0x3b, 0x62, 0xf1, 0x1c, 0xb4, 0x27, + 0x33, 0x85, 0x26, 0x60, 0x48, 0x16, 0xcb, 0xef, + 0xf8, 0xcd, 0x37, 0x02, 0x81, 0x81, 0x00, 0xce, + 0x15, 0x43, 0x6e, 0x4b, 0x0f, 0xf9, 0x3f, 0x87, + 0xc3, 0x41, 0x45, 0x97, 0xb1, 0x49, 0xc2, 0x19, + 0x23, 0x87, 0xe4, 0x24, 0x1c, 0x64, 0xe5, 0x28, + 0xcb, 0x43, 0x10, 0x14, 0x14, 0x0e, 0x19, 0xcb, + 0xbb, 0xdb, 0xfd, 0x11, 0x9d, 0x17, 0x68, 0x78, + 0x6d, 0x61, 0x70, 0x63, 0x3a, 0xa1, 0xb3, 0xf3, + 0xa7, 0x5b, 0x0e, 0xff, 0xb7, 0x61, 0x11, 0x54, + 0x91, 0x99, 0xe5, 0x91, 0x32, 0x2d, 0xeb, 0x3f, + 0xd8, 0x3e, 0xf7, 0xd4, 0xcb, 0xd2, 0xa3, 0x41, + 0xc1, 0xee, 0xc6, 0x92, 0x13, 0xeb, 0x7f, 0x42, + 0x58, 0xf4, 0xd0, 0xb2, 0x74, 0x1d, 0x8e, 0x87, + 0x46, 0xcd, 0x14, 0xb8, 0x16, 0xad, 0xb5, 0xbd, + 0x0d, 0x6c, 0x95, 0x5a, 0x16, 0xbf, 0xe9, 0x53, + 0xda, 0xfb, 0xed, 0x83, 0x51, 0x67, 0xa9, 0x55, + 0xab, 0x54, 0x02, 0x95, 0x20, 0xa6, 0x68, 0x17, + 0x53, 0xa8, 0xea, 0x43, 0xe5, 0xb0, 0xa3, 0x02, + 0x81, 0x80, 0x67, 0x9c, 0x32, 0x83, 0x39, 0x57, + 0xff, 0x73, 0xb0, 0x89, 0x64, 0x8b, 0xd6, 0xf0, + 0x0a, 0x2d, 0xe2, 0xaf, 0x30, 0x1c, 0x2a, 0x97, + 0xf3, 0x90, 0x9a, 0xab, 0x9b, 0x0b, 0x1b, 0x43, + 0x79, 0xa0, 0xa7, 0x3d, 0xe7, 0xbe, 0x8d, 0x9c, + 0xeb, 0xdb, 0xad, 0x40, 0xdd, 0xa9, 0x00, 0x80, + 0xb8, 0xe1, 0xb3, 0xa1, 0x6c, 0x25, 0x92, 0xe4, + 0x33, 0xb2, 0xbe, 0xeb, 0x4d, 0x74, 0x26, 0x5f, + 0x37, 0x43, 0x9c, 0x6c, 0x17, 0x76, 0x0a, 0x81, + 0x20, 0x82, 0xa1, 0x48, 0x2c, 0x2d, 0x45, 0xdc, + 0x0f, 0x62, 0x43, 0x32, 0xbb, 0xeb, 0x59, 0x41, + 0xf9, 0xca, 0x58, 0xce, 0x4a, 0x66, 0x53, 0x54, + 0xc8, 0x28, 0x10, 0x1e, 0x08, 0x71, 0x16, 0xd8, + 0x02, 0x71, 0x41, 0x58, 0xd4, 0x56, 0xcc, 0xf5, + 0xb1, 0x31, 0xa3, 0xed, 0x00, 0x85, 0x09, 0xbf, + 0x35, 0x95, 0x41, 0x29, 0x40, 0x19, 0x83, 0x35, + 0x24, 0x69, 0x02, 0x81, 0x80, 0x55, 0x10, 0x0b, + 0xcc, 0x3b, 0xa9, 0x75, 0x3d, 0x16, 0xe1, 0xae, + 0x50, 0x76, 0x63, 0x94, 0x49, 0x4c, 0xad, 0x10, + 0xcb, 0x47, 0x68, 0x7c, 0xf0, 0xe5, 0xdc, 0xb8, + 0x6a, 0xab, 0x8e, 0xf7, 0x9f, 0x08, 0x2c, 0x1b, + 0x8a, 0xa2, 0xb9, 0x8f, 0xce, 0xec, 0x5e, 0x61, + 0xa8, 0xcd, 0x1c, 0x87, 0x60, 0x4a, 0xc3, 0x1a, + 0x5f, 0xdf, 0x87, 0x26, 0xc6, 0xcb, 0x7c, 0x69, + 0xe4, 0x8b, 0x01, 0x06, 0x59, 0x22, 0xfa, 0x34, + 0x4b, 0x81, 0x87, 0x3c, 0x03, 0x6d, 0x02, 0x0a, + 0x77, 0xe6, 0x15, 0xd8, 0xcf, 0xa7, 0x68, 0x26, + 0x6c, 0xfa, 0x2b, 0xd9, 0x83, 0x5a, 0x2d, 0x0c, + 0x3b, 0x70, 0x1c, 0xd4, 0x48, 0xbe, 0xa7, 0x0a, + 0xd9, 0xbe, 0xdc, 0xc3, 0x0c, 0x21, 0x33, 0xb3, + 0x66, 0xff, 0x1c, 0x1b, 0xc8, 0x96, 0x76, 0xe8, + 0x6f, 0x44, 0x74, 0xbc, 0x9b, 0x1c, 0x7d, 0xc8, + 0xac, 0x21, 0xa8, 0x6e, 0x37, 0x02, 0x81, 0x80, + 0x2c, 0x7c, 0xad, 0x1e, 0x75, 0xf6, 0x69, 0x1d, + 0xe7, 0xa6, 0xca, 0x74, 0x7d, 0x67, 0xc8, 0x65, + 0x28, 0x66, 0xc4, 0x43, 0xa6, 0xbd, 0x40, 0x57, + 0xae, 0xb7, 0x65, 0x2c, 0x52, 0xf9, 0xe4, 0xc7, + 0x81, 0x7b, 0x56, 0xa3, 0xd2, 0x0d, 0xe8, 0x33, + 0x70, 0xcf, 0x06, 0x84, 0xb3, 0x4e, 0x44, 0x50, + 0x75, 0x61, 0x96, 0x86, 0x4b, 0xb6, 0x2b, 0xad, + 0xf0, 0xad, 0x57, 0xd0, 0x37, 0x0d, 0x1d, 0x35, + 0x50, 0xcb, 0x69, 0x22, 0x39, 0x29, 0xb9, 0x3a, + 0xd3, 0x29, 0x23, 0x02, 0x60, 0xf7, 0xab, 0x30, + 0x40, 0xda, 0x8e, 0x4d, 0x45, 0x70, 0x26, 0xf4, + 0xa2, 0x0d, 0xd0, 0x64, 0x5d, 0x47, 0x3c, 0x18, + 0xf4, 0xd4, 0x52, 0x95, 0x00, 0xae, 0x84, 0x6b, + 0x47, 0xb2, 0x3c, 0x82, 0xd3, 0x72, 0x53, 0xde, + 0x72, 0x2c, 0xf7, 0xc1, 0x22, 0x36, 0xd9, 0x18, + 0x56, 0xfe, 0x39, 0x28, 0x33, 0xe0, 0xdb, 0x03 +}; + +} // namespace + +namespace wvoec_mock { + +AuthenticationRoot::AuthenticationRoot(OEMCrypto_ProvisioningMethod method) : + provisioning_method_(method), + use_test_keybox_(false) { + if ((provisioning_method_ == OEMCrypto_DrmCertificate) && + !rsa_key_.LoadPkcs8RsaKey(kPrivateKey, kPrivateKeySize)) { + // This error message is OK in unit tests which use test certificate. + LOGE("FATAL ERROR: Platform uses a baked-in certificate instead of a " + "keybox, but the certificate could not be loaded."); + } +} + +KeyboxError AuthenticationRoot::ValidateKeybox() { + return keybox().Validate(); +} + +bool AuthenticationRoot::LoadTestRsaKey() { + return rsa_key_.LoadPkcs8RsaKey(kTestRSAPKCS8PrivateKeyInfo2_2048, + sizeof(kTestRSAPKCS8PrivateKeyInfo2_2048)); +} + +bool AuthenticationRoot::Validate() { + return NO_ERROR == ValidateKeybox(); +} + +} // namespace wvoec_mock diff --git a/mock/src/oemcrypto_auth_mock.h b/mock/src/oemcrypto_auth_mock.h new file mode 100644 index 0000000..980a45b --- /dev/null +++ b/mock/src/oemcrypto_auth_mock.h @@ -0,0 +1,76 @@ +// Copyright 2016 Google Inc. All Rights Reserved. +// +// Mock implementation of OEMCrypto APIs +// +#ifndef OEMCRYPTO_AUTH_MOCK_H_ +#define OEMCRYPTO_AUTH_MOCK_H_ + +#include +#include + +#include + +#include "OEMCryptoCENC.h" // Needed for enums only. +#include "oemcrypto_key_mock.h" +#include "oemcrypto_keybox_mock.h" +#include "oemcrypto_rsa_key_shared.h" +#include "wv_cdm_types.h" + +namespace wvoec_mock { + +class AuthenticationRoot { + public: + explicit AuthenticationRoot(OEMCrypto_ProvisioningMethod method); + ~AuthenticationRoot() {} + + bool Validate(); + + KeyboxError ValidateKeybox(); + + bool InstallKeybox(const uint8_t* keybox_data, size_t keybox_length) { + return keybox().InstallKeybox(keybox_data, keybox_length); + } + + const std::vector& DeviceKey(bool use_real_keybox = false) { + return use_real_keybox ? real_keybox().device_key() : + keybox().device_key(); + } + + const std::vector& DeviceId() { + return keybox().device_id(); + } + + size_t DeviceTokenLength() { + return keybox().key_data_length(); + } + + const uint8_t* DeviceToken() { + return keybox().key_data(); + } + + WvKeybox& keybox() { return use_test_keybox_ ? test_keybox_ : keybox_; } + bool UseTestKeybox(const uint8_t* keybox_data, size_t keybox_length) { + use_test_keybox_ = true; + return test_keybox_.InstallKeybox(keybox_data, keybox_length); + } + + RSA_shared_ptr& SharedRsaKey() { return rsa_key_; } + RSA* rsa_key() { return rsa_key_.get(); } + bool LoadTestRsaKey(); + + private: + OEMCrypto_ProvisioningMethod provisioning_method_; + WvKeybox& real_keybox() { return keybox_; } + + WvKeybox keybox_; + WvTestKeybox test_keybox_; + bool use_test_keybox_; + + RSA_shared_ptr rsa_key_; // If no keybox, this is baked in certificate. + + CORE_DISALLOW_COPY_AND_ASSIGN(AuthenticationRoot); +}; + +} // namespace wvoec_mock + +#endif // OEMCRYPTO_AUTH_MOCK_H_ diff --git a/mock/src/oemcrypto_engine_device_properties.cpp b/mock/src/oemcrypto_engine_device_properties.cpp new file mode 100644 index 0000000..b0f9059 --- /dev/null +++ b/mock/src/oemcrypto_engine_device_properties.cpp @@ -0,0 +1,15 @@ +// Copyright 2014 Google Inc. All Rights Reserved. +// +// Mock implementation of OEMCrypto APIs +// + +#include "oemcrypto_engine_mock.h" + +namespace wvoec_mock { + +CryptoEngine* CryptoEngine::MakeCryptoEngine( + std::auto_ptr file_system) { + return new CryptoEngine(file_system); +} + +} // namespace wvoec_mock diff --git a/mock/src/oemcrypto_engine_device_properties_L1.cpp b/mock/src/oemcrypto_engine_device_properties_L1.cpp new file mode 100644 index 0000000..f483231 --- /dev/null +++ b/mock/src/oemcrypto_engine_device_properties_L1.cpp @@ -0,0 +1,36 @@ +// Copyright 2014 Google Inc. All Rights Reserved. +// +// Mock implementation of OEMCrypto APIs +// +// This file contains oemcrypto engine properties that would be for a +// level 1 device. +#include "oemcrypto_engine_mock.h" + +namespace wvoec_mock { + +class L1CryptoEngine : public CryptoEngine { + public: + explicit L1CryptoEngine(std::auto_ptr file_system) + : CryptoEngine(file_system) {} + + bool config_local_display_only() { return true; } + + OEMCrypto_HDCP_Capability config_maximum_hdcp_capability() { + return HDCP_V2; + } + + bool config_is_anti_rollback_hw_present() { return true; } + + const char* config_security_level() { return "L1"; } + + // This should start at 0, and be incremented only when a security patch has + // been applied to the device that fixes a security bug. + uint8_t config_security_patch_level() { return 3; } +}; + +CryptoEngine* CryptoEngine::MakeCryptoEngine( + std::auto_ptr file_system) { + return new L1CryptoEngine(file_system); +} + +} // namespace wvoec_mock diff --git a/mock/src/oemcrypto_engine_device_properties_cert.cpp b/mock/src/oemcrypto_engine_device_properties_cert.cpp new file mode 100644 index 0000000..b853231 --- /dev/null +++ b/mock/src/oemcrypto_engine_device_properties_cert.cpp @@ -0,0 +1,35 @@ +// Copyright 2015 Google Inc. All Rights Reserved. +// +// Mock implementation of OEMCrypto APIs +// +// This file contains oemcrypto engine properties that would be for a device +// that does not have persistant storage or a keybox. +// +// Note: We also define it to be L2 for illustration only. Production devices +// are rarely level 2. +#include "oemcrypto_engine_mock.h" + +namespace wvoec_mock { + +class CertOnlyCryptoEngine : public CryptoEngine { + public: + explicit CertOnlyCryptoEngine(std::auto_ptr file_system) + : CryptoEngine(file_system) {} + + bool config_local_display_only() { return true; } + + bool config_supports_usage_table() { return false; } + + OEMCrypto_ProvisioningMethod config_provisioning_method() { + return OEMCrypto_DrmCertificate; + } + + const char* config_security_level() { return "L2"; } +}; + +CryptoEngine* CryptoEngine::MakeCryptoEngine( + std::auto_ptr file_system) { + return new CertOnlyCryptoEngine(file_system); +} + +} // namespace wvoec_mock diff --git a/mock/src/oemcrypto_engine_device_properties_mod.cpp b/mock/src/oemcrypto_engine_device_properties_mod.cpp new file mode 100644 index 0000000..4b4b693 --- /dev/null +++ b/mock/src/oemcrypto_engine_device_properties_mod.cpp @@ -0,0 +1,595 @@ +// Copyright 2017 Google Inc. All Rights Reserved. +// +// Mock implementation of OEMCrypto APIs +// +// This file contains oemcrypto engine properties that reads data from a file +// to decide what it's current status is. It is used for testing cdm code. +// The following properties are read from the file: +// log_level: logging level to use. +// 0 = LOG_ERROR, +// 1 = LOG_WARN, +// 2 = LOG_INFO, +// 3 = LOG_DEBUG, +// 4 = LOG_VERBOSE +// kLogging*: All logging flags found in oemcrypto/include/oemcrypto_logging.h +// can be turned on (1) or off (0). +// security_level: returned by OEMCrypto_SecurityLevel. +// secure_lib: If set, then this will be used as a path to +// the L1 liboemcrypto.so that we can use secure buffers. +// current_hdcp: returned by OEMCrypto_GetHDCPCapability and +// used to verify the key control block in methods like DecryptCENC. +// HDCP_NONE = 0, // No HDCP supported, no secure data path. +// HDCP_V1 = 1, // HDCP version 1.0 +// HDCP_V2 = 2, // HDCP version 2.0 Type 1. +// HDCP_V2_1 = 3, // HDCP version 2.1 Type 1. +// HDCP_V2_2 = 4, // HDCP version 2.2 Type 1. +// HDCP_NO_DIGITAL_OUTPUT = 0xff // No digital output. +// max_hdcp: returned by OEMCrypto_GetHDCPCapability. Same values as above. +// srm_update_supported: If "1", then srm update is supported. +// srm_initial_version: Initial value for srm version. +// This will be ignored after a reset. If this is not set, CurrentSRM will +// return NOT_IMPLEMENTED. +// srm_load_fail: If set to a nonzero number, then load_srm will +// fail and the version will not be updated. The number is converted to +// an OEMCryptoResult and returned. +// srm_load_version: If this is set, then it will be used as the +// new srm version after loading an SRM -- ignoring the contents of the SRM. +// If srm_load_version is -1, then the buffer passed into LoadSRM will be +// parsed. +// srm_blacklisted_device_attached: If set to "1", then a +// oemcrypto will act as if a blacklisted device is attached -- i.e. +// playback will be restricted to the local display only. +// srm_attached_device_id: If nonzero, the id of a blacklisted device. +// If this id is in the revocation list of an SRM file when it is loaded, +// playback will be restricted to the local display only. +// security_patch_level: This is the value returned by +// OEMCrypto_Security_Patch_Level. If the key control block requires a +// higher level, then OEMCrypto_LoadKeys will fail. +// max_buffer_size: maximum size of a buffer accepted by DecryptCENC and +// friends. If this is 0, there is no restriction. If it is 1, the +// minimum allowed value is used. +// use_keybox: If this is 1, then the test keybox is used. If this is zero, +// then the test OEM certificate is used. +// use_fallback: If this is nonzero, then the installed Level 1 library will +// be used to play content to a secure buffer. Decryption and key +// verification are done by the mock, but then the data is copied to the +// secure buffer using OEMCrypto_CopyBuffer. The filename of the fallback +// library is hardcoded to "level1_backup_liboemcrypto.so". It is +// recommended you use the install script to ensure you have the right +// filename. +// +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "log.h" +#include "oem_cert.h" +#include "oemcrypto_engine_mock.h" +#include "oemcrypto_logging.h" +#include "properties.h" +#include "string_conversions.h" + +namespace wvoec_mock { +namespace { +typedef OEMCryptoResult (*L1_Initialize_t)(void); +typedef OEMCryptoResult (*L1_Terminate_t)(void); +typedef OEMCryptoResult (*L1_CopyBuffer_t)(const uint8_t* data_addr, + size_t data_length, + OEMCrypto_DestBufferDesc* out_buffer, + uint8_t subsample_flags); +const std::string kDefaultOptionsFile = "/data/mediadrm/oemcrypto/options.txt"; +} // namespace + +class AndroidModifiableCryptoEngine : public CryptoEngine { + public: + AndroidModifiableCryptoEngine(std::auto_ptr file_system) + : CryptoEngine(file_system), + options_file_(kDefaultOptionsFile), + srm_loaded_(false), + srm_version_(0), + level1_valid_(false), + level1_library_(NULL) { + std::string path; + if (wvcdm::Properties::GetDeviceFilesBasePath(wvcdm::kSecurityLevelL3, + &path)) { + options_file_ = path + "options.txt"; + } + } + + void MaybeReadOptionsFile() { + static time_t last_check = 0; + static time_t last_changed = 0; + time_t now = time(NULL); + if (now > last_check + 5) { // Check every five seconds. + last_check = now; + struct stat file_stat; + if (stat(options_file_.c_str(), &file_stat)) { + LOGE("Could not stat %s: %s", options_file_.c_str(), strerror(errno)); + return; + } + if (file_stat.st_mtime > last_changed) { + last_changed = file_stat.st_mtime; + ReadOptionsFile(); + } + } + } + + void ReadOptionsFile() { + FILE *file = fopen(options_file_.c_str(), "r"); + if (!file) { + LOGE("Could not read %s %s", options_file_.c_str(), strerror(errno)); + return; + } + while (!feof(file)) { + char name[80 + 1]; + int64_t value; + if (fscanf(file, "%80s %lld", name, &value)) { + LOGD("Option %s = %lld", name, value); + options_[std::string(name)] = value; + } + } + fclose(file); + InitializeLogging(); + } + + int64_t GetOption(const std::string &key, int64_t default_value) { + MaybeReadOptionsFile(); + if (options_.find(key) == options_.end() ) { + LOGW("Option %s not set. Using default %lld", key.c_str(), default_value); + return default_value; + } + return options_[key]; + } + + void InitializeLogging() { + int log_level = GetOption("log_level", wvcdm::LOG_DEBUG); + int categories = 0; + if (GetOption("kLoggingTraceOEMCryptoCalls", 0) > 0) + categories |= kLoggingTraceOEMCryptoCalls; + if (GetOption("kLoggingDumpContentKeys", 0) > 0) + categories |= kLoggingDumpContentKeys; + if (GetOption("kLoggingDumpKeyControlBlocks", 0) > 0) + categories |= kLoggingDumpKeyControlBlocks; + if (GetOption("kLoggingDumpDerivedKeys", 0) > 0) + categories |= kLoggingDumpDerivedKeys; + if (GetOption("kLoggingTraceNonce", 0) > 0) + categories |= kLoggingTraceNonce; + if (GetOption("kLoggingTraceDecryption", 0) > 0) + categories |= kLoggingTraceDecryption; + if (GetOption("kLoggingTraceUsageTable", 0) > 0) + categories |= kLoggingTraceUsageTable; + if (GetOption("kLoggingTraceDecryptCalls", 0) > 0) + categories |= kLoggingTraceDecryptCalls; + if (GetOption("kLoggingDumpTraceAll", 0) > 0) + categories |= kLoggingDumpTraceAll; + SetLoggingSettings(log_level, categories); + } + +#define QUOTE_DEFINE(A) #A +#define QUOTE(A) QUOTE_DEFINE(A) +#define LOOKUP(Name, Function) \ + Name = (L1_##Name##t)dlsym(level1_library_, QUOTE(Function)); \ + if (!Name) { \ + LOGW("Could not load L1 %s.", \ + QUOTE(Function)); \ + Terminate(); \ + return false; \ + } + + virtual bool Initialize() { + LOGD("OEMCrypto Mock With Options " " " __DATE__ " " __TIME__); + MaybeReadOptionsFile(); + if (!GetOption("use_fallback", 1)) { + LOGD("Level 1 fallback ignored."); + return true; + } + level1_library_ = dlopen("level1_backup_liboemcrypto.so", RTLD_NOW); + if (level1_library_ == NULL) { + LOGE("Could not load backup: %s", dlerror()); + return false; + } + LOOKUP(Initialize_, OEMCrypto_Initialize); + LOOKUP(Terminate_, OEMCrypto_Terminate); + LOOKUP(CopyBuffer_, OEMCrypto_CopyBuffer); + level1_valid_ = true; + OEMCryptoResult sts = Initialize_(); + LOGD("L1 fall back initialized. status = %d.", sts); + if (sts != OEMCrypto_SUCCESS) { + LOGW("Terminating L1 because init failed."); + Terminate(); + LOGW("Continuing Mock without L1 fallback."); + } + return true; + } + + virtual void Terminate() { + if (level1_valid_) Terminate_(); + if (level1_library_ != NULL) { + LOGD("Closing L1 fall back.\n"); + dlclose(level1_library_); + level1_valid_ = false; + level1_library_ = NULL; + CopyBuffer_ = NULL; + Initialize_ = NULL; + Terminate_ = NULL; + } else { + LOGD("Terminate mock.\n"); + } + } + + const char *HDCPCapabilityAsString(OEMCrypto_HDCP_Capability value) { + switch (value) { + case HDCP_NONE: + return "No HDCP supported, no secure data path"; + case HDCP_V1: + return "HDCP version 1.0"; + case HDCP_V2: + return "HDCP version 2.0"; + case HDCP_V2_1: + return "HDCP version 2.1"; + case HDCP_V2_2: + return "HDCP version 2.2"; + case HDCP_NO_DIGITAL_OUTPUT: + return "No HDCP device attached/using local display with secure path"; + default: + return ""; + } + } + + + OEMCrypto_ProvisioningMethod config_provisioning_method() { + if (GetOption("use_keybox", 1)) { + return OEMCrypto_Keybox; + } else { + return OEMCrypto_OEMCertificate; + } + } + + OEMCryptoResult get_oem_certificate(SessionContext* session, + uint8_t* public_cert, + size_t* public_cert_length) { + if (GetOption("use_keybox", 1)) { + LOGD("OEM Cert asked for when use_keybox = 1."); + return OEMCrypto_ERROR_NOT_IMPLEMENTED; + } + if (kOEMPublicCertSize == 0) { + LOGD("OEM Cert Size is 0."); + return OEMCrypto_ERROR_NOT_IMPLEMENTED; + } + if (public_cert_length == NULL) { + LOGD("OEM Cert length is 0."); + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + if (*public_cert_length < kOEMPublicCertSize) { + *public_cert_length = kOEMPublicCertSize; + return OEMCrypto_ERROR_SHORT_BUFFER; + } + *public_cert_length = kOEMPublicCertSize; + if (public_cert == NULL) { + return OEMCrypto_ERROR_SHORT_BUFFER; + } + memcpy(public_cert, kOEMPublicCert, kOEMPublicCertSize); + if (!session->LoadRSAKey(kOEMPrivateKey, kOEMPrivateKeySize)) { + LOGE("Private RSA Key did not load correctly."); + return OEMCrypto_ERROR_INVALID_RSA_KEY; + } + return OEMCrypto_SUCCESS; + } + + // Returns "L3" for a software only library. L1 is for hardware protected + // data paths. + const char *config_security_level() { + switch (GetOption("security_level", 0)) { + default: + LOGW("Option security_level not set. Default is L3."); + case 3: + return "L3"; + case 2: + return "L2"; + case 1: + return "L1"; + } + } + + // Returns the HDCP version currently in use. + OEMCrypto_HDCP_Capability config_current_hdcp_capability() { + static OEMCrypto_HDCP_Capability current_hdcp = HDCP_NONE; + OEMCrypto_HDCP_Capability new_current_hdcp = + static_cast(GetOption("current_hdcp", 0)); + if (current_hdcp != new_current_hdcp) { + LOGI("OEMCrypto current HDCP changed from %d (%s) to %d (%s)", current_hdcp, + HDCPCapabilityAsString(current_hdcp), new_current_hdcp, + HDCPCapabilityAsString(new_current_hdcp)); + current_hdcp = new_current_hdcp; + } + return current_hdcp; + } + + // Returns the max HDCP version supported. + OEMCrypto_HDCP_Capability config_maximum_hdcp_capability() { + static OEMCrypto_HDCP_Capability max_hdcp = HDCP_NONE; + MaybeReadOptionsFile(); + OEMCrypto_HDCP_Capability new_max_hdcp = + static_cast(GetOption("max_hdcp", 0)); + if (max_hdcp != new_max_hdcp) { + LOGI("OEMCrypto max HDCP changed from %d (%s) to %d (%s)", max_hdcp, + HDCPCapabilityAsString(max_hdcp), new_max_hdcp, + HDCPCapabilityAsString(new_max_hdcp)); + max_hdcp = new_max_hdcp; + } + return max_hdcp; + } + + // This should start at 0, and be incremented only when a security patch has + // been applied to the device that fixes a security bug. + uint8_t config_security_patch_level() { + return GetOption("security_patch_level", 0); + } + + size_t max_buffer_size() { + int max = GetOption("max_buffer_size", 0); + // If max is 1, just use default max buffer. + if (max == 1) return CryptoEngine::max_buffer_size(); + return max; // If 0, no restriction. If something else, use that restriction. + } + + bool srm_update_supported() { + int supported = GetOption("srm_update_supported", 0); + LOGI("OEMCrypto mock %s supporting SRM update.", + supported ? "is" : "is not"); + return supported != 0; + } + + OEMCryptoResult current_srm_version(uint16_t *version) { + if (srm_loaded_) { + LOGV("SRM loaded. version used is %d.", srm_version_); + *version = srm_version_; + return OEMCrypto_SUCCESS; + } + int value = GetOption("srm_initial_version", -1); + if (value > 0) { + LOGV("SRM version from get option: %d.", value); + srm_version_ = value; + *version = value; + return OEMCrypto_SUCCESS; + } else { + LOGI("SRM initial version is %d -- reporting not implemented.", value); + return OEMCrypto_ERROR_NOT_IMPLEMENTED; + } + } + + // Convert uint24 or uint40 into a uint64. + int64_t unpack_odd_bytes(const uint8_t *buffer, size_t length) { + uint8_t small_buffer[8]; + memset(small_buffer, 0, 8); + if (length > 8) { + LOGE("OEMCrypto Mock: programmer error. unpack %d bytes.", length); + length = 8; + } + size_t offset = 8 - length; + memcpy(small_buffer + offset, buffer, length); + return wvcdm::htonll64(*reinterpret_cast(small_buffer)); + } + + OEMCryptoResult load_srm(const uint8_t *buffer, size_t buffer_length) { + if (!srm_update_supported()) { + LOGE("OEMCrypto mock update not supported, but load_srm called."); + return OEMCrypto_ERROR_NOT_IMPLEMENTED; + } + int result = GetOption("srm_load_fail", 0); + if (result > 0) { + LOGE("OEMCrypto mock load_srm returning error %d.", result); + return static_cast(result); + } + int new_version = GetOption("srm_load_version", -1); + if (new_version >= 0) { + if (new_version < srm_version_) { + LOGE("New SRM version is lower than existing SRM version: %d < %d", + new_version, srm_version_); + return OEMCrypto_ERROR_INVALID_CONTEXT; + } + srm_version_ = new_version; + LOGI("OEMCrypto mock told to change SRM version to %d.", srm_version_); + srm_loaded_ = true; + return OEMCrypto_SUCCESS; + } + if (buffer_length < 395) { + LOGE("OEMCrypto mock bad buffer size: %ld < 395.", buffer_length); + return OEMCrypto_ERROR_SHORT_BUFFER; + } + uint8_t srm_id = buffer[0] >> 4; + uint8_t hdcp2_indicator = buffer[0] & 0x0F; + uint8_t reserved = buffer[1]; + uint16_t version = htons(*reinterpret_cast(&buffer[2])); + if (reserved) { + LOGE("OEMCrypto mock. SRM's second byte nonzero: %02X.", reserved); + return OEMCrypto_ERROR_INVALID_CONTEXT; + } + uint8_t generation = buffer[4]; + if (generation > 1) { + LOGW("OEMCrypto mock. SRM Generation number is %d, but only first gen is parsed.", + generation); + LOGW("If the revoked device is in a a later generation, it will not be recognized."); + } + int64_t length = unpack_odd_bytes(buffer + 5, 3); // 24 bits. + if (length + 5 != buffer_length) { + LOGW("OEMCrypto mock. SRM length is %lld = 0x%llx, but I expected %zd = 0x%zx.", + length, length, buffer_length - 5, buffer_length - 5); + } + int64_t count = 0; + const uint8_t *ids; + if (srm_id == 8 && hdcp2_indicator == 0) { + // https://www.digital-cp.com/sites/default/files/specifications/HDCP%20Specification%20Rev1_4_Secure.pdf + count = buffer[8]; + LOGI("OEMCrypto mock loading HDCP1 SRM. version = %d. count=%lld.", + version, count); + ids = buffer + 9; + if (buffer_length < 9 + count*5) { + LOGE("OEMCrypto mock bad buffer size for count = %lld: %d < %lld.", + count, buffer_length, 12 + count*5); + return OEMCrypto_ERROR_SHORT_BUFFER; + } + } else if (srm_id == 9 && hdcp2_indicator == 1) { + // https://www.digital-cp.com/sites/default/files/specifications/HDCP%20on%20HDMI%20Specification%20Rev2_2_Final1.pdf + count = unpack_odd_bytes(buffer + 8, 2) >> 6; // 10 bits = 2 bytes - 6. + LOGI("OEMCrypto mock loading HDCP2 SRM. version = %d. count=%lld.", + version, count); + ids = buffer + 12; + if (buffer_length < 12 + count*5) { + LOGE("OEMCrypto mock bad buffer size for count: %d < %ld.", + buffer_length, 12 + count*5); + return OEMCrypto_ERROR_SHORT_BUFFER; + } + } else { + LOGE("OEMCrypto mock bad buffer start: %02X%02X%02X%02X...", buffer[0], + buffer[1], buffer[2], buffer[3]); + return OEMCrypto_ERROR_INVALID_CONTEXT; + } + for(size_t i = 0; i < count; i++) { + int64_t id = unpack_odd_bytes(ids + 5*i, 5); + srm_revocation_list_.push_back(id); + LOGI("OEMCrypto mock SRM revokes device %lld = 0x%llx", id, id); + } + srm_loaded_ = true; + srm_version_ = version; + return OEMCrypto_SUCCESS; + } + + OEMCryptoResult remove_srm() { + if (!srm_update_supported()) { + LOGE("OEMCrypto mock update not supported, bug load_srm called."); + return OEMCrypto_ERROR_NOT_IMPLEMENTED; + } + srm_version_ = 0; + srm_loaded_ = false; + return OEMCrypto_SUCCESS; + } + + bool srm_blacklisted_device_attached() { + if (GetOption("srm_load_version", -1) < 0) { + return scan_revoked_list(); + } + static int blacklisted = 0; + int new_value = GetOption("srm_blacklisted_device_attached", 0); + if (new_value != blacklisted) { + LOGI("SRM blacklisted device changed from %d to %d", blacklisted, + new_value); + blacklisted = new_value; + } + return blacklisted > 0; + } + + bool scan_revoked_list() { + static int64_t old_attached_id = 0; + int64_t attached_id = GetOption("srm_attached_device_id", 0); + bool print_all_ids = false; + if (attached_id != old_attached_id) { + LOGD("OEMCrypto mock -- ID of attached device is %lld = 0x%lld", + attached_id, attached_id); + old_attached_id = attached_id; + print_all_ids = true; + } + for (size_t i = 0; i < srm_revocation_list_.size(); i++) { + if (print_all_ids) { + LOGD("OEMCrypto mock: %d) revoked id %lld = 0x%lld.", i, + srm_revocation_list_[i], srm_revocation_list_[i]); + } + if (srm_revocation_list_[i] == attached_id) { + LOGD("OEMCrypto mock: attached device %lld = 0x%lld is revoked.", + attached_id, attached_id); + return true; + } + } + LOGD("OEMCrypto mock: attached device %lld is not revoked.", attached_id); + return false; + } + + virtual void adjust_destination(OEMCrypto_DestBufferDesc *out_description, + size_t data_length, uint8_t subsample_flags) { + if (out_description->type != OEMCrypto_BufferType_Secure) return; + if (!level1_valid_) { + static bool warned_once = false; + if (!warned_once) { + warned_once = true; + LOGW("OEMCrypto Mock: given secure buffer with no level1 fallback."); + } + return; + } + if (subsample_flags & OEMCrypto_FirstSubsample) { + final_destination_.type = OEMCrypto_BufferType_Secure; + final_destination_.buffer.secure.handle = + out_description->buffer.secure.handle; + final_destination_.buffer.secure.max_length = + out_description->buffer.secure.max_length; + final_destination_.buffer.secure.offset = + out_description->buffer.secure.offset; + temp_buffer_.resize(final_destination_.buffer.secure.max_length); + temp_buffer_length_ = 0; + } + if (temp_buffer_length_ != out_description->buffer.secure.offset) { + LOGW("OEMCrypto: offset into secure buffer is not correct %zd != %zd.", + temp_buffer_length_, out_description->buffer.secure.offset); + } + size_t new_length = temp_buffer_length_ + data_length; + if (new_length > temp_buffer_.size()) { + LOGW("Temp buffer was not big enough. %zd > %zd.", new_length, + temp_buffer_.size()); + temp_buffer_.resize(new_length); + } + destination_ = &temp_buffer_[temp_buffer_length_]; + temp_buffer_length_ = new_length; + } + + // Push destination buffer to L1 output. + virtual OEMCryptoResult PushDestination( + OEMCrypto_DestBufferDesc *out_description, uint8_t subsample_flags) { + if (level1_valid_ && + (out_description->type == OEMCrypto_BufferType_Secure)) { + if (subsample_flags & OEMCrypto_LastSubsample) { + return CopyBuffer_(&temp_buffer_[0], temp_buffer_length_, + &final_destination_, + OEMCrypto_FirstSubsample | OEMCrypto_LastSubsample); + } + } + return OEMCrypto_SUCCESS; + } + + private: + // If the SRM version has been loaded or not. If not, we use the system + // property to find the current SRM version. + bool srm_loaded_; + // Current srm version. Before an SRM has been loaded, this will be set from + // the system property. + int srm_version_; + // List of forbidden/revoked devices. + std::vector srm_revocation_list_; + + std::map options_; + + std::string options_file_; + bool level1_valid_; + void* level1_library_; + L1_CopyBuffer_t CopyBuffer_; + L1_Initialize_t Initialize_; + L1_Terminate_t Terminate_; + OEMCrypto_DestBufferDesc final_destination_; + std::vector temp_buffer_; + size_t temp_buffer_length_; // Length of temp buffer currently in use. +}; + +CryptoEngine* CryptoEngine::MakeCryptoEngine( + std::auto_ptr file_system) { + return new AndroidModifiableCryptoEngine(file_system); +} + +} // namespace wvoec_mock diff --git a/mock/src/oemcrypto_engine_device_properties_prov30.cpp b/mock/src/oemcrypto_engine_device_properties_prov30.cpp new file mode 100644 index 0000000..f9d2f30 --- /dev/null +++ b/mock/src/oemcrypto_engine_device_properties_prov30.cpp @@ -0,0 +1,82 @@ +// Copyright 2016 Google Inc. All Rights Reserved. +// +// Mock implementation of OEMCrypto APIs +// +// This file contains oemcrypto engine properties that would be for a +// level 2 device that does not have persistant storage or a keybox. +// Note: this is for illustration only. Production devices are rarely level 2. + +#include "oemcrypto_engine_mock.h" + +#include + +#include "log.h" +#include "oem_cert.h" + +namespace wvoec_mock { + +class Prov30CryptoEngine : public CryptoEngine { + public: + explicit Prov30CryptoEngine(std::auto_ptr file_system) + : CryptoEngine(file_system) {} + + bool config_local_display_only() { return true; } + + // Returns the max HDCP version supported. + OEMCrypto_HDCP_Capability config_maximum_hdcp_capability() { + return HDCP_NO_DIGITAL_OUTPUT; + } + + // Returns true if the client supports persistent storage of + // offline usage table information. + bool config_supports_usage_table() { + return false; + } + + // Returns true if the client uses a keybox as the root of trust. + bool config_supports_keybox() { + return false; + } + + // This version uses an OEM Certificate. + OEMCrypto_ProvisioningMethod config_provisioning_method() { + return OEMCrypto_OEMCertificate; + } + + OEMCryptoResult get_oem_certificate(SessionContext* session, + uint8_t* public_cert, + size_t* public_cert_length) { + if (kOEMPublicCertSize == 0) { + return OEMCrypto_ERROR_NOT_IMPLEMENTED; + } + if (public_cert_length == NULL) { + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + if (*public_cert_length < kOEMPublicCertSize) { + *public_cert_length = kOEMPublicCertSize; + return OEMCrypto_ERROR_SHORT_BUFFER; + } + *public_cert_length = kOEMPublicCertSize; + if (public_cert == NULL) { + return OEMCrypto_ERROR_SHORT_BUFFER; + } + memcpy(public_cert, kOEMPublicCert, kOEMPublicCertSize); + if (!session->LoadRSAKey(kOEMPrivateKey, kOEMPrivateKeySize)) { + LOGE("Private RSA Key did not load correctly."); + return OEMCrypto_ERROR_INVALID_RSA_KEY; + } + return OEMCrypto_SUCCESS; + } + + // Returns "L3" for a software only library. L1 is for hardware protected + // keys and data paths. L2 is for hardware protected keys but no data path + // protection. + const char* config_security_level() { return "L2"; } +}; + +CryptoEngine* CryptoEngine::MakeCryptoEngine( + std::auto_ptr file_system) { + return new Prov30CryptoEngine(file_system); +} + +} // namespace wvoec_mock diff --git a/mock/src/oemcrypto_engine_mock.cpp b/mock/src/oemcrypto_engine_mock.cpp new file mode 100644 index 0000000..51987b7 --- /dev/null +++ b/mock/src/oemcrypto_engine_mock.cpp @@ -0,0 +1,124 @@ +// Copyright 2013 Google Inc. All Rights Reserved. +// +// Mock implementation of OEMCrypto APIs +// +#include "oemcrypto_engine_mock.h" + +#include +#include +#include +#include +#include +#include + +#include + +#include "keys.h" +#include "log.h" +#include "oemcrypto_key_mock.h" +#include "oemcrypto_rsa_key_shared.h" +#include "string_conversions.h" +#include "wv_cdm_constants.h" + +namespace wvoec_mock { + +// Note: The class CryptoEngine is configured at compile time by compiling in +// different device property files. The methods in this file are generic to +// all configurations. See the files oemcrypto_engine_device_properties*.cpp +// for methods that are configured for specific configurations. + +CryptoEngine::CryptoEngine(std::auto_ptr file_system) + : root_of_trust_(config_provisioning_method()), + file_system_(file_system), + usage_table_(this) { + ERR_load_crypto_strings(); +} + +CryptoEngine::~CryptoEngine() { + wvcdm::AutoLock lock(session_table_lock_); + ActiveSessions::iterator it; + for (it = sessions_.begin(); it != sessions_.end(); ++it) { + delete it->second; + } + sessions_.clear(); + ERR_free_strings(); +} + +SessionId CryptoEngine::CreateSession() { + wvcdm::AutoLock lock(session_table_lock_); + static int unique_id = 1; + SessionId sid = (SessionId)++unique_id; + SessionContext* sctx = + new SessionContext(this, sid, root_of_trust_.SharedRsaKey()); + sessions_[sid] = sctx; + return sid; +} + +bool CryptoEngine::DestroySession(SessionId sid) { + SessionContext* sctx = FindSession(sid); + wvcdm::AutoLock lock(session_table_lock_); + if (sctx) { + sessions_.erase(sid); + delete sctx; + return true; + } else { + return false; + } +} + +SessionContext* CryptoEngine::FindSession(SessionId sid) { + wvcdm::AutoLock lock(session_table_lock_); + ActiveSessions::iterator it = sessions_.find(sid); + if (it != sessions_.end()) { + return it->second; + } + return NULL; +} + +OEMCrypto_HDCP_Capability CryptoEngine::config_current_hdcp_capability() { + return config_local_display_only() ? HDCP_NO_DIGITAL_OUTPUT : HDCP_V1; +} + +OEMCrypto_HDCP_Capability CryptoEngine::config_maximum_hdcp_capability() { + return HDCP_NO_DIGITAL_OUTPUT; +} + +OEMCryptoResult CryptoEngine::SetDestination( + 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; + 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 - + out_description->buffer.secure.offset; + break; + case OEMCrypto_BufferType_Direct: + // Direct buffer type is only used on some specialized devices where + // oemcrypto has a direct connection to the screen buffer. It is not, + // for example, supported on Android. + destination_ = NULL; + break; + default: + return OEMCrypto_ERROR_INVALID_CONTEXT; + } + if (out_description->type != OEMCrypto_BufferType_Direct && + max_length < data_length) { + LOGE("[SetDestination(): OEMCrypto_ERROR_SHORT_BUFFER]"); + return OEMCrypto_ERROR_SHORT_BUFFER; + } + adjust_destination(out_description, data_length, subsample_flags); + if ((out_description->type != OEMCrypto_BufferType_Direct) && + (destination_ == NULL)) { + return OEMCrypto_ERROR_INVALID_CONTEXT; + } + return OEMCrypto_SUCCESS; +} + +} // namespace wvoec_mock diff --git a/mock/src/oemcrypto_engine_mock.h b/mock/src/oemcrypto_engine_mock.h new file mode 100644 index 0000000..791c771 --- /dev/null +++ b/mock/src/oemcrypto_engine_mock.h @@ -0,0 +1,183 @@ +// Copyright 2013 Google Inc. All Rights Reserved. +// +// Mock implementation of OEMCrypto APIs +// +#ifndef MOCK_OEMCRYPTO_ENGINE_MOCK_H_ +#define MOCK_OEMCRYPTO_ENGINE_MOCK_H_ + +#include +#include +#include +#include +#include + +#include + +#include "OEMCryptoCENC.h" // Needed for enums only. +#include "file_store.h" +#include "lock.h" +#include "oemcrypto_auth_mock.h" +#include "oemcrypto_key_mock.h" +#include "oemcrypto_rsa_key_shared.h" +#include "oemcrypto_session.h" +#include "oemcrypto_usage_table_mock.h" +#include "wv_cdm_types.h" + +namespace wvoec_mock { + +typedef std::map ActiveSessions; + +class CryptoEngine { + public: + // 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. + // NOTE: The caller must instantiate a FileSystem object - ownership + // will be transferred to the new CryptoEngine object. + static CryptoEngine* MakeCryptoEngine( + std::auto_ptr file_system); + + virtual ~CryptoEngine(); + + virtual bool Initialize() { return true; } + + bool ValidRootOfTrust() { return root_of_trust_.Validate(); } + + bool InstallKeybox(const uint8_t* keybox, size_t keybox_length) { + return root_of_trust_.InstallKeybox(keybox, keybox_length); + } + + bool UseTestKeybox(const uint8_t* keybox_data, size_t keybox_length) { + return root_of_trust_.UseTestKeybox(keybox_data, keybox_length); + } + + bool LoadTestRsaKey() { return root_of_trust_.LoadTestRsaKey(); } + + KeyboxError ValidateKeybox() { return root_of_trust_.ValidateKeybox(); } + + const std::vector& DeviceRootKey(bool override_to_real = false) { + return root_of_trust_.DeviceKey(override_to_real); + } + + const std::vector& DeviceRootId() { + return root_of_trust_.DeviceId(); + } + + size_t DeviceRootTokenLength() { return root_of_trust_.DeviceTokenLength(); } + + const uint8_t* DeviceRootToken() { + return root_of_trust_.DeviceToken(); + } + + virtual void Terminate() {} + + SessionId CreateSession(); + + bool DestroySession(SessionId sid); + + SessionContext* FindSession(SessionId sid); + + size_t GetNumberOfOpenSessions() { return sessions_.size(); } + + size_t GetMaxNumberOfSessions() { + // An arbitrary limit for mock implementation. + static const size_t kMaxSupportedOEMCryptoSessions = 64; + return kMaxSupportedOEMCryptoSessions; + } + + // Returns the HDCP version currently in use. + virtual OEMCrypto_HDCP_Capability config_current_hdcp_capability(); + + // Returns the max HDCP version supported. + virtual OEMCrypto_HDCP_Capability config_maximum_hdcp_capability(); + + UsageTable& usage_table() { return usage_table_; } + wvcdm::FileSystem* file_system() { return file_system_.get(); } + + // If config_local_display_only() returns true, we pretend we are using a + // built-in display, instead of HDMI or WiFi output. + virtual bool config_local_display_only() { return false; } + + // A closed platform is permitted to use clear buffers. + virtual bool config_closed_platform() { return false; } + + // Returns true if the client supports persistent storage of + // offline usage table information. + virtual bool config_supports_usage_table() { return true; } + + virtual OEMCrypto_ProvisioningMethod config_provisioning_method() { + return OEMCrypto_Keybox; + } + + virtual OEMCryptoResult get_oem_certificate(SessionContext* session, + uint8_t* public_cert, + size_t* public_cert_length) { + return OEMCrypto_ERROR_NOT_IMPLEMENTED; + } + + // Used for OEMCrypto_IsAntiRollbackHwPresent. + virtual bool config_is_anti_rollback_hw_present() { return false; } + + // Returns "L3" for a software only library. L1 is for hardware protected + // data paths. + virtual const char* config_security_level() { return "L3"; } + + // This should start at 0, and be incremented only when a security patch has + // been applied to the device that fixes a security bug. + virtual uint8_t config_security_patch_level() { return 0; } + + // If 0 no restriction, otherwise it's the max buffer for DecryptCENC. + virtual size_t max_buffer_size() { return 1024 * 100; } // 100 KiB. + + virtual bool srm_update_supported() { return false; } + + virtual OEMCryptoResult current_srm_version(uint16_t* version) { + return OEMCrypto_ERROR_NOT_IMPLEMENTED; + } + + virtual OEMCryptoResult load_srm(const uint8_t* buffer, + size_t buffer_length) { + return OEMCrypto_ERROR_NOT_IMPLEMENTED; + } + + virtual OEMCryptoResult remove_srm() { + return OEMCrypto_ERROR_NOT_IMPLEMENTED; + } + + virtual bool srm_blacklisted_device_attached() { return false; } + + // Set destination pointer based on the output destination description. + OEMCryptoResult SetDestination(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) { + } + + // Push destination buffer to output -- used by subclasses for testing. + virtual OEMCryptoResult PushDestination( + OEMCrypto_DestBufferDesc* out_description, uint8_t subsample_flags) { + return OEMCrypto_SUCCESS; + } + + protected: + explicit CryptoEngine(std::auto_ptr file_system); + uint8_t* destination_; + + private: + ActiveSessions sessions_; + AuthenticationRoot root_of_trust_; + wvcdm::Lock session_table_lock_; + std::auto_ptr file_system_; + UsageTable usage_table_; + + CORE_DISALLOW_COPY_AND_ASSIGN(CryptoEngine); +}; + +} // namespace wvoec_mock + +#endif // MOCK_OEMCRYPTO_ENGINE_MOCK_H_ diff --git a/mock/src/oemcrypto_key_mock.cpp b/mock/src/oemcrypto_key_mock.cpp new file mode 100644 index 0000000..8f7832b --- /dev/null +++ b/mock/src/oemcrypto_key_mock.cpp @@ -0,0 +1,122 @@ +// Copyright 2013 Google Inc. All Rights Reserved. +// +// Mock implementation of OEMCrypto APIs +// +#include "oemcrypto_key_mock.h" + +#include +#include + +#include "log.h" +#include "oemcrypto_logging.h" +#include "wv_cdm_constants.h" + +namespace wvoec_mock { + +bool KeyControlBlock::Validate() { + if (memcmp(verification_, "kctl", 4) && // original verification + memcmp(verification_, "kc09", 4) && // add in version 9 api + memcmp(verification_, "kc10", 4) && // add in version 10 api + memcmp(verification_, "kc11", 4) && // add in version 11 api + memcmp(verification_, "kc12", 4) && // add in version 12 api + memcmp(verification_, "kc13", 4) && // add in version 13 api + memcmp(verification_, "kc14", 4)) { // add in version 14 api + LOGE("KCB: BAD verification string: %4.4s", verification_); + valid_ = false; + } else { + valid_ = true; + } + return valid_; +} + +// This extracts 4 bytes in network byte order to a 32 bit integer in +// host byte order. +uint32_t KeyControlBlock::ExtractField(const std::vector& str, + int idx) { + int bidx = idx * 4; + uint32_t t = static_cast(str[bidx]) << 24; + t |= static_cast(str[bidx + 1]) << 16; + t |= static_cast(str[bidx + 2]) << 8; + t |= static_cast(str[bidx + 3]); + return t; +} + +KeyControlBlock::KeyControlBlock( + const std::vector& key_control_string) { + if (key_control_string.size() < wvcdm::KEY_CONTROL_SIZE) { + LOGE("KCB: BAD Size: %d (not %d)", key_control_string.size(), + wvcdm::KEY_CONTROL_SIZE); + return; + } + + memcpy(verification_, &key_control_string[0], 4); + duration_ = ExtractField(key_control_string, 1); + nonce_ = ExtractField(key_control_string, 2); + control_bits_ = ExtractField(key_control_string, 3); + if (LogCategoryEnabled(kLoggingDumpKeyControlBlocks)) { + LOGD("KCB:"); + LOGD(" valid: %d", valid()); + LOGD(" duration: %d", duration()); + LOGD(" nonce: %08X", nonce()); + LOGD(" magic: %08X", verification()); + LOGD(" bits: %08X", control_bits()); + LOGD(" bit kSharedLicense %s.", + (control_bits() & kSharedLicense) ? "set" : "unset"); + LOGD(" bit kControlSRMVersionRequired %s.", + (control_bits() & kControlSRMVersionRequired) ? "set" : "unset"); + LOGD(" bit kControlDisableAnalogOutput %s.", + (control_bits() & kControlDisableAnalogOutput) ? "set" : "unset"); + LOGD(" bits kControlSecurityPatchLevel 0x%02x.", + (control_bits() & kControlSecurityPatchLevelMask) + >> kControlSecurityPatchLevelShift); + switch (control_bits() & kControlReplayMask) { + case kControlNonceRequired: + LOGD(" bits kControlReplay kControlNonceRequired."); + break; + case kControlNonceOrEntry: + LOGD(" bits kControlReplay kControlNonceOrEntry."); + break; + default: + LOGD(" bits kControlReplay unset."); + break; + } + LOGD(" bits kControlHDCPVersion 0x%02x.", + (control_bits() & kControlHDCPVersionMask) + >> kControlHDCPVersionShift); + LOGD(" bit kControlAllowEncrypt %s.", + (control_bits() & kControlAllowEncrypt) ? "set" : "unset"); + LOGD(" bit kControlAllowDecrypt %s.", + (control_bits() & kControlAllowDecrypt) ? "set" : "unset"); + LOGD(" bit kControlAllowSign %s.", + (control_bits() & kControlAllowSign) ? "set" : "unset"); + LOGD(" bit kControlAllowVerify %s.", + (control_bits() & kControlAllowVerify) ? "set" : "unset"); + LOGD(" bit kControlObserveDataPath %s.", + (control_bits() & kControlObserveDataPath) ? "set" : "unset"); + LOGD(" bit kControlObserveHDCP %s.", + (control_bits() & kControlObserveHDCP) ? "set" : "unset"); + LOGD(" bit kControlObserveCGMS %s.", + (control_bits() & kControlObserveCGMS) ? "set" : "unset"); + LOGD(" bit kControlDataPathSecure %s.", + (control_bits() & kControlDataPathSecure) ? "set" : "unset"); + LOGD(" bit kControlNonceEnabled %s.", + (control_bits() & kControlNonceEnabled) ? "set" : "unset"); + LOGD(" bit kControlHDCPRequired %s.", + (control_bits() & kControlHDCPRequired) ? "set" : "unset"); + uint32_t cgms_bits = control_bits() & 0x3; + const char* cgms_values[4] = {"free", "BAD", "once", "never"}; + LOGD(" CGMS = %s", cgms_values[cgms_bits]); + } + Validate(); +} + +void Key::UpdateDuration(const KeyControlBlock& control) { + control_.set_duration(control.duration()); +} + +void KeyControlBlock::RequireLocalDisplay() { + // Set all bits to require HDCP Local Display Only. + control_bits_ |= kControlHDCPVersionMask; +} + +} // namespace wvoec_mock diff --git a/mock/src/oemcrypto_key_mock.h b/mock/src/oemcrypto_key_mock.h new file mode 100644 index 0000000..2860ac0 --- /dev/null +++ b/mock/src/oemcrypto_key_mock.h @@ -0,0 +1,112 @@ +// Copyright 2013 Google Inc. All Rights Reserved. +// +// Mock implementation of OEMCrypto APIs +// +#ifndef OEMCRYPTO_KEY_MOCK_H_ +#define OEMCRYPTO_KEY_MOCK_H_ + +#include +#include +#include + +namespace wvoec_mock { + +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 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<& key_control_string); + ~KeyControlBlock() {} + + bool Validate(); + void Invalidate() { valid_ = false; } + + bool valid() const { return valid_; } + uint32_t duration() const { return duration_; } + void set_duration(uint32_t duration) { duration_ = duration; } + uint32_t nonce() const { return nonce_; } + const char* verification() const { return verification_; } + uint32_t control_bits() const { return control_bits_; } + void RequireLocalDisplay(); + + private: + uint32_t ExtractField(const std::vector& str, int idx); + + bool valid_; + char verification_[4]; + uint32_t duration_; + uint32_t nonce_; + uint32_t control_bits_; +}; + +// AES-128 crypto key, or HMAC signing key. +class Key { + public: + Key(const Key& key) + : value_(key.value_), control_(key.control_), ctr_mode_(key.ctr_mode_) {} + Key(const std::vector& key_string, const KeyControlBlock& control) + : value_(key_string), control_(control), ctr_mode_(true){}; + + virtual ~Key(){}; + void UpdateDuration(const KeyControlBlock& control); + virtual const std::vector& value() const { return value_; } + const KeyControlBlock& control() const { return control_; } + bool ctr_mode() const { return ctr_mode_; } + void set_ctr_mode(bool ctr_mode) { ctr_mode_ = ctr_mode; } + + private: + std::vector value_; + KeyControlBlock control_; + bool ctr_mode_; +}; + +// AES-256 entitlement key. |Key| holds the entitlement key. |EntitlementKey| +// holds the content key. +class EntitlementKey : public Key { + public: + EntitlementKey(const Key& key) : Key(key) {} + virtual ~EntitlementKey() {} + virtual const std::vector& value() const { return content_key_; } + const std::vector& content_key() { return content_key_; } + const std::vector& content_key_id() { return content_key_id_; } + const std::vector& entitlement_key() { return Key::value(); } + bool SetContentKey(const std::vector& content_key, + const std::vector& content_key_id) { + content_key_.assign(content_key.begin(), content_key.end()); + content_key_id_.assign(content_key_id.begin(), content_key_id.end()); + return true; + } + + private: + std::vector content_key_; + std::vector content_key_id_; +}; + +} // namespace wvoec_mock + +#endif // OEMCRYPTO_KEY_MOCK_H_ diff --git a/mock/src/oemcrypto_keybox_mock.cpp b/mock/src/oemcrypto_keybox_mock.cpp new file mode 100644 index 0000000..318d049 --- /dev/null +++ b/mock/src/oemcrypto_keybox_mock.cpp @@ -0,0 +1,112 @@ +// Copyright 2013 Google Inc. All Rights Reserved. +// +// Mock implementation of OEMCrypto APIs +// +#include "oemcrypto_keybox_mock.h" + +#include // needed for ntoh() +#include +#include +#include +#include "log.h" +#include "wv_keybox.h" +#include "wvcrc32.h" + +namespace wvoec_mock { + +namespace { + +const WidevineKeybox kTestKeybox = { + // Sample keybox used for test vectors + { + // deviceID + 0x54, 0x65, 0x73, 0x74, 0x4b, 0x65, 0x79, 0x30, // TestKey01 + 0x31, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ........ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ........ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ........ + }, { + // key + 0xfb, 0xda, 0x04, 0x89, 0xa1, 0x58, 0x16, 0x0e, + 0xa4, 0x02, 0xe9, 0x29, 0xe3, 0xb6, 0x8f, 0x04, + }, { + // data + 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x10, 0x19, + 0x07, 0xd9, 0xff, 0xde, 0x13, 0xaa, 0x95, 0xc1, + 0x22, 0x67, 0x80, 0x53, 0x36, 0x21, 0x36, 0xbd, + 0xf8, 0x40, 0x8f, 0x82, 0x76, 0xe4, 0xc2, 0xd8, + 0x7e, 0xc5, 0x2b, 0x61, 0xaa, 0x1b, 0x9f, 0x64, + 0x6e, 0x58, 0x73, 0x49, 0x30, 0xac, 0xeb, 0xe8, + 0x99, 0xb3, 0xe4, 0x64, 0x18, 0x9a, 0x14, 0xa8, + 0x72, 0x02, 0xfb, 0x02, 0x57, 0x4e, 0x70, 0x64, + 0x0b, 0xd2, 0x2e, 0xf4, 0x4b, 0x2d, 0x7e, 0x39, + }, { + // magic + 0x6b, 0x62, 0x6f, 0x78, + }, { + // Crc + 0x0a, 0x7a, 0x2c, 0x35, + } +}; + +} // namespace + +WvKeybox::WvKeybox() { + valid_ = Prepare(); +} + +KeyboxError WvKeybox::Validate() { + if (!valid_) { + LOGE("[KEYBOX NOT LOADED]"); + return OTHER_ERROR; + } + if (strncmp(reinterpret_cast(magic_), "kbox", 4) != 0) { + LOGE("[KEYBOX HAS BAD MAGIC]"); + return BAD_MAGIC; + } + uint32_t crc_computed; + uint32_t crc_stored; + uint8_t* crc_stored_bytes = (uint8_t*) &crc_stored; + memcpy(crc_stored_bytes, crc_, sizeof(crc_)); + WidevineKeybox keybox; + memset(&keybox, 0, sizeof(keybox)); + memcpy(keybox.device_id_, &device_id_[0], device_id_.size()); + memcpy(keybox.device_key_, &device_key_[0], sizeof(keybox.device_key_)); + memcpy(keybox.data_, key_data_, sizeof(keybox.data_)); + memcpy(keybox.magic_, magic_, sizeof(keybox.magic_)); + + crc_computed = ntohl(wvcrc32(reinterpret_cast(&keybox), + sizeof(keybox) - 4)); // Drop last 4 bytes. + if (crc_computed != crc_stored) { + LOGE("[KEYBOX CRC problem: computed = %08x, stored = %08x]\n", + crc_computed, crc_stored); + return BAD_CRC; + } + return NO_ERROR; +} + +bool WvKeybox::InstallKeybox(const uint8_t* buffer, size_t keyBoxLength) { + if (keyBoxLength != 128) { + return false; + } + + const WidevineKeybox* keybox + = reinterpret_cast(buffer); + size_t device_id_length + = strnlen(reinterpret_cast(keybox->device_id_), 32); + device_id_.assign(keybox->device_id_, + keybox->device_id_ + device_id_length); + device_key_.assign(keybox->device_key_, + keybox->device_key_ + sizeof(keybox->device_key_)); + memcpy(key_data_, keybox->data_, sizeof(keybox->data_)); + memcpy(magic_, keybox->magic_, sizeof(keybox->magic_)); + memcpy(crc_, keybox->crc_, sizeof(keybox->crc_)); + + return true; +} + +WvTestKeybox::WvTestKeybox() { + InstallKeybox(reinterpret_cast(&kTestKeybox), + sizeof(kTestKeybox)); +} + +} // namespace wvoec_mock diff --git a/mock/src/oemcrypto_keybox_mock.h b/mock/src/oemcrypto_keybox_mock.h new file mode 100644 index 0000000..f351b76 --- /dev/null +++ b/mock/src/oemcrypto_keybox_mock.h @@ -0,0 +1,51 @@ +// Copyright 2013 Google Inc. All Rights Reserved. +// +// Mock implementation of OEMCrypto APIs +// +#ifndef OEMCRYPTO_KEYBOX_MOCK_H_ +#define OEMCRYPTO_KEYBOX_MOCK_H_ + +#include "oemcrypto_key_mock.h" + +namespace wvoec_mock { + +const int DEVICE_KEY_LENGTH = 16; +typedef uint8_t WvKeyboxKey[DEVICE_KEY_LENGTH]; + +const int KEY_DATA_LENGTH = 72; +typedef uint8_t WvKeyboxKeyData[KEY_DATA_LENGTH]; + +enum KeyboxError { NO_ERROR, BAD_CRC, BAD_MAGIC, OTHER_ERROR }; + +// Widevine keybox +class WvKeybox { + public: + WvKeybox(); + ~WvKeybox() {} + + KeyboxError Validate(); + const std::vector& device_id() { return device_id_; } + std::vector& device_key() { return device_key_; } + const WvKeyboxKeyData& key_data() { return key_data_; } + size_t key_data_length() { return KEY_DATA_LENGTH; } + bool InstallKeybox(const uint8_t* keybox, size_t keyBoxLength); + + private: + bool Prepare(); + + bool valid_; + std::vector device_id_; + std::vector device_key_; + WvKeyboxKeyData key_data_; + uint8_t magic_[4]; + uint8_t crc_[4]; +}; + +class WvTestKeybox : public WvKeybox { + public: + WvTestKeybox(); +}; + +} // namespace wvoec_mock + +#endif // OEMCRYPTO_KEYBOX_MOCK_H_ diff --git a/mock/src/oemcrypto_keybox_testkey.cpp b/mock/src/oemcrypto_keybox_testkey.cpp new file mode 100644 index 0000000..71f2a15 --- /dev/null +++ b/mock/src/oemcrypto_keybox_testkey.cpp @@ -0,0 +1,54 @@ +// Copyright 2014 Google Inc. All Rights Reserved. +// +// Test keybox. + +#include "oemcrypto_keybox_mock.h" +#include "wv_keybox.h" + +namespace wvoec_mock { + +namespace { + +// Note: this is a valid keybox, but it is not accepted by production servers. +// However, it is different from the one used for most of the unit tests. +const WidevineKeybox kKeybox = { + // Sample keybox used for test vectors + { + // deviceID + 0x54, 0x65, 0x73, 0x74, 0x4b, 0x65, 0x79, 0x30, // TestKey02 + 0x32, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ........ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ........ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ........ + }, { + // key + 0x76, 0x5d, 0xce, 0x01, 0x04, 0x89, 0xb3, 0xd0, + 0xdf, 0xce, 0x54, 0x8a, 0x49, 0xda, 0xdc, 0xb6, + }, { + // data + 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x10, 0x19, + 0x92, 0x27, 0x0b, 0x1f, 0x1a, 0xd5, 0xc6, 0x93, + 0x19, 0x3f, 0xaa, 0x74, 0x1f, 0xdd, 0x5f, 0xb4, + 0xe9, 0x40, 0x2f, 0x34, 0xa4, 0x92, 0xf4, 0xae, + 0x9a, 0x52, 0x39, 0xbc, 0xb7, 0x24, 0x38, 0x13, + 0xab, 0xf4, 0x92, 0x96, 0xc4, 0x81, 0x60, 0x33, + 0xd8, 0xb8, 0x09, 0xc7, 0x55, 0x0e, 0x12, 0xfa, + 0xa8, 0x98, 0x62, 0x8a, 0xec, 0xea, 0x74, 0x8a, + 0x4b, 0xfa, 0x5a, 0x9e, 0xb6, 0x49, 0x0d, 0x80, + }, { + // magic + 0x6b, 0x62, 0x6f, 0x78, + }, { + // Crc + 0x2a, 0x3b, 0x3e, 0xe4, + } +}; + +} // namespace + +bool WvKeybox::Prepare() { + InstallKeybox(reinterpret_cast(&kKeybox), + sizeof(kKeybox)); + return true; +} + +} // namespace wvoec_mock diff --git a/mock/src/oemcrypto_logging.cpp b/mock/src/oemcrypto_logging.cpp new file mode 100644 index 0000000..b1ef8c6 --- /dev/null +++ b/mock/src/oemcrypto_logging.cpp @@ -0,0 +1,104 @@ +// Copyright 2014 Google Inc. All Rights Reserved. + +#include "oemcrypto_logging.h" + +#include + +namespace wvoec_mock { + +int logging_category_setting = 0x00; + +void SetLoggingSettings(int level, int categories) { + SetLoggingLevel(level); + TurnOffLoggingForAllCategories(); + AddLoggingForCategories(categories); +} + +void TurnOffLoggingForAllCategories() { logging_category_setting = 0; } + +void SetLoggingLevel(int level) { + wvcdm::g_cutoff = static_cast(level); +} + +void SetLoggingLevel(wvcdm::LogPriority level) { wvcdm::g_cutoff = level; } + +void AddLoggingForCategories(int categories) { + logging_category_setting |= categories; +} + +void RemoveLoggingForCategories(int categories) { + logging_category_setting &= ~categories; +} + +bool LogCategoryEnabled(int categories) { + return ((logging_category_setting & categories) != 0); +} + +void dump_hex_helper(std::string& buffer, std::string name, + const uint8_t* vector, size_t length) { + buffer += name + " = "; + if (vector == NULL) { + buffer += "NULL;\n"; + LOGE(buffer.c_str()); + return; + } + int a, b; + char int_to_hexcar[16] = {'0', '1', '2', '3', '4', '5', '6', '7', + '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'}; + for (size_t i = 0; i < length; i++) { + if (i == 0) { + buffer += "\n wvcdm::a2b_hex(\""; + } else if (i % 32 == 0) { + buffer += "\"\n \""; + } + a = vector[i] % 16; + b = (vector[i] - a) / 16; + buffer += int_to_hexcar[b]; + buffer += int_to_hexcar[a]; + } + buffer += "\");\n"; +} + +void dump_hex(std::string name, const uint8_t* vector, size_t length) { + std::string buffer = ""; + dump_hex_helper(buffer, name, vector, length); + LOGV(buffer.c_str()); +} + +void dump_array_part_helper(std::string& buffer, std::string array, + size_t index, std::string name, + const uint8_t* vector, size_t length) { + char index_str[256]; + + snprintf(index_str, sizeof index_str, "%zu", index); + + if (vector == NULL) { + buffer += array.c_str(); + buffer += "["; + buffer += index_str; + buffer += "]."; + buffer += name.c_str(); + buffer += " = NULL;\n"; + LOGW(buffer.c_str()); + return; + } + buffer += "std::string s"; + buffer += index_str; + buffer += "_"; + dump_hex_helper(buffer, name, vector, length); + buffer += array.c_str(); + buffer += "["; + buffer += index_str; + buffer += "]." + name + " = message_ptr + message.find(s"; + buffer += index_str; + buffer += "_" + name + ".data());\n"; +} + +void dump_array_part(std::string array, size_t index, std::string name, + const uint8_t* vector, size_t length) { + std::string buffer = ""; + dump_array_part_helper(buffer, array, index, name, vector, length); + LOGV(buffer.c_str()); +} + +} // namespace wvoec_mock diff --git a/mock/src/oemcrypto_mock.cpp b/mock/src/oemcrypto_mock.cpp new file mode 100644 index 0000000..09f1ada --- /dev/null +++ b/mock/src/oemcrypto_mock.cpp @@ -0,0 +1,1958 @@ +// Copyright 2013 Google Inc. All Rights Reserved. +// +// Reference implementation of OEMCrypto APIs +// +#include "OEMCryptoCENC.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "file_store.h" +#include "log.h" +#include "oemcrypto_engine_mock.h" +#include "oemcrypto_logging.h" +#include "oemcrypto_session.h" +#include "oemcrypto_usage_table_mock.h" +#include "string_conversions.h" +#include "wv_cdm_constants.h" + +namespace { +const uint8_t kBakedInCertificateMagicBytes[] = {0xDE, 0xAD, 0xBE, 0xEF}; + +// Return uint32 referenced through a potentially unaligned pointer. +// If the pointer is NULL, return 0. +uint32_t unaligned_dereference_uint32(const void* unaligned_ptr) { + if (unaligned_ptr == NULL) return 0; + uint32_t value; + const uint8_t* src = reinterpret_cast(unaligned_ptr); + uint8_t* dest = reinterpret_cast(&value); + for (unsigned long i=0; i < sizeof(value); i++) { + dest[i] = src[i]; + } + return value; +} + +} // namespace + +namespace wvoec_mock { + +static CryptoEngine* crypto_engine = NULL; + +typedef struct { + uint8_t signature[wvcdm::MAC_KEY_SIZE]; + uint8_t context[wvcdm::MAC_KEY_SIZE]; + uint8_t iv[wvcdm::KEY_IV_SIZE]; + uint8_t enc_rsa_key[]; +} WrappedRSAKey; + +extern "C" OEMCryptoResult OEMCrypto_Initialize(void) { + if (LogCategoryEnabled(kLoggingTraceOEMCryptoCalls)) { + LOGI("------------------------- OEMCrypto_Initialize(void)\n"); + } + if (crypto_engine) { + LOGE("------------------------- Calling Initialize without Terminate\n"); + delete crypto_engine; + crypto_engine = NULL; + } + // NOTE: This requires a compatible Filesystem implementation. + // NOTE: Ownership of the FileSystem object is transferred to CryptoEngine + std::auto_ptr fs(new wvcdm::FileSystem()); + crypto_engine = CryptoEngine::MakeCryptoEngine(fs); + + if (!crypto_engine || !crypto_engine->Initialize()) { + LOGE("[OEMCrypto_Initialize(): failed]"); + return OEMCrypto_ERROR_INIT_FAILED; + } + if (LogCategoryEnabled(kLoggingTraceOEMCryptoCalls)) { + LOGD("[OEMCrypto_Initialize(): success]"); + } + return OEMCrypto_SUCCESS; +} + +extern "C" OEMCryptoResult OEMCrypto_Terminate(void) { + if (LogCategoryEnabled(kLoggingTraceOEMCryptoCalls)) { + LOGI("----------------- OEMCryptoResult OEMCrypto_Terminate(void)\n"); + } + + if (!crypto_engine) { + LOGE("[OEMCrypto_Terminate(): not initialized]"); + return OEMCrypto_ERROR_TERMINATE_FAILED; + } + crypto_engine->Terminate(); + + delete crypto_engine; + crypto_engine = NULL; + if (LogCategoryEnabled(kLoggingTraceOEMCryptoCalls)) { + LOGD("[OEMCrypto_Terminate(): success]"); + } + return OEMCrypto_SUCCESS; +} + +extern "C" OEMCryptoResult OEMCrypto_OpenSession(OEMCrypto_SESSION* session) { + if (LogCategoryEnabled(kLoggingTraceOEMCryptoCalls)) { + LOGI("-- OEMCryptoResult OEMCrypto_OpenSession" + "(OEMCrypto_SESSION *session)\n"); + } + if (!crypto_engine) { + LOGE("OEMCrypto_OpenSession: OEMCrypto not initialized."); + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + if (crypto_engine->GetNumberOfOpenSessions() >= + crypto_engine->GetMaxNumberOfSessions()) { + LOGE("[OEMCrypto_OpenSession(): failed due to too many sessions]"); + return OEMCrypto_ERROR_TOO_MANY_SESSIONS; + } + SessionId sid = crypto_engine->CreateSession(); + *session = (OEMCrypto_SESSION)sid; + if (LogCategoryEnabled(kLoggingTraceOEMCryptoCalls)) { + LOGD("[OEMCrypto_OpenSession(): SID=%08x]", sid); + } + return OEMCrypto_SUCCESS; +} + +extern "C" OEMCryptoResult OEMCrypto_CloseSession(OEMCrypto_SESSION session) { + if (LogCategoryEnabled(kLoggingTraceOEMCryptoCalls)) { + LOGI("-- OEMCryptoResult OEMCrypto_CloseSession" + "(OEMCrypto_SESSION session)\n"); + } + if (!crypto_engine) { + LOGE("OEMCrypto_CloseSession: OEMCrypto not initialized."); + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + if (!crypto_engine->DestroySession((SessionId)session)) { + if (LogCategoryEnabled(kLoggingTraceOEMCryptoCalls)) { + LOGD("[OEMCrypto_CloseSession(SID=%08X): failed]", session); + } + return OEMCrypto_ERROR_CLOSE_SESSION_FAILED; + } else { + if (LogCategoryEnabled(kLoggingTraceOEMCryptoCalls)) { + LOGD("[OEMCrypto_CloseSession(SID=%08X): success]", session); + } + return OEMCrypto_SUCCESS; + } +} + +extern "C" OEMCryptoResult OEMCrypto_GenerateDerivedKeys( + 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) { + if (LogCategoryEnabled(kLoggingTraceOEMCryptoCalls)) { + LOGI("-- OEMCryptoResult OEMCrypto_GenerateDerivedKeys(\n"); + if (wvcdm::g_cutoff >= wvcdm::LOG_VERBOSE) { + dump_hex("mac_key_context", mac_key_context, + (size_t)mac_key_context_length); + dump_hex("enc_key_context", enc_key_context, + (size_t)enc_key_context_length); + } + } + if (!crypto_engine) { + LOGE("OEMCrypto_GenerateDerivedKeys: OEMCrypto not initialized."); + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + if (crypto_engine->config_provisioning_method() != OEMCrypto_Keybox) { + return OEMCrypto_ERROR_NOT_IMPLEMENTED; + } + if (!crypto_engine->ValidRootOfTrust()) { + LOGE("[OEMCrypto_GenerateDerivedKeys(): ERROR_KEYBOX_INVALID]"); + return OEMCrypto_ERROR_KEYBOX_INVALID; + } + + SessionContext* session_ctx = crypto_engine->FindSession(session); + if (!session_ctx || !session_ctx->isValid()) { + LOGE("[OEMCrypto_GenerateDerivedKeys(): ERROR_INVALID_SESSION]"); + return OEMCrypto_ERROR_INVALID_SESSION; + } + + const std::vector mac_ctx_str( + mac_key_context, mac_key_context + mac_key_context_length); + const std::vector enc_ctx_str( + enc_key_context, enc_key_context + enc_key_context_length); + + // Generate mac and encryption keys for current session context + if (!session_ctx->DeriveKeys(crypto_engine->DeviceRootKey(), mac_ctx_str, + enc_ctx_str)) { + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + if (LogCategoryEnabled(kLoggingTraceOEMCryptoCalls)) { + if (wvcdm::g_cutoff >= wvcdm::LOG_VERBOSE) { + dump_hex("mac_key_server", &session_ctx->mac_key_server()[0], + session_ctx->mac_key_server().size()); + dump_hex("mac_key_client", &session_ctx->mac_key_client()[0], + session_ctx->mac_key_client().size()); + dump_hex("enc_key", &session_ctx->encryption_key()[0], + session_ctx->encryption_key().size()); + } + } + return OEMCrypto_SUCCESS; +} + +extern "C" OEMCryptoResult OEMCrypto_GenerateNonce(OEMCrypto_SESSION session, + uint32_t* nonce) { + if (LogCategoryEnabled(kLoggingTraceOEMCryptoCalls)) { + LOGI("-- OEMCryptoResult OEMCrypto_GenerateNonce" + "(OEMCrypto_SESSION session,\n"); + } + if (!crypto_engine) { + LOGE("OEMCrypto_GenerateNonce: OEMCrypto not initialized."); + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + SessionContext* session_ctx = crypto_engine->FindSession(session); + if (!session_ctx || !session_ctx->isValid()) { + LOGE("[OEMCrypto_GenerateNonce(): ERROR_INVALID_SESSION]"); + return OEMCrypto_ERROR_INVALID_SESSION; + } + + // Prevent nonce flood. + static time_t last_nonce_time = 0; + static int nonce_count = 0; + time_t now = time(NULL); + if (now == last_nonce_time) { + nonce_count++; + if (nonce_count > 20) { + LOGE("[OEMCrypto_GenerateNonce(): Nonce Flood detected]"); + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + } else { + nonce_count = 1; + last_nonce_time = now; + } + + uint32_t nonce_value; + uint8_t* nonce_string = reinterpret_cast(&nonce_value); + + // Generate 4 bytes of random data + if (!RAND_bytes(nonce_string, 4)) { + LOGE("[OEMCrypto_GenerateNonce(): Random bytes failure]"); + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + session_ctx->AddNonce(nonce_value); + *nonce = nonce_value; + if (LogCategoryEnabled(kLoggingTraceOEMCryptoCalls)) { + LOGI("nonce = %08x\n", nonce_value); + } + return OEMCrypto_SUCCESS; +} + +extern "C" OEMCryptoResult OEMCrypto_GenerateSignature( + OEMCrypto_SESSION session, const uint8_t* message, size_t message_length, + uint8_t* signature, size_t* signature_length) { + if (LogCategoryEnabled(kLoggingTraceOEMCryptoCalls)) { + LOGI("-- OEMCryptoResult OEMCrypto_GenerateSignature(\n"); + if (wvcdm::g_cutoff >= wvcdm::LOG_VERBOSE) { + dump_hex("message", message, message_length); + } + } + if (!crypto_engine) { + LOGE("OEMCrypto_GenerateSignature: OEMCrypto Not Initialized."); + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + + if (*signature_length < SHA256_DIGEST_LENGTH) { + *signature_length = SHA256_DIGEST_LENGTH; + return OEMCrypto_ERROR_SHORT_BUFFER; + } + + if (message == NULL || message_length == 0 || signature == NULL || + signature_length == 0) { + LOGE("[OEMCrypto_GenerateSignature(): OEMCrypto_ERROR_INVALID_CONTEXT]"); + return OEMCrypto_ERROR_INVALID_CONTEXT; + } + + SessionContext* session_ctx = crypto_engine->FindSession(session); + if (!session_ctx || !session_ctx->isValid()) { + LOGE("[OEMCrypto_GenerateSignature(): ERROR_INVALID_SESSION]"); + return OEMCrypto_ERROR_INVALID_SESSION; + } + + if (session_ctx->GenerateSignature(message, message_length, signature, + signature_length)) { + if (LogCategoryEnabled(kLoggingTraceOEMCryptoCalls)) { + if (wvcdm::g_cutoff >= wvcdm::LOG_VERBOSE) { + dump_hex("signature", signature, *signature_length); + } + } + return OEMCrypto_SUCCESS; + } + return OEMCrypto_ERROR_UNKNOWN_FAILURE; +} + +bool RangeCheck(const uint8_t* message, uint32_t message_length, + const uint8_t* field, uint32_t field_length, bool allow_null) { + if (field == NULL) return allow_null; + if (field < message) return false; + if (field + field_length > message + message_length) return false; + return true; +} + +extern "C" OEMCryptoResult OEMCrypto_LoadKeys( + OEMCrypto_SESSION session, const uint8_t* message, size_t message_length, + const uint8_t* signature, size_t signature_length, + const uint8_t* enc_mac_key_iv, const uint8_t* enc_mac_keys, size_t num_keys, + const OEMCrypto_KeyObject* key_array, const uint8_t* pst, size_t pst_length, + const uint8_t* srm_requirement, OEMCrypto_LicenseType license_type) { + if (!crypto_engine) { + LOGE("OEMCrypto_LoadKeys: OEMCrypto Not Initialized."); + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + if (LogCategoryEnabled(kLoggingTraceOEMCryptoCalls)) { + LOGI("-- OEMCryptoResult OEMCrypto_LoadKeys(OEMCrypto_SESSION session,\n"); + if (wvcdm::g_cutoff >= wvcdm::LOG_VERBOSE) { + dump_hex("message", message, message_length); + dump_hex("signature", signature, signature_length); + dump_hex("enc_mac_key_iv", enc_mac_key_iv, wvcdm::KEY_IV_SIZE); + dump_hex("enc_mac_keys", enc_mac_keys, 2 * wvcdm::MAC_KEY_SIZE); + dump_hex("pst", pst, pst_length); + dump_hex("srm_requirement", srm_requirement, wvcdm::KEY_CONTROL_SIZE); + for (size_t i = 0; i < num_keys; i++) { + LOGV("key_array[%zu].key_id_length=%zu;\n", i, + key_array[i].key_id_length); + dump_array_part("key_array", i, "key_id", key_array[i].key_id, + key_array[i].key_id_length); + dump_array_part("key_array", i, "key_data_iv", key_array[i].key_data_iv, + wvcdm::KEY_IV_SIZE); + dump_array_part("key_array", i, "key_data", key_array[i].key_data, + key_array[i].key_data_length); + dump_array_part("key_array", i, "key_control_iv", + key_array[i].key_control_iv, wvcdm::KEY_IV_SIZE); + dump_array_part("key_array", i, "key_control", key_array[i].key_control, + wvcdm::KEY_IV_SIZE); + } + } + } + + if (!crypto_engine->ValidRootOfTrust()) { + LOGE("[OEMCrypto_LoadKeys(): ERROR_KEYBOX_INVALID]"); + return OEMCrypto_ERROR_KEYBOX_INVALID; + } + + SessionContext* session_ctx = crypto_engine->FindSession(session); + if (!session_ctx || !session_ctx->isValid()) { + LOGE("[OEMCrypto_LoadKeys(): ERROR_INVALID_SESSION]"); + return OEMCrypto_ERROR_INVALID_SESSION; + } + + if (message == NULL || message_length == 0 || signature == NULL || + signature_length == 0 || key_array == NULL || num_keys == 0) { + LOGE("[OEMCrypto_LoadKeys(): OEMCrypto_ERROR_INVALID_CONTEXT]"); + return OEMCrypto_ERROR_INVALID_CONTEXT; + } + + // Later on, we use pst_length to verify the the pst is valid. This makes + // sure that we aren't given a null string but told it has postiive length. + if (pst == NULL && pst_length > 0) { + LOGE("[OEMCrypto_LoadKeys(): OEMCrypto_ERROR_INVALID_ONCTEXT - null pst.]"); + return OEMCrypto_ERROR_INVALID_CONTEXT; + } + + // Range check + if (!RangeCheck(message, message_length, enc_mac_keys, + 2 * wvcdm::MAC_KEY_SIZE, true) || + !RangeCheck(message, message_length, enc_mac_key_iv, wvcdm::KEY_IV_SIZE, + true) || + !RangeCheck(message, message_length, pst, pst_length, true) || + !RangeCheck(message, message_length, srm_requirement, + wvcdm::SRM_REQUIREMENT_SIZE, true)) { + LOGE("[OEMCrypto_LoadKeys(): OEMCrypto_ERROR_INVALID_CONTEXT - range " + "check.]"); + return OEMCrypto_ERROR_INVALID_CONTEXT; + } + + for (unsigned int i = 0; i < num_keys; i++) { + if (!RangeCheck(message, message_length, key_array[i].key_id, + key_array[i].key_id_length, false) || + !RangeCheck(message, message_length, key_array[i].key_data, + key_array[i].key_data_length, false) || + !RangeCheck(message, message_length, key_array[i].key_data_iv, + wvcdm::KEY_IV_SIZE, false) || + !RangeCheck(message, message_length, key_array[i].key_control, + wvcdm::KEY_CONTROL_SIZE, false) || + !RangeCheck(message, message_length, key_array[i].key_control_iv, + wvcdm::KEY_IV_SIZE, false)) { + LOGE("[OEMCrypto_LoadKeys(): OEMCrypto_ERROR_INVALID_CONTEXT -range " + "check %d]", i); + return OEMCrypto_ERROR_INVALID_CONTEXT; + } + } + return session_ctx->LoadKeys(message, message_length, signature, + signature_length, enc_mac_key_iv, enc_mac_keys, + num_keys, key_array, pst, pst_length, + srm_requirement, license_type); +} + +extern "C" OEMCryptoResult OEMCrypto_LoadEntitledContentKeys( + OEMCrypto_SESSION session, + size_t num_keys, + const OEMCrypto_EntitledContentKeyObject* key_array) { + if (num_keys == 0) { + LOGE("[OEMCrypto_LoadEntitledContentKeys(): key_array is empty."); + return OEMCrypto_SUCCESS; + } + if (!key_array) { + LOGE("[OEMCrypto_LoadEntitledContentKeys(): missing key_array."); + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + if (!crypto_engine) { + LOGE("OEMCrypto_LoadEntitledContentKeys: OEMCrypto Not Initialized."); + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + SessionContext* session_ctx = crypto_engine->FindSession(session); + if (!session_ctx || !session_ctx->isValid()) { + LOGE("[OEMCrypto_LoadEntitledContentKeys(): ERROR_INVALID_SESSION]"); + return OEMCrypto_ERROR_INVALID_SESSION; + } + + return session_ctx->LoadEntitledContentKeys(num_keys, key_array); +} + +extern "C" OEMCryptoResult OEMCrypto_RefreshKeys( + OEMCrypto_SESSION session, const uint8_t* message, size_t message_length, + const uint8_t* signature, size_t signature_length, size_t num_keys, + const OEMCrypto_KeyRefreshObject* key_array) { + if (LogCategoryEnabled(kLoggingTraceOEMCryptoCalls)) { + LOGI("-- OEMCryptoResult OEMCrypto_RefreshKeys(num_keys=%zu)\n", num_keys); + } + if (!crypto_engine) { + LOGE("OEMCrypto_RefreshKeys: OEMCrypto Not Initialized."); + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + + if (!crypto_engine->ValidRootOfTrust()) { + LOGE("[OEMCrypto_RefreshKeys(): ERROR_KEYBOX_INVALID]"); + return OEMCrypto_ERROR_KEYBOX_INVALID; + } + + SessionContext* session_ctx = crypto_engine->FindSession(session); + if (!session_ctx || !session_ctx->isValid()) { + LOGE("[OEMCrypto_RefreshKeys(): ERROR_INVALID_SESSION]"); + return OEMCrypto_ERROR_INVALID_SESSION; + } + + if (message == NULL || message_length == 0 || signature == NULL || + signature_length == 0 || num_keys == 0) { + LOGE("[OEMCrypto_RefreshKeys(): OEMCrypto_ERROR_INVALID_CONTEXT]"); + return OEMCrypto_ERROR_INVALID_CONTEXT; + } + + // Range check + for (unsigned int i = 0; i < num_keys; i++) { + if (!RangeCheck(message, message_length, key_array[i].key_id, + key_array[i].key_id_length, true) || + !RangeCheck(message, message_length, key_array[i].key_control, + wvcdm::KEY_CONTROL_SIZE, false) || + !RangeCheck(message, message_length, key_array[i].key_control_iv, + wvcdm::KEY_IV_SIZE, true)) { + LOGE("[OEMCrypto_RefreshKeys(): Range Check %d]", i); + return OEMCrypto_ERROR_INVALID_CONTEXT; + } + } + + // Validate message signature + if (!session_ctx->ValidateMessage(message, message_length, signature, + signature_length)) { + LOGE("[OEMCrypto_RefreshKeys(): signature was invalid]"); + return OEMCrypto_ERROR_SIGNATURE_FAILURE; + } + + // Decrypt and refresh keys in key refresh object + OEMCryptoResult status = OEMCrypto_SUCCESS; + std::vector key_id; + std::vector key_control; + std::vector key_control_iv; + for (unsigned int i = 0; i < num_keys; i++) { + if (key_array[i].key_id != NULL) { + key_id.assign(key_array[i].key_id, + key_array[i].key_id + key_array[i].key_id_length); + key_control.assign(key_array[i].key_control, + key_array[i].key_control + wvcdm::KEY_CONTROL_SIZE); + if (key_array[i].key_control_iv == NULL) { + key_control_iv.clear(); + } else { + key_control_iv.assign(key_array[i].key_control_iv, + key_array[i].key_control_iv + wvcdm::KEY_IV_SIZE); + } + } else { + // key_id could be null if special control key type + // key_control is not encrypted in this case + key_id.clear(); + key_control_iv.clear(); + key_control.assign(key_array[i].key_control, + key_array[i].key_control + wvcdm::KEY_CONTROL_SIZE); + } + + status = session_ctx->RefreshKey(key_id, key_control, key_control_iv); + if (status != OEMCrypto_SUCCESS) { + LOGE("[OEMCrypto_RefreshKeys(): error %u in key %i]", status, i); + break; + } + } + + session_ctx->FlushNonces(); + if (status != OEMCrypto_SUCCESS) { + return status; + } + + session_ctx->StartTimer(); + return OEMCrypto_SUCCESS; +} + +extern "C" OEMCryptoResult OEMCrypto_QueryKeyControl( + OEMCrypto_SESSION session, const uint8_t* key_id, size_t key_id_length, + uint8_t* key_control_block, size_t* key_control_block_length) { + if (LogCategoryEnabled(kLoggingTraceOEMCryptoCalls)) { + LOGI("-- OEMCryptoResult OEMCrypto_QueryKeyControl(%d, id=%s)", session, + wvcdm::HexEncode(key_id, key_id_length).c_str()); + } + if (!crypto_engine) { + LOGE("OEMCrypto_QueryKeyControl: OEMCrypto Not Initialized."); + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + uint32_t* block = reinterpret_cast(key_control_block); + if ((key_control_block_length == NULL) || + (*key_control_block_length < wvcdm::KEY_CONTROL_SIZE)) { + LOGE("[OEMCrypto_QueryKeyControl(): OEMCrypto_ERROR_SHORT_BUFFER]"); + return OEMCrypto_ERROR_SHORT_BUFFER; + } + *key_control_block_length = wvcdm::KEY_CONTROL_SIZE; + if (key_id == NULL) { + LOGE("[OEMCrypto_QueryKeyControl(): key_id null. " + "OEMCrypto_ERROR_UNKNOWN_FAILURE]"); + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + + SessionContext* session_ctx = crypto_engine->FindSession(session); + if (!session_ctx || !session_ctx->isValid()) { + LOGE("[OEMCrypto_QueryKeyControl(): ERROR_INVALID_SESSION]"); + return OEMCrypto_ERROR_INVALID_SESSION; + } + const std::vector key_id_str = + std::vector(key_id, key_id + key_id_length); + if (!session_ctx->QueryKeyControlBlock(key_id_str, block)) { + LOGE("[OEMCrypto_QueryKeyControl(): FAIL]"); + return OEMCrypto_ERROR_NO_CONTENT_KEY; + } + return OEMCrypto_SUCCESS; +} + +extern "C" OEMCryptoResult OEMCrypto_SelectKey( + const OEMCrypto_SESSION session, const uint8_t* key_id, + size_t key_id_length, OEMCryptoCipherMode cipher_mode) { + if (LogCategoryEnabled(kLoggingTraceDecryptCalls)) { + LOGI("-- OEMCryptoResult OEMCrypto_SelectKey(%d, id=%s)", session, + wvcdm::HexEncode(key_id, key_id_length).c_str()); + } +#ifndef NDEBUG + if (!crypto_engine->ValidRootOfTrust()) { + LOGE("[OEMCrypto_SelectKey(): ERROR_KEYBOX_INVALID]"); + return OEMCrypto_ERROR_KEYBOX_INVALID; + } +#endif + + SessionContext* session_ctx = crypto_engine->FindSession(session); + if (!session_ctx || !session_ctx->isValid()) { + LOGE("[OEMCrypto_SelectKey(): ERROR_INVALID_SESSION]"); + return OEMCrypto_ERROR_INVALID_SESSION; + } + + const std::vector key_id_str = + std::vector(key_id, key_id + key_id_length); + return session_ctx->SelectContentKey(key_id_str, cipher_mode); +} + +extern "C" 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, + OEMCrypto_DestBufferDesc* out_buffer, + const OEMCrypto_CENCEncryptPatternDesc* pattern, uint8_t subsample_flags) { + if (LogCategoryEnabled(kLoggingTraceDecryptCalls)) { + LOGI("-- OEMCryptoResult OEMCrypto_DecryptCENC(%d)", session); + } + if (!crypto_engine) { + LOGE("OEMCrypto_DecryptCENC: OEMCrypto Not Initialized."); + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + if (data_addr == NULL || data_length == 0 || iv == NULL || + out_buffer == NULL) { + LOGE("[OEMCrypto_DecryptCENC(): OEMCrypto_ERROR_INVALID_CONTEXT]"); + return OEMCrypto_ERROR_INVALID_CONTEXT; + } + if (crypto_engine->max_buffer_size() > 0 && + data_length > crypto_engine->max_buffer_size()) { + // For testing reasons only, pretend that this integration only supports + // the minimum possible buffer size. + 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); + if (status != OEMCrypto_SUCCESS) { + LOGE("[OEMCrypto_DecryptCENC(): destination status: %d]", status); + return status; + } +#ifndef NDEBUG + if (!crypto_engine->ValidRootOfTrust()) { + LOGE("[OEMCrypto_DecryptCENC(): ERROR_KEYBOX_INVALID]"); + return OEMCrypto_ERROR_KEYBOX_INVALID; + } +#endif + + SessionContext* session_ctx = crypto_engine->FindSession(session); + if (!session_ctx || !session_ctx->isValid()) { + LOGE("[OEMCrypto_DecryptCENC(): ERROR_INVALID_SESSION]"); + return OEMCrypto_ERROR_INVALID_SESSION; + } + + OEMCryptoResult result = session_ctx->DecryptCENC( + iv, block_offset, pattern, data_addr, data_length, is_encrypted, + crypto_engine->destination(), out_buffer->type); + if (result != OEMCrypto_SUCCESS) return result; + return crypto_engine->PushDestination(out_buffer, subsample_flags); +} + +extern "C" OEMCryptoResult OEMCrypto_CopyBuffer( + const uint8_t* data_addr, size_t data_length, + OEMCrypto_DestBufferDesc* out_buffer, uint8_t subsample_flags) { + if (LogCategoryEnabled(kLoggingTraceDecryptCalls)) { + LOGI("-- OEMCryptoResult OEMCrypto_CopyBuffer(..)\n"); + } + if (!crypto_engine) { + LOGE("OEMCrypto_CopyBuffer: OEMCrypto Not Initialized."); + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + if (data_addr == NULL || out_buffer == NULL) { + LOGE("[OEMCrypto_CopyBuffer(): OEMCrypto_ERROR_INVALID_CONTEXT]"); + return OEMCrypto_ERROR_INVALID_CONTEXT; + } + if (crypto_engine->max_buffer_size() > 0 && + data_length > crypto_engine->max_buffer_size()) { + // For testing reasons only, pretend that this integration only supports + // the minimum possible buffer size. + 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); + if (status != OEMCrypto_SUCCESS) return status; + if (crypto_engine->destination() != NULL) { + memcpy(crypto_engine->destination(), data_addr, data_length); + } + return crypto_engine->PushDestination(out_buffer, subsample_flags); +} + +extern "C" OEMCryptoResult OEMCrypto_WrapKeybox(const uint8_t* keybox, + size_t keyBoxLength, + uint8_t* wrappedKeybox, + size_t* wrappedKeyBoxLength, + const uint8_t* transportKey, + size_t transportKeyLength) { + if (LogCategoryEnabled(kLoggingTraceOEMCryptoCalls)) { + LOGI("-- OEMCryptoResult OEMCrypto_WrapKeybox(const uint8_t *keybox,\n"); + } + if (crypto_engine->config_provisioning_method() != OEMCrypto_Keybox) { + return OEMCrypto_ERROR_NOT_IMPLEMENTED; + } + if (!keybox || !wrappedKeybox || !wrappedKeyBoxLength || + (keyBoxLength != *wrappedKeyBoxLength)) { + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + // This implementation ignores the transport key. For test keys, we + // don't need to encrypt the keybox. + memcpy(wrappedKeybox, keybox, keyBoxLength); + return OEMCrypto_SUCCESS; +} + +extern "C" OEMCryptoResult OEMCrypto_InstallKeybox(const uint8_t* keybox, + size_t keyBoxLength) { + if (LogCategoryEnabled(kLoggingTraceOEMCryptoCalls)) { + LOGI("-- OEMCryptoResult OEMCrypto_InstallKeybox(const uint8_t *keybox,\n"); + } + if (!crypto_engine) { + LOGE("OEMCrypto_InstallKeybox: OEMCrypto Not Initialized."); + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + if (crypto_engine->config_provisioning_method() != OEMCrypto_Keybox) { + return OEMCrypto_ERROR_NOT_IMPLEMENTED; + } + if (crypto_engine->InstallKeybox(keybox, keyBoxLength)) { + return OEMCrypto_SUCCESS; + } + return OEMCrypto_ERROR_WRITE_KEYBOX; +} + +extern "C" OEMCryptoResult OEMCrypto_LoadTestKeybox( + const uint8_t* buffer, size_t length) { + if (LogCategoryEnabled(kLoggingTraceOEMCryptoCalls)) { + LOGI("-- OEMCryptoResult OEMCrypto_LoadTestKeybox()\n"); + } + if (!crypto_engine) { + LOGE("OEMCrypto_LoadTestKeybox: OEMCrypto Not Initialized."); + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + if (crypto_engine->config_provisioning_method() != OEMCrypto_Keybox) { + return OEMCrypto_ERROR_NOT_IMPLEMENTED; + } + crypto_engine->UseTestKeybox(buffer, length); + return OEMCrypto_SUCCESS; +} + +extern "C" OEMCryptoResult OEMCrypto_IsKeyboxValid(void) { + if (LogCategoryEnabled(kLoggingTraceOEMCryptoCalls)) { + LOGI("-- OEMCryptoResult OEMCrypto_IsKeyboxValid(void) {\n"); + } + if (!crypto_engine) { + LOGE("OEMCrypto_IsKeyboxValid: OEMCrypto Not Initialized."); + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + if (crypto_engine->config_provisioning_method() != OEMCrypto_Keybox) { + return OEMCrypto_ERROR_NOT_IMPLEMENTED; + } + switch (crypto_engine->ValidateKeybox()) { + case NO_ERROR: + return OEMCrypto_SUCCESS; + case BAD_CRC: + return OEMCrypto_ERROR_BAD_CRC; + case BAD_MAGIC: + return OEMCrypto_ERROR_BAD_MAGIC; + default: + case OTHER_ERROR: + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } +} + +extern "C" OEMCrypto_ProvisioningMethod OEMCrypto_GetProvisioningMethod() { + if (LogCategoryEnabled(kLoggingTraceOEMCryptoCalls)) { + LOGI("-- OEMCrypto_ProvisioningMethod OEMCrypto_GetProvisioningMethod()"); + } + if (!crypto_engine) { + LOGE("OEMCrypto_GetProvisioningMethod: OEMCrypto Not Initialized."); + return OEMCrypto_ProvisioningError; + } + return crypto_engine->config_provisioning_method(); +} + +extern "C" OEMCryptoResult OEMCrypto_GetOEMPublicCertificate( + OEMCrypto_SESSION session, uint8_t* public_cert, + size_t* public_cert_length) { + if (LogCategoryEnabled(kLoggingTraceOEMCryptoCalls)) { + LOGI("-- OEMCryptoResult OEMCrypto_GetOEMPublicCertificate(%d) {\n", + session); + } + if (!crypto_engine) { + LOGE("OEMCrypto_GetOEMPublicCertificate: OEMCrypto Not Initialized."); + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + if (crypto_engine->config_provisioning_method() != OEMCrypto_OEMCertificate) { + LOGE("OEMCrypto_GetOEMPublicCertificate: Provisioning method = %d.", + crypto_engine->config_provisioning_method()); + return OEMCrypto_ERROR_NOT_IMPLEMENTED; + } + SessionContext* session_ctx = crypto_engine->FindSession(session); + if (!session_ctx || !session_ctx->isValid()) { + LOGE("[OEMCrypto_GetOEMPublicCertificate(): ERROR_INVALID_SESSION]"); + return OEMCrypto_ERROR_INVALID_SESSION; + } + return crypto_engine->get_oem_certificate(session_ctx, public_cert, + public_cert_length); +} + +extern "C" OEMCryptoResult OEMCrypto_GetDeviceID(uint8_t* deviceID, + size_t* idLength) { + if (LogCategoryEnabled(kLoggingTraceOEMCryptoCalls)) { + LOGI("-- OEMCryptoResult OEMCrypto_GetDeviceID(uint8_t* deviceID,\n"); + } + if (!crypto_engine) { + 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]"); + return OEMCrypto_ERROR_KEYBOX_INVALID; + } + + size_t dev_id_len = dev_id_string.size(); + if (*idLength < dev_id_len) { + *idLength = dev_id_len; + LOGE("[OEMCrypto_GetDeviceId(): ERROR_SHORT_BUFFER]"); + return OEMCrypto_ERROR_SHORT_BUFFER; + } + memset(deviceID, 0, *idLength); + memcpy(deviceID, &dev_id_string[0], dev_id_len); + *idLength = dev_id_len; + if (LogCategoryEnabled(kLoggingTraceOEMCryptoCalls)) { + LOGD("[OEMCrypto_GetDeviceId(): success]"); + } + return OEMCrypto_SUCCESS; +} + +extern "C" OEMCryptoResult OEMCrypto_GetKeyData(uint8_t* keyData, + size_t* keyDataLength) { + if (LogCategoryEnabled(kLoggingTraceOEMCryptoCalls)) { + LOGI("-- OEMCryptoResult OEMCrypto_GetKeyData(uint8_t* keyData,\n"); + } + if (!crypto_engine) { + LOGE("OEMCrypto_GetKeyData: OEMCrypto Not Initialized."); + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + if (crypto_engine->config_provisioning_method() != OEMCrypto_Keybox) { + return OEMCrypto_ERROR_NOT_IMPLEMENTED; + } + size_t length = crypto_engine->DeviceRootTokenLength(); + if (keyDataLength == NULL) { + LOGE("[OEMCrypto_GetKeyData(): null pointer. ERROR_UNKNOWN_FAILURE]"); + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + if (*keyDataLength < length) { + *keyDataLength = length; + LOGE("[OEMCrypto_GetKeyData(): ERROR_SHORT_BUFFER]"); + return OEMCrypto_ERROR_SHORT_BUFFER; + } + if (keyData == NULL) { + LOGE("[OEMCrypto_GetKeyData(): null pointer. ERROR_UNKNOWN_FAILURE]"); + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + memset(keyData, 0, *keyDataLength); + memcpy(keyData, crypto_engine->DeviceRootToken(), length); + *keyDataLength = length; + if (LogCategoryEnabled(kLoggingTraceOEMCryptoCalls)) { + LOGD("[OEMCrypto_GetKeyData(): success]"); + } + return OEMCrypto_SUCCESS; +} + +extern "C" OEMCryptoResult OEMCrypto_GetRandom(uint8_t* randomData, + size_t dataLength) { + if (LogCategoryEnabled(kLoggingTraceOEMCryptoCalls)) { + LOGI("-- OEMCryptoResult OEMCrypto_GetRandom"); + } + if (!randomData) { + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + if (RAND_bytes(randomData, dataLength)) { + return OEMCrypto_SUCCESS; + } + return OEMCrypto_ERROR_UNKNOWN_FAILURE; +} + +extern "C" OEMCryptoResult OEMCrypto_RewrapDeviceRSAKey30( + OEMCrypto_SESSION session, const uint32_t* unaligned_nonce, + const uint8_t* encrypted_message_key, size_t encrypted_message_key_length, + const uint8_t* enc_rsa_key, size_t enc_rsa_key_length, + const uint8_t* enc_rsa_key_iv, uint8_t* wrapped_rsa_key, + size_t* wrapped_rsa_key_length) { + uint32_t nonce = unaligned_dereference_uint32(unaligned_nonce); + if (LogCategoryEnabled(kLoggingTraceOEMCryptoCalls | kLoggingTraceNonce)) { + LOGI("-- OEMCryptoResult OEMCrypto_RewrapDeviceRSAKey30(%d)\n", session); + if (unaligned_nonce != NULL) { + LOGI("nonce = %08X;\n", nonce); + } else { + LOGI("nonce = NULL;\n"); + return OEMCrypto_ERROR_INVALID_CONTEXT; + } + if (wvcdm::g_cutoff >= wvcdm::LOG_VERBOSE) { + dump_hex("encrypted_message_key", encrypted_message_key, + encrypted_message_key_length); + dump_hex("enc_rsa_key", enc_rsa_key, enc_rsa_key_length); + dump_hex("enc_rsa_key_iv", enc_rsa_key_iv, wvcdm::KEY_IV_SIZE); + } + } + if (!crypto_engine) { + LOGE("OEMCrypto_RewrapDeviceRSAKey30: OEMCrypto Not Initialized."); + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + if (wrapped_rsa_key_length == NULL) { + LOGE("[OEMCrypto_RewrapDeviceRSAKey30(): OEMCrypto_ERROR_INVALID_CONTEXT]"); + return OEMCrypto_ERROR_INVALID_CONTEXT; + } + // 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. + size_t buffer_size = enc_rsa_key_length + sizeof(WrappedRSAKey); + + if (wrapped_rsa_key == NULL || *wrapped_rsa_key_length < buffer_size) { + if (LogCategoryEnabled(kLoggingDumpDerivedKeys)) { + LOGW("[OEMCrypto_RewrapDeviceRSAKey30(): Wrapped Keybox Short Buffer]"); + } + *wrapped_rsa_key_length = buffer_size; + return OEMCrypto_ERROR_SHORT_BUFFER; + } + *wrapped_rsa_key_length = buffer_size; // Tell caller how much space we used. + if (!crypto_engine->ValidRootOfTrust()) { + LOGE("[OEMCrypto_RewrapDeviceRSAKey30(): ERROR_KEYBOX_INVALID]"); + return OEMCrypto_ERROR_KEYBOX_INVALID; + } + SessionContext* session_ctx = crypto_engine->FindSession(session); + if (!session_ctx || !session_ctx->isValid()) { + LOGE("[OEMCrypto_RewrapDeviceRSAKey30(): ERROR_INVALID_SESSION]"); + return OEMCrypto_ERROR_INVALID_SESSION; + } + if (encrypted_message_key == NULL || encrypted_message_key_length == 0 || + enc_rsa_key == NULL || enc_rsa_key_iv == NULL || + unaligned_nonce == NULL) { + LOGE("[OEMCrypto_RewrapDeviceRSAKey30(): OEMCrypto_ERROR_INVALID_CONTEXT]"); + return OEMCrypto_ERROR_INVALID_CONTEXT; + } + + // Validate nonce + if (!session_ctx->CheckNonce(nonce)) { + return OEMCrypto_ERROR_INVALID_NONCE; + } + session_ctx->FlushNonces(); + + if (!session_ctx->InstallRSAEncryptedKey(encrypted_message_key, + encrypted_message_key_length)) { + LOGE("OEMCrypto_RewrapDeviceRSAKey30: " + "Error loading encrypted_message_key."); + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + + // Decrypt RSA key. + 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("[OEMCrypto_RewrapDeviceRSAKey30(): " + "Encrypted RSA has bad padding: %d]", + padding); + return OEMCrypto_ERROR_INVALID_RSA_KEY; + } + size_t rsa_key_length = enc_rsa_key_length - padding; + if (!session_ctx->LoadRSAKey(&pkcs8_rsa_key[0], rsa_key_length)) { + LOGE("[OEMCrypto_RewrapDeviceRSAKey30(): Failed to LoadRSAKey."); + return OEMCrypto_ERROR_INVALID_RSA_KEY; + } + + // Now we generate a wrapped keybox. + WrappedRSAKey* wrapped = reinterpret_cast(wrapped_rsa_key); + // Pick a random context and IV for generating keys. + if (!RAND_bytes(wrapped->context, sizeof(wrapped->context))) { + LOGE("[_RewrapDeviceRSAKey30(): RAND_bytes failed."); + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + if (!RAND_bytes(wrapped->iv, sizeof(wrapped->iv))) { + LOGE("[_RewrapDeviceRSAKey30(): RAND_bytes failed."); + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + const std::vector context( + wrapped->context, wrapped->context + sizeof(wrapped->context)); + // Generate mac and encryption keys for encrypting the signature. + if (!session_ctx->DeriveKeys(crypto_engine->DeviceRootKey(), context, + context)) { + LOGE("[_RewrapDeviceRSAKey30(): DeriveKeys failed."); + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + + // Encrypt rsa key with keybox. + if (!session_ctx->EncryptRSAKey(&pkcs8_rsa_key[0], enc_rsa_key_length, + wrapped->iv, wrapped->enc_rsa_key)) { + LOGE("[_RewrapDeviceRSAKey30(): EncrypteRSAKey failed."); + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + // The wrapped keybox must be signed with the same key we verify with. I'll + // pick the server key, so I don't have to modify LoadRSAKey. + unsigned int sig_length = sizeof(wrapped->signature); + if (LogCategoryEnabled(kLoggingDumpDerivedKeys)) { + LOGI(("message verified with HMAC and mac_key_server, key = " + + wvcdm::b2a_hex(session_ctx->mac_key_server())).c_str()); + } + if (!HMAC(EVP_sha256(), &session_ctx->mac_key_server()[0], + session_ctx->mac_key_server().size(), wrapped->context, + buffer_size - sizeof(wrapped->signature), wrapped->signature, + &sig_length)) { + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + if (LogCategoryEnabled(kLoggingTraceOEMCryptoCalls)) { + if (wvcdm::g_cutoff >= wvcdm::LOG_VERBOSE) { + dump_hex("wrapped_rsa_key", wrapped_rsa_key, *wrapped_rsa_key_length); + dump_hex("context", wrapped->context, sizeof(wrapped->context)); + dump_hex("iv", wrapped->iv, sizeof(wrapped->iv)); + } + } + return OEMCrypto_SUCCESS; +} + +extern "C" OEMCryptoResult OEMCrypto_RewrapDeviceRSAKey( + OEMCrypto_SESSION session, const uint8_t* message, size_t message_length, + const uint8_t* signature, size_t signature_length, + const uint32_t* unaligned_nonce, const uint8_t* enc_rsa_key, + size_t enc_rsa_key_length, const uint8_t* enc_rsa_key_iv, + uint8_t* wrapped_rsa_key, size_t* wrapped_rsa_key_length) { + uint32_t nonce = unaligned_dereference_uint32(unaligned_nonce); + if (LogCategoryEnabled(kLoggingTraceOEMCryptoCalls | kLoggingTraceNonce)) { + LOGI("-- OEMCryptoResult OEMCrypto_RewrapDeviceRSAKey(%d)\n", session); + if (wvcdm::g_cutoff >= wvcdm::LOG_VERBOSE) { + dump_hex("message", message, message_length); + dump_hex("signature", signature, signature_length); + } + if (unaligned_nonce != NULL) { + LOGI("nonce = %08X;\n", nonce); + } else { + LOGI("nonce = NULL;\n"); + return OEMCrypto_ERROR_INVALID_CONTEXT; + } + if (wvcdm::g_cutoff >= wvcdm::LOG_VERBOSE) { + dump_hex("enc_rsa_key", enc_rsa_key, enc_rsa_key_length); + dump_hex("enc_rsa_key_iv", enc_rsa_key_iv, wvcdm::KEY_IV_SIZE); + } + } + if (!crypto_engine) { + LOGE("OEMCrypto_RewrapDeviceRSAKey: OEMCrypto Not Initialized."); + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + if (crypto_engine->config_provisioning_method() != OEMCrypto_Keybox) { + return OEMCrypto_ERROR_NOT_IMPLEMENTED; + } + if (wrapped_rsa_key_length == NULL) { + LOGE("[OEMCrypto_RewrapDeviceRSAKey(): OEMCrypto_ERROR_INVALID_CONTEXT]"); + return OEMCrypto_ERROR_INVALID_CONTEXT; + } + // 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. + size_t buffer_size = enc_rsa_key_length + sizeof(WrappedRSAKey); + + if (wrapped_rsa_key == NULL || *wrapped_rsa_key_length < buffer_size) { + if (LogCategoryEnabled(kLoggingDumpDerivedKeys)) { + LOGW("[OEMCrypto_RewrapDeviceRSAKey(): Wrapped Keybox Short Buffer]"); + } + *wrapped_rsa_key_length = buffer_size; + return OEMCrypto_ERROR_SHORT_BUFFER; + } + *wrapped_rsa_key_length = buffer_size; // Tell caller how much space we used. + if (!crypto_engine->ValidRootOfTrust()) { + LOGE("[OEMCrypto_RewrapDeviceRSAKey(): ERROR_KEYBOX_INVALID]"); + return OEMCrypto_ERROR_KEYBOX_INVALID; + } + SessionContext* session_ctx = crypto_engine->FindSession(session); + if (!session_ctx || !session_ctx->isValid()) { + LOGE("[OEMCrypto_RewrapDeviceRSAKey(): ERROR_INVALID_SESSION]"); + return OEMCrypto_ERROR_INVALID_SESSION; + } + if (message == NULL || message_length == 0 || signature == NULL || + signature_length == 0 || unaligned_nonce == NULL || enc_rsa_key == NULL) { + LOGE("[OEMCrypto_RewrapDeviceRSAKey(): OEMCrypto_ERROR_INVALID_CONTEXT]"); + 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, wvcdm::KEY_IV_SIZE, + true)) { + LOGE("[OEMCrypto_RewrapDeviceRSAKey(): - range check.]"); + return OEMCrypto_ERROR_INVALID_CONTEXT; + } + + // Validate nonce + if (!session_ctx->CheckNonce(nonce)) { + return OEMCrypto_ERROR_INVALID_NONCE; + } + session_ctx->FlushNonces(); + + // Decrypt RSA key. + 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; + } + 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; + } + + // Now we generate a wrapped keybox. + WrappedRSAKey* wrapped = reinterpret_cast(wrapped_rsa_key); + // Pick a random context and IV for generating keys. + if (!RAND_bytes(wrapped->context, sizeof(wrapped->context))) { + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + if (!RAND_bytes(wrapped->iv, sizeof(wrapped->iv))) { + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + const std::vector context( + wrapped->context, wrapped->context + sizeof(wrapped->context)); + // Generate mac and encryption keys for encrypting the signature. + if (!session_ctx->DeriveKeys(crypto_engine->DeviceRootKey(), context, + context)) { + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + + // Encrypt rsa key with keybox. + if (!session_ctx->EncryptRSAKey(&pkcs8_rsa_key[0], enc_rsa_key_length, + wrapped->iv, wrapped->enc_rsa_key)) { + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + // The wrapped keybox must be signed with the same key we verify with. I'll + // pick the server key, so I don't have to modify LoadRSAKey. + unsigned int sig_length = sizeof(wrapped->signature); + if (LogCategoryEnabled(kLoggingDumpDerivedKeys)) { + LOGI(("message verified with HMAC and mac_key_server, key = " + + wvcdm::b2a_hex(session_ctx->mac_key_server())).c_str()); + } + if (!HMAC(EVP_sha256(), &session_ctx->mac_key_server()[0], + session_ctx->mac_key_server().size(), wrapped->context, + buffer_size - sizeof(wrapped->signature), wrapped->signature, + &sig_length)) { + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + if (LogCategoryEnabled(kLoggingTraceOEMCryptoCalls)) { + if (wvcdm::g_cutoff >= wvcdm::LOG_VERBOSE) { + dump_hex("wrapped_rsa_key", wrapped_rsa_key, *wrapped_rsa_key_length); + dump_hex("signature", wrapped->signature, sizeof(wrapped->signature)); + dump_hex("context", wrapped->context, sizeof(wrapped->context)); + dump_hex("iv", wrapped->iv, sizeof(wrapped->iv)); + } + } + return OEMCrypto_SUCCESS; +} + +extern "C" OEMCryptoResult OEMCrypto_LoadDeviceRSAKey( + OEMCrypto_SESSION session, const uint8_t* wrapped_rsa_key, + size_t wrapped_rsa_key_length) { + if (wrapped_rsa_key == NULL) { + LOGE("[OEMCrypto_LoadDeviceRSAKey(): OEMCrypto_ERROR_INVALID_CONTEXT]"); + return OEMCrypto_ERROR_INVALID_CONTEXT; + } + if (!crypto_engine) { + LOGE("OEMCrypto_LoadDeviceRSAKey: OEMCrypto Not Initialized."); + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + if (crypto_engine->config_provisioning_method() == OEMCrypto_DrmCertificate) { + // If we are using a baked in cert, the "wrapped RSA key" should actually be + // the magic value for baked-in certificates. + if (wrapped_rsa_key_length != sizeof(kBakedInCertificateMagicBytes) || + memcmp(kBakedInCertificateMagicBytes, wrapped_rsa_key, + wrapped_rsa_key_length) != 0) { + LOGE("OEMCrypto_LoadDeviceRSAKey: Baked in Cert has wrong size."); + return OEMCrypto_ERROR_INVALID_RSA_KEY; + } else { + return OEMCrypto_SUCCESS; + } + } + const WrappedRSAKey* wrapped = + reinterpret_cast(wrapped_rsa_key); + if (LogCategoryEnabled(kLoggingTraceOEMCryptoCalls)) { + LOGI("-- OEMCryptoResult OEMCrypto_LoadDeviceRSAKey()\n"); + if (wvcdm::g_cutoff >= wvcdm::LOG_VERBOSE) { + dump_hex("wrapped_rsa_key", wrapped_rsa_key, wrapped_rsa_key_length); + dump_hex("signature", wrapped->signature, sizeof(wrapped->signature)); + dump_hex("context", wrapped->context, sizeof(wrapped->context)); + dump_hex("iv", wrapped->iv, sizeof(wrapped->iv)); + } + } + if (!crypto_engine->ValidRootOfTrust()) { + LOGE("[OEMCrypto_LoadDeviceRSAKey(): ERROR_KEYBOX_INVALID]"); + return OEMCrypto_ERROR_KEYBOX_INVALID; + } + + SessionContext* session_ctx = crypto_engine->FindSession(session); + if (!session_ctx || !session_ctx->isValid()) { + LOGE("[OEMCrypto_LoadDeviceRSAKey(): ERROR_INVALID_SESSION]"); + return OEMCrypto_ERROR_INVALID_SESSION; + } + const std::vector context( + wrapped->context, wrapped->context + sizeof(wrapped->context)); + // Generate mac and encryption keys for encrypting the signature. + if (!session_ctx->DeriveKeys(crypto_engine->DeviceRootKey(), context, + context)) { + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + // Decrypt RSA key. + std::vector pkcs8_rsa_key(wrapped_rsa_key_length - + sizeof(wrapped->signature)); + size_t enc_rsa_key_length = wrapped_rsa_key_length - sizeof(WrappedRSAKey); + if (!session_ctx->DecryptRSAKey(wrapped->enc_rsa_key, enc_rsa_key_length, + wrapped->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("[LoadDeviceRSAKey(): Encrypted RSA has bad padding: %d]", padding); + return OEMCrypto_ERROR_INVALID_RSA_KEY; + } + 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; + } + return OEMCrypto_SUCCESS; +} + +extern "C" OEMCryptoResult OEMCrypto_LoadTestRSAKey() { + if (LogCategoryEnabled(kLoggingTraceOEMCryptoCalls)) { + LOGI("-- OEMCryptoResult OEMCrypto_LoadTestRSAKey()\n"); + } + if (!crypto_engine) { + LOGE("OEMCrypto_LoadTestRSAKey: OEMCrypto Not Initialized."); + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + if (crypto_engine->LoadTestRsaKey()) return OEMCrypto_SUCCESS; + return OEMCrypto_ERROR_UNKNOWN_FAILURE; +} + +extern "C" OEMCryptoResult OEMCrypto_GenerateRSASignature( + OEMCrypto_SESSION session, const uint8_t* message, size_t message_length, + uint8_t* signature, size_t* signature_length, + RSA_Padding_Scheme padding_scheme) { + if (LogCategoryEnabled(kLoggingTraceOEMCryptoCalls)) { + LOGI("-- OEMCryptoResult OEMCrypto_GenerateRSASignature()\n"); + if (wvcdm::g_cutoff >= wvcdm::LOG_VERBOSE) { + dump_hex("message", message, message_length); + dump_hex("message", message, message_length); + } + } + if (!crypto_engine) { + LOGE("OEMCrypto_GenerateRSASignature: OEMCrypto Not Initialized."); + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + + if (signature_length == 0) { + LOGE("[OEMCrypto_GenerateRSASignature(): OEMCrypto_ERROR_INVALID_CONTEXT]"); + return OEMCrypto_ERROR_INVALID_CONTEXT; + } + + SessionContext* session_ctx = crypto_engine->FindSession(session); + if (!session_ctx || !session_ctx->isValid()) { + LOGE("[OEMCrypto_GenerateRSASignature(): ERROR_INVALID_SESSION]"); + return OEMCrypto_ERROR_INVALID_SESSION; + } + + size_t required_size = session_ctx->RSASignatureSize(); + if (*signature_length < required_size) { + *signature_length = required_size; + return OEMCrypto_ERROR_SHORT_BUFFER; + } + + if (message == NULL || message_length == 0 || signature == NULL || + signature_length == 0) { + LOGE("[OEMCrypto_GenerateRSASignature(): OEMCrypto_ERROR_INVALID_CONTEXT]"); + return OEMCrypto_ERROR_INVALID_CONTEXT; + } + + OEMCryptoResult sts = session_ctx->GenerateRSASignature( + message, message_length, signature, signature_length, padding_scheme); + if (sts == OEMCrypto_SUCCESS) { + if (LogCategoryEnabled(kLoggingTraceOEMCryptoCalls)) { + if (wvcdm::g_cutoff >= wvcdm::LOG_VERBOSE) { + dump_hex("signature", signature, *signature_length); + } + } + } + return sts; +} + +extern "C" 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, + size_t enc_key_context_length) { + if (LogCategoryEnabled(kLoggingTraceOEMCryptoCalls)) { + LOGI("-- OEMCryptoResult OEMCrypto_DeriveKeysFromSessionKey(\n"); + if (wvcdm::g_cutoff >= wvcdm::LOG_VERBOSE) { + dump_hex("enc_session_key", enc_session_key, enc_session_key_length); + dump_hex("mac_key_context", mac_key_context, + (size_t)mac_key_context_length); + dump_hex("enc_key_context", enc_key_context, + (size_t)enc_key_context_length); + } + } + if (!crypto_engine) { + LOGE("OEMCrypto_DeriveKeysFromSessionKey: OEMCrypto Not Initialized."); + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + if (!crypto_engine->ValidRootOfTrust()) { + LOGE("[OEMCrypto_GenerateDerivedKeys(): ERROR_KEYBOX_INVALID]"); + return OEMCrypto_ERROR_KEYBOX_INVALID; + } + + SessionContext* session_ctx = crypto_engine->FindSession(session); + if (!session_ctx || !session_ctx->isValid()) { + LOGE("[OEMCrypto_GenerateDerivedKeys(): ERROR_INVALID_SESSION]"); + return OEMCrypto_ERROR_INVALID_SESSION; + } + + if (session_ctx->allowed_schemes() != kSign_RSASSA_PSS) { + LOGE("[OEMCrypto_GenerateDerivedKeys(): x509 key used to derive keys]"); + return OEMCrypto_ERROR_INVALID_RSA_KEY; + } + + const std::vector ssn_key_str( + enc_session_key, enc_session_key + enc_session_key_length); + const std::vector mac_ctx_str( + mac_key_context, mac_key_context + mac_key_context_length); + const std::vector enc_ctx_str( + enc_key_context, enc_key_context + enc_key_context_length); + + // Generate mac and encryption keys for current session context + if (!session_ctx->RSADeriveKeys(ssn_key_str, mac_ctx_str, enc_ctx_str)) { + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + if (LogCategoryEnabled(kLoggingTraceOEMCryptoCalls)) { + if (wvcdm::g_cutoff >= wvcdm::LOG_VERBOSE) { + dump_hex("mac_key_server", &session_ctx->mac_key_server()[0], + session_ctx->mac_key_server().size()); + dump_hex("mac_key", &session_ctx->mac_key_client()[0], + session_ctx->mac_key_client().size()); + dump_hex("enc_key", &session_ctx->encryption_key()[0], + session_ctx->encryption_key().size()); + } + } + return OEMCrypto_SUCCESS; +} + +extern "C" uint32_t OEMCrypto_APIVersion() { return 14; } + +extern "C" uint8_t OEMCrypto_Security_Patch_Level() { + uint8_t security_patch_level = crypto_engine->config_security_patch_level(); + if (LogCategoryEnabled(kLoggingTraceOEMCryptoCalls)) { + LOGI("-- uint8_t OEMCrypto_Security_Patch_Level(); // returns %d.\n", + security_patch_level); + } + return security_patch_level; +} + +extern "C" const char* OEMCrypto_SecurityLevel() { + const char* security_level = crypto_engine->config_security_level(); + if (LogCategoryEnabled(kLoggingTraceOEMCryptoCalls)) { + LOGI("-- const char* OEMCrypto_SecurityLevel(); // returns %s.\n", + security_level); + } + return security_level; +} + +extern "C" OEMCryptoResult OEMCrypto_GetHDCPCapability( + OEMCrypto_HDCP_Capability* current, OEMCrypto_HDCP_Capability* maximum) { + if (LogCategoryEnabled(kLoggingTraceOEMCryptoCalls)) { + LOGI("-- OEMCryptoResult OEMCrypto_GetHDCPCapability(%p, %p)\n", current, + maximum); + } + if (!crypto_engine) { + LOGE("OEMCrypto_GetHDCPCapability: OEMCrypto Not Initialized."); + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + if (current == NULL) return OEMCrypto_ERROR_UNKNOWN_FAILURE; + if (maximum == NULL) return OEMCrypto_ERROR_UNKNOWN_FAILURE; + *current = crypto_engine->config_current_hdcp_capability(); + *maximum = crypto_engine->config_maximum_hdcp_capability(); + if (LogCategoryEnabled(kLoggingTraceOEMCryptoCalls)) { + LOGI("-- current_hdcp=%d, max_hdcp=%d\n", *current, *maximum); + } + return OEMCrypto_SUCCESS; +} + +extern "C" uint32_t OEMCrypto_GetAnalogOutputFlags() { +// TODO(b/69867568, fredgc): parameterize this. + return 0; +} + +extern "C" bool OEMCrypto_SupportsUsageTable() { + bool supports_usage = crypto_engine->config_supports_usage_table(); + if (LogCategoryEnabled(kLoggingTraceOEMCryptoCalls)) { + LOGI("-- bool OEMCrypto_SupportsUsageTable(); // returns %s.\n", + (supports_usage ? "true" : "false")); + } + return supports_usage; +} + +extern "C" OEMCryptoResult OEMCrypto_GetNumberOfOpenSessions(size_t* count) { + if (LogCategoryEnabled(kLoggingTraceOEMCryptoCalls)) { + LOGI("-- OEMCryptoResult OEMCrypto_GetNumberOfOpenSessions(%p)\n", count); + } + if (!crypto_engine) { + LOGE("OEMCrypto_GetNumberOfOpenSessions: OEMCrypto Not Initialized."); + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + if (count == NULL) return OEMCrypto_ERROR_UNKNOWN_FAILURE; + *count = crypto_engine->GetNumberOfOpenSessions(); + return OEMCrypto_SUCCESS; +} + +extern "C" OEMCryptoResult OEMCrypto_GetMaxNumberOfSessions(size_t* maximum) { + if (LogCategoryEnabled(kLoggingTraceOEMCryptoCalls)) { + LOGI("-- OEMCryptoResult OEMCrypto_GetMaxNumberOfSessions(%p)\n", maximum); + } + if (!crypto_engine) { + LOGE("OEMCrypto_GetMaxNumberOfSessions: OEMCrypto Not Initialized."); + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + if (maximum == NULL) return OEMCrypto_ERROR_UNKNOWN_FAILURE; + *maximum = crypto_engine->GetMaxNumberOfSessions(); + return OEMCrypto_SUCCESS; +} + +extern "C" bool OEMCrypto_IsAntiRollbackHwPresent() { + bool anti_rollback_hw_present = + crypto_engine->config_is_anti_rollback_hw_present(); + + if (LogCategoryEnabled(kLoggingTraceOEMCryptoCalls)) { + LOGI("-- bool OEMCrypto_IsAntiRollbackHwPresent(): %d\n", + anti_rollback_hw_present); + } + return anti_rollback_hw_present; +} + +extern "C" uint32_t OEMCrypto_SupportedCertificates() { + if (!crypto_engine) { + LOGE("OEMCrypto_GetProvisioningMethod: OEMCrypto Not Initialized."); + return 0; + } + if (crypto_engine->config_provisioning_method() == OEMCrypto_DrmCertificate) { + return 0; + } + return OEMCrypto_Supports_RSA_2048bit | OEMCrypto_Supports_RSA_3072bit | + OEMCrypto_Supports_RSA_CAST; +} + +extern "C" 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) { + if (LogCategoryEnabled(kLoggingTraceOEMCryptoCalls)) { + LOGI("-- OEMCryptoResult OEMCrypto_Generic_Encrypt( algorithm=%d\n", + algorithm); + if (wvcdm::g_cutoff >= wvcdm::LOG_VERBOSE) { + dump_hex("in_buffer", in_buffer, buffer_length); + dump_hex("iv", iv, wvcdm::KEY_IV_SIZE); + } + } + if (!crypto_engine) { + LOGE("OEMCrypto_Generic_Encrypt: OEMCrypto Not Initialized."); + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + if (!crypto_engine->ValidRootOfTrust()) { + LOGE("[OEMCrypto_Generic_Encrypt(): ERROR_KEYBOX_INVALID]"); + return OEMCrypto_ERROR_KEYBOX_INVALID; + } + SessionContext* session_ctx = crypto_engine->FindSession(session); + if (!session_ctx || !session_ctx->isValid()) { + LOGE("[OEMCrypto_Generic_Encrypt(): ERROR_INVALID_SESSION]"); + return OEMCrypto_ERROR_INVALID_SESSION; + } + if (in_buffer == NULL || buffer_length == 0 || iv == NULL || + out_buffer == NULL) { + LOGE("[OEMCrypto_Generic_Encrypt(): OEMCrypto_ERROR_INVALID_CONTEXT]"); + return OEMCrypto_ERROR_INVALID_CONTEXT; + } + OEMCryptoResult sts = session_ctx->Generic_Encrypt(in_buffer, buffer_length, + iv, algorithm, out_buffer); + if (LogCategoryEnabled(kLoggingTraceOEMCryptoCalls)) { + if (wvcdm::g_cutoff >= wvcdm::LOG_VERBOSE) { + dump_hex("out_buffer", out_buffer, buffer_length); + } + } + return sts; +} + +extern "C" 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) { + if (LogCategoryEnabled(kLoggingTraceOEMCryptoCalls)) { + LOGI("-- OEMCryptoResult OEMCrypto_Generic_Decrypt( algorithm=%d\n", + algorithm); + if (wvcdm::g_cutoff >= wvcdm::LOG_VERBOSE) { + dump_hex("in_buffer", in_buffer, buffer_length); + dump_hex("iv", iv, wvcdm::KEY_IV_SIZE); + } + } + if (!crypto_engine) { + LOGE("OEMCrypto_Generic_Decrypt: OEMCrypto Not Initialized."); + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + if (!crypto_engine->ValidRootOfTrust()) { + LOGE("[OEMCrypto_Generic_Decrypt(): ERROR_KEYBOX_INVALID]"); + return OEMCrypto_ERROR_KEYBOX_INVALID; + } + SessionContext* session_ctx = crypto_engine->FindSession(session); + if (!session_ctx || !session_ctx->isValid()) { + LOGE("[OEMCrypto_Generic_Decrypt(): ERROR_INVALID_SESSION]"); + return OEMCrypto_ERROR_INVALID_SESSION; + } + if (in_buffer == NULL || buffer_length == 0 || iv == NULL || + out_buffer == NULL) { + LOGE("[OEMCrypto_Generic_Decrypt(): OEMCrypto_ERROR_INVALID_CONTEXT]"); + return OEMCrypto_ERROR_INVALID_CONTEXT; + } + OEMCryptoResult sts = session_ctx->Generic_Decrypt(in_buffer, buffer_length, + iv, algorithm, out_buffer); + if (LogCategoryEnabled(kLoggingTraceOEMCryptoCalls)) { + if (wvcdm::g_cutoff >= wvcdm::LOG_VERBOSE) { + dump_hex("out_buffer", out_buffer, buffer_length); + } + } + return sts; +} + +extern "C" OEMCryptoResult OEMCrypto_Generic_Sign(OEMCrypto_SESSION session, + const uint8_t* in_buffer, + size_t buffer_length, + OEMCrypto_Algorithm algorithm, + uint8_t* signature, + size_t* signature_length) { + if (LogCategoryEnabled(kLoggingTraceOEMCryptoCalls)) { + LOGI("-- OEMCryptoResult OEMCrypto_Generic_Sign( algorithm=%d\n", + algorithm); + if (wvcdm::g_cutoff >= wvcdm::LOG_VERBOSE) { + dump_hex("in_buffer", in_buffer, buffer_length); + } + } + if (!crypto_engine) { + LOGE("OEMCrypto_Generic_Sign: OEMCrypto Not Initialized."); + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + if (!crypto_engine->ValidRootOfTrust()) { + LOGE("[OEMCrypto_Generic_Sign(): ERROR_KEYBOX_INVALID]"); + return OEMCrypto_ERROR_KEYBOX_INVALID; + } + SessionContext* session_ctx = crypto_engine->FindSession(session); + if (!session_ctx || !session_ctx->isValid()) { + LOGE("[OEMCrypto_Generic_Sign(): ERROR_INVALID_SESSION]"); + return OEMCrypto_ERROR_INVALID_SESSION; + } + if (*signature_length < SHA256_DIGEST_LENGTH) { + *signature_length = SHA256_DIGEST_LENGTH; + return OEMCrypto_ERROR_SHORT_BUFFER; + } + if (in_buffer == NULL || buffer_length == 0 || signature == NULL) { + LOGE("[OEMCrypto_Generic_Sign(): OEMCrypto_ERROR_INVALID_CONTEXT]"); + return OEMCrypto_ERROR_INVALID_CONTEXT; + } + OEMCryptoResult sts = session_ctx->Generic_Sign( + in_buffer, buffer_length, algorithm, signature, signature_length); + if (LogCategoryEnabled(kLoggingTraceOEMCryptoCalls)) { + if (wvcdm::g_cutoff >= wvcdm::LOG_VERBOSE) { + dump_hex("signature", signature, *signature_length); + } + } + return sts; +} + +extern "C" 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) { + if (LogCategoryEnabled(kLoggingTraceOEMCryptoCalls)) { + LOGI("-- OEMCryptoResult OEMCrypto_Generic_Verify( algorithm=%d\n", + algorithm); + if (wvcdm::g_cutoff >= wvcdm::LOG_VERBOSE) { + dump_hex("in_buffer", in_buffer, buffer_length); + dump_hex("signature", signature, signature_length); + } + } + if (!crypto_engine) { + LOGE("OEMCrypto_Generic_Verify: OEMCrypto Not Initialized."); + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + if (!crypto_engine->ValidRootOfTrust()) { + LOGE("[OEMCrypto_Generic_Verify(): ERROR_KEYBOX_INVALID]"); + return OEMCrypto_ERROR_KEYBOX_INVALID; + } + SessionContext* session_ctx = crypto_engine->FindSession(session); + if (!session_ctx || !session_ctx->isValid()) { + LOGE("[OEMCrypto_Generic_Verify(): ERROR_INVALID_SESSION]"); + return OEMCrypto_ERROR_INVALID_SESSION; + } + if (signature_length != SHA256_DIGEST_LENGTH) { + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + if (in_buffer == NULL || buffer_length == 0 || signature == NULL) { + LOGE("[OEMCrypto_Generic_Verify(): OEMCrypto_ERROR_INVALID_CONTEXT]"); + return OEMCrypto_ERROR_INVALID_CONTEXT; + } + return session_ctx->Generic_Verify(in_buffer, buffer_length, algorithm, + signature, signature_length); +} + +// TODO(fredgc): remove this. +extern "C" OEMCryptoResult OEMCrypto_UpdateUsageTable() { + if (LogCategoryEnabled(kLoggingTraceOEMCryptoCalls)) { + LOGI("-- OEMCryptoResult OEMCrypto_UpdateUsageTable();\n"); + } + return OEMCrypto_ERROR_NOT_IMPLEMENTED; +} + +extern "C" OEMCryptoResult OEMCrypto_DeactivateUsageEntry( + OEMCrypto_SESSION session, const uint8_t* pst, size_t pst_length) { + if (LogCategoryEnabled(kLoggingTraceOEMCryptoCalls)) { + LOGI("-- OEMCryptoResult OEMCrypto_DeactivateUsageEntry(\n"); + if (wvcdm::g_cutoff >= wvcdm::LOG_VERBOSE) { + dump_hex("pst", pst, pst_length); + } + } + if (!crypto_engine) { + LOGE("OEMCrypto_DeactivateUsageEntry: OEMCrypto Not Initialized."); + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + if (!crypto_engine->config_supports_usage_table()) { + return OEMCrypto_ERROR_NOT_IMPLEMENTED; + } + SessionContext* session_ctx = crypto_engine->FindSession(session); + if (!session_ctx || !session_ctx->isValid()) { + LOGE("[OEMCrypto_DeactivateUsageEntry(): ERROR_INVALID_SESSION]"); + return OEMCrypto_ERROR_INVALID_SESSION; + } + std::vector pstv(pst, pst + pst_length); + return session_ctx->DeactivateUsageEntry(pstv); +} + +extern "C" OEMCryptoResult OEMCrypto_ReportUsage(OEMCrypto_SESSION session, + const uint8_t* pst, + size_t pst_length, + uint8_t* buffer, + size_t* buffer_length) { + if (LogCategoryEnabled(kLoggingTraceOEMCryptoCalls)) { + LOGI("-- OEMCryptoResult OEMCrypto_ReportUsage(\n"); + if (wvcdm::g_cutoff >= wvcdm::LOG_VERBOSE) { + dump_hex("pst", pst, pst_length); + } + } + if (!crypto_engine) { + LOGE("OEMCrypto_ReportUsage: OEMCrypto Not Initialized."); + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + if (!crypto_engine->config_supports_usage_table()) { + return OEMCrypto_ERROR_NOT_IMPLEMENTED; + } + if (!buffer_length) { + return OEMCrypto_ERROR_INVALID_CONTEXT; + } + SessionContext* session_ctx = crypto_engine->FindSession(session); + if (!session_ctx || !session_ctx->isValid()) { + LOGE("[OEMCrypto_ReportUsage(): ERROR_INVALID_SESSION]"); + return OEMCrypto_ERROR_INVALID_SESSION; + } + std::vector pstv(pst, pst + pst_length); + OEMCryptoResult sts = session_ctx->ReportUsage(pstv, buffer, buffer_length); + if (LogCategoryEnabled(kLoggingTraceOEMCryptoCalls)) { + if (wvcdm::g_cutoff >= wvcdm::LOG_VERBOSE) { + dump_hex("usage buffer", buffer, *buffer_length); + } + } + return sts; +} + +extern "C" OEMCryptoResult OEMCrypto_DeleteUsageEntry( + OEMCrypto_SESSION, const uint8_t*, size_t, const uint8_t*, size_t, + const uint8_t*, size_t) { + // TODO(fredgc): delete this. + return OEMCrypto_ERROR_NOT_IMPLEMENTED; +} + +extern "C" OEMCryptoResult OEMCrypto_ForceDeleteUsageEntry(const uint8_t*, + size_t) { + // TODO(fredgc): delete this. + return OEMCrypto_ERROR_NOT_IMPLEMENTED; +} + +extern "C" OEMCryptoResult OEMCrypto_DeleteOldUsageTable() { + if (LogCategoryEnabled(kLoggingTraceOEMCryptoCalls)) { + LOGI("-- OEMCryptoResult OEMCrypto_DeleteOldUsageTable()\n"); + } + if (!crypto_engine) { + LOGE("OEMCrypto_DeleteOldUsageTable: OEMCrypto Not Initialized."); + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + if (!crypto_engine->config_supports_usage_table()) { + return OEMCrypto_ERROR_NOT_IMPLEMENTED; + } + return crypto_engine->usage_table().DeleteOldUsageTable(); +} + +extern "C" bool OEMCrypto_IsSRMUpdateSupported() { + if (LogCategoryEnabled(kLoggingTraceOEMCryptoCalls)) { + LOGI("-- OEMCryptoResult OEMCrypto_IsSRMUpdateSupported()\n"); + } + if (!crypto_engine) { + LOGE("OEMCrypto_IsSRMUpdateSupported: OEMCrypto Not Initialized."); + return false; + } + bool result = crypto_engine->srm_update_supported(); + if (LogCategoryEnabled(kLoggingTraceOEMCryptoCalls)) { + LOGI("-- OEMCryptoResult OEMCrypto_IsSRMUpdateSupported returning %s\n", + result ? "TRUE" : "FALSE"); + } + return result; +} + +extern "C" OEMCryptoResult OEMCrypto_GetCurrentSRMVersion(uint16_t* version) { + if (LogCategoryEnabled(kLoggingTraceOEMCryptoCalls)) { + LOGI("-- OEMCryptoResult OEMCrypto_GetCurrentSRMVersion()\n"); + } + if (!crypto_engine) { + LOGE("OEMCrypto_GetCurrentSRMVersion: OEMCrypto Not Initialized."); + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + if (crypto_engine->config_local_display_only()) { + return OEMCrypto_LOCAL_DISPLAY_ONLY; + } + OEMCryptoResult result = crypto_engine->current_srm_version(version); + if (result == OEMCrypto_SUCCESS && + LogCategoryEnabled(kLoggingTraceOEMCryptoCalls)) { + LOGI("-- OEMCryptoResult OEMCrypto_GetCurrentSRMVersion returning %d\n", + *version); + } + return result; +} + +extern "C" OEMCryptoResult OEMCrypto_LoadSRM(const uint8_t* buffer, + size_t buffer_length) { + if (LogCategoryEnabled(kLoggingTraceOEMCryptoCalls)) { + LOGI("-- OEMCryptoResult OEMCrypto_LoadSRM(length=%d)\n", buffer_length); + } + if (!crypto_engine) { + LOGE("OEMCrypto_LoadSRM: OEMCrypto Not Initialized."); + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + return crypto_engine->load_srm(buffer, buffer_length); +} + +extern "C" OEMCryptoResult OEMCrypto_RemoveSRM() { + if (LogCategoryEnabled(kLoggingTraceOEMCryptoCalls)) { + LOGI("-- OEMCryptoResult OEMCrypto_RemoveSRM()\n"); + } + if (!crypto_engine) { + LOGE("OEMCrypto_RemoveSRM: OEMCrypto Not Initialized."); + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + return crypto_engine->remove_srm(); +} + +extern "C" OEMCryptoResult OEMCrypto_CreateUsageTableHeader( + uint8_t* header_buffer, + size_t* header_buffer_length) { + if (LogCategoryEnabled(kLoggingTraceOEMCryptoCalls)) { + LOGI("-- OEMCryptoResult OEMCrypto_CreateUsageTableHeader()\n"); + } + if (!crypto_engine) { + LOGE("OEMCrypto_CreateUsageTableHeader: OEMCrypto Not Initialized."); + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + if (!crypto_engine->config_supports_usage_table()) { + LOGE("OEMCrypto_CreateUsageTableHeader: Configured without Usage Tables."); + return OEMCrypto_ERROR_NOT_IMPLEMENTED; + } + return crypto_engine->usage_table() + .CreateUsageTableHeader(header_buffer, header_buffer_length); +} + +extern "C" OEMCryptoResult OEMCrypto_LoadUsageTableHeader( + const uint8_t* buffer, size_t buffer_length) { + if (LogCategoryEnabled(kLoggingTraceOEMCryptoCalls)) { + LOGI("-- OEMCryptoResult OEMCrypto_LoadUsageTableHeader()\n"); + } + if (!crypto_engine) { + LOGE("OEMCrypto_LoadUsageTableHeader: OEMCrypto Not Initialized."); + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + if (!crypto_engine->config_supports_usage_table()) { + return OEMCrypto_ERROR_NOT_IMPLEMENTED; + } + if (!buffer) { + LOGE("OEMCrypto_LoadUsageTableHeader: buffer null."); + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + std::vector bufferv(buffer, buffer + buffer_length); + return crypto_engine->usage_table().LoadUsageTableHeader(bufferv); +} + +extern "C" OEMCryptoResult OEMCrypto_CreateNewUsageEntry( + OEMCrypto_SESSION session, uint32_t* usage_entry_number) { + if (LogCategoryEnabled(kLoggingTraceOEMCryptoCalls)) { + LOGI("-- OEMCryptoResult OEMCrypto_CreateNewUsageEntry(\n"); + } + if (!crypto_engine) { + LOGE("OEMCrypto_CreateNewUsageEntry: OEMCrypto Not Initialized."); + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + if (!crypto_engine->config_supports_usage_table()) { + return OEMCrypto_ERROR_NOT_IMPLEMENTED; + } + SessionContext* session_ctx = crypto_engine->FindSession(session); + if (!session_ctx || !session_ctx->isValid()) { + LOGE("[OEMCrypto_CreateNewUsageEntry(): ERROR_INVALID_SESSION]"); + return OEMCrypto_ERROR_INVALID_SESSION; + } + if (!usage_entry_number) { + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + OEMCryptoResult sts = session_ctx->CreateNewUsageEntry(usage_entry_number); + if (LogCategoryEnabled(kLoggingTraceOEMCryptoCalls)) { + LOGI("-- usage_entry_number = %d", *usage_entry_number); + } + return sts; +} + +extern "C" OEMCryptoResult OEMCrypto_LoadUsageEntry(OEMCrypto_SESSION session, + uint32_t index, + const uint8_t* buffer, + size_t buffer_size) { + if (LogCategoryEnabled(kLoggingTraceOEMCryptoCalls)) { + LOGI("-- OEMCryptoResult OEMCrypto_LoadUsageEntry(\n"); + } + if (!crypto_engine) { + LOGE("OEMCrypto_LoadUsageEntry: OEMCrypto Not Initialized."); + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + if (!crypto_engine->config_supports_usage_table()) { + return OEMCrypto_ERROR_NOT_IMPLEMENTED; + } + SessionContext* session_ctx = crypto_engine->FindSession(session); + if (!session_ctx || !session_ctx->isValid()) { + LOGE("[OEMCrypto_LoadUsageEntry(): ERROR_INVALID_SESSION]"); + return OEMCrypto_ERROR_INVALID_SESSION; + } + if (!buffer) { + LOGE("[OEMCrypto_LoadUsageEntry(): buffer null]"); + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + std::vector bufferv(buffer, buffer + buffer_size); + return session_ctx->LoadUsageEntry(index, bufferv); +} + +extern "C" OEMCryptoResult OEMCrypto_UpdateUsageEntry( + OEMCrypto_SESSION session, uint8_t* header_buffer, + size_t* header_buffer_length, uint8_t* entry_buffer, + size_t* entry_buffer_length) { + if (LogCategoryEnabled(kLoggingTraceOEMCryptoCalls)) { + LOGI("-- OEMCryptoResult OEMCrypto_UpdateUsageEntry(\n"); + } + if (!crypto_engine) { + LOGE("OEMCrypto_UpdateUsageEntry: OEMCrypto Not Initialized."); + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + if (!crypto_engine->config_supports_usage_table()) { + return OEMCrypto_ERROR_NOT_IMPLEMENTED; + } + if (!header_buffer_length || !entry_buffer_length) { + return OEMCrypto_ERROR_INVALID_CONTEXT; + } + SessionContext* session_ctx = crypto_engine->FindSession(session); + if (!session_ctx || !session_ctx->isValid()) { + LOGE("[OEMCrypto_UpdateUsageEntry(): ERROR_INVALID_SESSION]"); + return OEMCrypto_ERROR_INVALID_SESSION; + } + return session_ctx->UpdateUsageEntry(header_buffer, header_buffer_length, + entry_buffer, entry_buffer_length); +} + +extern "C" OEMCryptoResult OEMCrypto_ShrinkUsageTableHeader( + uint32_t new_table_size, uint8_t* header_buffer, + size_t* header_buffer_length) { + if (LogCategoryEnabled(kLoggingTraceOEMCryptoCalls)) { + LOGI("-- OEMCryptoResult OEMCrypto_ShrinkUsageTableHeader()\n"); + } + if (!crypto_engine) { + LOGE("OEMCrypto_ShrinkUsageTableHeader: OEMCrypto Not Initialized."); + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + if (!crypto_engine->config_supports_usage_table()) { + return OEMCrypto_ERROR_NOT_IMPLEMENTED; + } + return crypto_engine->usage_table().ShrinkUsageTableHeader( + new_table_size, header_buffer, header_buffer_length); +} + +extern "C" OEMCryptoResult OEMCrypto_MoveEntry(OEMCrypto_SESSION session, + uint32_t new_index) { + if (LogCategoryEnabled(kLoggingTraceOEMCryptoCalls)) { + LOGI("-- OEMCryptoResult OEMCrypto_MoveEntry(\n"); + } + if (!crypto_engine) { + LOGE("OEMCrypto_MoveEntry: OEMCrypto Not Initialized."); + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + if (!crypto_engine->config_supports_usage_table()) { + return OEMCrypto_ERROR_NOT_IMPLEMENTED; + } + SessionContext* session_ctx = crypto_engine->FindSession(session); + if (!session_ctx || !session_ctx->isValid()) { + LOGE("[OEMCrypto_MoveEntry(): ERROR_INVALID_SESSION]"); + return OEMCrypto_ERROR_INVALID_SESSION; + } + return session_ctx->MoveEntry(new_index); +} + +extern "C" OEMCryptoResult OEMCrypto_CopyOldUsageEntry( + OEMCrypto_SESSION session, const uint8_t* pst, size_t pst_length) { + if (LogCategoryEnabled(kLoggingTraceOEMCryptoCalls)) { + LOGI("-- OEMCryptoResult OEMCrypto_CopyOldUsageEntry(\n"); + } + if (!crypto_engine) { + LOGE("OEMCrypto_CopyOldUsageEntry: OEMCrypto Not Initialized."); + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + if (!crypto_engine->config_supports_usage_table()) { + return OEMCrypto_ERROR_NOT_IMPLEMENTED; + } + SessionContext* session_ctx = crypto_engine->FindSession(session); + if (!session_ctx || !session_ctx->isValid()) { + LOGE("[OEMCrypto_CopyOldUsageEntry(): ERROR_INVALID_SESSION]"); + return OEMCrypto_ERROR_INVALID_SESSION; + } + std::vector pstv(pst, pst + pst_length); + return session_ctx->CopyOldUsageEntry(pstv); +} + +extern "C" +OEMCryptoResult OEMCrypto_CreateOldUsageEntry(uint64_t time_since_license_received, + uint64_t time_since_first_decrypt, + uint64_t time_since_last_decrypt, + OEMCrypto_Usage_Entry_Status status, + uint8_t *server_mac_key, + uint8_t *client_mac_key, + const uint8_t* pst, + size_t pst_length) { + if (LogCategoryEnabled(kLoggingTraceOEMCryptoCalls)) { + LOGI("-- OEMCryptoResult OEMCrypto_CreateOldUsageEntry()\n"); + } + if (!crypto_engine) { + LOGE("OEMCrypto_CreateOldUsageEntry: OEMCrypto Not Initialized."); + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + if (!crypto_engine->config_supports_usage_table()) { + return OEMCrypto_ERROR_NOT_IMPLEMENTED; + } + return crypto_engine->usage_table().CreateOldUsageEntry( + time_since_license_received, time_since_first_decrypt, + time_since_last_decrypt, status, server_mac_key, client_mac_key, pst, + pst_length); +} + +} // namespace wvoec_mock diff --git a/mock/src/oemcrypto_nonce_table.cpp b/mock/src/oemcrypto_nonce_table.cpp new file mode 100644 index 0000000..0945e16 --- /dev/null +++ b/mock/src/oemcrypto_nonce_table.cpp @@ -0,0 +1,67 @@ +// Copyright 2017 Google Inc. All Rights Reserved. +// +// Mock implementation of OEMCrypto APIs +// +#include "oemcrypto_nonce_table.h" + +namespace wvoec_mock { + +void NonceTable::AddNonce(uint32_t nonce) { + int new_slot = -1; + int oldest_slot = -1; + + // Flush any nonces that have been checked but not flushed. + // After flush, nonces will be either valid or invalid. + Flush(); + + for (int i = 0; i < kTableSize; ++i) { + // Increase age of all valid nonces. + if (kNTStateValid == state_[i]) { + ++age_[i]; + if (-1 == oldest_slot) { + oldest_slot = i; + } else { + if (age_[i] > age_[oldest_slot]) { + oldest_slot = i; + } + } + } else { + if (-1 == new_slot) { + age_[i] = 0; + nonces_[i] = nonce; + state_[i] = kNTStateValid; + new_slot = i; + } + } + } + if (-1 == new_slot) { + // reuse oldest + // assert (oldest_slot != -1) + int i = oldest_slot; + age_[i] = 0; + nonces_[i] = nonce; + state_[i] = kNTStateValid; + } +} + +bool NonceTable::CheckNonce(uint32_t nonce) { + for (int i = 0; i < kTableSize; ++i) { + if (kNTStateInvalid != state_[i]) { + if (nonce == nonces_[i]) { + state_[i] = kNTStateFlushPending; + return true; + } + } + } + return false; +} + +void NonceTable::Flush() { + for (int i = 0; i < kTableSize; ++i) { + if (kNTStateFlushPending == state_[i]) { + state_[i] = kNTStateInvalid; + } + } +} + +} // namespace wvoec_mock diff --git a/mock/src/oemcrypto_nonce_table.h b/mock/src/oemcrypto_nonce_table.h new file mode 100644 index 0000000..293c02b --- /dev/null +++ b/mock/src/oemcrypto_nonce_table.h @@ -0,0 +1,38 @@ +// Copyright 2017 Google Inc. All Rights Reserved. +// +// Mock implementation of OEMCrypto APIs +// +#ifndef MOCK_OEMCRYPTO_NONCE_TABLE_H_ +#define MOCK_OEMCRYPTO_NONCE_TABLE_H_ + +#include + +namespace wvoec_mock { + +class NonceTable { + public: + static const int kTableSize = 16; + NonceTable() { + for (int i = 0; i < kTableSize; ++i) { + state_[i] = kNTStateInvalid; + } + } + ~NonceTable() {} + void AddNonce(uint32_t nonce); + bool CheckNonce(uint32_t nonce); + void Flush(); + + private: + enum NonceTableState { + kNTStateInvalid, + kNTStateValid, + kNTStateFlushPending + }; + NonceTableState state_[kTableSize]; + uint32_t age_[kTableSize]; + uint32_t nonces_[kTableSize]; +}; + +} // namespace wvoec_mock + +#endif // MOCK_OEMCRYPTO_NONCE_TABLE_H_ diff --git a/mock/src/oemcrypto_old_usage_table_mock.cpp b/mock/src/oemcrypto_old_usage_table_mock.cpp new file mode 100644 index 0000000..033747d --- /dev/null +++ b/mock/src/oemcrypto_old_usage_table_mock.cpp @@ -0,0 +1,240 @@ +// Copyright 2013 Google Inc. All Rights Reserved. +// +// Mock implementation of OEMCrypto APIs +// +// This is from the v12 version of oemcrypto usage tables. It is used for +// devices that upgrade from v12 to v13 in the field, and need to convert from +// the old type of usage table to the new. +#include "oemcrypto_old_usage_table_mock.h" + +#include +#include + +#include +#include + +#include +#include +#include +#include + +#include "file_store.h" +#include "log.h" +#include "oemcrypto_engine_mock.h" +#include "oemcrypto_logging.h" +#include "properties.h" +#include "pst_report.h" +#include "string_conversions.h" +#include "wv_cdm_constants.h" + +namespace wvoec_mock { + +OldUsageTableEntry::OldUsageTableEntry(const std::vector &pst_hash) + : pst_hash_(pst_hash), + time_of_license_received_(time(NULL)), + time_of_first_decrypt_(0), + time_of_last_decrypt_(0), + status_(kUnused) {} + +OldUsageTableEntry::~OldUsageTableEntry() {} + +OldUsageTableEntry::OldUsageTableEntry(const OldStoredUsageEntry *buffer) { + pst_hash_.assign(buffer->pst_hash, buffer->pst_hash + SHA256_DIGEST_LENGTH); + time_of_license_received_ = buffer->time_of_license_received; + time_of_first_decrypt_ = buffer->time_of_first_decrypt; + time_of_last_decrypt_ = buffer->time_of_last_decrypt; + status_ = buffer->status; + mac_key_server_.assign(buffer->mac_key_server, + buffer->mac_key_server + wvcdm::MAC_KEY_SIZE); + mac_key_client_.assign(buffer->mac_key_client, + buffer->mac_key_client + wvcdm::MAC_KEY_SIZE); +} + +OldUsageTable::OldUsageTable(CryptoEngine *ce) { + ce_ = ce; + generation_ = 0; + table_.clear(); + + // Load saved table. + wvcdm::FileSystem* file_system = ce->file_system(); + wvcdm::File *file; + std::string path; + // Note: this path is OK for a real implementation, but using security level 1 + // would be better. + if (!wvcdm::Properties::GetDeviceFilesBasePath(wvcdm::kSecurityLevelL3, + &path)) { + LOGE("OldUsageTable: Unable to get base path"); + return; + } + std::string filename = path + "UsageTable.dat"; + if (!file_system->Exists(filename)) { + if (LogCategoryEnabled(kLoggingTraceUsageTable)) { + LOGI("OldUsageTable: No saved usage table. Creating new table."); + } + return; + } + + size_t file_size = file_system->FileSize(filename); + std::vector encrypted_buffer(file_size); + std::vector buffer(file_size); + OldStoredUsageTable *stored_table = + reinterpret_cast(&buffer[0]); + OldStoredUsageTable *encrypted_table = + reinterpret_cast(&encrypted_buffer[0]); + + file = file_system->Open(filename, wvcdm::FileSystem::kReadOnly); + if (!file) { + LOGE("OldUsageTable: File open failed: %s", path.c_str()); + return; + } + file->Read(reinterpret_cast(&encrypted_buffer[0]), file_size); + file->Close(); + + // Verify the signature of the usage table file. + + // This should be encrypted and signed with a device specific key. + // For the reference implementation, I'm just going to use the keybox key. + const bool override_to_real = true; + const std::vector &key = ce_->DeviceRootKey(override_to_real); + + uint8_t computed_signature[SHA256_DIGEST_LENGTH]; + unsigned int sig_length = sizeof(computed_signature); + if (!HMAC(EVP_sha256(), &key[0], key.size(), + &encrypted_buffer[SHA256_DIGEST_LENGTH], + file_size - SHA256_DIGEST_LENGTH, computed_signature, + &sig_length)) { + LOGE("OldUsageTable: Could not recreate signature."); + table_.clear(); + return; + } + if (memcmp(encrypted_table->signature, computed_signature, sig_length)) { + LOGE("OldUsageTable: Invalid signature given: %s", + wvcdm::HexEncode(&encrypted_buffer[0], sig_length).c_str()); + LOGE("OldUsageTable: Invalid signature computed: %s", + wvcdm::HexEncode(computed_signature, sig_length).c_str()); + table_.clear(); + return; + } + + // Next, decrypt the table. + uint8_t iv_buffer[wvcdm::KEY_IV_SIZE]; + memcpy(iv_buffer, encrypted_table->iv, wvcdm::KEY_IV_SIZE); + AES_KEY aes_key; + AES_set_decrypt_key(&key[0], 128, &aes_key); + AES_cbc_encrypt(&encrypted_buffer[SHA256_DIGEST_LENGTH + wvcdm::KEY_IV_SIZE], + &buffer[SHA256_DIGEST_LENGTH + wvcdm::KEY_IV_SIZE], + file_size - SHA256_DIGEST_LENGTH - wvcdm::KEY_IV_SIZE, + &aes_key, iv_buffer, AES_DECRYPT); + + // Next, read the generation number from a different location. + // On a real implementation, you should NOT put the generation number in + // a file in user space. It should be stored in secure memory. For the + // reference implementation, we'll just pretend this is secure. + std::string filename2 = path + "GenerationNumber.dat"; + file = file_system->Open(filename2, wvcdm::FileSystem::kReadOnly); + if (!file) { + LOGE("OldUsageTable: File open failed: %s (clearing table)", path.c_str()); + generation_ = 0; + table_.clear(); + return; + } + file->Read(reinterpret_cast(&generation_), sizeof(int64_t)); + file->Close(); + if (stored_table->generation == generation_ + 1) { + if (LogCategoryEnabled(kLoggingTraceUsageTable)) { + LOGW("OldUsageTable: File is one generation old. Acceptable rollback."); + } + } else if (stored_table->generation == generation_ - 1) { + if (LogCategoryEnabled(kLoggingTraceUsageTable)) { + LOGW("OldUsageTable: File is one generation new. Acceptable rollback."); + } + // This might happen if the generation number was rolled back? + } else if (stored_table->generation != generation_) { + LOGE("OldUsageTable: Rollback detected. Clearing Usage Table. %lx -> %lx", + generation_, stored_table->generation); + table_.clear(); + generation_ = 0; + return; + } + + // At this point, the stored table looks valid. We can load in all the + // entries. + for (uint64_t i = 0; i < stored_table->count; i++) { + OldUsageTableEntry *entry = + new OldUsageTableEntry(&stored_table->entries[i].entry); + table_[entry->pst_hash()] = entry; + } + if (LogCategoryEnabled(kLoggingTraceUsageTable)) { + LOGI("OldUsageTable: loaded %d entries.", stored_table->count); + } +} + +OldUsageTableEntry *OldUsageTable::FindEntry(const std::vector &pst) { + wvcdm::AutoLock lock(lock_); + return FindEntryLocked(pst); +} + +OldUsageTableEntry *OldUsageTable::FindEntryLocked(const std::vector &pst) { + std::vector pst_hash; + if (!ComputeHash(pst, pst_hash)) { + LOGE("OldUsageTable: Could not compute hash of pst."); + return NULL; + } + EntryMap::iterator it = table_.find(pst_hash); + if (it == table_.end()) { + return NULL; + } + return it->second; +} + +OldUsageTableEntry *OldUsageTable::CreateEntry(const std::vector &pst) { + std::vector pst_hash; + if (!ComputeHash(pst, pst_hash)) { + LOGE("OldUsageTable: Could not compute hash of pst."); + return NULL; + } + OldUsageTableEntry *entry = new OldUsageTableEntry(pst_hash); + wvcdm::AutoLock lock(lock_); + table_[pst_hash] = entry; + return entry; +} + +void OldUsageTable::Clear() { + wvcdm::AutoLock lock(lock_); + for (EntryMap::iterator i = table_.begin(); i != table_.end(); ++i) { + if (i->second) delete i->second; + } + table_.clear(); +} + +void OldUsageTable::DeleteFile(CryptoEngine *ce) { + wvcdm::FileSystem* file_system = ce->file_system(); + std::string path; + // Note: this path is OK for a real implementation, but using security level 1 + // would be better. + if (!wvcdm::Properties::GetDeviceFilesBasePath(wvcdm::kSecurityLevelL3, + &path)) { + LOGE("OldUsageTable: Unable to get base path"); + return; + } + std::string filename = path + "UsageTable.dat"; + if (file_system->Exists(filename)) { + if (!file_system->Remove(filename)) { + LOGE("DeleteOldUsageTable: error removing file."); + } + } +} + +bool OldUsageTable::ComputeHash(const std::vector &pst, + std::vector &pst_hash) { + // The PST is not fixed size, and we have no promises that it is reasonbly + // sized, so we compute a hash of it, and store that instead. + pst_hash.resize(SHA256_DIGEST_LENGTH); + SHA256_CTX context; + if (!SHA256_Init(&context)) return false; + if (!SHA256_Update(&context, &pst[0], pst.size())) return false; + if (!SHA256_Final(&pst_hash[0], &context)) return false; + return true; +} + +} // namespace wvoec_mock diff --git a/mock/src/oemcrypto_old_usage_table_mock.h b/mock/src/oemcrypto_old_usage_table_mock.h new file mode 100644 index 0000000..f59fba5 --- /dev/null +++ b/mock/src/oemcrypto_old_usage_table_mock.h @@ -0,0 +1,93 @@ + // Copyright 2013 Google Inc. All Rights Reserved. +// +// Mock implementation of OEMCrypto APIs +// +// This is from the v12 version of oemcrypto usage tables. It is used for +// devices that upgrade from v12 to v13 in the field, and need to convert from +// the old type of usage table to the new. +#ifndef OEMCRYPTO_OLD_USAGE_TABLE_MOCK_H_ +#define OEMCRYPTO_OLD_USAGE_TABLE_MOCK_H_ + +#include +#include +#include +#include + +#include "lock.h" +#include "OEMCryptoCENC.h" +#include "openssl/sha.h" +#include "wv_cdm_constants.h" + +namespace wvoec_mock { + +class CryptoEngine; +class UsagetTableEntry; + +struct OldStoredUsageEntry { + // To save disk space, we only store a hash of the pst. + uint8_t pst_hash[SHA256_DIGEST_LENGTH]; + int64_t time_of_license_received; + int64_t time_of_first_decrypt; + int64_t time_of_last_decrypt; + enum OEMCrypto_Usage_Entry_Status status; + uint8_t mac_key_server[wvcdm::MAC_KEY_SIZE]; + uint8_t mac_key_client[wvcdm::MAC_KEY_SIZE]; +}; + +typedef union { + struct OldStoredUsageEntry entry; + uint8_t padding[128]; // multiple of block size and bigger than entry size. +} AlignedOldStoredUsageEntry; + +struct OldStoredUsageTable { + uint8_t signature[SHA256_DIGEST_LENGTH]; + uint8_t iv[wvcdm::KEY_IV_SIZE]; + int64_t generation; + uint64_t count; + AlignedOldStoredUsageEntry entries[]; +}; + +class OldUsageTableEntry { + public: + OldUsageTableEntry(const std::vector &pst_hash); + OldUsageTableEntry(const OldStoredUsageEntry *buffer); + ~OldUsageTableEntry(); + const std::vector &pst_hash() const { return pst_hash_; } + + private: + std::vector pst_hash_; + int64_t time_of_license_received_; + int64_t time_of_first_decrypt_; + int64_t time_of_last_decrypt_; + enum OEMCrypto_Usage_Entry_Status status_; + std::vector mac_key_server_; + std::vector mac_key_client_; + + friend class UsageTableEntry; + friend class UsageTable; +}; + +class OldUsageTable { + public: + OldUsageTable(CryptoEngine *ce); + ~OldUsageTable() { Clear(); } + OldUsageTableEntry *FindEntry(const std::vector &pst); + OldUsageTableEntry *CreateEntry(const std::vector &pst); + void Clear(); + static void DeleteFile(CryptoEngine *ce); + + private: + OldUsageTableEntry *FindEntryLocked(const std::vector &pst); + bool ComputeHash(const std::vector &pst, + std::vector &pst_hash); + + typedef std::map, OldUsageTableEntry *> EntryMap; + EntryMap table_; + wvcdm::Lock lock_; + int64_t generation_; + CryptoEngine *ce_; +}; + +} // namespace wvoec_mock + +#endif // OEMCRYPTO_OLD_USAGE_TABLE_MOCK_H_ diff --git a/mock/src/oemcrypto_rsa_key_shared.cpp b/mock/src/oemcrypto_rsa_key_shared.cpp new file mode 100644 index 0000000..ff65577 --- /dev/null +++ b/mock/src/oemcrypto_rsa_key_shared.cpp @@ -0,0 +1,98 @@ +// Copyright 2013 Google Inc. All Rights Reserved. +// +// Mock implementation of OEMCrypto APIs +// +#include "oemcrypto_rsa_key_shared.h" + +#include + +#include +#include +#include +#include +#include + +#include "oemcrypto_logging.h" + +namespace { + +void dump_openssl_error() { + while (unsigned long err = ERR_get_error()) { + char buffer[120]; + LOGE("openssl error -- %lu -- %s", + err, ERR_error_string(err, buffer)); + } +} + +} // namespace + +namespace wvoec_mock { + +void RSA_shared_ptr::reset() { + if (rsa_key_ && key_owned_) { + RSA_free(rsa_key_); + } + key_owned_ = false; + rsa_key_ = NULL; +} + +bool RSA_shared_ptr::LoadPkcs8RsaKey(const uint8_t* buffer, size_t length) { + assert(buffer != NULL); + reset(); + uint8_t* pkcs8_rsa_key = const_cast(buffer); + BIO* bio = BIO_new_mem_buf(pkcs8_rsa_key, length); + if (bio == NULL) { + LOGE("[LoadPkcs8RsaKey(): Could not allocate bio buffer]"); + return false; + } + bool success = true; + PKCS8_PRIV_KEY_INFO* pkcs8_pki = d2i_PKCS8_PRIV_KEY_INFO_bio(bio, NULL); + if (pkcs8_pki == NULL) { + BIO_reset(bio); + pkcs8_pki = d2i_PKCS8_PRIV_KEY_INFO_bio(bio, NULL); + if (pkcs8_pki == NULL) { + LOGE("[LoadPkcs8RsaKey(): d2i_PKCS8_PRIV_KEY_INFO_bio returned NULL]"); + success = false; + } + } + EVP_PKEY* evp = NULL; + if (success) { + evp = EVP_PKCS82PKEY(pkcs8_pki); + if (evp == NULL) { + LOGE("[LoadPkcs8RsaKey(): EVP_PKCS82PKEY returned NULL]"); + success = false; + } + } + if (success) { + rsa_key_ = EVP_PKEY_get1_RSA(evp); + if (rsa_key_ == NULL) { + LOGE("[LoadPkcs8RsaKey(): PrivateKeyInfo did not contain an RSA key]"); + success = false; + } + key_owned_ = true; + } + if (evp != NULL) { + EVP_PKEY_free(evp); + } + if (pkcs8_pki != NULL) { + PKCS8_PRIV_KEY_INFO_free(pkcs8_pki); + } + BIO_free(bio); + if (!success) { + return false; + } + switch (RSA_check_key(rsa_key_)) { + case 1: // valid. + return true; + case 0: // not valid. + LOGE("[LoadPkcs8RsaKey(): rsa key not valid]"); + dump_openssl_error(); + return false; + default: // -1 == check failed. + LOGE("[LoadPkcs8RsaKey(): error checking rsa key]"); + dump_openssl_error(); + return false; + } +} + +} // namespace wvoec_mock diff --git a/mock/src/oemcrypto_rsa_key_shared.h b/mock/src/oemcrypto_rsa_key_shared.h new file mode 100644 index 0000000..4a815e9 --- /dev/null +++ b/mock/src/oemcrypto_rsa_key_shared.h @@ -0,0 +1,37 @@ +// Copyright 2016 Google Inc. All Rights Reserved. +// +// Mock implementation of OEMCrypto APIs +// +#ifndef OEMCRYPTO_RSA_KEY_SHARED_H_ +#define OEMCRYPTO_RSA_KEY_SHARED_H_ + +#include + +#include + +namespace wvoec_mock { + +// Shared pointer with specialized destructor. This pointer is only shared +// from a CryptoEngine to a Session -- so we don't have to use full reference +// counting. +class RSA_shared_ptr { + public: + RSA_shared_ptr() : rsa_key_(NULL), key_owned_(false) {} + ~RSA_shared_ptr() { reset(); }; + // Explicitly allow copy as share. + explicit RSA_shared_ptr(const RSA_shared_ptr& other) : + rsa_key_(other.rsa_key_), key_owned_(false) {} + RSA* get() { return rsa_key_; } + void reset(); + bool LoadPkcs8RsaKey(const uint8_t* buffer, size_t length); + + private: + void operator=(const RSA_shared_ptr); // disallow assign. + + RSA* rsa_key_; + bool key_owned_; +}; + +} // namespace wvoec_mock + +#endif // OEMCRYPTO_RSA_KEY_SHARED_H_ diff --git a/mock/src/oemcrypto_session.cpp b/mock/src/oemcrypto_session.cpp new file mode 100644 index 0000000..1e63af8 --- /dev/null +++ b/mock/src/oemcrypto_session.cpp @@ -0,0 +1,1611 @@ +// Copyright 2017 Google Inc. All Rights Reserved. +// +// Mock implementation of OEMCrypto APIs +// +#include "oemcrypto_session.h" + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "keys.h" +#include "log.h" +#include "oemcrypto_engine_mock.h" +#include "oemcrypto_key_mock.h" +#include "oemcrypto_logging.h" +#include "oemcrypto_rsa_key_shared.h" +#include "string_conversions.h" +#include "wv_cdm_constants.h" + +static const int kPssSaltLength = 20; + +namespace { + +// Increment counter for AES-CTR. The CENC spec specifies we increment only +// the low 64 bits of the IV counter, and leave the high 64 bits alone. +void ctr128_inc64(uint8_t* counter) { + uint32_t n = 16; + do { + if (++counter[--n] != 0) return; + } while (n > 8); +} + +void dump_boringssl_error() { + while (unsigned long err = ERR_get_error()) { + char buffer[120]; + ERR_error_string_n(err, buffer, sizeof(buffer)); + LOGE("BoringSSL Error -- %lu -- %s", err, buffer); + } +} + +} // namespace + +namespace wvoec_mock { + +/***************************************/ + +class ContentKeysContext : public SessionContextKeys { + public: + explicit ContentKeysContext() {} + virtual ~ContentKeysContext() {} + virtual size_t size() { return session_keys_.size(); } + bool Insert(const KeyId& key_id, const Key& key_data); + virtual Key* Find(const KeyId& key_id); + virtual void Remove(const KeyId& key_id); + virtual void UpdateDuration(const KeyControlBlock& control); + + virtual OEMCrypto_LicenseType type() { return OEMCrypto_ContentLicense; } + + virtual bool SetContentKey(const KeyId& entitlement_id, + const KeyId& content_key_id, + const std::vector& content_key); + virtual bool GetEntitlementKey( + const KeyId& entitlement_id, + const std::vector** entitlement_key); + + private: + SessionKeyTable session_keys_; + CORE_DISALLOW_COPY_AND_ASSIGN(ContentKeysContext); +}; + +bool ContentKeysContext::Insert(const KeyId& key_id, const Key& key_data) { + return session_keys_.Insert(key_id, key_data); +} + +Key* ContentKeysContext::Find(const KeyId& key_id) { + return session_keys_.Find(key_id); +} + +void ContentKeysContext::Remove(const KeyId& key_id) { + session_keys_.Remove(key_id); +} + +void ContentKeysContext::UpdateDuration(const KeyControlBlock& control) { + session_keys_.UpdateDuration(control); +} + +bool ContentKeysContext::SetContentKey( + const KeyId& entitlement_id, const KeyId& content_key_id, + const std::vector& content_key) { + // Unsupported action for this type. + return false; +} + +bool ContentKeysContext::GetEntitlementKey( + const KeyId& entitlement_id, const std::vector** key) { + // Unsupported action for this type. + return false; +}; + +/***************************************/ + +class EntitlementKeysContext : public SessionContextKeys { + public: + EntitlementKeysContext() {} + virtual ~EntitlementKeysContext() {} + virtual size_t size() { return session_keys_.size(); } + bool Insert(const KeyId& key_id, const Key& key_data); + virtual Key* Find(const KeyId& key_id); + virtual void Remove(const KeyId& key_id); + virtual void UpdateDuration(const KeyControlBlock& control); + virtual bool SetContentKey(const KeyId& entitlement_id, + const KeyId& content_key_id, + const std::vector& content_key); + virtual bool GetEntitlementKey(const KeyId& entitlement_id, + const std::vector** key); + + virtual OEMCrypto_LicenseType type() { return OEMCrypto_EntitlementLicense; } + + private: + EntitlementKeyTable session_keys_; + CORE_DISALLOW_COPY_AND_ASSIGN(EntitlementKeysContext); +}; + +bool EntitlementKeysContext::Insert(const KeyId& key_id, const Key& key_data) { + return session_keys_.Insert(key_id, key_data); +} + +Key* EntitlementKeysContext::Find(const KeyId& key_id) { + return session_keys_.Find(key_id); +} + +void EntitlementKeysContext::Remove(const KeyId& key_id) { + session_keys_.Remove(key_id); +} + +void EntitlementKeysContext::UpdateDuration(const KeyControlBlock& control) { + session_keys_.UpdateDuration(control); +} + +bool EntitlementKeysContext::SetContentKey( + const KeyId& entitlement_id, const KeyId& content_key_id, + const std::vector& content_key) { + return session_keys_.SetContentKey(entitlement_id, content_key_id, + content_key); +} + +bool EntitlementKeysContext::GetEntitlementKey( + const KeyId& entitlement_id, const std::vector** out_key) { + return session_keys_.GetEntitlementKey(entitlement_id, out_key); +} + +/***************************************/ + +SessionContext::~SessionContext() { + if (usage_entry_) { + delete usage_entry_; + usage_entry_ = NULL; + } + if (session_keys_) { + delete session_keys_; + session_keys_ = NULL; + } +} + +// Internal utility function to derive key using CMAC-128 +bool SessionContext::DeriveKey(const std::vector& key, + const std::vector& context, int counter, + std::vector* out) { + if (key.empty() || counter > 4 || context.empty() || out == NULL) { + LOGE("[DeriveKey(): OEMCrypto_ERROR_INVALID_CONTEXT]"); + return false; + } + + const EVP_CIPHER* cipher = EVP_aes_128_cbc(); + CMAC_CTX* cmac_ctx = CMAC_CTX_new(); + + if (!cmac_ctx) { + LOGE("[DeriveKey(): OEMCrypto_ERROR_CMAC_FAILURE]"); + return false; + } + + if (!CMAC_Init(cmac_ctx, &key[0], key.size(), cipher, 0)) { + LOGE("[DeriveKey(): OEMCrypto_ERROR_CMAC_FAILURE]"); + CMAC_CTX_free(cmac_ctx); + return false; + } + + std::vector message; + message.push_back(counter); + message.insert(message.end(), context.begin(), context.end()); + + if (!CMAC_Update(cmac_ctx, &message[0], message.size())) { + LOGE("[DeriveKey(): OEMCrypto_ERROR_CMAC_FAILURE]"); + CMAC_CTX_free(cmac_ctx); + return false; + } + + size_t reslen; + uint8_t res[128]; + if (!CMAC_Final(cmac_ctx, res, &reslen)) { + LOGE("[DeriveKey(): OEMCrypto_ERROR_CMAC_FAILURE]"); + CMAC_CTX_free(cmac_ctx); + return false; + } + + out->assign(res, res + reslen); + + CMAC_CTX_free(cmac_ctx); + + return true; +} + +bool SessionContext::DeriveKeys(const std::vector& master_key, + const std::vector& mac_key_context, + const std::vector& enc_key_context) { + // Generate derived key for mac key + std::vector mac_key_server; + std::vector mac_key_client; + std::vector mac_key_part2; + if (!DeriveKey(master_key, mac_key_context, 1, &mac_key_server)) { + return false; + } + if (!DeriveKey(master_key, mac_key_context, 2, &mac_key_part2)) { + return false; + } + mac_key_server.insert(mac_key_server.end(), mac_key_part2.begin(), + mac_key_part2.end()); + + if (!DeriveKey(master_key, mac_key_context, 3, &mac_key_client)) { + return false; + } + if (!DeriveKey(master_key, mac_key_context, 4, &mac_key_part2)) { + return false; + } + mac_key_client.insert(mac_key_client.end(), mac_key_part2.begin(), + mac_key_part2.end()); + + // Generate derived key for encryption key + std::vector enc_key; + if (!DeriveKey(master_key, enc_key_context, 1, &enc_key)) { + return false; + } + + if (LogCategoryEnabled(kLoggingDumpDerivedKeys)) { + LOGI((" mac_key_context = " + wvcdm::b2a_hex(mac_key_context)).c_str()); + LOGI((" enc_key_context = " + wvcdm::b2a_hex(enc_key_context)).c_str()); + LOGI((" mac_key_server = " + wvcdm::b2a_hex(mac_key_server)).c_str()); + LOGI((" mac_key_client = " + wvcdm::b2a_hex(mac_key_client)).c_str()); + LOGI((" enc_key = " + wvcdm::b2a_hex(enc_key)).c_str()); + } + + set_mac_key_server(mac_key_server); + set_mac_key_client(mac_key_client); + set_encryption_key(enc_key); + return true; +} + +bool SessionContext::RSADeriveKeys( + const std::vector& enc_session_key, + const std::vector& mac_key_context, + const std::vector& enc_key_context) { + if (!rsa_key()) { + LOGE("[RSADeriveKeys(): no RSA key set]"); + return false; + } + if (enc_session_key.size() != static_cast(RSA_size(rsa_key()))) { + LOGE("[RSADeriveKeys(): encrypted session key wrong size:%zu, expected %d]", + enc_session_key.size(), RSA_size(rsa_key())); + dump_boringssl_error(); + return false; + } + session_key_.resize(RSA_size(rsa_key())); + int decrypted_size = + RSA_private_decrypt(enc_session_key.size(), &enc_session_key[0], + &session_key_[0], rsa_key(), RSA_PKCS1_OAEP_PADDING); + if (-1 == decrypted_size) { + LOGE("[RSADeriveKeys(): error decrypting session key.]"); + dump_boringssl_error(); + return false; + } + session_key_.resize(decrypted_size); + if (decrypted_size != static_cast(wvcdm::KEY_SIZE)) { + LOGE("[RSADeriveKeys(): error. Session key is wrong size: %d.]", + decrypted_size); + dump_boringssl_error(); + session_key_.clear(); + return false; + } + return DeriveKeys(session_key_, mac_key_context, enc_key_context); +} + +// 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) { + if (message == NULL || message_length == 0 || signature == NULL || + signature_length == 0) { + LOGE("[OEMCrypto_GenerateSignature(): OEMCrypto_ERROR_INVALID_CONTEXT]"); + return false; + } + + const uint8_t *mac_key = NULL; + bool using_usage_mac_key_client = false; + if (mac_key_client_.size() == wvcdm::MAC_KEY_SIZE) { + // If we have a mac key, use it. + mac_key = &mac_key_client_[0]; + } else if (usage_entry_status_ == kUsageEntryLoaded) { + // If not, but we have a usage entry, use its key. + mac_key = usage_entry_->mac_key_client(); + using_usage_mac_key_client = true; + } else { + return false; + } + + if (*signature_length < SHA256_DIGEST_LENGTH) { + *signature_length = SHA256_DIGEST_LENGTH; + return false; + } + + if (using_usage_mac_key_client && + LogCategoryEnabled(kLoggingDumpDerivedKeys)) { + std::vector usage_entry_mac_key_client( + usage_entry_->mac_key_client(), + usage_entry_->mac_key_client() + wvcdm::MAC_KEY_SIZE * sizeof(uint8_t)); + LOGI(("message signed with HMAC and usage_entry_'s mac_key_client, " + "mac_key_client = " + + wvcdm::b2a_hex(usage_entry_mac_key_client)).c_str()); + } else if (LogCategoryEnabled(kLoggingDumpDerivedKeys)) { + LOGI(("message signed with HMAC and mac_key_client_, mac_key_client_ = " + + wvcdm::b2a_hex(mac_key_client_)).c_str()); + } + + unsigned int md_len = *signature_length; + if (HMAC(EVP_sha256(), mac_key, wvcdm::MAC_KEY_SIZE, message, message_length, + signature, &md_len)) { + *signature_length = md_len; + return true; + } + return false; +} + +size_t SessionContext::RSASignatureSize() { + if (!rsa_key()) { + LOGE("[GenerateRSASignature(): no RSA key set]"); + return 0; + } + return static_cast(RSA_size(rsa_key())); +} + +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 == NULL || message_length == 0 || signature == NULL || + signature_length == 0) { + LOGE("[GenerateRSASignature(): OEMCrypto_ERROR_INVALID_CONTEXT]"); + return OEMCrypto_ERROR_INVALID_CONTEXT; + } + if (!rsa_key()) { + LOGE("[GenerateRSASignature(): 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]"); + return OEMCrypto_ERROR_INVALID_RSA_KEY; + } + // This is the standard padding scheme used for license requests. + if (padding_scheme == kSign_RSASSA_PSS) { + // Hash the message using SHA1. + uint8_t hash[SHA_DIGEST_LENGTH]; + if (!SHA1(message, message_length, hash)) { + LOGE("[GeneratRSASignature(): 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(), NULL, kPssSaltLength); + if (status == -1) { + LOGE("[GeneratRSASignature(): 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("[GeneratRSASignature(): 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) { + if (message_length > 83) { + LOGE("[GeneratRSASignature(): 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("[GeneratRSASignature(): 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; + } + return OEMCrypto_SUCCESS; +} + +// Validate message signature +bool SessionContext::ValidateMessage(const uint8_t* given_message, + size_t message_length, + const uint8_t* given_signature, + size_t signature_length) { + if (signature_length != SHA256_DIGEST_LENGTH) { + return false; + } + uint8_t computed_signature[SHA256_DIGEST_LENGTH]; + memset(computed_signature, 0, SHA256_DIGEST_LENGTH); + unsigned int md_len = SHA256_DIGEST_LENGTH; + if (LogCategoryEnabled(kLoggingDumpDerivedKeys)) { + LOGI(("message verified with HMAC and mac_key_server, key = " + + wvcdm::b2a_hex(mac_key_server_)).c_str()); + } + if (!HMAC(EVP_sha256(), &mac_key_server_[0], mac_key_server_.size(), + given_message, message_length, computed_signature, &md_len)) { + LOGE("ValidateMessage: Could not compute signature."); + return false; + } + if (memcmp(given_signature, computed_signature, signature_length)) { + LOGE("Invalid signature given: %s", + wvcdm::HexEncode(given_signature, signature_length).c_str()); + LOGE("Invalid signature computed: %s", + wvcdm::HexEncode(computed_signature, signature_length).c_str()); + return false; + } + return true; +} + +OEMCryptoResult SessionContext::CheckStatusOnline(uint32_t nonce, + uint32_t control) { + if (!(control & kControlNonceEnabled)) { + 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."); + return OEMCrypto_ERROR_INVALID_CONTEXT; + case kUsageEntryLoaded: + LOGE("LoadKeys: Session reloaded existing entry."); + return OEMCrypto_ERROR_INVALID_CONTEXT; + case kUsageEntryNew: + return OEMCrypto_SUCCESS; + default: // invalid status. + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } +} + +OEMCryptoResult SessionContext::CheckStatusOffline(uint32_t nonce, + uint32_t control) { + if (control & kControlNonceEnabled) { + 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."); + return OEMCrypto_ERROR_INVALID_CONTEXT; + case kUsageEntryLoaded: + // Repeat load. Calling function will verify pst and keys. + return OEMCrypto_SUCCESS; + case kUsageEntryNew: + // First load. Verify nonce. + if (!CheckNonce(nonce)) return OEMCrypto_ERROR_INVALID_NONCE; + return OEMCrypto_SUCCESS; + default: // invalid status. + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } +} + +OEMCryptoResult SessionContext::CheckNonceOrEntry( + const KeyControlBlock& key_control_block) { + switch (key_control_block.control_bits() & kControlReplayMask) { + case kControlNonceRequired: // Online license. Nonce always required. + return CheckStatusOnline(key_control_block.nonce(), + key_control_block.control_bits()); + break; + case kControlNonceOrEntry: // Offline license. Nonce required on first use. + return CheckStatusOffline(key_control_block.nonce(), + key_control_block.control_bits()); + break; + default: + if ((key_control_block.control_bits() & kControlNonceEnabled) && + (!CheckNonce(key_control_block.nonce()))) { + LOGE("LoadKeys: BAD Nonce"); + return OEMCrypto_ERROR_INVALID_NONCE; + } + } + return OEMCrypto_SUCCESS; +} + +void SessionContext::StartTimer() { timer_start_ = time(NULL); } + +uint32_t SessionContext::CurrentTimer() { + time_t now = time(NULL); + return now - timer_start_; +} + +OEMCryptoResult SessionContext::LoadKeys( + const uint8_t* message, size_t message_length, const uint8_t* signature, + size_t signature_length, const uint8_t* enc_mac_key_iv, + const uint8_t* enc_mac_keys, size_t num_keys, + const OEMCrypto_KeyObject* key_array, const uint8_t* pst, size_t pst_length, + const uint8_t* srm_requirement, OEMCrypto_LicenseType license_type) { + // Validate message signature + if (!ValidateMessage(message, message_length, signature, signature_length)) { + return OEMCrypto_ERROR_SIGNATURE_FAILURE; + } + + if (!session_keys_) { + switch (license_type) { + case OEMCrypto_ContentLicense: + session_keys_ = new ContentKeysContext(); + break; + + case OEMCrypto_EntitlementLicense: + session_keys_ = new EntitlementKeysContext(); + break; + + default: + return OEMCrypto_ERROR_INVALID_CONTEXT; + } + } else { + if (session_keys_->type() != license_type) { + return OEMCrypto_ERROR_INVALID_CONTEXT; + } + } + + StartTimer(); + + if (srm_requirement) { + const std::string kSRMVerificationString = "HDCPDATA"; + if (memcmp(srm_requirement, kSRMVerificationString.c_str(), + kSRMVerificationString.size())) { + LOGE("SRM Requirement Data has bad verification string: %8s", + srm_requirement); + return OEMCrypto_ERROR_INVALID_CONTEXT; + } + uint32_t minimum_version = + htonl(*reinterpret_cast(srm_requirement + 8)); + uint16_t current_version = 0; + if (OEMCrypto_SUCCESS != ce_->current_srm_version(¤t_version)) { + 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", + current_version, minimum_version); + srm_requirements_status_ = InvalidSRMVersion; + } else if (ce_->srm_blacklisted_device_attached()) { + LOGW("[LoadKeys: SRM blacklisted device attached]"); + srm_requirements_status_ = InvalidSRMVersion; + } else { + LOGI("[LoadKeys: SRM Versions is %d, required: %d]", current_version, + minimum_version); + srm_requirements_status_ = ValidSRMVersion; + } + } + + // If there are already keys installed in this session, then we can load + // a shared license. + bool second_license = (session_keys_->size() > 0); + + // Decrypt and install keys in key object + // Each key will have a key control block. They will all have the same nonce. + OEMCryptoResult status = OEMCrypto_SUCCESS; + std::vector key_id; + std::vector enc_key_data; + std::vector key_data_iv; + std::vector key_control; + std::vector key_control_iv; + for (unsigned int i = 0; i < num_keys; i++) { + key_id.assign(key_array[i].key_id, + key_array[i].key_id + key_array[i].key_id_length); + enc_key_data.assign(key_array[i].key_data, + key_array[i].key_data + key_array[i].key_data_length); + key_data_iv.assign(key_array[i].key_data_iv, + key_array[i].key_data_iv + wvcdm::KEY_IV_SIZE); + if (key_array[i].key_control == NULL) { + status = OEMCrypto_ERROR_UNKNOWN_FAILURE; + break; + } + key_control.assign(key_array[i].key_control, + key_array[i].key_control + wvcdm::KEY_CONTROL_SIZE); + key_control_iv.assign(key_array[i].key_control_iv, + key_array[i].key_control_iv + wvcdm::KEY_IV_SIZE); + + OEMCryptoResult result = InstallKey( + key_id, enc_key_data, key_data_iv, key_control, key_control_iv, + second_license); + if (result != OEMCrypto_SUCCESS) { + status = result; + break; + } + } + FlushNonces(); + if (status != OEMCrypto_SUCCESS) return status; + + // enc_mac_key can be NULL if license renewal is not supported + if (enc_mac_keys != NULL) { + // V2.1 license protocol: update mac keys after processing license response + const std::vector enc_mac_keys_str = std::vector( + enc_mac_keys, enc_mac_keys + 2 * wvcdm::MAC_KEY_SIZE); + const std::vector enc_mac_key_iv_str = std::vector( + enc_mac_key_iv, enc_mac_key_iv + wvcdm::KEY_IV_SIZE); + + if (!UpdateMacKeys(enc_mac_keys_str, enc_mac_key_iv_str)) { + LOGE("Failed to update mac keys.\n"); + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + } + if (usage_entry_) { + OEMCryptoResult result = OEMCrypto_SUCCESS; + switch (usage_entry_status_) { + case kNoUsageEntry: + if (pst_length > 0) { + LOGE("LoadKeys: PST specified but no usage entry loaded."); + return OEMCrypto_ERROR_INVALID_CONTEXT; + } + break; // no extra check. + case kUsageEntryNew: + result = usage_entry_->SetPST(pst, pst_length); + if (result != OEMCrypto_SUCCESS) { + return result; + } + if (!usage_entry_->SetMacKeys(mac_key_server_, mac_key_client_)) { + LOGE("LoadKeys: Usage table can't set keys.\n"); + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + if (LogCategoryEnabled(kLoggingDumpDerivedKeys)) { + LOGI(("usage_entry_'s mac_key_client_ has changed to = " + + wvcdm::b2a_hex(mac_key_client_)).c_str()); + } + break; + case kUsageEntryLoaded: + if (!usage_entry_->VerifyPST(pst, pst_length)) { + return OEMCrypto_ERROR_WRONG_PST; + } + if (!usage_entry_->VerifyMacKeys(mac_key_server_, mac_key_client_)) { + LOGE("LoadKeys: Usage table entry mac keys do not match.\n"); + return OEMCrypto_ERROR_WRONG_KEYS; + } + if (usage_entry_->Inactive()) return OEMCrypto_ERROR_LICENSE_INACTIVE; + break; + } + } + return OEMCrypto_SUCCESS; +} + +OEMCryptoResult SessionContext::LoadEntitledContentKeys( + size_t num_keys, + const OEMCrypto_EntitledContentKeyObject* key_array) { + if (!key_array) { + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + if (!session_keys_ || session_keys_->type() != OEMCrypto_EntitlementLicense) { + return OEMCrypto_ERROR_INVALID_CONTEXT; + } + for (size_t i = 0; i < num_keys; ++i) { + const OEMCrypto_EntitledContentKeyObject* key_data = &key_array[i]; + std::vector entitlement_key_id; + entitlement_key_id.assign(key_data->entitlement_key_id, + key_data->entitlement_key_id + + key_data->entitlement_key_id_length); + + const std::vector* entitlement_key = NULL; + if (!session_keys_->GetEntitlementKey(entitlement_key_id, + &entitlement_key)) { + return OEMCrypto_KEY_NOT_ENTITLED; + } + std::vector content_key; + std::vector iv; + std::vector encrypted_content_key; + std::vector content_key_id; + + iv.assign(key_data->content_key_data_iv, + key_data->content_key_data_iv + 16); + encrypted_content_key.assign( + key_data->content_key_data, + key_data->content_key_data + key_data->content_key_data_length); + content_key_id.assign( + key_data->content_key_id, + key_data->content_key_id + key_data->content_key_id_length); + if (!DecryptEntitlement(*entitlement_key, iv, + encrypted_content_key, &content_key)) { + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + if (!session_keys_->SetContentKey( + entitlement_key_id, content_key_id, content_key)) { + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + } + return OEMCrypto_SUCCESS; +} + +OEMCryptoResult SessionContext::InstallKey( + const KeyId& key_id, const std::vector& key_data, + const std::vector& key_data_iv, + const std::vector& key_control, + const std::vector& key_control_iv, + bool second_license) { + // Decrypt encrypted key_data using derived encryption key and offered iv + std::vector content_key; + std::vector key_control_str; + + if (!DecryptMessage(encryption_key_, key_data_iv, key_data, &content_key)) { + LOGE("[Installkey(): Could not decrypt key data]"); + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + + if (LogCategoryEnabled(kLoggingDumpContentKeys)) { + LOGI((" InstallKey: key_id = " + wvcdm::b2a_hex(key_id)).c_str()); + LOGI( + (" InstallKey: content_key = " + wvcdm::b2a_hex(content_key)).c_str()); + LOGI((" InstallKey: key_control = " + wvcdm::b2a_hex(key_control_str)) + .c_str()); + } + + // Key control must be supplied by license server + if (key_control.empty()) { + LOGE("[Installkey(): WARNING: No Key Control]"); + return OEMCrypto_ERROR_INVALID_CONTEXT; + } + if (key_control_iv.empty()) { + LOGE("[Installkey(): ERROR: No Key Control IV]"); + return OEMCrypto_ERROR_INVALID_CONTEXT; + } + if (!DecryptMessage(content_key, key_control_iv, key_control, + &key_control_str)) { + LOGE("[Installkey(): ERROR: Could not decrypt content key]"); + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + + if (LogCategoryEnabled(kLoggingDumpKeyControlBlocks)) { + LOGD("Key ID: %s", wvcdm::b2a_hex(key_id).c_str()); + } + KeyControlBlock key_control_block(key_control_str); + if (!key_control_block.valid()) { + LOGE("Error parsing key control."); + return OEMCrypto_ERROR_INVALID_CONTEXT; + } + if ((key_control_block.control_bits() & + kControlRequireAntiRollbackHardware) && + !ce_->config_is_anti_rollback_hw_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() & kControlSecurityPatchLevelMask) >> + kControlSecurityPatchLevelShift; + if (minimum_patch_level > OEMCrypto_Security_Patch_Level()) { + LOGE("[InstallKey(): security patch level: %d. Minimum:%d]", + OEMCrypto_Security_Patch_Level(), minimum_patch_level); + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + OEMCryptoResult result = CheckNonceOrEntry(key_control_block); + if (result != OEMCrypto_SUCCESS) { + LOGE("LoadKeys: Failed Nonce/PST check."); + return result; + } + if (key_control_block.control_bits() & kSharedLicense) { + if (!second_license) { + LOGE("LoadKeys: Shared License, but no keys previously loaded."); + return OEMCrypto_ERROR_MISSING_MASTER; + } + } + if (key_control_block.control_bits() & kControlSRMVersionRequired) { + if (srm_requirements_status_ == NoSRMVersion) { + LOGE("[LoadKeys: control bit says SRM version required]"); + return OEMCrypto_ERROR_INVALID_CONTEXT; + } + if (srm_requirements_status_ == InvalidSRMVersion) { + // If the SRM version is too small, treat this key as local display only. + key_control_block.RequireLocalDisplay(); + } + } + + Key key(content_key, key_control_block); + if (!session_keys_) { + return OEMCrypto_ERROR_INVALID_CONTEXT; + } + session_keys_->Insert(key_id, key); + return OEMCrypto_SUCCESS; +} + +bool SessionContext::InstallRSAEncryptedKey( + const uint8_t* encrypted_message_key, size_t encrypted_message_key_length) { + encryption_key_.resize(RSA_size(rsa_key())); + int decrypted_size = RSA_private_decrypt( + encrypted_message_key_length, encrypted_message_key, &encryption_key_[0], + rsa_key(), RSA_PKCS1_OAEP_PADDING); + if (-1 == decrypted_size) { + LOGE("[RSADeriveKeys(): error decrypting session key.]"); + dump_boringssl_error(); + return false; + } + encryption_key_.resize(decrypted_size); + if (decrypted_size != static_cast(wvcdm::KEY_SIZE)) { + LOGE("[RSADeriveKeys(): error. Session key is wrong size: %d.]", + decrypted_size); + dump_boringssl_error(); + encryption_key_.clear(); + return false; + } + return true; +} + +OEMCryptoResult SessionContext::RefreshKey( + const KeyId& key_id, const std::vector& key_control, + const std::vector& key_control_iv) { + if (!session_keys_) { + return OEMCrypto_ERROR_INVALID_CONTEXT; + } + if (key_id.empty()) { + // Key control is not encrypted if key id is NULL + KeyControlBlock key_control_block(key_control); + if (!key_control_block.valid()) { + LOGE("Parse key control error."); + return OEMCrypto_ERROR_INVALID_CONTEXT; + } + if ((key_control_block.control_bits() & kControlNonceEnabled) && + (!CheckNonce(key_control_block.nonce()))) { + LOGE("KCB: BAD Nonce"); + return OEMCrypto_ERROR_INVALID_NONCE; + } + // Apply duration to all keys in this session + session_keys_->UpdateDuration(key_control_block); + return OEMCrypto_SUCCESS; + } + + Key* content_key = session_keys_->Find(key_id); + + if (NULL == content_key) { + if (LogCategoryEnabled(kLoggingDumpKeyControlBlocks)) { + LOGD("Error: no matching content key."); + } + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + + if (key_control.empty()) { + if (LogCategoryEnabled(kLoggingDumpKeyControlBlocks)) { + LOGD("Error: no key_control."); + } + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + + const std::vector content_key_value = content_key->value(); + + // Decrypt encrypted key control block + std::vector control; + if (key_control_iv.empty()) { + if (LogCategoryEnabled(kLoggingDumpKeyControlBlocks)) { + LOGD("Key control block is NOT encrypted."); + } + control = key_control; + } else { + if (LogCategoryEnabled(kLoggingDumpKeyControlBlocks)) { + LOGD("Key control block is encrypted."); + } + if (!DecryptMessage(content_key_value, key_control_iv, key_control, + &control)) { + if (LogCategoryEnabled(kLoggingDumpKeyControlBlocks)) { + LOGD("Error decrypting key control block."); + } + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + } + + KeyControlBlock key_control_block(control); + if (!key_control_block.valid()) { + if (LogCategoryEnabled(kLoggingDumpKeyControlBlocks)) { + LOGD("Parse key control error."); + } + return OEMCrypto_ERROR_INVALID_CONTEXT; + } + if ((key_control_block.control_bits() & kControlNonceEnabled) && + (!CheckNonce(key_control_block.nonce()))) { + LOGE("KCB: BAD Nonce"); + return OEMCrypto_ERROR_INVALID_NONCE; + } + content_key->UpdateDuration(key_control_block); + return OEMCrypto_SUCCESS; +} + +bool SessionContext::DecryptRSAKey(const uint8_t* enc_rsa_key, + size_t enc_rsa_key_length, + const uint8_t* enc_rsa_key_iv, + uint8_t* pkcs8_rsa_key) { + // Decrypt rsa key with keybox. + uint8_t iv_buffer[wvcdm::KEY_IV_SIZE]; + memcpy(iv_buffer, enc_rsa_key_iv, wvcdm::KEY_IV_SIZE); + AES_KEY aes_key; + AES_set_decrypt_key(&encryption_key_[0], 128, &aes_key); + AES_cbc_encrypt(enc_rsa_key, pkcs8_rsa_key, enc_rsa_key_length, &aes_key, + iv_buffer, AES_DECRYPT); + return true; +} + +bool SessionContext::EncryptRSAKey(const uint8_t* pkcs8_rsa_key, + size_t enc_rsa_key_length, + const uint8_t* enc_rsa_key_iv, + uint8_t* enc_rsa_key) { + // Encrypt rsa key with keybox. + uint8_t iv_buffer[wvcdm::KEY_IV_SIZE]; + memcpy(iv_buffer, enc_rsa_key_iv, wvcdm::KEY_IV_SIZE); + AES_KEY aes_key; + AES_set_encrypt_key(&encryption_key_[0], 128, &aes_key); + AES_cbc_encrypt(pkcs8_rsa_key, enc_rsa_key, enc_rsa_key_length, &aes_key, + iv_buffer, AES_ENCRYPT); + return true; +} + +bool SessionContext::LoadRSAKey(const uint8_t* pkcs8_rsa_key, + size_t rsa_key_length) { + rsa_key_.reset(); + if (rsa_key_length < 8) { + LOGE("[LoadRSAKey(): Very Short Buffer]"); + return false; + } + if ((memcmp(pkcs8_rsa_key, "SIGN", 4) == 0)) { + uint32_t schemes_n; + memcpy((uint8_t*)&schemes_n, pkcs8_rsa_key + 4, sizeof(uint32_t)); + allowed_schemes_ = htonl(schemes_n); + pkcs8_rsa_key += 8; + rsa_key_length -= 8; + } else { + allowed_schemes_ = kSign_RSASSA_PSS; + } + return rsa_key_.LoadPkcs8RsaKey(pkcs8_rsa_key, rsa_key_length); +} + +OEMCryptoResult SessionContext::CheckKeyUse(const std::string& log_string, + uint32_t use_type, + 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()); + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + if (control.control_bits() & kControlDataPathSecure) { + if (!ce_->config_closed_platform() && + buffer_type == OEMCrypto_BufferType_Clear) { + LOGE("[%s(): Secure key with insecure buffer]", log_string.c_str()); + return OEMCrypto_ERROR_DECRYPT_FAILED; + } + } + if (control.control_bits() & kControlReplayMask) { + if (!CheckUsageEntry()) { + LOGE("[%s(): usage entry not valid]", log_string.c_str()); + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + } + if (control.duration() > 0) { + if (control.duration() < CurrentTimer()) { + LOGE("[%s(): key expired.", log_string.c_str()); + return OEMCrypto_ERROR_KEY_EXPIRED; + } + } + if (!ce_->config_local_display_only()) { + // Only look at HDCP and Analog restrictions if the display can be + // non-local. + if (control.control_bits() & kControlHDCPRequired) { + uint8_t required_hdcp = + (control.control_bits() & kControlHDCPVersionMask) >> + kControlHDCPVersionShift; + if (ce_->srm_blacklisted_device_attached()) { + required_hdcp = HDCP_NO_DIGITAL_OUTPUT; + } + // For reference implementation, we pretend we can handle the current + // HDCP version. + if (required_hdcp > ce_->config_current_hdcp_capability() || + ce_->config_current_hdcp_capability() == 0) { + return OEMCrypto_ERROR_INSUFFICIENT_HDCP; + } + } + } + if (!ce_->config_local_display_only() || + buffer_type == OEMCrypto_BufferType_Clear) { + if (control.control_bits() & kControlDisableAnalogOutput) { + LOGE("[%s(): control bit says disable analog.", log_string.c_str()); + return OEMCrypto_ERROR_ANALOG_OUTPUT; + } + } + return OEMCrypto_SUCCESS; +} + +OEMCryptoResult SessionContext::Generic_Encrypt(const uint8_t* in_buffer, + size_t buffer_length, + const uint8_t* iv, + OEMCrypto_Algorithm algorithm, + uint8_t* out_buffer) { + // Check there is a content key + if (current_content_key() == NULL) { + LOGE("[Generic_Encrypt(): OEMCrypto_ERROR_NO_CONTENT_KEY]"); + return OEMCrypto_ERROR_NO_CONTENT_KEY; + } + const std::vector& key = current_content_key()->value(); + // Set the AES key. + if (static_cast(key.size()) != AES_BLOCK_SIZE) { + LOGE("[Generic_Encrypt(): CONTENT_KEY has wrong size: %d", key.size()); + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + OEMCryptoResult result = CheckKeyUse("Generic_Encrypt", kControlAllowEncrypt, + OEMCrypto_BufferType_Clear); + if (result != OEMCrypto_SUCCESS) return result; + if (algorithm != OEMCrypto_AES_CBC_128_NO_PADDING) { + LOGE("[Generic_Encrypt(): algorithm bad."); + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + if (buffer_length % AES_BLOCK_SIZE != 0) { + LOGE("[Generic_Encrypt(): buffers size bad."); + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + const uint8_t* key_u8 = &key[0]; + AES_KEY aes_key; + if (AES_set_encrypt_key(key_u8, AES_BLOCK_SIZE * 8, &aes_key) != 0) { + LOGE("[Generic_Encrypt(): FAILURE]"); + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + uint8_t iv_buffer[wvcdm::KEY_IV_SIZE]; + memcpy(iv_buffer, iv, wvcdm::KEY_IV_SIZE); + AES_cbc_encrypt(in_buffer, out_buffer, buffer_length, &aes_key, iv_buffer, + AES_ENCRYPT); + return OEMCrypto_SUCCESS; +} + +OEMCryptoResult SessionContext::Generic_Decrypt(const uint8_t* in_buffer, + size_t buffer_length, + const uint8_t* iv, + OEMCrypto_Algorithm algorithm, + uint8_t* out_buffer) { + // Check there is a content key + if (current_content_key() == NULL) { + LOGE("[Generic_Decrypt(): OEMCrypto_ERROR_NO_CONTENT_KEY]"); + return OEMCrypto_ERROR_NO_CONTENT_KEY; + } + 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."); + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + OEMCryptoResult result = CheckKeyUse("Generic_Decrypt", kControlAllowDecrypt, + OEMCrypto_BufferType_Clear); + if (result != OEMCrypto_SUCCESS) return result; + + if (algorithm != OEMCrypto_AES_CBC_128_NO_PADDING) { + LOGE("[Generic_Decrypt(): bad algorithm."); + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + if (buffer_length % AES_BLOCK_SIZE != 0) { + LOGE("[Generic_Decrypt(): bad buffer size."); + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + const uint8_t* key_u8 = &key[0]; + AES_KEY aes_key; + if (AES_set_decrypt_key(key_u8, AES_BLOCK_SIZE * 8, &aes_key) != 0) { + LOGE("[Generic_Decrypt(): FAILURE]"); + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + uint8_t iv_buffer[wvcdm::KEY_IV_SIZE]; + memcpy(iv_buffer, iv, wvcdm::KEY_IV_SIZE); + AES_cbc_encrypt(in_buffer, out_buffer, buffer_length, &aes_key, iv_buffer, + AES_DECRYPT); + return OEMCrypto_SUCCESS; +} + +OEMCryptoResult SessionContext::Generic_Sign(const uint8_t* in_buffer, + size_t buffer_length, + OEMCrypto_Algorithm algorithm, + uint8_t* signature, + size_t* signature_length) { + // Check there is a content key + if (current_content_key() == NULL) { + LOGE("[Generic_Sign(): OEMCrypto_ERROR_NO_CONTENT_KEY]"); + return OEMCrypto_ERROR_NO_CONTENT_KEY; + } + if (*signature_length < SHA256_DIGEST_LENGTH) { + *signature_length = SHA256_DIGEST_LENGTH; + LOGE("[Generic_Sign(): bad signature length."); + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + const std::vector& key = current_content_key()->value(); + if (static_cast(key.size()) != SHA256_DIGEST_LENGTH) { + LOGE("[Generic_Sign(): CONTENT_KEY has wrong size; %d", key.size()); + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + OEMCryptoResult result = CheckKeyUse("Generic_Sign", kControlAllowSign, + OEMCrypto_BufferType_Clear); + if (result != OEMCrypto_SUCCESS) return result; + if (algorithm != OEMCrypto_HMAC_SHA256) { + LOGE("[Generic_Sign(): bad algorithm."); + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + unsigned int md_len = *signature_length; + if (HMAC(EVP_sha256(), &key[0], key.size(), in_buffer, buffer_length, + signature, &md_len)) { + *signature_length = md_len; + return OEMCrypto_SUCCESS; + } + LOGE("[Generic_Sign(): hmac failed."); + dump_boringssl_error(); + return OEMCrypto_ERROR_UNKNOWN_FAILURE; +} + +OEMCryptoResult SessionContext::Generic_Verify(const uint8_t* in_buffer, + size_t buffer_length, + OEMCrypto_Algorithm algorithm, + const uint8_t* signature, + size_t signature_length) { + // Check there is a content key + if (current_content_key() == NULL) { + LOGE("[Decrypt_Verify(): OEMCrypto_ERROR_NO_CONTENT_KEY]"); + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + if (signature_length < SHA256_DIGEST_LENGTH) { + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + const std::vector& key = current_content_key()->value(); + if (static_cast(key.size()) != SHA256_DIGEST_LENGTH) { + LOGE("[Generic_Verify(): CONTENT_KEY has wrong size: %d", key.size()); + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + OEMCryptoResult result = CheckKeyUse("Generic_Verify", kControlAllowVerify, + OEMCrypto_BufferType_Clear); + if (result != OEMCrypto_SUCCESS) return result; + if (algorithm != OEMCrypto_HMAC_SHA256) { + LOGE("[Generic_Verify(): bad algorithm."); + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + unsigned int md_len = signature_length; + uint8_t computed_signature[SHA256_DIGEST_LENGTH]; + if (HMAC(EVP_sha256(), &key[0], key.size(), in_buffer, buffer_length, + computed_signature, &md_len)) { + if (0 == memcmp(signature, computed_signature, SHA256_DIGEST_LENGTH)) { + return OEMCrypto_SUCCESS; + } else { + return OEMCrypto_ERROR_SIGNATURE_FAILURE; + } + } + LOGE("[Generic_Verify(): HMAC failed."); + dump_boringssl_error(); + return OEMCrypto_ERROR_UNKNOWN_FAILURE; +} + +bool SessionContext::UpdateMacKeys(const std::vector& enc_mac_keys, + const std::vector& iv) { + // Decrypt mac key from enc_mac_key using device_keya + std::vector mac_keys; + if (!DecryptMessage(encryption_key_, iv, enc_mac_keys, &mac_keys)) { + return false; + } + mac_key_server_ = std::vector( + mac_keys.begin(), mac_keys.begin() + wvcdm::MAC_KEY_SIZE); + mac_key_client_ = std::vector(mac_keys.begin() + wvcdm::MAC_KEY_SIZE, + mac_keys.end()); + if (LogCategoryEnabled(kLoggingDumpDerivedKeys)) { + LOGI(("mac_key_client_ has been updated to = " + + wvcdm::b2a_hex(mac_key_client_)).c_str()); + } + return true; +} + +bool SessionContext::QueryKeyControlBlock(const KeyId& key_id, uint32_t* data) { + if (!session_keys_) { + return false; + } + const Key* content_key = session_keys_->Find(key_id); + if (LogCategoryEnabled(kLoggingTraceDecryption)) { + LOGI(("Select Key: key_id = " + wvcdm::b2a_hex(key_id)).c_str()); + if (content_key) { + LOGI(("Select Key: key = " + wvcdm::b2a_hex(content_key->value())) + .c_str()); + } else { + LOGI("Select Key: key = null."); + } + } + if (NULL == content_key) { + LOGE("[QueryKeyControlBlock(): No key matches key id]"); + return false; + } + data[0] = 0; // verification optional. + data[1] = htonl(content_key->control().duration()); + data[2] = 0; // nonce optional. + data[3] = htonl(content_key->control().control_bits()); + return true; +} + +OEMCryptoResult SessionContext::SelectContentKey( + const KeyId& key_id, OEMCryptoCipherMode cipher_mode) { + if (LogCategoryEnabled(kLoggingTraceDecryption)) { + LOGI(" Select Key: key_id = %s", wvcdm::b2a_hex(key_id).c_str()); + LOGI(" Select Key: cipher_mode = %s", + (cipher_mode == OEMCrypto_CipherMode_CTR) ? "CTR" : "CBC"); + } + if (!session_keys_) { + LOGE("Select Key: no session keys."); + return OEMCrypto_ERROR_INVALID_CONTEXT; + } + Key* content_key = session_keys_->Find(key_id); + if (NULL == content_key) { + LOGE("[SelectContentKey(): No key matches key id]"); + return OEMCrypto_ERROR_NO_CONTENT_KEY; + } + if (LogCategoryEnabled(kLoggingTraceDecryption)) { + LOGI((" Select Key: key = " + wvcdm::b2a_hex(content_key->value())) + .c_str()); + } + content_key->set_ctr_mode(cipher_mode == OEMCrypto_CipherMode_CTR); + current_content_key_ = content_key; + const KeyControlBlock& control = current_content_key()->control(); + + if (control.duration() > 0) { + if (control.duration() < CurrentTimer()) { + LOGE("[SelectContentKey(): KEY_EXPIRED %d versus %d]", control.duration(), + CurrentTimer()); + return OEMCrypto_ERROR_KEY_EXPIRED; + } + } + return OEMCrypto_SUCCESS; +} + +void SessionContext::AddNonce(uint32_t nonce) { nonce_table_.AddNonce(nonce); } + +bool SessionContext::CheckNonce(uint32_t nonce) { + return nonce_table_.CheckNonce(nonce); +} + +void SessionContext::FlushNonces() { nonce_table_.Flush(); } + +bool SessionContext::CheckUsageEntry() { + if (!usage_entry_) return false; + return usage_entry_->CheckForUse(); +} + +OEMCryptoResult SessionContext::CreateNewUsageEntry( + uint32_t* usage_entry_number) { + OEMCryptoResult result = ce_->usage_table().CreateNewUsageEntry( + this, &usage_entry_, usage_entry_number); + if (usage_entry_) { + usage_entry_status_ = kUsageEntryNew; + } + return result; +} + +OEMCryptoResult SessionContext::LoadUsageEntry( + uint32_t index, const std::vector& buffer) { + OEMCryptoResult result = + ce_->usage_table().LoadUsageEntry(this, &usage_entry_, index, buffer); + if (usage_entry_) { + usage_entry_status_ = kUsageEntryLoaded; + } + return result; +} + +OEMCryptoResult SessionContext::UpdateUsageEntry(uint8_t* header_buffer, + size_t* header_buffer_length, + uint8_t* entry_buffer, + size_t* entry_buffer_length) { + if (!usage_entry_) { + LOGE("UpdateUsageEntry: Session has no entry."); + return OEMCrypto_ERROR_INVALID_CONTEXT; + } + return ce_->usage_table().UpdateUsageEntry(this, usage_entry_, header_buffer, + header_buffer_length, entry_buffer, + entry_buffer_length); +} + +OEMCryptoResult SessionContext::DeactivateUsageEntry( + const std::vector& pst) { + if (!usage_entry_) return OEMCrypto_ERROR_INVALID_CONTEXT; + usage_entry_->Deactivate(pst); + return OEMCrypto_SUCCESS; +} + +OEMCryptoResult SessionContext::ReportUsage(const std::vector& pst, + uint8_t* buffer, + size_t* buffer_length) { + if (!usage_entry_) return OEMCrypto_ERROR_INVALID_CONTEXT; + return usage_entry_->ReportUsage(pst, buffer, buffer_length); +} + +OEMCryptoResult SessionContext::MoveEntry(uint32_t new_index) { + if (!usage_entry_) return OEMCrypto_ERROR_INVALID_CONTEXT; + return ce_->usage_table().MoveEntry(usage_entry_, new_index); +} + +OEMCryptoResult SessionContext::CopyOldUsageEntry( + const std::vector& pst) { + if (!usage_entry_) return OEMCrypto_ERROR_INVALID_CONTEXT; + return usage_entry_->CopyOldUsageEntry(pst); +} + +// Internal utility function to decrypt the message +bool SessionContext::DecryptMessage(const std::vector& key, + const std::vector& iv, + const std::vector& message, + std::vector* decrypted) { + if (key.empty() || iv.empty() || message.empty() || !decrypted) { + LOGE("[DecryptMessage(): OEMCrypto_ERROR_INVALID_CONTEXT]"); + return false; + } + decrypted->resize(message.size()); + uint8_t iv_buffer[16]; + memcpy(iv_buffer, &iv[0], 16); + AES_KEY aes_key; + AES_set_decrypt_key(&key[0], 128, &aes_key); + AES_cbc_encrypt(&message[0], &(decrypted->front()), message.size(), &aes_key, + iv_buffer, AES_DECRYPT); + return true; +} + +bool SessionContext::DecryptEntitlement( + const std::vector& key, + const std::vector& iv, + const std::vector& message, + std::vector* decrypted) { + if (key.empty() || iv.empty() || message.empty() || !decrypted) { + LOGE("[DecryptMessage(): OEMCrypto_ERROR_INVALID_CONTEXT]"); + return false; + } + decrypted->resize(message.size()); + uint8_t iv_buffer[16]; + memcpy(iv_buffer, &iv[0], 16); + AES_KEY aes_key; + AES_set_decrypt_key(&key[0], 256, &aes_key); + AES_cbc_encrypt(&message[0], &(decrypted->front()), message.size(), &aes_key, + iv_buffer, AES_DECRYPT); + return true; +} + +OEMCryptoResult SessionContext::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) { + // If the data is clear, we do not need a current key selected. + if (!is_encrypted) { + if (buffer_type != OEMCrypto_BufferType_Direct) { + memcpy(reinterpret_cast(clear_data), cipher_data, + cipher_data_length); + return OEMCrypto_SUCCESS; + } + // For reference implementation, we quietly drop the clear direct video. + return OEMCrypto_SUCCESS; + } + + // Check there is a content key + if (current_content_key() == NULL) { + LOGE("[DecryptCTR(): OEMCrypto_ERROR_NO_CONTENT_KEY]"); + return OEMCrypto_ERROR_DECRYPT_FAILED; + } + + OEMCryptoResult result = CheckKeyUse("DecryptCENC", 0, buffer_type); + if (result != OEMCrypto_SUCCESS) return result; + + const std::vector& content_key = current_content_key()->value(); + + // Set the AES key. + if (static_cast(content_key.size()) != AES_BLOCK_SIZE) { + LOGE("[DecryptCTR(): CONTENT_KEY has wrong size: %d", content_key.size()); + return OEMCrypto_ERROR_DECRYPT_FAILED; + } + const uint8_t* key_u8 = &content_key[0]; + + if (buffer_type == OEMCrypto_BufferType_Direct) { + // For reference implementation, we quietly drop the decrypted direct video. + return OEMCrypto_SUCCESS; + } + + if (!current_content_key()->ctr_mode()) { + if (block_offset > 0) return OEMCrypto_ERROR_INVALID_CONTEXT; + return DecryptCBC(key_u8, iv, pattern, cipher_data, cipher_data_length, + clear_data); + } + if (pattern->skip > 0) { + return PatternDecryptCTR(key_u8, iv, block_offset, pattern, cipher_data, + cipher_data_length, clear_data); + } + return DecryptCTR(key_u8, iv, block_offset, cipher_data, cipher_data_length, + clear_data); +} + +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) { + AES_KEY aes_key; + AES_set_decrypt_key(&key[0], AES_BLOCK_SIZE * 8, &aes_key); + uint8_t iv[AES_BLOCK_SIZE]; + uint8_t next_iv[AES_BLOCK_SIZE]; + memcpy(iv, &initial_iv[0], AES_BLOCK_SIZE); + + size_t l = 0; + size_t pattern_offset = pattern->offset; + while (l < cipher_data_length) { + size_t size = + std::min(cipher_data_length - l, static_cast(AES_BLOCK_SIZE)); + size_t pattern_length = pattern->encrypt + pattern->skip; + bool skip_block = + (pattern_offset >= pattern->encrypt) && (pattern_length > 0); + if (pattern_length > 0) { + pattern_offset = (pattern_offset + 1) % pattern_length; + } + if (skip_block || (size < AES_BLOCK_SIZE)) { + memcpy(&clear_data[l], &cipher_data[l], size); + } else { + uint8_t aes_output[AES_BLOCK_SIZE]; + // Save the iv for the next block, in case cipher_data is in the same + // buffer as clear_data. + memcpy(next_iv, &cipher_data[l], AES_BLOCK_SIZE); + AES_decrypt(&cipher_data[l], aes_output, &aes_key); + for (size_t n = 0; n < AES_BLOCK_SIZE; n++) { + clear_data[l + n] = aes_output[n] ^ iv[n]; + } + memcpy(iv, next_iv, AES_BLOCK_SIZE); + } + l += size; + } + return OEMCrypto_SUCCESS; +} + +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) { + 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; + while (l < cipher_data_length) { + size_t size = + std::min(cipher_data_length - l, AES_BLOCK_SIZE - block_offset); + size_t pattern_length = pattern->encrypt + pattern->skip; + bool skip_block = + (pattern_offset >= pattern->encrypt) && (pattern_length > 0); + if (pattern_length > 0) { + pattern_offset = (pattern_offset + 1) % pattern_length; + } + if (skip_block) { + memcpy(&clear_data[l], &cipher_data[l], size); + } else { + uint8_t aes_output[AES_BLOCK_SIZE]; + AES_encrypt(iv, aes_output, &aes_key); + for (size_t n = 0; n < size; n++) { + clear_data[l + n] = aes_output[n + block_offset] ^ cipher_data[l + n]; + } + ctr128_inc64(iv); + } + l += size; + block_offset = 0; + } + return OEMCrypto_SUCCESS; +} + +// This is a special case of PatternDecryptCTR with no skip pattern. It uses +// more optimized versions of openssl's implementation of AES CTR mode. +OEMCryptoResult SessionContext::DecryptCTR(const uint8_t* key_u8, + const uint8_t* iv, + size_t block_offset, + const uint8_t* cipher_data, + size_t cipher_data_length, + uint8_t* clear_data) { + // Local copy (will be modified). + // Allocated as 64-bit ints to enforce 64-bit alignment for later access as a + // 64-bit value. + uint64_t aes_iv[2]; + assert(sizeof(aes_iv) == AES_BLOCK_SIZE); + // The double-cast is needed to comply with strict aliasing rules. + uint8_t* aes_iv_u8 = + reinterpret_cast(reinterpret_cast(aes_iv)); + memcpy(aes_iv_u8, &iv[0], AES_BLOCK_SIZE); + + // The CENC spec specifies we increment only the low 64 bits of the IV + // counter, and leave the high 64 bits alone. This is different from the + // OpenSSL implementation, which increments the entire 128 bit iv. That is + // why we implement the CTR loop ourselves. + size_t l = 0; + if (block_offset > 0 && l < cipher_data_length) { + // Encrypt the IV. + uint8_t ecount_buf[AES_BLOCK_SIZE]; + + AES_KEY aes_key; + if (AES_set_encrypt_key(key_u8, AES_BLOCK_SIZE * 8, &aes_key) != 0) { + LOGE("[DecryptCTR(): FAILURE]"); + return OEMCrypto_ERROR_DECRYPT_FAILED; + } + AES_encrypt(aes_iv_u8, ecount_buf, &aes_key); + for (int n = block_offset; n < AES_BLOCK_SIZE && l < cipher_data_length; + ++n, ++l) { + clear_data[l] = cipher_data[l] ^ ecount_buf[n]; + } + ctr128_inc64(aes_iv_u8); + block_offset = 0; + } + + uint64_t remaining = cipher_data_length - l; + int out_len = 0; + + while (remaining) { +#if (OPENSSL_VERSION_NUMBER < 0x10100000L) + EVP_CIPHER_CTX ctx_struct; + EVP_CIPHER_CTX* evp_cipher_ctx = &ctx_struct; + EVP_CIPHER_CTX_init(evp_cipher_ctx); +#else + EVP_CIPHER_CTX* evp_cipher_ctx = EVP_CIPHER_CTX_new(); +#endif + EVP_CIPHER_CTX_set_padding(evp_cipher_ctx, 0); + if (!EVP_DecryptInit_ex(evp_cipher_ctx, EVP_aes_128_ctr(), NULL, key_u8, + aes_iv_u8)) { + LOGE("[DecryptCTR(): EVP_INIT ERROR]"); +#if (OPENSSL_VERSION_NUMBER < 0x10100000L) + EVP_CIPHER_CTX_cleanup(evp_cipher_ctx); +#else + EVP_CIPHER_CTX_free(evp_cipher_ctx); +#endif + return OEMCrypto_ERROR_DECRYPT_FAILED; + } + + // Test the MSB of the counter portion of the initialization vector. If the + // value is 0xFF the counter is near wrapping. In this case we calculate + // the number of bytes we can safely decrypt before the counter wraps. + uint64_t decrypt_length = 0; + if (aes_iv_u8[8] == 0xFF) { + uint64_t bottom_64_bits = wvcdm::ntohll64(aes_iv[1]); + uint64_t bytes_before_iv_wrap = (~bottom_64_bits + 1) * AES_BLOCK_SIZE; + decrypt_length = + bytes_before_iv_wrap < remaining ? bytes_before_iv_wrap : remaining; + } else { + decrypt_length = remaining; + } + + if (!EVP_DecryptUpdate(evp_cipher_ctx, &clear_data[l], &out_len, + &cipher_data[l], decrypt_length)) { + LOGE("[DecryptCTR(): EVP_UPDATE_ERROR]"); +#if (OPENSSL_VERSION_NUMBER < 0x10100000L) + EVP_CIPHER_CTX_cleanup(evp_cipher_ctx); +#else + EVP_CIPHER_CTX_free(evp_cipher_ctx); +#endif + return OEMCrypto_ERROR_DECRYPT_FAILED; + } + l += decrypt_length; + remaining = cipher_data_length - l; + + int final; + if (!EVP_DecryptFinal_ex(evp_cipher_ctx, + &clear_data[cipher_data_length - remaining], + &final)) { + LOGE("[DecryptCTR(): EVP_FINAL_ERROR]"); +#if (OPENSSL_VERSION_NUMBER < 0x10100000L) + EVP_CIPHER_CTX_cleanup(evp_cipher_ctx); +#else + EVP_CIPHER_CTX_free(evp_cipher_ctx); +#endif + return OEMCrypto_ERROR_DECRYPT_FAILED; + } +#if (OPENSSL_VERSION_NUMBER < 0x10100000L) + EVP_CIPHER_CTX_cleanup(evp_cipher_ctx); +#else + EVP_CIPHER_CTX_free(evp_cipher_ctx); +#endif + + // If remaining is not zero, reset the iv before the second pass. + if (remaining) { + memcpy(aes_iv_u8, &iv[0], AES_BLOCK_SIZE); + memset(&aes_iv_u8[8], 0, AES_BLOCK_SIZE / 2); + } + } + return OEMCrypto_SUCCESS; +} + +} // namespace wvoec_mock diff --git a/mock/src/oemcrypto_session.h b/mock/src/oemcrypto_session.h new file mode 100644 index 0000000..edba1df --- /dev/null +++ b/mock/src/oemcrypto_session.h @@ -0,0 +1,251 @@ +// Copyright 2017 Google Inc. All Rights Reserved. +// +// Mock implementation of OEMCrypto APIs +// +#ifndef MOCK_OEMCRYPTO_SESSION_H_ +#define MOCK_OEMCRYPTO_SESSION_H_ + +#include +#include +#include +#include + +#include + +#include "OEMCryptoCENC.h" // Needed for enums only. +#include "file_store.h" +#include "lock.h" +#include "oemcrypto_auth_mock.h" +#include "oemcrypto_key_mock.h" +#include "oemcrypto_nonce_table.h" +#include "oemcrypto_rsa_key_shared.h" +#include "oemcrypto_session_key_table.h" +#include "oemcrypto_usage_table_mock.h" +#include "wv_cdm_types.h" + +namespace wvoec_mock { + +class CryptoEngine; +typedef uint32_t SessionId; + +enum SRMVersionStatus { NoSRMVersion, ValidSRMVersion, InvalidSRMVersion }; + +// TODO(jfore): Is there a better name? +class SessionContextKeys { + public: + virtual OEMCrypto_LicenseType type() = 0; + virtual size_t size() = 0; + virtual bool Insert(const KeyId& key_id, const Key& key_data) = 0; + virtual Key* Find(const KeyId& key_id) = 0; + virtual void Remove(const KeyId& key_id) = 0; + virtual void UpdateDuration(const KeyControlBlock& control) = 0; + + // Methods supported exclusively for entitlement keys. Returns false if + // entitlement keys are not found or not supported by the current key table. + // It is the caller's responsibility to check the context. + virtual bool SetContentKey(const KeyId& entitlement_id, + const KeyId& content_key_id, + const std::vector& content_key) = 0; + virtual bool GetEntitlementKey(const KeyId& entitlement_id, + const std::vector** key) = 0; + + virtual ~SessionContextKeys() {} + + protected: + SessionContextKeys() {} + + private: + CORE_DISALLOW_COPY_AND_ASSIGN(SessionContextKeys); +}; + +class SessionContext { + private: + SessionContext() {} + + public: + SessionContext(CryptoEngine* ce, SessionId sid, const RSA_shared_ptr& rsa_key) + : valid_(true), + ce_(ce), + id_(sid), + current_content_key_(NULL), + session_keys_(NULL), + rsa_key_(rsa_key), + allowed_schemes_(kSign_RSASSA_PSS), + usage_entry_(NULL), + srm_requirements_status_(NoSRMVersion), + usage_entry_status_(kNoUsageEntry) {} + ~SessionContext(); + + bool isValid() { return valid_; } + + bool DeriveKeys(const std::vector& master_key, + const std::vector& mac_context, + const std::vector& enc_context); + bool RSADeriveKeys(const std::vector& enc_session_key, + const std::vector& mac_context, + const std::vector& enc_context); + bool GenerateSignature(const uint8_t* message, size_t message_length, + uint8_t* signature, size_t* signature_length); + size_t RSASignatureSize(); + OEMCryptoResult GenerateRSASignature(const uint8_t* message, + size_t message_length, + uint8_t* signature, + size_t* signature_length, + RSA_Padding_Scheme padding_scheme); + 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); + + OEMCryptoResult Generic_Encrypt(const uint8_t* in_buffer, + size_t buffer_length, const uint8_t* iv, + OEMCrypto_Algorithm algorithm, + uint8_t* out_buffer); + OEMCryptoResult Generic_Decrypt(const uint8_t* in_buffer, + size_t buffer_length, const uint8_t* iv, + OEMCrypto_Algorithm algorithm, + uint8_t* out_buffer); + OEMCryptoResult Generic_Sign(const uint8_t* in_buffer, size_t buffer_length, + OEMCrypto_Algorithm algorithm, + uint8_t* signature, size_t* signature_length); + OEMCryptoResult Generic_Verify(const uint8_t* in_buffer, size_t buffer_length, + OEMCrypto_Algorithm algorithm, + const uint8_t* signature, + size_t signature_length); + void StartTimer(); + uint32_t CurrentTimer(); // (seconds). + OEMCryptoResult LoadKeys( + const uint8_t* message, size_t message_length, const uint8_t* signature, + size_t signature_length, const uint8_t* enc_mac_key_iv, + const uint8_t* enc_mac_keys, size_t num_keys, + const OEMCrypto_KeyObject* key_array, const uint8_t* pst, + size_t pst_length, const uint8_t* srm_requirement, + OEMCrypto_LicenseType license_type); + OEMCryptoResult LoadEntitledContentKeys( + size_t num_keys, + const OEMCrypto_EntitledContentKeyObject* key_array); + OEMCryptoResult InstallKey(const KeyId& key_id, + const std::vector& key_data, + const std::vector& key_data_iv, + const std::vector& key_control, + const std::vector& key_control_iv, + bool second_license); + bool InstallRSAEncryptedKey(const uint8_t* encrypted_message_key, + size_t encrypted_message_key_length); + bool DecryptRSAKey(const uint8_t* enc_rsa_key, size_t enc_rsa_key_length, + const uint8_t* wrapped_rsa_key_iv, uint8_t* pkcs8_rsa_key); + bool EncryptRSAKey(const uint8_t* pkcs8_rsa_key, size_t enc_rsa_key_length, + const uint8_t* enc_rsa_key_iv, uint8_t* enc_rsa_key); + bool LoadRSAKey(const uint8_t* pkcs8_rsa_key, size_t rsa_key_length); + OEMCryptoResult RefreshKey(const KeyId& key_id, + const std::vector& key_control, + const std::vector& key_control_iv); + bool UpdateMacKeys(const std::vector& mac_keys, + const std::vector& iv); + bool QueryKeyControlBlock(const KeyId& key_id, uint32_t* data); + OEMCryptoResult SelectContentKey(const KeyId& key_id, + OEMCryptoCipherMode cipher_mode); + const Key* current_content_key(void) { return current_content_key_; } + void set_mac_key_server(const std::vector& mac_key_server) { + mac_key_server_ = mac_key_server; + } + const std::vector& mac_key_server() { return mac_key_server_; } + void set_mac_key_client(const std::vector& mac_key_client) { + mac_key_client_ = mac_key_client; + } + const std::vector& mac_key_client() { return mac_key_client_; } + + void set_encryption_key(const std::vector& enc_key) { + encryption_key_ = enc_key; + } + const std::vector& encryption_key() { return encryption_key_; } + uint32_t allowed_schemes() const { return allowed_schemes_; } + + void AddNonce(uint32_t nonce); + bool CheckNonce(uint32_t nonce); + void FlushNonces(); + + OEMCryptoResult CreateNewUsageEntry(uint32_t* usage_entry_number); + OEMCryptoResult LoadUsageEntry(uint32_t index, + const std::vector& buffer); + OEMCryptoResult UpdateUsageEntry(uint8_t* header_buffer, + size_t* header_buffer_length, + uint8_t* entry_buffer, + size_t* entry_buffer_length); + OEMCryptoResult DeactivateUsageEntry(const std::vector& pst); + OEMCryptoResult ReportUsage(const std::vector& pst, uint8_t* buffer, + size_t* buffer_length); + OEMCryptoResult MoveEntry(uint32_t new_index); + OEMCryptoResult CopyOldUsageEntry(const std::vector& pst); + + private: + bool DeriveKey(const std::vector& key, + const std::vector& context, int counter, + std::vector* out); + bool DecryptMessage(const std::vector& key, + const std::vector& iv, + const std::vector& message, + std::vector* decrypted); + bool DecryptEntitlement(const std::vector& key, + const std::vector& iv, + const std::vector& message, + std::vector* decrypted); + // Either verify the nonce or usage entry, as required by the key control + // block. + OEMCryptoResult CheckNonceOrEntry(const KeyControlBlock& key_control_block); + // If there is a usage entry, check that it is not inactive. + // It also updates the status of the entry if needed. + bool CheckUsageEntry(); + // Check that the usage entry status is valid for online use. + 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 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 PatternDecryptCTR( + const uint8_t* key, const uint8_t* iv, size_t block_offset, + const OEMCrypto_CENCEncryptPatternDesc* 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, + size_t block_offset, const uint8_t* cipher_data, + size_t cipher_data_length, uint8_t* clear_data); + // Checks if the key is allowed for the specified type. If there is a usage + // entry, it also checks the usage entry. + OEMCryptoResult CheckKeyUse(const std::string& log_string, uint32_t use_type, + OEMCryptoBufferType buffer_type); + RSA* rsa_key() { return rsa_key_.get(); } + + bool valid_; + CryptoEngine* ce_; + SessionId id_; + std::vector mac_key_server_; + std::vector mac_key_client_; + std::vector encryption_key_; + std::vector session_key_; + const Key* current_content_key_; + SessionContextKeys* session_keys_; + NonceTable nonce_table_; + RSA_shared_ptr rsa_key_; + uint32_t allowed_schemes_; // for RSA signatures. + time_t timer_start_; + UsageTableEntry* usage_entry_; + SRMVersionStatus srm_requirements_status_; + enum UsageEntryStatus { + kNoUsageEntry, // No entry loaded for this session. + kUsageEntryNew, // After entry was created. + kUsageEntryLoaded, // After loading entry or loading keys. + }; + UsageEntryStatus usage_entry_status_; + CORE_DISALLOW_COPY_AND_ASSIGN(SessionContext); +}; + +} // namespace wvoec_mock + +#endif // MOCK_OEMCRYPTO_SESSION_H_ diff --git a/mock/src/oemcrypto_session_key_table.cpp b/mock/src/oemcrypto_session_key_table.cpp new file mode 100644 index 0000000..5eec8d6 --- /dev/null +++ b/mock/src/oemcrypto_session_key_table.cpp @@ -0,0 +1,117 @@ +// Copyright 2017 Google Inc. All Rights Reserved. +// +// Mock implementation of OEMCrypto APIs +// +#include "oemcrypto_session_key_table.h" + +#include "keys.h" +#include "log.h" + +namespace wvoec_mock { + +SessionKeyTable::~SessionKeyTable() { + for (KeyMap::iterator i = keys_.begin(); i != keys_.end(); ++i) { + if (NULL != i->second) { + delete i->second; + } + } +} + +bool SessionKeyTable::Insert(const KeyId key_id, const Key& key_data) { + if (keys_.find(key_id) != keys_.end()) return false; + keys_[key_id] = new Key(key_data); + return true; +} + +Key* SessionKeyTable::Find(const KeyId key_id) { + if (keys_.find(key_id) == keys_.end()) { + return NULL; + } + return keys_[key_id]; +} + +void SessionKeyTable::Remove(const KeyId key_id) { + if (keys_.find(key_id) != keys_.end()) { + delete keys_[key_id]; + keys_.erase(key_id); + } +} + +void SessionKeyTable::UpdateDuration(const KeyControlBlock& control) { + for (KeyMap::iterator it = keys_.begin(); it != keys_.end(); ++it) { + it->second->UpdateDuration(control); + } +} + +bool EntitlementKeyTable::Insert(const KeyId key_id, const Key& key_data) { + // |key_id| and |key_data| are for an entitlement key. Insert a new + // entitlement key entry. + if (keys_.find(key_id) != keys_.end()) return false; + keys_[key_id] = new EntitlementKey(key_data); + // If this is a new insertion, we don't have a content key assigned yet. + return true; +} + +Key* EntitlementKeyTable::Find(const KeyId key_id) { + // |key_id| refers to a content key. + ContentIdToEntitlementIdMap::iterator it = + contentid_to_entitlementid_.find(key_id); + if (it == contentid_to_entitlementid_.end()) { + return NULL; + } + + if (keys_.find(it->second) == keys_.end()) { + return NULL; + } + return keys_[it->second]; +} + +void EntitlementKeyTable::Remove(const KeyId key_id) { + // |key_id| refers to a content key. No one currently calls Remove so this + // method is free to change if needed. + ContentIdToEntitlementIdMap::iterator it = + contentid_to_entitlementid_.find(key_id); + if (it == contentid_to_entitlementid_.end()) { + return; + } + keys_.erase(it->second); + contentid_to_entitlementid_.erase(key_id); +} + +void EntitlementKeyTable::UpdateDuration(const KeyControlBlock& control) { + for (EntitlementKeyMap::iterator it = keys_.begin(); it != keys_.end(); + ++it) { + it->second->UpdateDuration(control); + } +} + +bool EntitlementKeyTable::SetContentKey( + const KeyId& entitlement_id, const KeyId& content_key_id, + const std::vector content_key) { + EntitlementKeyMap::iterator it = keys_.find(entitlement_id); + if (it == keys_.end()) { + return false; + } + contentid_to_entitlementid_.erase(it->second->content_key_id()); + if (!it->second->SetContentKey(content_key_id, content_key)) { + return false; + } + contentid_to_entitlementid_[content_key_id] = entitlement_id; + return true; +} + +bool EntitlementKeyTable::GetEntitlementKey( + const KeyId& entitlement_id, + const std::vector** entitlement_key) { + if (!entitlement_key) { + return false; + } + EntitlementKeyMap::iterator it = keys_.find(entitlement_id); + if (it == keys_.end()) { + return false; + } + *entitlement_key = &it->second->entitlement_key(); + return true; +} + +} // namespace wvoec_mock diff --git a/mock/src/oemcrypto_session_key_table.h b/mock/src/oemcrypto_session_key_table.h new file mode 100644 index 0000000..221db3b --- /dev/null +++ b/mock/src/oemcrypto_session_key_table.h @@ -0,0 +1,68 @@ +// Copyright 2017 Google Inc. All Rights Reserved. +// +// Mock implementation of OEMCrypto APIs +// +#ifndef MOCK_OEMCRYPTO_SESSION_KEY_TABLE_H_ +#define MOCK_OEMCRYPTO_SESSION_KEY_TABLE_H_ + +#include +#include +#include + +#include "oemcrypto_key_mock.h" +#include "wv_cdm_types.h" + +namespace wvoec_mock { + +class SessionContext; +class CryptoEngine; +class UsageTable; +class UsageTableEntry; + +typedef std::vector KeyId; +typedef std::map KeyMap; +typedef std::map EntitlementKeyMap; + +// SessionKeyTable holds the keys for the current session +class SessionKeyTable { + public: + SessionKeyTable() {} + ~SessionKeyTable(); + + bool Insert(const KeyId key_id, const Key& key_data); + Key* Find(const KeyId key_id); + void Remove(const KeyId key_id); + void UpdateDuration(const KeyControlBlock& control); + size_t size() const { return keys_.size(); } + + private: + KeyMap keys_; + + CORE_DISALLOW_COPY_AND_ASSIGN(SessionKeyTable); +}; + +class EntitlementKeyTable { + typedef std::map ContentIdToEntitlementIdMap; + + public: + EntitlementKeyTable() {} + ~EntitlementKeyTable() {} + bool Insert(const KeyId key_id, const Key& key_data); + Key* Find(const KeyId key_id); + void Remove(const KeyId key_id); + void UpdateDuration(const KeyControlBlock& control); + size_t size() const { return contentid_to_entitlementid_.size(); } + bool SetContentKey(const KeyId& entitlement_id, const KeyId& content_key_id, + const std::vector content_key); + bool GetEntitlementKey(const KeyId& entitlement_id, + const std::vector** entitlement_key); + + private: + EntitlementKeyMap keys_; + ContentIdToEntitlementIdMap contentid_to_entitlementid_; + CORE_DISALLOW_COPY_AND_ASSIGN(EntitlementKeyTable); +}; + +} // namespace wvoec_mock + +#endif // MOCK_OEMCRYPTO_SESSION_KEY_TABLE_H_ diff --git a/mock/src/oemcrypto_usage_table_mock.cpp b/mock/src/oemcrypto_usage_table_mock.cpp new file mode 100644 index 0000000..4285e95 --- /dev/null +++ b/mock/src/oemcrypto_usage_table_mock.cpp @@ -0,0 +1,752 @@ +// Copyright 2013 Google Inc. All Rights Reserved. +// +// Mock implementation of OEMCrypto APIs +// +#include "oemcrypto_usage_table_mock.h" + +#include +#include + +#include +#include + +#include +#include +#include +#include + +#include "file_store.h" +#include "log.h" +#include "oemcrypto_engine_mock.h" +#include "oemcrypto_logging.h" +#include "oemcrypto_old_usage_table_mock.h" +#include "properties.h" +#include "pst_report.h" +#include "string_conversions.h" +#include "wv_cdm_constants.h" + +namespace wvoec_mock { +namespace { +const size_t kMagicLength = 8; +const char* kEntryVerification = "USEENTRY"; +const char* kHeaderVerification = "USEHEADR"; +// Offset into a signed block where we start encrypting. We need to +// skip the signature and the iv. +const size_t kEncryptionOffset = SHA256_DIGEST_LENGTH + SHA256_DIGEST_LENGTH; + +// A structure that holds an usage entry and its signature. +struct SignedEntryBlock { + uint8_t signature[SHA256_DIGEST_LENGTH]; + uint8_t iv[SHA256_DIGEST_LENGTH]; + uint8_t verification[kMagicLength]; + StoredUsageEntry data; +}; + +// This has the data in the header of constant size. There is also an array +// of generation numbers. +struct SignedHeaderBlock { + uint8_t signature[SHA256_DIGEST_LENGTH]; + uint8_t iv[SHA256_DIGEST_LENGTH]; + uint8_t verification[kMagicLength]; + int64_t master_generation; + uint64_t count; +}; + +} // namespace + +UsageTableEntry::UsageTableEntry(UsageTable* table, uint32_t index, + int64_t generation) + : usage_table_(table), recent_decrypt_(false), forbid_report_(true) { + memset(&data_, 0, sizeof(data_)); + data_.generation_number = generation; + data_.index = index; +} + +UsageTableEntry::~UsageTableEntry() { usage_table_->ReleaseEntry(data_.index); } + +OEMCryptoResult UsageTableEntry::SetPST(const uint8_t* pst, size_t pst_length) { + if (pst_length > kMaxPSTLength) return OEMCrypto_ERROR_BUFFER_TOO_LARGE; + data_.pst_length = pst_length; + if (!pst) return OEMCrypto_ERROR_INVALID_CONTEXT; + memcpy(data_.pst, pst, pst_length); + data_.time_of_license_received = time(NULL); + return OEMCrypto_SUCCESS; +} + +bool UsageTableEntry::VerifyPST(const uint8_t* pst, size_t pst_length) { + if (pst_length > kMaxPSTLength) return false; + if (data_.pst_length != pst_length) return false; + if (!pst) return false; + return 0 == memcmp(pst, data_.pst, pst_length); +} + +bool UsageTableEntry::VerifyMacKeys(const std::vector& server, + const std::vector& client) { + return (server.size() == wvcdm::MAC_KEY_SIZE) && + (client.size() == wvcdm::MAC_KEY_SIZE) && + (0 == memcmp(&server[0], data_.mac_key_server, wvcdm::MAC_KEY_SIZE)) && + (0 == memcmp(&client[0], data_.mac_key_client, wvcdm::MAC_KEY_SIZE)); +} + +bool UsageTableEntry::SetMacKeys(const std::vector& server, + const std::vector& client) { + if ((server.size() != wvcdm::MAC_KEY_SIZE) || + (client.size() != wvcdm::MAC_KEY_SIZE)) + return false; + memcpy(data_.mac_key_server, &server[0], wvcdm::MAC_KEY_SIZE); + memcpy(data_.mac_key_client, &client[0], wvcdm::MAC_KEY_SIZE); + return true; +} + +bool UsageTableEntry::CheckForUse() { + if (Inactive()) return false; + recent_decrypt_ = true; + if (data_.status == kUnused) { + data_.status = kActive; + data_.time_of_first_decrypt = time(NULL); + data_.generation_number++; + usage_table_->IncrementGeneration(); + } + return true; +} + +void UsageTableEntry::Deactivate(const std::vector& pst) { + if (data_.status == kUnused) { + data_.status = kInactiveUnused; + } else if (data_.status == kActive) { + data_.status = kInactiveUsed; + } + forbid_report_ = true; + data_.generation_number++; + usage_table_->IncrementGeneration(); +} + +OEMCryptoResult UsageTableEntry::ReportUsage(const std::vector& pst, + uint8_t* buffer, + size_t* buffer_length) { + if (forbid_report_) return OEMCrypto_ERROR_ENTRY_NEEDS_UPDATE; + if (recent_decrypt_) return OEMCrypto_ERROR_ENTRY_NEEDS_UPDATE; + if (pst.size() == 0 || pst.size() > kMaxPSTLength || + pst.size() != data_.pst_length) { + LOGE("ReportUsage: bad pst length = %d, should be %d.", + pst.size(), data_.pst_length); + return OEMCrypto_ERROR_WRONG_PST; + } + if (memcmp(&pst[0], data_.pst, data_.pst_length)) { + LOGE("ReportUsage: wrong pst %s, should be %s.", + wvcdm::b2a_hex(pst).c_str(), + wvcdm::HexEncode(data_.pst, data_.pst_length).c_str()); + return OEMCrypto_ERROR_WRONG_PST; + } + size_t length_needed = wvcdm::Unpacked_PST_Report::report_size(pst.size()); + if (*buffer_length < length_needed) { + *buffer_length = length_needed; + return OEMCrypto_ERROR_SHORT_BUFFER; + } + if (!buffer) { + LOGE("ReportUsage: buffer was null pointer."); + return OEMCrypto_ERROR_INVALID_CONTEXT; + } + wvcdm::Unpacked_PST_Report pst_report(buffer); + int64_t now = time(NULL); + pst_report.set_seconds_since_license_received(now - + data_.time_of_license_received); + pst_report.set_seconds_since_first_decrypt(now - data_.time_of_first_decrypt); + pst_report.set_seconds_since_last_decrypt(now - data_.time_of_last_decrypt); + pst_report.set_status(data_.status); + pst_report.set_clock_security_level(kSecureTimer); + pst_report.set_pst_length(data_.pst_length); + memcpy(pst_report.pst(), data_.pst, data_.pst_length); + unsigned int md_len = SHA_DIGEST_LENGTH; + if (LogCategoryEnabled(kLoggingDumpDerivedKeys)) { + std::vector mac_key_client( + data_.mac_key_client, + data_.mac_key_client + wvcdm::MAC_KEY_SIZE * sizeof(uint8_t)); + LOGI(("message signed with HMAC and data_.mac_key_client, " + "mac_key_client = " + + wvcdm::b2a_hex(mac_key_client)).c_str()); + } + if (!HMAC(EVP_sha1(), data_.mac_key_client, wvcdm::MAC_KEY_SIZE, + buffer + SHA_DIGEST_LENGTH, length_needed - SHA_DIGEST_LENGTH, + pst_report.signature(), &md_len)) { + LOGE("ReportUsage: could not compute signature."); + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + return OEMCrypto_SUCCESS; +} + +void UsageTableEntry::UpdateAndIncrement() { + if (recent_decrypt_) { + data_.time_of_last_decrypt = time(NULL); + recent_decrypt_ = false; + } + data_.generation_number++; + usage_table_->IncrementGeneration(); + forbid_report_ = false; +} + +OEMCryptoResult UsageTableEntry::SaveData(CryptoEngine* ce, + SessionContext* session, + uint8_t* signed_buffer, + size_t buffer_size) { + // buffer_size was determined by calling function. + if (buffer_size != SignedEntrySize()) return OEMCrypto_ERROR_UNKNOWN_FAILURE; + std::vector clear_buffer(buffer_size); + memset(&clear_buffer[0], 0, buffer_size); + memset(signed_buffer, 0, buffer_size); + SignedEntryBlock* clear = + reinterpret_cast(&clear_buffer[0]); + SignedEntryBlock* encrypted = + reinterpret_cast(signed_buffer); + clear->data = this->data_; // Copy the current data. + memcpy(clear->verification, kEntryVerification, kMagicLength); + + // This should be encrypted and signed with a device specific key. + // For the reference implementation, I'm just going to use the keybox key. + const bool override_to_real = true; + const std::vector& key = ce->DeviceRootKey(override_to_real); + + // Encrypt the entry. + RAND_bytes(encrypted->iv, wvcdm::KEY_IV_SIZE); + uint8_t iv_buffer[wvcdm::KEY_IV_SIZE]; // working iv buffer. + memcpy(iv_buffer, encrypted->iv, wvcdm::KEY_IV_SIZE); + AES_KEY aes_key; + AES_set_encrypt_key(&key[0], 128, &aes_key); + AES_cbc_encrypt( + &clear_buffer[kEncryptionOffset], &signed_buffer[kEncryptionOffset], + buffer_size - kEncryptionOffset, &aes_key, iv_buffer, AES_ENCRYPT); + + // Sign the entry. + unsigned int sig_length = SHA256_DIGEST_LENGTH; + if (!HMAC(EVP_sha256(), &key[0], key.size(), + &signed_buffer[SHA256_DIGEST_LENGTH], + buffer_size - SHA256_DIGEST_LENGTH, encrypted->signature, + &sig_length)) { + LOGE("SaveUsageEntry: Could not sign entry."); + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + return OEMCrypto_SUCCESS; +} + +OEMCryptoResult UsageTableEntry::LoadData(CryptoEngine* ce, uint32_t index, + const std::vector& buffer) { + if (buffer.size() < SignedEntrySize()) return OEMCrypto_ERROR_SHORT_BUFFER; + if (buffer.size() > SignedEntrySize()) + LOGW("LoadUsageTableEntry: buffer is large. %d > %d", buffer.size(), + SignedEntrySize()); + std::vector clear_buffer(buffer.size()); + SignedEntryBlock* clear = + reinterpret_cast(&clear_buffer[0]); + const SignedEntryBlock* encrypted = + reinterpret_cast(&buffer[0]); + + // This should be encrypted and signed with a device specific key. + // For the reference implementation, I'm just going to use the keybox key. + const bool override_to_real = true; + const std::vector& key = ce->DeviceRootKey(override_to_real); + + // Verify the signature of the usage entry. Sign encrypted into clear buffer. + unsigned int sig_length = SHA256_DIGEST_LENGTH; + if (!HMAC(EVP_sha256(), &key[0], key.size(), &buffer[SHA256_DIGEST_LENGTH], + buffer.size() - SHA256_DIGEST_LENGTH, clear->signature, + &sig_length)) { + LOGE("LoadUsageEntry: Could not sign entry."); + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + if (memcmp(clear->signature, encrypted->signature, SHA256_DIGEST_LENGTH)) { + LOGE("LoadUsageEntry: Signature did not match."); + LOGE("LoadUsageEntry: Invalid signature given: %s", + wvcdm::HexEncode(encrypted->signature, sig_length).c_str()); + LOGE("LoadUsageEntry: Invalid signature computed: %s", + wvcdm::HexEncode(clear->signature, sig_length).c_str()); + return OEMCrypto_ERROR_SIGNATURE_FAILURE; + } + + // Next, decrypt the entry. + uint8_t iv_buffer[wvcdm::KEY_IV_SIZE]; + memcpy(iv_buffer, encrypted->iv, wvcdm::KEY_IV_SIZE); + AES_KEY aes_key; + AES_set_decrypt_key(&key[0], 128, &aes_key); + AES_cbc_encrypt(&buffer[kEncryptionOffset], &clear_buffer[kEncryptionOffset], + buffer.size() - kEncryptionOffset, &aes_key, iv_buffer, + AES_DECRYPT); + + // Check the verification string is correct. + if (memcmp(kEntryVerification, clear->verification, kMagicLength)) { + LOGE("LoadUsageEntry: Invalid magic: %s=%8.8s expected: %s=%8.8s", + wvcdm::HexEncode(clear->verification, kMagicLength).c_str(), + clear->verification, + wvcdm::HexEncode(reinterpret_cast(kEntryVerification), + kMagicLength).c_str(), + reinterpret_cast(kEntryVerification)); + return OEMCrypto_ERROR_BAD_MAGIC; + } + + // Check that the index is correct. + if (index != clear->data.index) { + LOGE("LoadUsageEntry: entry says index is %d, not %d", clear->data.index, + index); + return OEMCrypto_ERROR_INVALID_SESSION; + } + if (clear->data.status > kInactiveUnused) { + LOGE("LoadUsageEntry: entry has bad status %d", clear->data.status); + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + this->data_ = clear->data; + return OEMCrypto_SUCCESS; +} + +OEMCryptoResult UsageTableEntry::CopyOldUsageEntry( + const std::vector& pst) { + OldUsageTableEntry* old_entry = usage_table_->FindOldUsageEntry(pst); + if (!old_entry) return OEMCrypto_ERROR_WRONG_PST; + data_.time_of_license_received = old_entry->time_of_license_received_; + data_.time_of_first_decrypt = old_entry->time_of_first_decrypt_; + data_.time_of_last_decrypt = old_entry->time_of_last_decrypt_; + data_.status = old_entry->status_; + if (old_entry->mac_key_server_.size() != wvcdm::MAC_KEY_SIZE) { + LOGE("CopyOldEntry: Old entry has bad server mac key."); + } else { + memcpy(data_.mac_key_server, &(old_entry->mac_key_server_[0]), + wvcdm::MAC_KEY_SIZE); + } + if (old_entry->mac_key_client_.size() != wvcdm::MAC_KEY_SIZE) { + LOGE("CopyOldEntry: Old entry has bad client mac key."); + } else { + memcpy(data_.mac_key_client, &(old_entry->mac_key_client_[0]), + wvcdm::MAC_KEY_SIZE); + if (LogCategoryEnabled(kLoggingDumpDerivedKeys)) { + std::vector mac_key_client( + data_.mac_key_client, + data_.mac_key_client + wvcdm::MAC_KEY_SIZE * sizeof(uint8_t)); + LOGI(("data_.mac_key_client has changed to = " + + wvcdm::b2a_hex(mac_key_client)).c_str()); + } + } + if (pst.size() > kMaxPSTLength) { + LOGE("CopyOldEntry: PST Length was too large. Truncating."); + data_.pst_length = kMaxPSTLength; + } else { + data_.pst_length = pst.size(); + } + memcpy(data_.pst, &pst[0], data_.pst_length); + data_.pst[data_.pst_length] = '\0'; + return OEMCrypto_SUCCESS; +} + + +size_t UsageTableEntry::SignedEntrySize() { + size_t base = sizeof(SignedEntryBlock); + // round up to make even number of blocks: + size_t blocks = (base - 1) / wvcdm::KEY_IV_SIZE + 1; + return blocks * wvcdm::KEY_IV_SIZE; +} + +UsageTable::~UsageTable() { + if (old_table_) { + delete old_table_; + old_table_ = NULL; + } +} + +size_t UsageTable::SignedHeaderSize(size_t count) { + size_t base = sizeof(SignedHeaderBlock) + count * sizeof(int64_t); + // round up to make even number of blocks: + size_t blocks = (base - 1) / wvcdm::KEY_IV_SIZE + 1; + return blocks * wvcdm::KEY_IV_SIZE; +} + +OEMCryptoResult UsageTable::UpdateUsageEntry(SessionContext* session, + UsageTableEntry* entry, + uint8_t* header_buffer, + size_t* header_buffer_length, + uint8_t* entry_buffer, + size_t* entry_buffer_length) { + size_t signed_header_size = SignedHeaderSize(generation_numbers_.size()); + if (*entry_buffer_length < UsageTableEntry::SignedEntrySize() || + *header_buffer_length < signed_header_size) { + *entry_buffer_length = UsageTableEntry::SignedEntrySize(); + *header_buffer_length = signed_header_size; + return OEMCrypto_ERROR_SHORT_BUFFER; + } + *entry_buffer_length = UsageTableEntry::SignedEntrySize(); + *header_buffer_length = signed_header_size; + if ((!header_buffer) || (!entry_buffer)) + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + entry->UpdateAndIncrement(); + generation_numbers_[entry->index()] = entry->generation_number(); + OEMCryptoResult result = + entry->SaveData(ce_, session, entry_buffer, *entry_buffer_length); + if (result != OEMCrypto_SUCCESS) return result; + result = SaveUsageTableHeader(header_buffer, *header_buffer_length); + return result; +} + +OEMCryptoResult UsageTable::CreateNewUsageEntry(SessionContext* session, + UsageTableEntry** entry, + uint32_t* usage_entry_number) { + if (!header_loaded_) { + LOGE("CreateNewUsageEntry: Header not loaded."); + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + if (!entry) return OEMCrypto_ERROR_UNKNOWN_FAILURE; + if (!usage_entry_number) return OEMCrypto_ERROR_UNKNOWN_FAILURE; + uint32_t index = generation_numbers_.size(); + UsageTableEntry* new_entry = + new UsageTableEntry(this, index, master_generation_number_); + generation_numbers_.push_back(master_generation_number_); + sessions_.push_back(session); + master_generation_number_++; + *entry = new_entry; + *usage_entry_number = index; + return OEMCrypto_SUCCESS; +} + +OEMCryptoResult UsageTable::LoadUsageEntry(SessionContext* session, + UsageTableEntry** entry, + uint32_t index, + const std::vector& buffer) { + if (!header_loaded_) { + LOGE("CreateNewUsageEntry: Header not loaded."); + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + if (!entry) return OEMCrypto_ERROR_UNKNOWN_FAILURE; + if (index >= generation_numbers_.size()) + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + if (sessions_[index]) { + LOGE("LoadUsageEntry: index %d used by other session.", index); + return OEMCrypto_ERROR_INVALID_SESSION; + } + UsageTableEntry* new_entry = + new UsageTableEntry(this, index, master_generation_number_); + + OEMCryptoResult status = new_entry->LoadData(ce_, index, buffer); + if (status != OEMCrypto_SUCCESS) { + delete new_entry; + return status; + } + if (new_entry->generation_number() != generation_numbers_[index]) { + LOGE("Generation SKEW: %ld -> %ld", new_entry->generation_number(), + generation_numbers_[index]); + if ((new_entry->generation_number() + 1 < generation_numbers_[index]) || + (new_entry->generation_number() - 1 > generation_numbers_[index])) { + delete new_entry; + return OEMCrypto_ERROR_GENERATION_SKEW; + } + status = OEMCrypto_WARNING_GENERATION_SKEW; + } + sessions_[index] = session; + *entry = new_entry; + return status; +} + +OEMCryptoResult UsageTable::ShrinkUsageTableHeader( + uint32_t new_table_size, uint8_t* header_buffer, + size_t* header_buffer_length) { + if (new_table_size > generation_numbers_.size()) { + LOGE("OEMCrypto_ShrinkUsageTableHeader: %d > %zd.", new_table_size, + generation_numbers_.size()); + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + size_t signed_header_size = SignedHeaderSize(new_table_size); + if (*header_buffer_length < signed_header_size) { + *header_buffer_length = signed_header_size; + return OEMCrypto_ERROR_SHORT_BUFFER; + } + *header_buffer_length = signed_header_size; + if (!header_buffer) { + LOGE("OEMCrypto_ShrinkUsageTableHeader: buffer null."); + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + for (size_t i = new_table_size; i < sessions_.size(); ++i) { + if (sessions_[i]) { + LOGE("ShrinkUsageTableHeader: session open for %d", i); + return OEMCrypto_ERROR_ENTRY_IN_USE; + } + } + generation_numbers_.resize(new_table_size); + sessions_.resize(new_table_size); + master_generation_number_++; + return SaveUsageTableHeader(header_buffer, *header_buffer_length); +} + +OEMCryptoResult UsageTable::SaveUsageTableHeader(uint8_t* signed_buffer, + size_t buffer_size) { + if (!SaveGenerationNumber()) return OEMCrypto_ERROR_UNKNOWN_FAILURE; + size_t count = generation_numbers_.size(); + // buffer_size was determined by calling function. + if (buffer_size != SignedHeaderSize(count)) + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + std::vector clear_buffer(buffer_size); + memset(&clear_buffer[0], 0, buffer_size); + memset(signed_buffer, 0, buffer_size); + SignedHeaderBlock* clear = + reinterpret_cast(&clear_buffer[0]); + SignedHeaderBlock* encrypted = + reinterpret_cast(signed_buffer); + + // Pack the clear data into the clear buffer. + memcpy(clear->verification, kHeaderVerification, kMagicLength); + clear->master_generation = master_generation_number_; + clear->count = count; + // This points to the variable size part of the buffer. + int64_t* stored_generations = + reinterpret_cast(&clear_buffer[sizeof(SignedHeaderBlock)]); + std::copy(generation_numbers_.begin(), generation_numbers_.begin() + count, + stored_generations); + + // This should be encrypted and signed with a device specific key. + // For the reference implementation, I'm just going to use the keybox key. + const bool override_to_real = true; + const std::vector& key = ce_->DeviceRootKey(override_to_real); + + // Encrypt the entry. + RAND_bytes(encrypted->iv, wvcdm::KEY_IV_SIZE); + uint8_t iv_buffer[wvcdm::KEY_IV_SIZE]; // working iv buffer. + memcpy(iv_buffer, encrypted->iv, wvcdm::KEY_IV_SIZE); + AES_KEY aes_key; + AES_set_encrypt_key(&key[0], 128, &aes_key); + AES_cbc_encrypt( + &clear_buffer[kEncryptionOffset], &signed_buffer[kEncryptionOffset], + buffer_size - kEncryptionOffset, &aes_key, iv_buffer, AES_ENCRYPT); + + // Sign the entry. + unsigned int sig_length = SHA256_DIGEST_LENGTH; + if (!HMAC(EVP_sha256(), &key[0], key.size(), + &signed_buffer[SHA256_DIGEST_LENGTH], + buffer_size - SHA256_DIGEST_LENGTH, encrypted->signature, + &sig_length)) { + LOGE("SaveUsageHeader: Could not sign entry."); + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + return OEMCrypto_SUCCESS; +} + +OEMCryptoResult UsageTable::LoadUsageTableHeader( + const std::vector& buffer) { + if (!LoadGenerationNumber(false)) return OEMCrypto_ERROR_UNKNOWN_FAILURE; + + if (buffer.size() < SignedHeaderSize(0)) return OEMCrypto_ERROR_SHORT_BUFFER; + std::vector clear_buffer(buffer.size()); + SignedHeaderBlock* clear = + reinterpret_cast(&clear_buffer[0]); + const SignedHeaderBlock* encrypted = + reinterpret_cast(&buffer[0]); + + // This should be encrypted and signed with a device specific key. + // For the reference implementation, I'm just going to use the keybox key. + const bool override_to_real = true; + const std::vector& key = ce_->DeviceRootKey(override_to_real); + + // Verify the signature of the usage entry. Sign encrypted into clear buffer. + unsigned int sig_length = SHA256_DIGEST_LENGTH; + if (!HMAC(EVP_sha256(), &key[0], key.size(), &buffer[SHA256_DIGEST_LENGTH], + buffer.size() - SHA256_DIGEST_LENGTH, clear->signature, + &sig_length)) { + LOGE("LoadUsageTableHeader: Could not sign entry."); + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + if (memcmp(clear->signature, encrypted->signature, SHA256_DIGEST_LENGTH)) { + LOGE("LoadUsageTableHeader: Signature did not match."); + LOGE("LoadUsageTableHeader: Invalid signature given: %s", + wvcdm::HexEncode(encrypted->signature, sig_length).c_str()); + LOGE("LoadUsageTableHeader: Invalid signature computed: %s", + wvcdm::HexEncode(clear->signature, sig_length).c_str()); + return OEMCrypto_ERROR_SIGNATURE_FAILURE; + } + + // Next, decrypt the entry. + uint8_t iv_buffer[wvcdm::KEY_IV_SIZE]; + memcpy(iv_buffer, encrypted->iv, wvcdm::KEY_IV_SIZE); + AES_KEY aes_key; + AES_set_decrypt_key(&key[0], 128, &aes_key); + AES_cbc_encrypt(&buffer[kEncryptionOffset], &clear_buffer[kEncryptionOffset], + buffer.size() - kEncryptionOffset, &aes_key, iv_buffer, + AES_DECRYPT); + + // Check the verification string is correct. + if (memcmp(kHeaderVerification, clear->verification, kMagicLength)) { + LOGE("LoadUsageTableHeader: Invalid magic: %s=%8.8s expected: %s=%8.8s", + wvcdm::HexEncode(clear->verification, kMagicLength).c_str(), + clear->verification, + wvcdm::HexEncode(reinterpret_cast(kHeaderVerification), + kMagicLength).c_str(), + reinterpret_cast(kHeaderVerification)); + return OEMCrypto_ERROR_BAD_MAGIC; + } + + // Check that size is correct, now that we know what it should be. + if (buffer.size() < SignedHeaderSize(clear->count)) { + return OEMCrypto_ERROR_SHORT_BUFFER; + } + if (buffer.size() > SignedHeaderSize(clear->count)) { + LOGW("LoadUsageTableHeader: buffer is large. %d > %d", buffer.size(), + SignedHeaderSize(clear->count)); + } + + OEMCryptoResult status = OEMCrypto_SUCCESS; + if (clear->master_generation != master_generation_number_) { + LOGE("Generation SKEW: %ld -> %ld", clear->master_generation, + master_generation_number_); + if ((clear->master_generation + 1 < master_generation_number_) || + (clear->master_generation - 1 > master_generation_number_)) { + return OEMCrypto_ERROR_GENERATION_SKEW; + } + status = OEMCrypto_WARNING_GENERATION_SKEW; + } + int64_t* stored_generations = + reinterpret_cast(&clear_buffer[0] + sizeof(SignedHeaderBlock)); + generation_numbers_.assign(stored_generations, + stored_generations + clear->count); + sessions_.clear(); + sessions_.resize(clear->count); + header_loaded_ = true; + return status; +} + +OEMCryptoResult UsageTable::MoveEntry(UsageTableEntry* entry, + uint32_t new_index) { + if (new_index >= generation_numbers_.size()) { + LOGE("MoveEntry: index beyond end of usage table %d >= %d", new_index, + generation_numbers_.size()); + return OEMCrypto_ERROR_INVALID_CONTEXT; + } + if (sessions_[new_index]) { + LOGE("MoveEntry: session open for %d", new_index); + return OEMCrypto_ERROR_ENTRY_IN_USE; + } + if (!entry) { + LOGE("MoveEntry: null entry"); + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + sessions_[new_index] = sessions_[entry->index()]; + sessions_[entry->index()] = 0; + + entry->set_index(new_index); + generation_numbers_[new_index] = master_generation_number_; + entry->set_generation_number(master_generation_number_); + master_generation_number_++; + return OEMCrypto_SUCCESS; +} + +void UsageTable::IncrementGeneration() { + master_generation_number_++; + SaveGenerationNumber(); +} + +bool UsageTable::SaveGenerationNumber() { + wvcdm::FileSystem* file_system = ce_->file_system(); + std::string path; + // Note: this path is OK for a real implementation, but using security level 1 + // would be better. + if (!wvcdm::Properties::GetDeviceFilesBasePath(wvcdm::kSecurityLevelL3, + &path)) { + LOGE("UsageTable: Unable to get base path"); + return false; + } + // On a real implementation, you should NOT put the generation number in + // a file in user space. It should be stored in secure memory. + std::string filename = path + "GenerationNumber.dat"; + wvcdm::File* file = file_system->Open( + filename, wvcdm::FileSystem::kCreate | wvcdm::FileSystem::kTruncate); + if (!file) { + LOGE("UsageTable: File open failed: %s", path.c_str()); + return false; + } + file->Write(reinterpret_cast(&master_generation_number_), + sizeof(int64_t)); + file->Close(); + return true; +} + +bool UsageTable::LoadGenerationNumber(bool or_make_new_one) { + wvcdm::FileSystem* file_system = ce_->file_system(); + std::string path; + // Note: this path is OK for a real implementation, but using security level 1 + // would be better. + if (!wvcdm::Properties::GetDeviceFilesBasePath(wvcdm::kSecurityLevelL3, + &path)) { + LOGE("UsageTable: Unable to get base path"); + return false; + } + // On a real implementation, you should NOT put the generation number in + // a file in user space. It should be stored in secure memory. + std::string filename = path + "GenerationNumber.dat"; + wvcdm::File* file = file_system->Open(filename, + wvcdm::FileSystem::kReadOnly); + if (!file) { + if (or_make_new_one) { + RAND_bytes(reinterpret_cast(&master_generation_number_), + sizeof(int64_t)); + return true; + } + LOGE("UsageTable: File open failed: %s (clearing table)", path.c_str()); + master_generation_number_ = 0; + return false; + } + file->Read(reinterpret_cast(&master_generation_number_), + sizeof(int64_t)); + file->Close(); + return true; +} + +OEMCryptoResult UsageTable::CreateUsageTableHeader( + uint8_t* header_buffer, size_t* header_buffer_length) { + size_t signed_header_size = SignedHeaderSize(0); + if (*header_buffer_length < signed_header_size) { + *header_buffer_length = signed_header_size; + return OEMCrypto_ERROR_SHORT_BUFFER; + } + *header_buffer_length = signed_header_size; + if (!LoadGenerationNumber(true)) return OEMCrypto_ERROR_UNKNOWN_FAILURE; + // Make sure there are no entries that are currently tied to an open session. + for (size_t i=0; i < sessions_.size(); ++i) { + if (sessions_[i] != NULL) { + LOGE("CreateUsageTableHeader: index %d used by session.", i); + return OEMCrypto_ERROR_INVALID_SESSION; + } + } + sessions_.clear(); + generation_numbers_.clear(); + header_loaded_ = true; + return SaveUsageTableHeader(header_buffer, *header_buffer_length); +} + +OldUsageTableEntry* UsageTable::FindOldUsageEntry( + const std::vector& pst) { + if (!old_table_) old_table_ = new OldUsageTable(ce_); + return old_table_->FindEntry(pst); +} + +OEMCryptoResult UsageTable::DeleteOldUsageTable() { + if (old_table_) { + old_table_->Clear(); + delete old_table_; + old_table_ = NULL; + } + OldUsageTable::DeleteFile(ce_); + return OEMCrypto_SUCCESS; +} + +OEMCryptoResult UsageTable::CreateOldUsageEntry( + uint64_t time_since_license_received, uint64_t time_since_first_decrypt, + uint64_t time_since_last_decrypt, OEMCrypto_Usage_Entry_Status status, + uint8_t* server_mac_key, uint8_t* client_mac_key, const uint8_t* pst, + size_t pst_length) { + if (!old_table_) old_table_ = new OldUsageTable(ce_); + std::vector pstv(pst, pst+pst_length); + OldUsageTableEntry *old_entry = old_table_->CreateEntry(pstv); + + int64_t now = time(NULL); + old_entry->time_of_license_received_ = now - time_since_license_received; + old_entry->time_of_first_decrypt_ = now - time_since_first_decrypt; + old_entry->time_of_last_decrypt_ = now - time_since_last_decrypt; + old_entry->status_ = status; + old_entry->mac_key_server_.assign(server_mac_key, + server_mac_key + wvcdm::MAC_KEY_SIZE); + old_entry->mac_key_client_.assign(client_mac_key, + client_mac_key + wvcdm::MAC_KEY_SIZE); + return OEMCrypto_SUCCESS; +} + +} // namespace wvoec_mock diff --git a/mock/src/oemcrypto_usage_table_mock.h b/mock/src/oemcrypto_usage_table_mock.h new file mode 100644 index 0000000..4ec70be --- /dev/null +++ b/mock/src/oemcrypto_usage_table_mock.h @@ -0,0 +1,137 @@ +// Copyright 2013 Google Inc. All Rights Reserved. +// +// Mock implementation of OEMCrypto APIs +// +#ifndef OEMCRYPTO_USAGE_TABLE_MOCK_H_ +#define OEMCRYPTO_USAGE_TABLE_MOCK_H_ + +#include +#include +#include +#include + +#include "OEMCryptoCENC.h" +#include "file_store.h" +#include "lock.h" +#include "openssl/sha.h" +#include "wv_cdm_constants.h" + +namespace wvoec_mock { + +class SessionContext; +class CryptoEngine; +class UsageTable; +class OldUsageTable; +class OldUsageTableEntry; + +const size_t kMaxPSTLength = 255; +// This is the data we store offline. +struct StoredUsageEntry { + int64_t generation_number; + int64_t time_of_license_received; + int64_t time_of_first_decrypt; + int64_t time_of_last_decrypt; + enum OEMCrypto_Usage_Entry_Status status; + uint8_t mac_key_server[wvcdm::MAC_KEY_SIZE]; + uint8_t mac_key_client[wvcdm::MAC_KEY_SIZE]; + uint32_t index; + uint8_t pst[kMaxPSTLength+1]; // add 1 for padding. + uint8_t pst_length; +}; + +class UsageTableEntry { + public: + UsageTableEntry(UsageTable* table, uint32_t index, int64_t generation); + // owner_(owner), session_(session), loaded_(false) {} + ~UsageTableEntry(); // Free memory, remove reference in header. + bool Inactive() { return data_.status >= kInactive; } + OEMCryptoResult SetPST(const uint8_t* pst, size_t pst_length); + bool VerifyPST(const uint8_t* pst, size_t pst_length); + bool VerifyMacKeys(const std::vector& server, + const std::vector& client); + bool SetMacKeys(const std::vector& server, + const std::vector& client); + // Returns false if the entry is inactive. Otherwise, returns true. + // If the status was unused, it is updated, and decrypt times are flaged + // for update. + bool CheckForUse(); + void Deactivate(const std::vector& pst); + OEMCryptoResult ReportUsage(const std::vector& pst, uint8_t* buffer, + size_t* buffer_length); + void UpdateAndIncrement(); + OEMCryptoResult SaveData(CryptoEngine* ce, SessionContext* session, + uint8_t* signed_buffer, size_t buffer_size); + OEMCryptoResult LoadData(CryptoEngine* ce, uint32_t index, + const std::vector& buffer); + OEMCryptoResult CopyOldUsageEntry(const std::vector& pst); + int64_t generation_number() { return data_.generation_number; } + void set_generation_number(int64_t value) { data_.generation_number = value; } + void set_index(int32_t index) { data_.index = index; } + uint32_t index() { return data_.index; } + static size_t SignedEntrySize(); + const uint8_t* mac_key_server() { return data_.mac_key_server; } + const uint8_t* mac_key_client() { return data_.mac_key_client; } + + private: + UsageTable* usage_table_; // Owner of this object. + bool recent_decrypt_; + bool forbid_report_; + StoredUsageEntry data_; +}; + +class UsageTable { + public: + explicit UsageTable(CryptoEngine* ce) + : ce_(ce), header_loaded_(false), old_table_(NULL){}; + ~UsageTable(); + + OEMCryptoResult CreateNewUsageEntry(SessionContext* session, + UsageTableEntry** entry, + uint32_t* usage_entry_number); + OEMCryptoResult LoadUsageEntry(SessionContext* session, + UsageTableEntry** entry, uint32_t index, + const std::vector& buffer); + OEMCryptoResult UpdateUsageEntry(SessionContext* session, + UsageTableEntry* entry, + uint8_t* header_buffer, + size_t* header_buffer_length, + uint8_t* entry_buffer, + size_t* entry_buffer_length); + OEMCryptoResult MoveEntry(UsageTableEntry* entry, uint32_t new_index); + OEMCryptoResult CreateUsageTableHeader(uint8_t* header_buffer, + size_t* header_buffer_length); + OEMCryptoResult LoadUsageTableHeader(const std::vector& buffer); + OEMCryptoResult ShrinkUsageTableHeader(uint32_t new_table_size, + uint8_t* header_buffer, + size_t* header_buffer_length); + void ReleaseEntry(uint32_t index) { sessions_[index] = 0; } + void IncrementGeneration(); + static size_t SignedHeaderSize(size_t count); + OldUsageTableEntry* FindOldUsageEntry(const std::vector& pst); + OEMCryptoResult DeleteOldUsageTable(); + OEMCryptoResult CreateOldUsageEntry(uint64_t time_since_license_received, + uint64_t time_since_first_decrypt, + uint64_t time_since_last_decrypt, + OEMCrypto_Usage_Entry_Status status, + uint8_t *server_mac_key, + uint8_t *client_mac_key, + const uint8_t* pst, + size_t pst_length); + + private: + OEMCryptoResult SaveUsageTableHeader(uint8_t* signed_buffer, + size_t buffer_size); + bool SaveGenerationNumber(); + bool LoadGenerationNumber(bool or_make_new_one); + + CryptoEngine* ce_; + bool header_loaded_; + int64_t master_generation_number_; + std::vector generation_numbers_; + std::vector sessions_; + OldUsageTable *old_table_; +}; + +} // namespace wvoec_mock + +#endif // OEMCRYPTO_USAGE_TABLE_MOCK_H_ diff --git a/mock/src/wv_keybox.h b/mock/src/wv_keybox.h new file mode 100644 index 0000000..6fdd0fa --- /dev/null +++ b/mock/src/wv_keybox.h @@ -0,0 +1,26 @@ +// Copyright 2013 Google Inc. All Rights Reserved. + +#ifndef WV_KEYBOX_H_ +#define WV_KEYBOX_H_ + +#include + +namespace wvoec_mock { + +// This is the format of a Widevine keybox. +typedef struct { // 128 bytes total. + // C character string identifying the device. Null terminated. + uint8_t device_id_[32]; + // 128 bit AES key assigned to device. Generated by Widevine. + uint8_t device_key_[16]; + // Key Data. Encrypted data. + uint8_t data_[72]; + // Constant code used to recognize a valid keybox "kbox" = 0x6b626f78. + uint8_t magic_[4]; + // The CRC checksum of the first 124 bytes of the keybox. + uint8_t crc_[4]; +} WidevineKeybox; + +} // namespace wvoec_mock + +#endif // WV_KEYBOX_H_ diff --git a/mock/src/wvcrc.cpp b/mock/src/wvcrc.cpp new file mode 100644 index 0000000..8c5025c --- /dev/null +++ b/mock/src/wvcrc.cpp @@ -0,0 +1,94 @@ +// Copyright 2012 Google Inc. All Rights Reserved. +// +// Compute CRC32 Checksum. Needed for verification of WV Keybox. +// +#include +#include "wvcrc32.h" + +#define INIT_CRC32 0xffffffff + +uint32_t wvrunningcrc32(const uint8_t* p_begin, int i_count, uint32_t i_crc) { + static uint32_t CRC32[256] = { + 0x00000000, 0x04c11db7, 0x09823b6e, 0x0d4326d9, + 0x130476dc, 0x17c56b6b, 0x1a864db2, 0x1e475005, + 0x2608edb8, 0x22c9f00f, 0x2f8ad6d6, 0x2b4bcb61, + 0x350c9b64, 0x31cd86d3, 0x3c8ea00a, 0x384fbdbd, + 0x4c11db70, 0x48d0c6c7, 0x4593e01e, 0x4152fda9, + 0x5f15adac, 0x5bd4b01b, 0x569796c2, 0x52568b75, + 0x6a1936c8, 0x6ed82b7f, 0x639b0da6, 0x675a1011, + 0x791d4014, 0x7ddc5da3, 0x709f7b7a, 0x745e66cd, + 0x9823b6e0, 0x9ce2ab57, 0x91a18d8e, 0x95609039, + 0x8b27c03c, 0x8fe6dd8b, 0x82a5fb52, 0x8664e6e5, + 0xbe2b5b58, 0xbaea46ef, 0xb7a96036, 0xb3687d81, + 0xad2f2d84, 0xa9ee3033, 0xa4ad16ea, 0xa06c0b5d, + 0xd4326d90, 0xd0f37027, 0xddb056fe, 0xd9714b49, + 0xc7361b4c, 0xc3f706fb, 0xceb42022, 0xca753d95, + 0xf23a8028, 0xf6fb9d9f, 0xfbb8bb46, 0xff79a6f1, + 0xe13ef6f4, 0xe5ffeb43, 0xe8bccd9a, 0xec7dd02d, + 0x34867077, 0x30476dc0, 0x3d044b19, 0x39c556ae, + 0x278206ab, 0x23431b1c, 0x2e003dc5, 0x2ac12072, + 0x128e9dcf, 0x164f8078, 0x1b0ca6a1, 0x1fcdbb16, + 0x018aeb13, 0x054bf6a4, 0x0808d07d, 0x0cc9cdca, + 0x7897ab07, 0x7c56b6b0, 0x71159069, 0x75d48dde, + 0x6b93dddb, 0x6f52c06c, 0x6211e6b5, 0x66d0fb02, + 0x5e9f46bf, 0x5a5e5b08, 0x571d7dd1, 0x53dc6066, + 0x4d9b3063, 0x495a2dd4, 0x44190b0d, 0x40d816ba, + 0xaca5c697, 0xa864db20, 0xa527fdf9, 0xa1e6e04e, + 0xbfa1b04b, 0xbb60adfc, 0xb6238b25, 0xb2e29692, + 0x8aad2b2f, 0x8e6c3698, 0x832f1041, 0x87ee0df6, + 0x99a95df3, 0x9d684044, 0x902b669d, 0x94ea7b2a, + 0xe0b41de7, 0xe4750050, 0xe9362689, 0xedf73b3e, + 0xf3b06b3b, 0xf771768c, 0xfa325055, 0xfef34de2, + 0xc6bcf05f, 0xc27dede8, 0xcf3ecb31, 0xcbffd686, + 0xd5b88683, 0xd1799b34, 0xdc3abded, 0xd8fba05a, + 0x690ce0ee, 0x6dcdfd59, 0x608edb80, 0x644fc637, + 0x7a089632, 0x7ec98b85, 0x738aad5c, 0x774bb0eb, + 0x4f040d56, 0x4bc510e1, 0x46863638, 0x42472b8f, + 0x5c007b8a, 0x58c1663d, 0x558240e4, 0x51435d53, + 0x251d3b9e, 0x21dc2629, 0x2c9f00f0, 0x285e1d47, + 0x36194d42, 0x32d850f5, 0x3f9b762c, 0x3b5a6b9b, + 0x0315d626, 0x07d4cb91, 0x0a97ed48, 0x0e56f0ff, + 0x1011a0fa, 0x14d0bd4d, 0x19939b94, 0x1d528623, + 0xf12f560e, 0xf5ee4bb9, 0xf8ad6d60, 0xfc6c70d7, + 0xe22b20d2, 0xe6ea3d65, 0xeba91bbc, 0xef68060b, + 0xd727bbb6, 0xd3e6a601, 0xdea580d8, 0xda649d6f, + 0xc423cd6a, 0xc0e2d0dd, 0xcda1f604, 0xc960ebb3, + 0xbd3e8d7e, 0xb9ff90c9, 0xb4bcb610, 0xb07daba7, + 0xae3afba2, 0xaafbe615, 0xa7b8c0cc, 0xa379dd7b, + 0x9b3660c6, 0x9ff77d71, 0x92b45ba8, 0x9675461f, + 0x8832161a, 0x8cf30bad, 0x81b02d74, 0x857130c3, + 0x5d8a9099, 0x594b8d2e, 0x5408abf7, 0x50c9b640, + 0x4e8ee645, 0x4a4ffbf2, 0x470cdd2b, 0x43cdc09c, + 0x7b827d21, 0x7f436096, 0x7200464f, 0x76c15bf8, + 0x68860bfd, 0x6c47164a, 0x61043093, 0x65c52d24, + 0x119b4be9, 0x155a565e, 0x18197087, 0x1cd86d30, + 0x029f3d35, 0x065e2082, 0x0b1d065b, 0x0fdc1bec, + 0x3793a651, 0x3352bbe6, 0x3e119d3f, 0x3ad08088, + 0x2497d08d, 0x2056cd3a, 0x2d15ebe3, 0x29d4f654, + 0xc5a92679, 0xc1683bce, 0xcc2b1d17, 0xc8ea00a0, + 0xd6ad50a5, 0xd26c4d12, 0xdf2f6bcb, 0xdbee767c, + 0xe3a1cbc1, 0xe760d676, 0xea23f0af, 0xeee2ed18, + 0xf0a5bd1d, 0xf464a0aa, 0xf9278673, 0xfde69bc4, + 0x89b8fd09, 0x8d79e0be, 0x803ac667, 0x84fbdbd0, + 0x9abc8bd5, 0x9e7d9662, 0x933eb0bb, 0x97ffad0c, + 0xafb010b1, 0xab710d06, 0xa6322bdf, 0xa2f33668, + 0xbcb4666d, 0xb8757bda, 0xb5365d03, 0xb1f740b4 + }; + + /* Calculate the CRC */ + while (i_count > 0) { + i_crc = (i_crc << 8) ^ CRC32[(i_crc >> 24) ^ ((uint32_t) * p_begin)]; + p_begin++; + i_count--; + } + + return(i_crc); +} + +uint32_t wvcrc32(const uint8_t* p_begin, int i_count) { + return(wvrunningcrc32(p_begin, i_count, INIT_CRC32)); +} + +uint32_t wvcrc32n(const uint8_t* p_begin, int i_count) { + return htonl(wvrunningcrc32(p_begin, i_count, INIT_CRC32)); +} diff --git a/mock/src/wvcrc32.h b/mock/src/wvcrc32.h new file mode 100644 index 0000000..db18039 --- /dev/null +++ b/mock/src/wvcrc32.h @@ -0,0 +1,15 @@ +// Copyright 2013 Google Inc. All Rights Reserved. +// +// Compute CRC32 Checksum. Needed for verification of WV Keybox. +// +#ifndef WVCRC32_H_ +#define WVCRC32_H_ + +#include + +uint32_t wvcrc32(const uint8_t* p_begin, int i_count); + +// Convert to network byte order +uint32_t wvcrc32n(const uint8_t* p_begin, int i_count); + +#endif // WVCRC32_H_ diff --git a/mock/test/oemcrypto_logging_test.cpp b/mock/test/oemcrypto_logging_test.cpp new file mode 100644 index 0000000..8b5da34 --- /dev/null +++ b/mock/test/oemcrypto_logging_test.cpp @@ -0,0 +1,119 @@ +// Copyright 2014 Google Inc. All Rights Reserved. + +#include "OEMCryptoCENC.h" + +#include +#include +#include "log.h" +#include "oemcrypto_logging.h" +#include "oemcrypto_mock.cpp" + +class OEMCryptoLoggingTest : public ::testing::Test { + protected: + OEMCryptoLoggingTest() {} + + void SetUp() { + ::testing::Test::SetUp(); + ASSERT_EQ(OEMCrypto_SUCCESS, OEMCrypto_Initialize()); + } + + void TearDown() { + OEMCrypto_Terminate(); + ::testing::Test::TearDown(); + } +}; + +TEST_F(OEMCryptoLoggingTest, TestDumpHexFunctions) { + uint8_t vector[] = {0xFA, 0x11, 0x28, 0x33}; + std::string buffer; + wvoec_mock::dump_hex_helper(buffer, "name", vector, 4u); + ASSERT_EQ("name = \n wvcdm::a2b_hex(\"FA112833\");\n", buffer); + + uint8_t vector2[] = { + 0xFA, 0x11, 0x28, 0x33, 0xFA, 0x11, 0x28, 0x33, 0xFA, 0x11, + 0x28, 0x33, 0xFA, 0x11, 0x28, 0x33, 0xFA, 0x11, 0x28, 0x33, + 0xFA, 0x11, 0x28, 0x33, 0x01, 0x14, 0x28, 0xAB, 0xFA, 0xCD, + 0xEF, 0x67, 0x01, 0x14, 0x28, 0xAB, 0xFA, 0xCD, 0xEF, 0x67, + }; + + buffer.clear(); // dump_hex_helper appends to buffer + wvoec_mock::dump_hex_helper(buffer, "name", vector2, 40u); + ASSERT_EQ( + "name = \n " + "wvcdm::a2b_hex(" + "\"FA112833FA112833FA112833FA112833FA112833FA112833011428ABFACDEF67\"\n " + " \"011428ABFACDEF67\");\n", + buffer); + + buffer.clear(); // dump_hex_helper appends to buffer + wvoec_mock::dump_array_part_helper(buffer, "array", 5u, "name", vector2, 40u); + ASSERT_EQ( + "std::string s5_name = \n " + "wvcdm::a2b_hex(" + "\"FA112833FA112833FA112833FA112833FA112833FA112833011428ABFACDEF67\"\n " + " \"011428ABFACDEF67\");\narray[5].name = message_ptr + " + "message.find(s5_name.data());\n", + buffer); + + buffer.clear(); // dump_hex_helper appends to buffer + wvoec_mock::dump_array_part_helper(buffer, "array", 5u, "name", NULL, 40u); + ASSERT_EQ("array[5].name = NULL;\n", buffer); +} + +TEST_F(OEMCryptoLoggingTest, TestChangeLoggingLevel) { + wvoec_mock::SetLoggingLevel(wvcdm::LOG_WARN); + ASSERT_EQ(wvcdm::LOG_WARN, wvcdm::g_cutoff); + + wvoec_mock::SetLoggingLevel(wvcdm::LOG_INFO); + ASSERT_EQ(wvcdm::LOG_INFO, wvcdm::g_cutoff); + + wvoec_mock::SetLoggingSettings(wvcdm::LOG_WARN, + wvoec_mock::kLoggingDumpTraceAll); + ASSERT_EQ(wvcdm::LOG_WARN, wvcdm::g_cutoff); + ASSERT_TRUE(wvoec_mock::LogCategoryEnabled(wvoec_mock::kLoggingDumpTraceAll)); + wvoec_mock::TurnOffLoggingForAllCategories(); + + wvoec_mock::SetLoggingLevel(wvcdm::LOG_VERBOSE); + ASSERT_EQ(wvcdm::LOG_VERBOSE, wvcdm::g_cutoff); + + wvoec_mock::SetLoggingLevel(wvcdm::LOG_WARN); +} + +TEST_F(OEMCryptoLoggingTest, TestChangeLoggingCategories) { + using namespace wvoec_mock; + TurnOffLoggingForAllCategories(); + ASSERT_FALSE(LogCategoryEnabled(kLoggingTraceDecryption | + kLoggingTraceOEMCryptoCalls)); + + AddLoggingForCategories(kLoggingDumpKeyControlBlocks | + kLoggingDumpDerivedKeys); + ASSERT_TRUE(LogCategoryEnabled(kLoggingDumpKeyControlBlocks)); + ASSERT_FALSE(LogCategoryEnabled(kLoggingTraceUsageTable)); + ASSERT_TRUE(LogCategoryEnabled(kLoggingDumpTraceAll)); + + RemoveLoggingForCategories(kLoggingDumpKeyControlBlocks | + kLoggingTraceUsageTable); + ASSERT_FALSE(LogCategoryEnabled(kLoggingDumpKeyControlBlocks)); + + ASSERT_TRUE(LogCategoryEnabled(kLoggingDumpDerivedKeys)); + ASSERT_FALSE(LogCategoryEnabled(kLoggingTraceUsageTable)); + + TurnOffLoggingForAllCategories(); + ASSERT_FALSE(LogCategoryEnabled(kLoggingTraceUsageTable)); + + AddLoggingForCategories(kLoggingDumpTraceAll); + ASSERT_TRUE(LogCategoryEnabled(kLoggingDumpKeyControlBlocks)); + + ASSERT_TRUE(LogCategoryEnabled(kLoggingTraceOEMCryptoCalls)); + ASSERT_TRUE(LogCategoryEnabled(kLoggingDumpContentKeys)); + ASSERT_TRUE(LogCategoryEnabled(kLoggingDumpKeyControlBlocks)); + ASSERT_TRUE(LogCategoryEnabled(kLoggingDumpDerivedKeys)); + ASSERT_TRUE(LogCategoryEnabled(kLoggingTraceNonce)); + ASSERT_TRUE(LogCategoryEnabled(kLoggingTraceDecryption)); + ASSERT_TRUE(LogCategoryEnabled(kLoggingTraceUsageTable)); + ASSERT_TRUE(LogCategoryEnabled(kLoggingTraceDecryptCalls)); + ASSERT_TRUE(LogCategoryEnabled(kLoggingDumpTraceAll)); + + RemoveLoggingForCategories(kLoggingDumpKeyControlBlocks); + ASSERT_FALSE(LogCategoryEnabled(kLoggingDumpKeyControlBlocks)); +} diff --git a/test/oec_device_features.cpp b/test/oec_device_features.cpp new file mode 100644 index 0000000..12e2d65 --- /dev/null +++ b/test/oec_device_features.cpp @@ -0,0 +1,227 @@ +// Copyright 2016 Google Inc. All Rights Reserved. +// +// OEMCrypto device features for unit tests +// +#include "oec_device_features.h" + +#include + +#include + +#include "oec_test_data.h" + +namespace wvoec { + +DeviceFeatures global_features; + +void DeviceFeatures::Initialize(bool is_cast_receiver, + bool force_load_test_keybox) { + cast_receiver = is_cast_receiver; + uses_keybox = false; + uses_certificate = false; + loads_certificate = false; + generic_crypto = false; + usage_table = false; + supports_rsa_3072 = false; + api_version = 0; + derive_key_method = NO_METHOD; + if (OEMCrypto_SUCCESS != OEMCrypto_Initialize()) { + 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(); + printf("provisioning_method = %s\n", + ProvisioningMethodName(provisioning_method)); + uses_keybox = + (OEMCrypto_ERROR_NOT_IMPLEMENTED != OEMCrypto_GetKeyData(buffer, &size)); + printf("uses_keybox = %s.\n", uses_keybox ? "true" : "false"); + OEMCrypto_SESSION session; + OEMCryptoResult result = OEMCrypto_OpenSession(&session); + if (result != OEMCrypto_SUCCESS) { + printf("--- ERROR: Could not open session: %d ----\n", result); + } + // 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. + loads_certificate = true; + } else { + // Other devices are either broken, or they have a baked in certificate. + loads_certificate = false; + } + printf("loads_certificate = %s.\n", loads_certificate ? "true" : "false"); + uses_certificate = (OEMCrypto_ERROR_NOT_IMPLEMENTED != + OEMCrypto_GenerateRSASignature(session, buffer, 0, buffer, + &size, kSign_RSASSA_PSS)); + printf("uses_certificate = %s.\n", uses_certificate ? "true" : "false"); + generic_crypto = + (OEMCrypto_ERROR_NOT_IMPLEMENTED != + OEMCrypto_Generic_Encrypt(session, buffer, 0, buffer, + OEMCrypto_AES_CBC_128_NO_PADDING, buffer)); + printf("generic_crypto = %s.\n", generic_crypto ? "true" : "false"); + OEMCrypto_CloseSession(session); + api_version = OEMCrypto_APIVersion(); + printf("api_version = %d.\n", api_version); + // These unit tests only work with new usage tables. We do not test v12 + // usage tables. + if (api_version > 12) usage_table = OEMCrypto_SupportsUsageTable(); + printf("usage_table = %s.\n", usage_table ? "true" : "false"); + if (force_load_test_keybox) { + derive_key_method = FORCE_TEST_KEYBOX; + } else { + PickDerivedKey(); + } + if (api_version >= 13) { + uint32_t supported_cert = OEMCrypto_SupportedCertificates(); + if (supported_cert & OEMCrypto_Supports_RSA_CAST) { + cast_receiver = true; + } + if (supported_cert & OEMCrypto_Supports_RSA_3072bit) { + supports_rsa_3072 = true; + } + } + printf("cast_receiver = %s.\n", cast_receiver ? "true" : "false"); + switch (derive_key_method) { + case NO_METHOD: + printf("NO_METHOD: Cannot derive known session keys.\n"); + // Note: cast_receiver left unchanged because set by user on command line. + uses_keybox = false; + uses_certificate = false; + loads_certificate = false; + generic_crypto = false; + usage_table = false; + break; + case LOAD_TEST_KEYBOX: + printf("LOAD_TEST_KEYBOX: Call LoadTestKeybox before deriving keys.\n"); + break; + case LOAD_TEST_RSA_KEY: + printf("LOAD_TEST_RSA_KEY: Call LoadTestRSAKey before deriving keys.\n"); + break; + case EXISTING_TEST_KEYBOX: + printf("EXISTING_TEST_KEYBOX: Keybox is already the test keybox.\n"); + break; + case FORCE_TEST_KEYBOX: + printf("FORCE_TEST_KEYBOX: User requested calling InstallKeybox.\n"); + break; + case TEST_PROVISION_30: + printf("TEST_PROVISION_30: Device provisioed with OEM Cert.\n"); + break; + } + OEMCrypto_Terminate(); +} + +std::string DeviceFeatures::RestrictFilter(const std::string& initial_filter) { + std::string filter = initial_filter; + if (!uses_keybox) FilterOut(&filter, "*KeyboxTest*"); + if (derive_key_method + != FORCE_TEST_KEYBOX) FilterOut(&filter, "*ForceKeybox*"); + if (!uses_certificate) FilterOut(&filter, "OEMCrypto*Cert*"); + if (!loads_certificate) FilterOut(&filter, "OEMCryptoLoadsCert*"); + if (!generic_crypto) FilterOut(&filter, "*GenericCrypto*"); + if (!cast_receiver) FilterOut(&filter, "*CastReceiver*"); + if (!usage_table) FilterOut(&filter, "*UsageTable*"); + if (derive_key_method == NO_METHOD) FilterOut(&filter, "*SessionTest*"); + if (provisioning_method + != OEMCrypto_OEMCertificate) FilterOut(&filter, "*Prov30*"); + if (!supports_rsa_3072) FilterOut(&filter, "*RSAKey3072*"); + if (api_version < 9) FilterOut(&filter, "*API09*"); + if (api_version < 10) FilterOut(&filter, "*API10*"); + if (api_version < 11) FilterOut(&filter, "*API11*"); + if (api_version < 12) FilterOut(&filter, "*API12*"); + if (api_version < 13) FilterOut(&filter, "*API13*"); + if (api_version < 14) FilterOut(&filter, "*API14*"); + // Performance tests take a long time. Filter them out if they are not + // specifically requested. + if (filter.find("Performance") == std::string::npos) { + FilterOut(&filter, "*Performance*"); + } + return filter; +} + +void DeviceFeatures::PickDerivedKey() { + if (api_version >= 12) { + switch (provisioning_method) { + case OEMCrypto_OEMCertificate: + derive_key_method = TEST_PROVISION_30; + return; + case OEMCrypto_DrmCertificate: + if (OEMCrypto_ERROR_NOT_IMPLEMENTED != OEMCrypto_LoadTestRSAKey()) { + derive_key_method = LOAD_TEST_RSA_KEY; + } + return; + case OEMCrypto_Keybox: + // Fall through to api_version < 12 case. + break; + case OEMCrypto_ProvisioningError: + printf( + "ERROR: OEMCrypto_GetProvisioningMethod() returns " + "OEMCrypto_ProvisioningError\n"); + // Then fall through to api_version < 12 case. + break; + } + } + if (uses_keybox) { + // If device uses a keybox, try to load the test keybox. + // TODO(jfore): Update to pass in parameters. + if (OEMCrypto_ERROR_NOT_IMPLEMENTED != OEMCrypto_LoadTestKeybox(NULL, 0)) { + derive_key_method = LOAD_TEST_KEYBOX; + } else if (IsTestKeyboxInstalled()) { + derive_key_method = EXISTING_TEST_KEYBOX; + } + } else if (OEMCrypto_ERROR_NOT_IMPLEMENTED != OEMCrypto_LoadTestRSAKey()) { + derive_key_method = LOAD_TEST_RSA_KEY; + } +} + +bool DeviceFeatures::IsTestKeyboxInstalled() { + uint8_t key_data[256]; + size_t key_data_len = sizeof(key_data); + if (OEMCrypto_GetKeyData(key_data, &key_data_len) != OEMCrypto_SUCCESS) + return false; + if (key_data_len != sizeof(kTestKeybox.data_)) return false; + if (memcmp(key_data, kTestKeybox.data_, key_data_len)) return false; + uint8_t dev_id[128] = {0}; + size_t dev_id_len = 128; + if (OEMCrypto_GetDeviceID(dev_id, &dev_id_len) != OEMCrypto_SUCCESS) + return false; + // We use strncmp instead of memcmp because we don't really care about the + // multiple '\0' characters at the end of the device id. + return 0 == strncmp(reinterpret_cast(dev_id), + reinterpret_cast(kTestKeybox.device_id_), + sizeof(kTestKeybox.device_id_)); +} + +void DeviceFeatures::FilterOut(std::string* current_filter, + const std::string& new_filter) { + if (current_filter->find('-') == std::string::npos) { + *current_filter += "-" + new_filter; + } else { + *current_filter += ":" + new_filter; + } +} + +const char* ProvisioningMethodName(OEMCrypto_ProvisioningMethod method) { + switch (method) { + case OEMCrypto_ProvisioningError: + return "OEMCrypto_ProvisioningError"; + case OEMCrypto_DrmCertificate: + return "OEMCrypto_DrmCertificate"; + case OEMCrypto_Keybox: + return "OEMCrypto_Keybox"; + case OEMCrypto_OEMCertificate: + return "OEMCrypto_OEMCertificate"; + } + // Not reachable + return ""; +} + +} // namespace wvoec diff --git a/test/oec_device_features.h b/test/oec_device_features.h new file mode 100644 index 0000000..becea2a --- /dev/null +++ b/test/oec_device_features.h @@ -0,0 +1,47 @@ +#ifndef CDM_OEC_DEVICE_FEATURES_H_ +#define CDM_OEC_DEVICE_FEATURES_H_ + +#include + +#include "OEMCryptoCENC.h" +#include "wv_keybox.h" + +namespace wvoec { + +class DeviceFeatures { + public: + enum DeriveMethod { // Method to use derive session keys. + NO_METHOD, // Cannot derive known session keys. + LOAD_TEST_KEYBOX, // Call LoadTestKeybox before deriving keys. + LOAD_TEST_RSA_KEY, // Call LoadTestRSAKey before deriving keys. + EXISTING_TEST_KEYBOX, // Keybox is already the test keybox. + FORCE_TEST_KEYBOX, // User requested calling InstallKeybox. + TEST_PROVISION_30, // Device has OEM Certificate installed. + }; + + enum DeriveMethod derive_key_method; + bool uses_keybox; // Device uses a keybox to derive session keys. + bool uses_certificate; // Device uses a certificate to derive session keys. + bool loads_certificate; // Device can load a certificate from the server. + bool generic_crypto; // Device supports generic crypto. + bool cast_receiver; // Device supports alternate rsa signature padding. + bool usage_table; // Device saves usage information. + bool supports_rsa_3072; // Device supports 3072 bit RSA keys. + uint32_t api_version; + OEMCrypto_ProvisioningMethod provisioning_method; + + void Initialize(bool is_cast_receiver, bool force_load_test_keybox); + std::string RestrictFilter(const std::string& initial_filter); + + private: + void PickDerivedKey(); + bool IsTestKeyboxInstalled(); + void FilterOut(std::string* current_filter, const std::string& new_filter); +}; + +extern DeviceFeatures global_features; +const char* ProvisioningMethodName(OEMCrypto_ProvisioningMethod method); + +} // namespace wvoec + +#endif // CDM_OEC_DEVICE_FEATURES_H_ diff --git a/test/oec_session_util.cpp b/test/oec_session_util.cpp new file mode 100644 index 0000000..c5afd7c --- /dev/null +++ b/test/oec_session_util.cpp @@ -0,0 +1,1284 @@ +// Copyright 2016 Google Inc. All Rights Reserved. +// +// OEMCrypto unit tests +// + +#include "oec_session_util.h" + +#include // needed for ntoh() +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include "OEMCryptoCENC.h" +#include "log.h" +#include "oec_device_features.h" +#include "oec_test_data.h" +#include "oemcrypto_key_mock.h" +#include "string_conversions.h" +#include "wv_cdm_constants.h" +#include "wv_cdm_types.h" +#include "wv_keybox.h" + +using namespace std; + +// GTest requires PrintTo to be in the same namespace as the thing it prints, +// which is std::vector in this case. +namespace std { +void PrintTo(const vector& value, ostream* os) { + *os << wvcdm::b2a_hex(value); +} +} // namespace std + +namespace { +int GetRandBytes(unsigned char* buf, int num) { + // returns 1 on success, -1 if not supported, or 0 if other failure. + return RAND_bytes(buf, num); +} + +void DeleteX509Stack(STACK_OF(X509)* stack) { + sk_X509_pop_free(stack, X509_free); +} + +} // namespace + +namespace wvoec { + +// Increment counter for AES-CTR. The CENC spec specifies we increment only +// the low 64 bits of the IV counter, and leave the high 64 bits alone. This +// is different from the BoringSSL implementation, so we implement the CTR loop +// ourselves. +void ctr128_inc64(int64_t increaseBy, uint8_t* iv) { + ASSERT_NE(static_cast(NULL), iv); + uint64_t* counterBuffer = reinterpret_cast(&iv[8]); + (*counterBuffer) = + wvcdm::htonll64(wvcdm::ntohll64(*counterBuffer) + increaseBy); +} + +// Some compilers don't like the macro htonl within an ASSERT_EQ. +uint32_t htonl_fnc(uint32_t x) { return htonl(x); } + +void dump_boringssl_error() { + while (unsigned long err = ERR_get_error()) { + char buffer[120]; + ERR_error_string_n(err, buffer, sizeof(buffer)); + cout << "BoringSSL Error -- " << buffer << "\n"; + } +} + +template +class boringssl_ptr { + public: + explicit boringssl_ptr(T* p = NULL) : ptr_(p) {} + ~boringssl_ptr() { + if (ptr_) func(ptr_); + } + T& operator*() const { return *ptr_; } + T* operator->() const { return ptr_; } + T* get() const { return ptr_; } + bool NotNull() const { return ptr_; } + + private: + T* ptr_; + CORE_DISALLOW_COPY_AND_ASSIGN(boringssl_ptr); +}; + +Session::Session() + : open_(false), + forced_session_id_(false), + session_id_(0), + mac_key_server_(wvcdm::MAC_KEY_SIZE), + mac_key_client_(wvcdm::MAC_KEY_SIZE), + enc_key_(wvcdm::KEY_SIZE), + public_rsa_(0), + message_size_(sizeof(MessageData)), + num_keys_(4) { // Most tests only use 4 keys. + // Other tests will explicitly call set_num_keys. + // Stripe the padded message. + for (size_t i = 0; i < sizeof(padded_message_.padding); i++) { + padded_message_.padding[i] = i % 0x100; + } +} + +Session::~Session() { + if (!forced_session_id_ && open_) close(); + if (public_rsa_) RSA_free(public_rsa_); +} + +void Session::open() { + EXPECT_FALSE(forced_session_id_); + EXPECT_FALSE(open_); + ASSERT_EQ(OEMCrypto_SUCCESS, OEMCrypto_OpenSession(&session_id_)); + open_ = true; +} + +void Session::SetSessionId(uint32_t session_id) { + EXPECT_FALSE(open_); + session_id_ = session_id; + forced_session_id_ = true; +} + +void Session::close() { + EXPECT_TRUE(open_ || forced_session_id_); + if (open_) { + ASSERT_EQ(OEMCrypto_SUCCESS, OEMCrypto_CloseSession(session_id_)); + } + forced_session_id_ = false; + open_ = false; +} + +void Session::GenerateNonce(int* error_counter) { + if (OEMCrypto_SUCCESS == OEMCrypto_GenerateNonce(session_id(), &nonce_)) { + return; + } + if (error_counter) { + (*error_counter)++; + } else { + sleep(1); // wait a second, then try again. + ASSERT_EQ(OEMCrypto_SUCCESS, + OEMCrypto_GenerateNonce(session_id(), &nonce_)); + } +} + +void Session::FillDefaultContext(vector* mac_context, + vector* enc_context) { + /* Context strings + * These context strings are normally created by the CDM layer + * from a license request message. + * They are used to test MAC and ENC key generation. + */ + *mac_context = wvcdm::a2b_hex( + "41555448454e5449434154494f4e000a4c08001248000000020000101907d9ff" + "de13aa95c122678053362136bdf8408f8276e4c2d87ec52b61aa1b9f646e5873" + "4930acebe899b3e464189a14a87202fb02574e70640bd22ef44b2d7e3912250a" + "230a14080112100915007caa9b5931b76a3a85f046523e10011a093938373635" + "34333231180120002a0c31383836373837343035000000000200"); + *enc_context = wvcdm::a2b_hex( + "454e4352595054494f4e000a4c08001248000000020000101907d9ffde13aa95" + "c122678053362136bdf8408f8276e4c2d87ec52b61aa1b9f646e58734930aceb" + "e899b3e464189a14a87202fb02574e70640bd22ef44b2d7e3912250a230a1408" + "0112100915007caa9b5931b76a3a85f046523e10011a09393837363534333231" + "180120002a0c31383836373837343035000000000080"); +} + +void Session::DeriveKey(const uint8_t* key, const vector& context, + int counter, vector* out) { + ASSERT_FALSE(context.empty()); + ASSERT_GE(4, counter); + ASSERT_NE(static_cast(NULL), out); + + const EVP_CIPHER* cipher = EVP_aes_128_cbc(); + CMAC_CTX* cmac_ctx = CMAC_CTX_new(); + ASSERT_NE(static_cast(NULL), cmac_ctx); + + ASSERT_EQ(1, CMAC_Init(cmac_ctx, key, wvcdm::KEY_SIZE, cipher, 0)); + + std::vector message; + message.push_back(counter); + message.insert(message.end(), context.begin(), context.end()); + + ASSERT_EQ(1, CMAC_Update(cmac_ctx, &message[0], message.size())); + + size_t reslen; + uint8_t res[128]; + ASSERT_EQ(1, CMAC_Final(cmac_ctx, res, &reslen)); + + out->assign(res, res + reslen); + CMAC_CTX_free(cmac_ctx); +} + +void Session::DeriveKeys(const uint8_t* master_key, + const vector& mac_key_context, + const vector& enc_key_context) { + // Generate derived key for mac key + std::vector mac_key_part2; + DeriveKey(master_key, mac_key_context, 1, &mac_key_server_); + DeriveKey(master_key, mac_key_context, 2, &mac_key_part2); + mac_key_server_.insert(mac_key_server_.end(), mac_key_part2.begin(), + mac_key_part2.end()); + + DeriveKey(master_key, mac_key_context, 3, &mac_key_client_); + DeriveKey(master_key, mac_key_context, 4, &mac_key_part2); + mac_key_client_.insert(mac_key_client_.end(), mac_key_part2.begin(), + mac_key_part2.end()); + + // Generate derived key for encryption key + DeriveKey(master_key, enc_key_context, 1, &enc_key_); +} + +void Session::GenerateDerivedKeysFromKeybox( + const wvoec_mock::WidevineKeybox& keybox) { + GenerateNonce(); + vector mac_context; + vector enc_context; + FillDefaultContext(&mac_context, &enc_context); + ASSERT_EQ(OEMCrypto_SUCCESS, + OEMCrypto_GenerateDerivedKeys(session_id(), &mac_context[0], + mac_context.size(), &enc_context[0], + enc_context.size())); + + DeriveKeys(keybox.device_key_, mac_context, enc_context); +} + +void Session::GenerateDerivedKeysFromSessionKey() { + // Uses test certificate. + GenerateNonce(); + vector session_key; + vector enc_session_key; + if (public_rsa_ == NULL) PreparePublicKey(); + ASSERT_TRUE(GenerateRSASessionKey(&session_key, &enc_session_key)); + vector mac_context; + vector enc_context; + FillDefaultContext(&mac_context, &enc_context); + ASSERT_EQ(OEMCrypto_SUCCESS, + OEMCrypto_DeriveKeysFromSessionKey( + session_id(), &enc_session_key[0], enc_session_key.size(), + &mac_context[0], mac_context.size(), &enc_context[0], + enc_context.size())); + + DeriveKeys(&session_key[0], mac_context, enc_context); +} + +void Session::LoadTestKeys(const std::string& pst, bool new_mac_keys) { + uint8_t* pst_ptr = NULL; + if (pst.length() > 0) { + pst_ptr = encrypted_license().pst; + } + if (new_mac_keys) { + ASSERT_EQ(OEMCrypto_SUCCESS, + OEMCrypto_LoadKeys(session_id(), message_ptr(), message_size_, + &signature_[0], signature_.size(), + encrypted_license().mac_key_iv, + encrypted_license().mac_keys, num_keys_, + key_array_, pst_ptr, pst.length(), NULL, + OEMCrypto_ContentLicense)); + // Update new generated keys. + memcpy(&mac_key_server_[0], license_.mac_keys, wvcdm::MAC_KEY_SIZE); + memcpy(&mac_key_client_[0], license_.mac_keys + wvcdm::MAC_KEY_SIZE, + wvcdm::MAC_KEY_SIZE); + } else { + ASSERT_EQ( + OEMCrypto_SUCCESS, + OEMCrypto_LoadKeys(session_id(), message_ptr(), message_size_, + &signature_[0], signature_.size(), NULL, NULL, + num_keys_, key_array_, pst_ptr, pst.length(), NULL, + OEMCrypto_ContentLicense)); + } + VerifyTestKeys(); +} + +void Session::LoadEnitlementTestKeys(const std::string& pst, + bool new_mac_keys, + OEMCryptoResult expected_sts) { + uint8_t* pst_ptr = NULL; + if (pst.length() > 0) { + pst_ptr = encrypted_license().pst; + } + if (new_mac_keys) { + ASSERT_EQ(expected_sts, + OEMCrypto_LoadKeys(session_id(), message_ptr(), message_size_, + &signature_[0], signature_.size(), + encrypted_license().mac_key_iv, + encrypted_license().mac_keys, num_keys_, + key_array_, pst_ptr, pst.length(), NULL, + OEMCrypto_EntitlementLicense)); + // Update new generated keys. + memcpy(&mac_key_server_[0], license_.mac_keys, wvcdm::MAC_KEY_SIZE); + memcpy(&mac_key_client_[0], license_.mac_keys + wvcdm::MAC_KEY_SIZE, + wvcdm::MAC_KEY_SIZE); + } else { + ASSERT_EQ( + expected_sts, + OEMCrypto_LoadKeys(session_id(), message_ptr(), message_size_, + &signature_[0], signature_.size(), NULL, NULL, + num_keys_, key_array_, pst_ptr, pst.length(), NULL, + OEMCrypto_EntitlementLicense)); + } +} + +void Session::FillEntitledKeyArray() { + for (size_t i = 0; i < num_keys_; ++i) { + EntitledContentKeyData* key_data = &entitled_key_data_[i]; + + entitled_key_array_[i].entitlement_key_id = key_array_[i].key_id; + entitled_key_array_[i].entitlement_key_id_length = + key_array_[i].key_id_length; + + EXPECT_EQ( + 1, GetRandBytes(key_data->content_key_id, + sizeof(key_data->content_key_id))); + entitled_key_array_[i].content_key_id = key_data->content_key_id; + entitled_key_array_[i].content_key_id_length = + sizeof(key_data->content_key_id); + + EXPECT_EQ( + 1, GetRandBytes(key_data->content_key_data, + sizeof(key_data->content_key_data))); + entitled_key_array_[i].content_key_data = key_data->content_key_data; + entitled_key_array_[i].content_key_data_length = + sizeof(key_data->content_key_data); + + EXPECT_EQ( + 1, GetRandBytes(entitled_key_data_[i].content_key_data_iv, + sizeof(entitled_key_data_[i].content_key_data_iv))); + entitled_key_array_[i].content_key_data_iv = key_data->content_key_data_iv; + } +} + +void Session::LoadEntitledContentKeys(OEMCryptoResult expected_sts) { + // Create a copy of the stored |entitled_key_array_|. + std::vector encrypted_entitled_key_array; + encrypted_entitled_key_array.resize(num_keys_); + memcpy(&encrypted_entitled_key_array[0], &entitled_key_array_[0], + sizeof(OEMCrypto_EntitledContentKeyObject) * num_keys_); + + // Create a encrypted version of all of the content keys stored in + // |entitled_key_array_|. + std::vector > encrypted_content_keys; + encrypted_content_keys.resize(num_keys_); + + for (size_t i = 0; i < num_keys_; ++i) { + // Load the entitlement key from |key_array_|. + AES_KEY aes_key; + AES_set_encrypt_key(&key_array_[i].key_data[0], 256, &aes_key); + encrypted_content_keys[i].resize( + encrypted_entitled_key_array[i].content_key_data_length); + + // Encrypt the content key with the entitlement key. + uint8_t iv[16]; + memcpy(&iv[0], &encrypted_entitled_key_array[i].content_key_data[0], 16); + AES_cbc_encrypt( + &entitled_key_array_[i].content_key_data[0], + const_cast( + &encrypted_entitled_key_array[i].content_key_data[0]), + encrypted_entitled_key_array[i].content_key_data_length, + &aes_key, iv, AES_ENCRYPT); + + // Set the |encrypted_entitled_key_array| to point to the encrypted copy + // of the content key. + encrypted_entitled_key_array[i].content_key_data = + encrypted_content_keys[i].data(); + } + ASSERT_EQ(expected_sts, + OEMCrypto_LoadEntitledContentKeys( + session_id(), num_keys_, &encrypted_entitled_key_array[0])); + if (expected_sts != OEMCrypto_SUCCESS) { + return; + } + VerifyEntitlementTestKeys(); +} + +void Session::VerifyTestKeys() { + for (unsigned int i = 0; i < num_keys_; i++) { + KeyControlBlock block; + size_t size = sizeof(block); + OEMCryptoResult sts = OEMCrypto_QueryKeyControl( + session_id(), license_.keys[i].key_id, license_.keys[i].key_id_length, + reinterpret_cast(&block), &size); + if (sts != OEMCrypto_ERROR_NOT_IMPLEMENTED) { + ASSERT_EQ(OEMCrypto_SUCCESS, sts); + ASSERT_EQ(sizeof(block), size); + // control duration and bits stored in network byte order. For printing + // we change to host byte order. + ASSERT_EQ((htonl_fnc(license_.keys[i].control.duration)), + (htonl_fnc(block.duration))) + << "For key " << i; + ASSERT_EQ(htonl_fnc(license_.keys[i].control.control_bits), + htonl_fnc(block.control_bits)) + << "For key " << i; + } + } +} + +void Session::VerifyEntitlementTestKeys() { + for (unsigned int i = 0; i < num_keys_; i++) { + KeyControlBlock block; + size_t size = sizeof(block); + OEMCryptoResult sts = OEMCrypto_QueryKeyControl( + session_id(), entitled_key_array_[i].content_key_id, + entitled_key_array_[i].content_key_id_length, + reinterpret_cast(&block), &size); + if (sts != OEMCrypto_ERROR_NOT_IMPLEMENTED) { + ASSERT_EQ(OEMCrypto_SUCCESS, sts); + ASSERT_EQ(sizeof(block), size); + // control duration and bits stored in network byte order. For printing + // we change to host byte order. + ASSERT_EQ((htonl_fnc(license_.keys[i].control.duration)), + (htonl_fnc(block.duration))) + << "For key " << i; + ASSERT_EQ(htonl_fnc(license_.keys[i].control.control_bits), + htonl_fnc(block.control_bits)) + << "For key " << i; + } + } +} + +void Session::RefreshTestKeys(const size_t key_count, uint32_t control_bits, + uint32_t nonce, OEMCryptoResult expected_result) { + // Note: we store the message in encrypted_license_, but the refresh key + // message is not actually encrypted. It is, however, signed. + // FillRefreshMessage fills the message with a duration of kLongDuration. + FillRefreshMessage(key_count, control_bits, nonce); + ServerSignBuffer(reinterpret_cast(&padded_message_), + message_size_, &signature_); + OEMCrypto_KeyRefreshObject key_array[key_count]; + FillRefreshArray(key_array, key_count); + OEMCryptoResult sts = OEMCrypto_RefreshKeys( + session_id(), message_ptr(), message_size_, &signature_[0], + signature_.size(), key_count, key_array); + ASSERT_EQ(expected_result, sts); + + 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); + 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); + if (expected_result == OEMCrypto_SUCCESS) { + ASSERT_NO_FATAL_FAILURE(TestDecryptCTR(false, OEMCrypto_SUCCESS)); + } else { + ASSERT_NO_FATAL_FAILURE( + TestDecryptCTR(false, OEMCrypto_ERROR_UNKNOWN_FAILURE)); + } +} + +void Session::SetKeyId(int index, const string& key_id) { + MessageKeyData& key = license_.keys[index]; + key.key_id_length = key_id.length(); + ASSERT_LE(key.key_id_length, kTestKeyIdMaxLength); + memcpy(key.key_id, key_id.data(), key.key_id_length); +} + +void Session::FillSimpleMessage(uint32_t duration, uint32_t control, + uint32_t nonce, const std::string& pst) { + EXPECT_EQ( + 1, GetRandBytes(license_.mac_key_iv, sizeof(license_.mac_key_iv))); + EXPECT_EQ(1, GetRandBytes(license_.mac_keys, sizeof(license_.mac_keys))); + for (unsigned int i = 0; i < num_keys_; i++) { + memset(license_.keys[i].key_id, 0, kTestKeyIdMaxLength); + license_.keys[i].key_id_length = kDefaultKeyIdLength; + memset(license_.keys[i].key_id, i, license_.keys[i].key_id_length); + EXPECT_EQ(1, GetRandBytes(license_.keys[i].key_data, + sizeof(license_.keys[i].key_data))); + license_.keys[i].key_data_length = wvcdm::KEY_SIZE; + EXPECT_EQ(1, GetRandBytes(license_.keys[i].key_iv, + sizeof(license_.keys[i].key_iv))); + EXPECT_EQ(1, GetRandBytes(license_.keys[i].control_iv, + sizeof(license_.keys[i].control_iv))); + if (global_features.api_version == 14) { + // For version 14, we require OEMCrypto to handle kc14 for all licenses. + memcpy(license_.keys[i].control.verification, "kc14", 4); + } else if (global_features.api_version == 13) { + // For version 13, we require OEMCrypto to handle kc13 for all licenses. + memcpy(license_.keys[i].control.verification, "kc13", 4); + } else if (global_features.api_version == 12) { + // For version 12, we require OEMCrypto to handle kc12 for all licenses. + memcpy(license_.keys[i].control.verification, "kc12", 4); + } else if (control & wvoec_mock::kControlSecurityPatchLevelMask) { + // For versions before 12, we require the special key control block only + // when there are newer features present. + memcpy(license_.keys[i].control.verification, "kc11", 4); + } else if (control & wvoec_mock::kControlRequireAntiRollbackHardware) { + memcpy(license_.keys[i].control.verification, "kc10", 4); + } else if (control & (wvoec_mock::kControlHDCPVersionMask | + wvoec_mock::kControlReplayMask)) { + memcpy(license_.keys[i].control.verification, "kc09", 4); + } else { + memcpy(license_.keys[i].control.verification, "kctl", 4); + } + license_.keys[i].control.duration = htonl(duration); + license_.keys[i].control.nonce = htonl(nonce); + license_.keys[i].control.control_bits = htonl(control); + license_.keys[i].cipher_mode = OEMCrypto_CipherMode_CTR; + } + memcpy(license_.pst, pst.c_str(), min(sizeof(license_.pst), pst.length())); + pst_ = pst; +} + +void Session::FillSimpleEntitlementMessage( + uint32_t duration, uint32_t control, uint32_t nonce, + const std::string& pst) { + EXPECT_EQ( + 1, GetRandBytes(license_.mac_key_iv, sizeof(license_.mac_key_iv))); + EXPECT_EQ(1, GetRandBytes(license_.mac_keys, sizeof(license_.mac_keys))); + for (unsigned int i = 0; i < num_keys_; i++) { + memset(license_.keys[i].key_id, 0, kTestKeyIdMaxLength); + license_.keys[i].key_id_length = kDefaultKeyIdLength; + memset(license_.keys[i].key_id, i, license_.keys[i].key_id_length); + EXPECT_EQ(1, GetRandBytes(license_.keys[i].key_data, + sizeof(license_.keys[i].key_data))); + license_.keys[i].key_data_length = wvcdm::KEY_SIZE * 2; // AES-256 keys + EXPECT_EQ(1, GetRandBytes(license_.keys[i].key_iv, + sizeof(license_.keys[i].key_iv))); + EXPECT_EQ(1, GetRandBytes(license_.keys[i].control_iv, + sizeof(license_.keys[i].control_iv))); + if (global_features.api_version == 14) { + // For version 13, we require OEMCrypto to handle kc14 for all licenses. + memcpy(license_.keys[i].control.verification, "kc14", 4); + } else if (global_features.api_version == 13) { + // For version 13, we require OEMCrypto to handle kc13 for all licenses. + memcpy(license_.keys[i].control.verification, "kc13", 4); + } else if (global_features.api_version == 12) { + // For version 12, we require OEMCrypto to handle kc12 for all licenses. + memcpy(license_.keys[i].control.verification, "kc12", 4); + } else if (control & wvoec_mock::kControlSecurityPatchLevelMask) { + // For versions before 12, we require the special key control block only + // when there are newer features present. + memcpy(license_.keys[i].control.verification, "kc11", 4); + } else if (control & wvoec_mock::kControlRequireAntiRollbackHardware) { + memcpy(license_.keys[i].control.verification, "kc10", 4); + } else if (control & (wvoec_mock::kControlHDCPVersionMask | + wvoec_mock::kControlReplayMask)) { + memcpy(license_.keys[i].control.verification, "kc09", 4); + } else { + memcpy(license_.keys[i].control.verification, "kctl", 4); + } + license_.keys[i].control.duration = htonl(duration); + license_.keys[i].control.nonce = htonl(nonce); + license_.keys[i].control.control_bits = htonl(control); + license_.keys[i].cipher_mode = OEMCrypto_CipherMode_CTR; + } + memcpy(license_.pst, pst.c_str(), min(sizeof(license_.pst), pst.length())); + pst_ = pst; +} + +void Session::FillRefreshMessage(size_t key_count, uint32_t control_bits, + uint32_t nonce) { + for (unsigned int i = 0; i < key_count; i++) { + encrypted_license().keys[i].key_id_length = license_.keys[i].key_id_length; + memcpy(encrypted_license().keys[i].key_id, license_.keys[i].key_id, + encrypted_license().keys[i].key_id_length); + if (global_features.api_version == 14) { + // For version 14, we require OEMCrypto to handle kc14 for all licenses. + memcpy(license_.keys[i].control.verification, "kc14", 4); + } else if (global_features.api_version == 13) { + // For version 13, we require OEMCrypto to handle kc13 for all licenses. + memcpy(encrypted_license().keys[i].control.verification, "kc13", 4); + } else if (global_features.api_version == 12) { + // For version 12, we require OEMCrypto to handle kc12 for all licenses. + memcpy(encrypted_license().keys[i].control.verification, "kc12", 4); + } else { + // For versions before 12, we require the special key control block only + // when there are newer features present. + memcpy(encrypted_license().keys[i].control.verification, "kctl", 4); + } + encrypted_license().keys[i].control.duration = htonl(kLongDuration); + encrypted_license().keys[i].control.nonce = htonl(nonce); + encrypted_license().keys[i].control.control_bits = htonl(control_bits); + } +} + +void Session::EncryptAndSign() { + encrypted_license() = license_; + + uint8_t iv_buffer[16]; + memcpy(iv_buffer, &license_.mac_key_iv[0], wvcdm::KEY_IV_SIZE); + AES_KEY aes_key; + AES_set_encrypt_key(&enc_key_[0], 128, &aes_key); + AES_cbc_encrypt(&license_.mac_keys[0], &encrypted_license().mac_keys[0], + 2 * wvcdm::MAC_KEY_SIZE, &aes_key, iv_buffer, AES_ENCRYPT); + + for (unsigned int i = 0; i < num_keys_; i++) { + memcpy(iv_buffer, &license_.keys[i].control_iv[0], wvcdm::KEY_IV_SIZE); + AES_set_encrypt_key(&license_.keys[i].key_data[0], 128, &aes_key); + AES_cbc_encrypt( + reinterpret_cast(&license_.keys[i].control), + reinterpret_cast(&encrypted_license().keys[i].control), + wvcdm::KEY_SIZE, &aes_key, iv_buffer, AES_ENCRYPT); + + memcpy(iv_buffer, &license_.keys[i].key_iv[0], wvcdm::KEY_IV_SIZE); + AES_set_encrypt_key(&enc_key_[0], 128, &aes_key); + AES_cbc_encrypt( + &license_.keys[i].key_data[0], &encrypted_license().keys[i].key_data[0], + license_.keys[i].key_data_length, &aes_key, iv_buffer, AES_ENCRYPT); + } + memcpy(encrypted_license().pst, license_.pst, sizeof(license_.pst)); + ServerSignBuffer(reinterpret_cast(&padded_message_), + message_size_, &signature_); + FillKeyArray(encrypted_license(), key_array_); +} + +void Session::EncryptProvisioningMessage( + RSAPrivateKeyMessage* data, RSAPrivateKeyMessage* encrypted, + const vector& encryption_key) { + ASSERT_EQ(encryption_key.size(), wvcdm::KEY_SIZE); + *encrypted = *data; + size_t padding = wvcdm::KEY_SIZE - (data->rsa_key_length % wvcdm::KEY_SIZE); + memset(data->rsa_key + data->rsa_key_length, static_cast(padding), + padding); + encrypted->rsa_key_length = data->rsa_key_length + padding; + uint8_t iv_buffer[16]; + memcpy(iv_buffer, &data->rsa_key_iv[0], wvcdm::KEY_IV_SIZE); + AES_KEY aes_key; + AES_set_encrypt_key(&encryption_key[0], 128, &aes_key); + AES_cbc_encrypt(&data->rsa_key[0], &encrypted->rsa_key[0], + encrypted->rsa_key_length, &aes_key, iv_buffer, AES_ENCRYPT); +} + +void Session::ServerSignBuffer(const uint8_t* data, size_t data_length, + std::vector* signature) { + ASSERT_LE(data_length, kMaxMessageSize); + signature->assign(SHA256_DIGEST_LENGTH, 0); + unsigned int md_len = SHA256_DIGEST_LENGTH; + HMAC(EVP_sha256(), &mac_key_server_[0], mac_key_server_.size(), data, + data_length, &(signature->front()), &md_len); +} + +void Session::ClientSignMessage(const vector& data, + std::vector* signature) { + signature->assign(SHA256_DIGEST_LENGTH, 0); + unsigned int md_len = SHA256_DIGEST_LENGTH; + HMAC(EVP_sha256(), &mac_key_client_[0], mac_key_client_.size(), + &(data.front()), data.size(), &(signature->front()), &md_len); +} + +void Session::VerifyClientSignature(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; + sts = OEMCrypto_GenerateSignature(session_id(), &data[0], data.size(), NULL, + &gen_signature_length); + ASSERT_EQ(OEMCrypto_ERROR_SHORT_BUFFER, sts); + ASSERT_EQ(static_cast(32), gen_signature_length); + vector gen_signature(gen_signature_length); + sts = OEMCrypto_GenerateSignature(session_id(), &data[0], data.size(), + &gen_signature[0], &gen_signature_length); + ASSERT_EQ(OEMCrypto_SUCCESS, sts); + std::vector expected_signature; + ClientSignMessage(data, &expected_signature); + ASSERT_EQ(expected_signature, gen_signature); +} + +void Session::FillKeyArray(const MessageData& data, + OEMCrypto_KeyObject* key_array) { + for (unsigned int i = 0; i < num_keys_; i++) { + key_array[i].key_id = data.keys[i].key_id; + key_array[i].key_id_length = data.keys[i].key_id_length; + key_array[i].key_data_iv = data.keys[i].key_iv; + key_array[i].key_data = data.keys[i].key_data; + key_array[i].key_data_length = data.keys[i].key_data_length; + key_array[i].key_control_iv = data.keys[i].control_iv; + key_array[i].key_control = + reinterpret_cast(&data.keys[i].control); + } +} + +void Session::FillRefreshArray(OEMCrypto_KeyRefreshObject* key_array, + size_t key_count) { + for (size_t i = 0; i < key_count; i++) { + if (key_count > 1) { + key_array[i].key_id = encrypted_license().keys[i].key_id; + key_array[i].key_id_length = encrypted_license().keys[i].key_id_length; + } else { + key_array[i].key_id = NULL; + key_array[i].key_id_length = 0; + } + key_array[i].key_control_iv = NULL; + key_array[i].key_control = + reinterpret_cast(&encrypted_license().keys[i].control); + } +} + +void Session::EncryptCTR(const vector& in_buffer, const uint8_t* key, + const uint8_t* starting_iv, + vector* out_buffer) { + ASSERT_NE(static_cast(NULL), key); + ASSERT_NE(static_cast(NULL), starting_iv); + ASSERT_NE(static_cast(NULL), out_buffer); + AES_KEY aes_key; + AES_set_encrypt_key(key, AES_BLOCK_SIZE * 8, &aes_key); + out_buffer->resize(in_buffer.size()); + + uint8_t iv[AES_BLOCK_SIZE]; // Current iv. + + memcpy(iv, &starting_iv[0], AES_BLOCK_SIZE); + size_t l = 0; // byte index into encrypted subsample. + while (l < in_buffer.size()) { + uint8_t aes_output[AES_BLOCK_SIZE]; + AES_encrypt(iv, aes_output, &aes_key); + for (size_t n = 0; n < AES_BLOCK_SIZE && l < in_buffer.size(); n++, l++) { + (*out_buffer)[l] = aes_output[n] ^ in_buffer[l]; + } + ctr128_inc64(1, iv); + } +} + +void Session::TestDecryptCTR(bool select_key_first, + OEMCryptoResult expected_result, int key_index) { + OEMCryptoResult sts; + if (select_key_first) { + // Select the key (from FillSimpleMessage) + sts = OEMCrypto_SelectKey(session_id(), license_.keys[key_index].key_id, + license_.keys[key_index].key_id_length, + OEMCrypto_CipherMode_CTR); + ASSERT_EQ(OEMCrypto_SUCCESS, sts); + } + + vector unencryptedData(256); + for (size_t i = 0; i < unencryptedData.size(); i++) + unencryptedData[i] = i % 256; + EXPECT_EQ(1, GetRandBytes(&unencryptedData[0], unencryptedData.size())); + vector encryptionIv(wvcdm::KEY_IV_SIZE); + EXPECT_EQ(1, GetRandBytes(&encryptionIv[0], wvcdm::KEY_IV_SIZE)); + vector encryptedData(unencryptedData.size()); + EncryptCTR(unencryptedData, license_.keys[key_index].key_data, + &encryptionIv[0], &encryptedData); + + // 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_CENCEncryptPatternDesc pattern; + pattern.encrypt = 0; + pattern.skip = 0; + pattern.offset = 0; + // Decrypt the data + sts = OEMCrypto_DecryptCENC( + session_id(), &encryptedData[0], encryptedData.size(), true, + &encryptionIv[0], 0, &destBuffer, &pattern, + OEMCrypto_FirstSubsample | OEMCrypto_LastSubsample); + // We only have a few errors that we test are reported. + if (expected_result == OEMCrypto_SUCCESS) { // No error. + ASSERT_EQ(OEMCrypto_SUCCESS, sts); + ASSERT_EQ(unencryptedData, outputBuffer); + } else { + ASSERT_NO_FATAL_FAILURE(TestDecryptResult(expected_result, sts)); + ASSERT_NE(unencryptedData, outputBuffer); + } +} + +void Session::TestDecryptResult(OEMCryptoResult expected_result, + OEMCryptoResult actual_result) { + + if (expected_result == OEMCrypto_SUCCESS) { // No error. + ASSERT_EQ(OEMCrypto_SUCCESS, actual_result); + } else if (expected_result == OEMCrypto_ERROR_KEY_EXPIRED && + 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_ERROR_INSUFFICIENT_HDCP) { + // Report HDCP errors. + ASSERT_EQ(OEMCrypto_ERROR_INSUFFICIENT_HDCP, actual_result); + } else if (expected_result == OEMCrypto_ERROR_ANALOG_OUTPUT) { + // Report analog errors. + ASSERT_EQ(OEMCrypto_ERROR_ANALOG_OUTPUT, actual_result); + } else { + // OEM's can fine tune other error codes for debugging. + ASSERT_NE(OEMCrypto_SUCCESS, actual_result); + } +} + +void Session::TestSelectExpired(unsigned int key_index) { + if (global_features.api_version >= 13) { + OEMCryptoResult status = + OEMCrypto_SelectKey(session_id(), license().keys[key_index].key_id, + license().keys[key_index].key_id_length, + OEMCrypto_CipherMode_CTR); + // It is OK for SelectKey to succeed with an expired key, but if there is + // an error, it must be OEMCrypto_ERROR_KEY_EXIRED. + if (status != OEMCrypto_SUCCESS) { + ASSERT_EQ(OEMCrypto_ERROR_KEY_EXPIRED, status); + } + } +} + +void Session::LoadOEMCert(bool verify_cert) { + // Get the OEM Public Cert from OEMCrypto + vector public_cert; + size_t public_cert_length = 0; + ASSERT_EQ(OEMCrypto_ERROR_SHORT_BUFFER, + OEMCrypto_GetOEMPublicCertificate(session_id(), NULL, + &public_cert_length)); + ASSERT_LT(0u, public_cert_length); + public_cert.resize(public_cert_length); + ASSERT_EQ(OEMCrypto_SUCCESS, + OEMCrypto_GetOEMPublicCertificate(session_id(), &public_cert[0], + &public_cert_length)); + + // Load the certificate chain into a BoringSSL X509 Stack + const boringssl_ptr x509_stack( + sk_X509_new_null()); + ASSERT_TRUE(x509_stack.NotNull()) << "Unable to allocate X509 stack."; + + CBS pkcs7; + CBS_init(&pkcs7, public_cert.data(), public_cert.size()); + if (!PKCS7_get_certificates(x509_stack.get(), &pkcs7)) { + dump_boringssl_error(); + FAIL() << "Unable to deserialize certificate chain."; + } + + STACK_OF(X509)* certs = x509_stack.get(); + + // Load the public cert's key into public_rsa_ and verify, if requested + for (size_t i = 0; certs && i < sk_X509_num(certs); i++) { + X509* x509_cert = sk_X509_value(certs, i); + boringssl_ptr pubkey(X509_get_pubkey(x509_cert)); + ASSERT_TRUE(pubkey.NotNull()); + if (i == 0) { + public_rsa_ = EVP_PKEY_get1_RSA(pubkey.get()); + if (!public_rsa_) { + cout << "d2i_RSAPrivateKey failed.\n"; + dump_boringssl_error(); + ASSERT_TRUE(NULL != public_rsa_); + } + } + if (verify_cert) { + vector buffer(80); + + X509_NAME* name = X509_get_subject_name(x509_cert); + printf(" OEM Certificate Name: %s\n", + X509_NAME_oneline(name, &buffer[0], buffer.size())); + boringssl_ptr store(X509_STORE_new()); + ASSERT_TRUE(store.NotNull()); + boringssl_ptr store_ctx( + X509_STORE_CTX_new()); + ASSERT_TRUE(store_ctx.NotNull()); + + X509_STORE_CTX_init(store_ctx.get(), store.get(), x509_cert, NULL); + + // TODO(fredgc): Verify cert is signed by Google. + + int result = X509_verify_cert(store_ctx.get()); + ASSERT_GE(0, result) << " OEM Cert not valid. " << + X509_verify_cert_error_string( + X509_STORE_CTX_get_error(store_ctx.get())); + if (result == 0) { + printf("Cert not verified: %s.\n", + X509_verify_cert_error_string( + X509_STORE_CTX_get_error(store_ctx.get()))); + } + } + } +} + +void Session::MakeRSACertificate(struct RSAPrivateKeyMessage* encrypted, + size_t message_size, + std::vector* signature, + uint32_t allowed_schemes, + const vector& rsa_key, + const vector* encryption_key) { + if (encryption_key == NULL) encryption_key = &enc_key_; + 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(); + } + EXPECT_EQ(1, GetRandBytes(message.rsa_key_iv, wvcdm::KEY_IV_SIZE)); + message.nonce = nonce_; + + EncryptProvisioningMessage(&message, encrypted, *encryption_key); + 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[0], + signature.size(), &encrypted.nonce, encrypted.rsa_key, + encrypted.rsa_key_length, encrypted.rsa_key_iv, NULL, + &wrapped_key_length)); + wrapped_key->clear(); + wrapped_key->assign(wrapped_key_length, 0); + OEMCryptoResult sts = OEMCrypto_RewrapDeviceRSAKey( + session_id(), message_ptr, message_size, &signature[0], 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[0], + encrypted_message_key.size(), encrypted.rsa_key, + encrypted.rsa_key_length, encrypted.rsa_key_iv, NULL, + &wrapped_key_length)); + wrapped_key->clear(); + wrapped_key->assign(wrapped_key_length, 0); + OEMCryptoResult sts = OEMCrypto_RewrapDeviceRSAKey30( + session_id(), &nonce_, &encrypted_message_key[0], + 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 == NULL) { + rsa_key = kTestRSAPKCS8PrivateKeyInfo2_2048; + rsa_key_length = sizeof(kTestRSAPKCS8PrivateKeyInfo2_2048); + } + uint8_t* p = const_cast(rsa_key); + boringssl_ptr bio(BIO_new_mem_buf(p, rsa_key_length)); + ASSERT_TRUE(bio.NotNull()); + boringssl_ptr pkcs8_pki( + d2i_PKCS8_PRIV_KEY_INFO_bio(bio.get(), NULL)); + ASSERT_TRUE(pkcs8_pki.NotNull()); + boringssl_ptr evp(EVP_PKCS82PKEY(pkcs8_pki.get())); + ASSERT_TRUE(evp.NotNull()); + if (public_rsa_) RSA_free(public_rsa_); + public_rsa_ = EVP_PKEY_get1_RSA(evp.get()); + if (!public_rsa_) { + cout << "d2i_RSAPrivateKey failed. "; + dump_boringssl_error(); + FAIL() << "Could not parse public RSA key."; + } + switch (RSA_check_key(public_rsa_)) { + case 1: // valid. + return; + case 0: // not valid. + dump_boringssl_error(); + FAIL() << "[rsa key not valid] "; + default: // -1 == check failed. + dump_boringssl_error(); + FAIL() << "[error checking rsa key] "; + } +} + +bool Session::VerifyPSSSignature(EVP_PKEY* pkey, const uint8_t* message, + size_t message_length, + const uint8_t* signature, + size_t signature_length) { + EVP_MD_CTX md_ctx_struct; + EVP_MD_CTX* md_ctx = &md_ctx_struct; + EVP_MD_CTX_init(md_ctx); + EVP_PKEY_CTX* pkey_ctx = NULL; + + if (EVP_DigestVerifyInit(md_ctx, &pkey_ctx, EVP_sha1(), NULL /* no ENGINE */, + pkey) != 1) { + LOGE("EVP_DigestVerifyInit failed in VerifyPSSSignature"); + goto err; + } + + if (EVP_PKEY_CTX_set_signature_md(pkey_ctx, + const_cast(EVP_sha1())) != 1) { + LOGE("EVP_PKEY_CTX_set_signature_md failed in VerifyPSSSignature"); + goto err; + } + + if (EVP_PKEY_CTX_set_rsa_padding(pkey_ctx, RSA_PKCS1_PSS_PADDING) != 1) { + LOGE("EVP_PKEY_CTX_set_rsa_padding failed in VerifyPSSSignature"); + goto err; + } + + if (EVP_PKEY_CTX_set_rsa_pss_saltlen(pkey_ctx, SHA_DIGEST_LENGTH) != 1) { + LOGE("EVP_PKEY_CTX_set_rsa_pss_saltlen failed in VerifyPSSSignature"); + goto err; + } + + if (EVP_DigestVerifyUpdate(md_ctx, message, message_length) != 1) { + LOGE("EVP_DigestVerifyUpdate failed in VerifyPSSSignature"); + goto err; + } + + if (EVP_DigestVerifyFinal(md_ctx, const_cast(signature), + signature_length) != 1) { + LOGE( + "EVP_DigestVerifyFinal failed in VerifyPSSSignature. (Probably a bad " + "signature.)"); + goto err; + } + + EVP_MD_CTX_cleanup(md_ctx); + return true; + +err: + dump_boringssl_error(); + EVP_MD_CTX_cleanup(md_ctx); + return false; +} + +void Session::VerifyRSASignature(const vector& message, + const uint8_t* signature, + size_t signature_length, + RSA_Padding_Scheme padding_scheme) { + EXPECT_TRUE(NULL != public_rsa_) + << "No public RSA key loaded in test code.\n"; + + EXPECT_EQ(static_cast(RSA_size(public_rsa_)), signature_length) + << "Signature size is wrong. " << signature_length << ", should be " + << RSA_size(public_rsa_) << "\n"; + + if (padding_scheme == kSign_RSASSA_PSS) { + boringssl_ptr pkey(EVP_PKEY_new()); + ASSERT_EQ(1, EVP_PKEY_set1_RSA(pkey.get(), public_rsa_)); + + const bool ok = VerifyPSSSignature(pkey.get(), &message[0], message.size(), + signature, signature_length); + EXPECT_TRUE(ok) << "PSS signature check failed."; + } else if (padding_scheme == kSign_PKCS1_Block1) { + vector padded_digest(signature_length); + int size; + // RSA_public_decrypt decrypts the signature, and then verifies that + // it was padded with RSA PKCS1 padding. + size = RSA_public_decrypt(signature_length, signature, &padded_digest[0], + public_rsa_, RSA_PKCS1_PADDING); + EXPECT_GT(size, 0); + padded_digest.resize(size); + EXPECT_EQ(message, padded_digest); + } else { + EXPECT_TRUE(false) << "Padding scheme not supported."; + } +} + +bool Session::GenerateRSASessionKey(vector* session_key, + vector* enc_session_key) { + if (!public_rsa_) { + cout << "No public RSA key loaded in test code.\n"; + return false; + } + *session_key = wvcdm::a2b_hex("6fa479c731d2770b6a61a5d1420bb9d1"); + enc_session_key->assign(RSA_size(public_rsa_), 0); + int status = RSA_public_encrypt(session_key->size(), &(session_key->front()), + &(enc_session_key->front()), public_rsa_, + RSA_PKCS1_OAEP_PADDING); + int size = static_cast(RSA_size(public_rsa_)); + if (status != size) { + cout << "GenerateRSASessionKey error encrypting session key.\n"; + dump_boringssl_error(); + return false; + } + return true; +} + +void Session::InstallRSASessionTestKey(const vector& wrapped_rsa_key) { + ASSERT_EQ(OEMCrypto_SUCCESS, + OEMCrypto_LoadDeviceRSAKey(session_id(), &wrapped_rsa_key[0], + wrapped_rsa_key.size())); + GenerateDerivedKeysFromSessionKey(); +} + +void Session::CreateNewUsageEntry() { + ASSERT_EQ(OEMCrypto_SUCCESS, + OEMCrypto_CreateNewUsageEntry(session_id(), &usage_entry_number_)); +} + +void Session::UpdateUsageEntry(std::vector* header_buffer) { + size_t header_buffer_length = 0; + size_t entry_buffer_length = 0; + ASSERT_EQ( + OEMCrypto_ERROR_SHORT_BUFFER, + OEMCrypto_UpdateUsageEntry(session_id(), NULL, &header_buffer_length, + NULL, &entry_buffer_length)); + ASSERT_LT(0u, header_buffer_length); + header_buffer->resize(header_buffer_length); + ASSERT_LT(0u, entry_buffer_length); + encrypted_usage_entry_.resize(entry_buffer_length); + ASSERT_EQ(OEMCrypto_SUCCESS, + OEMCrypto_UpdateUsageEntry( + session_id(), &(header_buffer->front()), &header_buffer_length, + &encrypted_usage_entry_[0], &entry_buffer_length)); +} + +void Session::DeactivateUsageEntry(const std::string& pst) { + ASSERT_EQ(OEMCrypto_SUCCESS, + OEMCrypto_DeactivateUsageEntry( + session_id(), reinterpret_cast(pst.c_str()), + pst.length())); +} + +void Session::LoadUsageEntry(uint32_t index, const vector& buffer) { + usage_entry_number_ = index; + encrypted_usage_entry_ = buffer; + ASSERT_EQ( + OEMCrypto_SUCCESS, + OEMCrypto_LoadUsageEntry(session_id(), index, &buffer[0], buffer.size())); +} + +void Session::MoveUsageEntry(uint32_t new_index, + std::vector* header_buffer, + OEMCryptoResult expect_result) { + + ASSERT_NO_FATAL_FAILURE(open()); + ASSERT_NO_FATAL_FAILURE(ReloadUsageEntry()); + ASSERT_EQ(expect_result, OEMCrypto_MoveEntry(session_id(), new_index)); + if (expect_result == OEMCrypto_SUCCESS) { + usage_entry_number_ = new_index; + ASSERT_NO_FATAL_FAILURE(UpdateUsageEntry(header_buffer)); + } + ASSERT_NO_FATAL_FAILURE(close()); +} + +void Session::GenerateReport(const std::string& pst, + OEMCryptoResult expected_result, + Session* other) { + ASSERT_TRUE(open_); + if (other) { // If other is specified, copy mac keys. + mac_key_server_ = other->mac_key_server_; + mac_key_client_ = other->mac_key_client_; + } + size_t length = 0; + OEMCryptoResult sts = OEMCrypto_ReportUsage( + session_id(), reinterpret_cast(pst.c_str()), pst.length(), + &pst_report_buffer_[0], &length); + if (expected_result == OEMCrypto_SUCCESS) { + ASSERT_EQ(OEMCrypto_ERROR_SHORT_BUFFER, sts); + } + if (sts == OEMCrypto_ERROR_SHORT_BUFFER) { + ASSERT_EQ(wvcdm::Unpacked_PST_Report::report_size(pst.length()), length); + pst_report_buffer_.assign(length, 0xFF); // Fill with garbage values. + } + sts = OEMCrypto_ReportUsage(session_id(), + reinterpret_cast(pst.c_str()), + pst.length(), &pst_report_buffer_[0], &length); + ASSERT_EQ(expected_result, sts); + if (expected_result != OEMCrypto_SUCCESS) { + return; + } + ASSERT_EQ(pst_report_buffer_.size(), length); + vector computed_signature(SHA_DIGEST_LENGTH); + unsigned int sig_len = SHA_DIGEST_LENGTH; + HMAC(EVP_sha1(), &mac_key_client_[0], mac_key_client_.size(), + &pst_report_buffer_[SHA_DIGEST_LENGTH], length - SHA_DIGEST_LENGTH, + &computed_signature[0], &sig_len); + EXPECT_EQ(0, memcmp(&computed_signature[0], pst_report().signature(), + SHA_DIGEST_LENGTH)); + EXPECT_GE(kInactiveUnused, pst_report().status()); + EXPECT_GE(kHardwareSecureClock, pst_report().clock_security_level()); + 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. + ASSERT_NO_FATAL_FAILURE(VerifyClientSignature()); +} + +void Session::VerifyPST(const Test_PST_Report& expected) { + wvcdm::Unpacked_PST_Report computed = pst_report(); + EXPECT_EQ(expected.status, computed.status()); + char* pst_ptr = reinterpret_cast(computed.pst()); + std::string computed_pst(pst_ptr, pst_ptr + computed.pst_length()); + ASSERT_EQ(expected.pst, computed_pst); + EXPECT_NEAR(expected.seconds_since_license_received, + computed.seconds_since_license_received(), + kTimeTolerance); + // Decrypt times only valid on licenses that have been active. + if (expected.status == kActive || expected.status == kInactiveUsed) { + EXPECT_NEAR(expected.seconds_since_first_decrypt, + computed.seconds_since_first_decrypt(), + kUsageTableTimeTolerance); + EXPECT_NEAR(expected.seconds_since_last_decrypt, + computed.seconds_since_last_decrypt(), + kUsageTableTimeTolerance); + } + std::vector signature(SHA_DIGEST_LENGTH); + unsigned int md_len = SHA_DIGEST_LENGTH; + if (!HMAC(EVP_sha1(), &mac_key_client_[0], mac_key_client_.size(), + &pst_report_buffer_[0] + SHA_DIGEST_LENGTH, + pst_report_buffer_.size() - SHA_DIGEST_LENGTH, + &signature[0], &md_len)) { + cout << "Error computing HMAC.\n"; + dump_boringssl_error(); + } + EXPECT_EQ(0, memcmp(computed.signature(), &signature[0], + SHA_DIGEST_LENGTH)); +} + +// 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) { + int64_t k10Minutes = 60 * 10; // in seconds. + if (t > k10Minutes) return now - t; + return t; +} + +void Session::GenerateVerifyReport(const std::string& pst, + OEMCrypto_Usage_Entry_Status status, + int64_t time_license_received, + int64_t time_first_decrypt, + int64_t time_last_decrypt) { + ASSERT_NO_FATAL_FAILURE(GenerateReport(pst)); + Test_PST_Report expected(pst, status); + time_t now = time(NULL); + expected.seconds_since_license_received = + MaybeAdjustTime(time_license_received, now); + expected.seconds_since_first_decrypt = + MaybeAdjustTime(time_first_decrypt, now); + expected.seconds_since_last_decrypt = MaybeAdjustTime(time_last_decrypt, now); + ASSERT_NO_FATAL_FAILURE(VerifyPST(expected)); +} + +void Session::CreateOldEntry(const Test_PST_Report& report) { + OEMCryptoResult result = OEMCrypto_CreateOldUsageEntry( + report.seconds_since_license_received, + report.seconds_since_first_decrypt, + report.seconds_since_last_decrypt, + report.status, &mac_key_server_[0], + &mac_key_client_[0], + reinterpret_cast(report.pst.c_str()), + report.pst.length()); + if (result == OEMCrypto_ERROR_NOT_IMPLEMENTED) return; + ASSERT_EQ(OEMCrypto_SUCCESS, result); +} + +void Session::CopyAndVerifyOldEntry(const Test_PST_Report& report, + std::vector* header_buffer) { + ASSERT_NO_FATAL_FAILURE(CreateNewUsageEntry()); + OEMCryptoResult result = OEMCrypto_CopyOldUsageEntry( + session_id(), reinterpret_cast(report.pst.c_str()), + report.pst.length()); + if (result == OEMCrypto_ERROR_NOT_IMPLEMENTED) { + cout << "WARNING: OEMCrypto CANNOT copy old usage table to new." << endl; + return; + } + ASSERT_NO_FATAL_FAILURE(UpdateUsageEntry(header_buffer)); + ASSERT_NO_FATAL_FAILURE(GenerateReport(report.pst)); + ASSERT_NO_FATAL_FAILURE(VerifyPST(report)); +} + +const uint8_t* Session::message_ptr() { + return reinterpret_cast(&encrypted_license()); +} + +void Session::set_message_size(size_t size) { + message_size_ = size; + ASSERT_GE(message_size_, sizeof(MessageData)); + ASSERT_LE(message_size_, kMaxMessageSize); +} + +} // namespace wvoec diff --git a/test/oec_session_util.h b/test/oec_session_util.h new file mode 100644 index 0000000..15c123a --- /dev/null +++ b/test/oec_session_util.h @@ -0,0 +1,412 @@ +#ifndef CDM_OEC_SESSION_UTIL_H_ +#define CDM_OEC_SESSION_UTIL_H_ + +// Copyright 2016 Google Inc. All Rights Reserved. +// +// OEMCrypto unit tests +// +#include +#include +#include + +#include "oec_device_features.h" +#include "pst_report.h" +#include "wv_cdm_constants.h" + +using namespace std; + +// GTest requires PrintTo to be in the same namespace as the thing it prints, +// which is std::vector in this case. +namespace std { + +void PrintTo(const vector& value, ostream* os); + +} // namespace std + +namespace wvoec { + +const size_t kMaxNumKeys = 20; + +namespace { +#if defined(TEST_SPEED_MULTIPLIER) // Can slow test time limits when + // debugging is slowing everything. +const int kSpeedMultiplier = TEST_SPEED_MULTIPLIER; +#else +const 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; +} // namespace + +typedef struct { + uint8_t verification[4]; + uint32_t duration; + uint32_t nonce; + uint32_t control_bits; +} KeyControlBlock; + +// 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; + +// Most content will use a key id that is 16 bytes long. +const int kDefaultKeyIdLength = 16; + +const size_t kMaxTestRSAKeyLength = 2000; // Rough estimate. +const size_t kMaxPSTLength = 255; // In specification. +const size_t kMaxMessageSize = 8 * 1024; // In specification. +const size_t kMaxDecryptSize = 100 * 1024; // In specification. + +typedef struct { + uint8_t key_id[kTestKeyIdMaxLength]; + size_t key_id_length; + uint8_t key_data[wvcdm::MAC_KEY_SIZE]; + size_t key_data_length; + uint8_t key_iv[wvcdm::KEY_IV_SIZE]; + uint8_t control_iv[wvcdm::KEY_IV_SIZE]; + KeyControlBlock control; + // Note: cipher_mode may not be part of a real signed message. For these + // tests, it is convenient to keep it in this structure anyway. + OEMCryptoCipherMode cipher_mode; +} MessageKeyData; + +// This structure will be signed to simulate a message from the server. +struct MessageData { + MessageKeyData keys[kMaxNumKeys]; + uint8_t mac_key_iv[wvcdm::KEY_IV_SIZE]; + uint8_t mac_keys[2 * wvcdm::MAC_KEY_SIZE]; + uint8_t pst[kMaxPSTLength]; +}; + +// This structure will be signed to simulate a provisioning response from the +// server. +struct RSAPrivateKeyMessage { + uint8_t rsa_key[kMaxTestRSAKeyLength]; + uint8_t rsa_key_iv[wvcdm::KEY_IV_SIZE]; + size_t rsa_key_length; + uint32_t nonce; +}; + +struct Test_PST_Report { + Test_PST_Report(const std::string& pst_in, + OEMCrypto_Usage_Entry_Status status_in) + : status(status_in), pst(pst_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; +}; + +struct EntitledContentKeyData { + uint8_t entitlement_key_id[wvcdm::KEY_SIZE]; + uint8_t content_key_id[wvcdm::KEY_SIZE]; + uint8_t content_key_data_iv[wvcdm::KEY_SIZE]; + uint8_t content_key_data[wvcdm::KEY_SIZE]; +}; + +// Increment counter for AES-CTR. The CENC spec specifies we increment only +// the low 64 bits of the IV counter, and leave the high 64 bits alone. This +// is different from the OpenSSL implementation, so we implement the CTR loop +// ourselves. +void ctr128_inc64(int64_t increaseBy, uint8_t* iv); + +// Some compilers don't like the macro htonl within an ASSERT_EQ. +uint32_t htonl_fnc(uint32_t x); + +// Prints error string from BoringSSL +void dump_boringssl_error(); + +class Session { + public: + Session(); + ~Session(); + + // Returns the most recently generated nonce. + // Valid after call to GenerateNonce. + uint32_t get_nonce() { return nonce_; } + // Valid after call to open(). + uint32_t session_id() { return (uint32_t)session_id_; } + // Call OEMCrypto_OpenSession, with GTest ASSERTs. + void open(); + // Call OEMCrypto_CloseSession, with GTest ASSERTs. + void close(); + // Artifically set session id without calling OEMCrypto_OpenSession. This is + // used in core/test/generic_crypto_unittest.cpp. + void SetSessionId(uint32_t session_id); + uint32_t GetOecSessionId() { return session_id_; } + // Generates one nonce. If error_counter is null, this will sleep 1 second + // and try again if a nonce flood has been detected. If error_counter is + // not null, it will be incremented when a nonce flood is detected. + void GenerateNonce(int* error_counter = NULL); + // Fill the vectors with test context which generate known mac and enc keys. + void FillDefaultContext(vector* mac_context, + vector* enc_context); + // Generate known mac and enc keys using OEMCrypto_GenerateDerivedKeys and + // also fill out enc_key_, mac_key_server_, and mac_key_client_. + void GenerateDerivedKeysFromKeybox(const wvoec_mock::WidevineKeybox& keybox); + // Generate known mac and enc keys using OEMCrypto_DeriveKeysFromSessionKey + // and also fill out enc_key_, mac_key_server_, and mac_key_client_. + void GenerateDerivedKeysFromSessionKey(); + // Loads and verifies the keys in the message pointed to by message_ptr() + // using OEMCrypto_LoadKeys. This message should have already been created + // by FillSimpleMessage, modified if needed, and then encrypted and signed by + // the server's mac key in EncryptAndSign. + void LoadTestKeys(const std::string& pst = "", bool new_mac_keys = true); + // Loads the entitlement keys in the message pointed to by message_ptr() + // using OEMCrypto_LoadKeys. This message should have already been created + // by FillSimpleEntitlementMessage, modified if needed, and then encrypted + // and signed by the server's mac key in EncryptAndSign. + void LoadEnitlementTestKeys(const std::string& pst = "", + bool new_mac_keys = true, + OEMCryptoResult expected_sts = OEMCrypto_SUCCESS); + // Fills an OEMCrypto_EntitledContentKeyObject using the information from + // the license_ and randomly generated content keys. This method should be + // called after LoadEnitlementTestKeys. + void FillEntitledKeyArray(); + // Encrypts and loads the entitled content keys via + // OEMCrypto_LoadEntitledContentKeys. + void LoadEntitledContentKeys( + OEMCryptoResult expected_sts = OEMCrypto_SUCCESS); + // This uses OEMCrypto_QueryKeyControl to check that the keys in OEMCrypto + // have the correct key control data. + void VerifyTestKeys(); + // This uses OEMCrypto_QueryKeyControl to check that the keys in OEMCrypto + // have the correct key control data. + void VerifyEntitlementTestKeys(); + // This creates a refresh key or license renewal message, signs it with the + // server's mac key, and calls OEMCrypto_RefreshKeys. + void RefreshTestKeys(const size_t key_count, uint32_t control_bits, + uint32_t nonce, OEMCryptoResult expected_result); + // This sets the key id in the current message data to the specified string. + // This is used to test with different key id lengths. + void SetKeyId(int index, const string& key_id); + // This fills the data structure license_ with key information. This data + // can be modified, and then should be encrypted and signed in EncryptAndSign + // before being loaded in LoadTestKeys. + void FillSimpleMessage(uint32_t duration, uint32_t control, uint32_t nonce, + const std::string& pst = ""); + // This fills the data structure license_ with entitlement key information. + // This data can be modified, and then should be encrypted and signed in + // EncryptAndSign before being loaded in LoadEnitlementTestKeys. + void FillSimpleEntitlementMessage( + uint32_t duration, uint32_t control, + uint32_t nonce, const std::string& pst = ""); + // Like FillSimpleMessage, this fills encrypted_license_ with data. The name + // is a little misleading: the license renewal message is not encrypted, it + // is just signed. The signature is computed in RefreshTestKeys, above. + void FillRefreshMessage(size_t key_count, uint32_t control_bits, + uint32_t nonce); + // This copies data from license_ to encrypted_license_, and then encrypts + // each field in the key array appropriately. It then signes the buffer with + // 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 encrypts an RSAPrivateKeyMessage with encryption_key so that it may be + // loaded with OEMCrypto_RewrapDeviceRSAKey. + void EncryptProvisioningMessage(RSAPrivateKeyMessage* data, + RSAPrivateKeyMessage* encrypted, + const vector& encryption_key); + // Sign the buffer with server's mac key. + void ServerSignBuffer(const uint8_t* data, size_t data_length, + std::vector* signature); + // Sign the buffer with client's known mac key. Known test keys must be + // installed first. + void ClientSignMessage(const vector& data, + std::vector* signature); + // This checks the signature generated by OEMCrypto_GenerateSignature against + // that generaged by ClientSignMessage. + void VerifyClientSignature(size_t data_length = 400); + // 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); + // As in FillKeyArray but for the license renewal message passed to + // OEMCrypto_RefreshKeys. + void FillRefreshArray(OEMCrypto_KeyRefreshObject* key_array, + size_t key_count); + // Encrypt a block of data using CTR mode. + void EncryptCTR(const vector& in_buffer, const uint8_t* key, + const uint8_t* starting_iv, vector* out_buffer); + // Encrypt some data and pass to OEMCrypto_DecryptCENC to verify decryption. + void TestDecryptCTR(bool select_key_first = true, + OEMCryptoResult expected_result = OEMCrypto_SUCCESS, + int key_index = 0); + // This compares the actual result with the expected result. If OEMCrypto is + // an older version, we allow it to report an equivalent error code. + void TestDecryptResult(OEMCryptoResult expected_result, + OEMCryptoResult actual_result); + // Verify that an attempt to select an expired key either succeeds, or gives + // an actionable error code. + void TestSelectExpired(unsigned int key_index); + // Calls OEMCrypto_GetOEMPublicCertificate 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 vector* encryption_key = NULL); + // 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, + size_t message_size, const std::vector& signature, + vector* wrapped_key, bool force); + // Loads the specified RSA public key into public_rsa_. If rsa_key is null, + // the default test key is loaded. + void PreparePublicKey(const uint8_t* rsa_key = NULL, + size_t rsa_key_length = 0); + // Verifies the given signature is from the given message and RSA key, pkey. + static bool VerifyPSSSignature(EVP_PKEY* pkey, const uint8_t* message, + size_t message_length, + const uint8_t* signature, + size_t signature_length); + // Verify that the message was signed by the private key associated with + // |public_rsa_| using the specified padding scheme. + void VerifyRSASignature(const vector& message, + const uint8_t* signature, size_t signature_length, + RSA_Padding_Scheme padding_scheme); + // Encrypts a known session key with public_rsa_ for use in future calls to + // OEMCrypto_DeriveKeysFromSessionKey or OEMCrypto_RewrapDeviceRSAKey30. + // The unencrypted session key is stored in session_key. + bool GenerateRSASessionKey(vector* session_key, + vector* enc_session_key); + // Calls OEMCrypto_RewrapDeviceRSAKey30 with the given provisioning response + // message. If force is true, we assert that the key loads successfully. + void RewrapRSAKey30(const struct RSAPrivateKeyMessage& encrypted, + const std::vector& encrypted_message_key, + vector* wrapped_key, bool force); + // Loads the specified wrapped_rsa_key into OEMCrypto, and then runs + // GenerateDerivedKeysFromSessionKey to install known encryption and mac keys. + void InstallRSASessionTestKey(const vector& wrapped_rsa_key); + // Creates a new usage entry, and keeps track of the index. + void CreateNewUsageEntry(); + // Copy encrypted usage entry from other session, and then load it. + // This session must already be open. + void LoadUsageEntry(uint32_t index, const vector& buffer); + // Copy encrypted usage entry from other session. + // This session must already be open. + void LoadUsageEntry(const Session& other) { + LoadUsageEntry(other.usage_entry_number(), other.encrypted_usage_entry()); + } + // Reload previously used usage entry. + void ReloadUsageEntry() { LoadUsageEntry(*this); } + // Update the usage entry and save the header to the specified buffer. + void UpdateUsageEntry(std::vector* header_buffer); + // Deactivate this session's usage entry. + void DeactivateUsageEntry(const std::string& pst); + // The usage entry number for this session's usage entry. + uint32_t usage_entry_number() const { return usage_entry_number_; } + void set_usage_entry_number(uint32_t v) { usage_entry_number_ = v; } + // The encrypted buffer holding the recently updated and saved usage entry. + const vector& encrypted_usage_entry() const { + return encrypted_usage_entry_; + } + // 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 + // order to verify signatures. + void GenerateReport(const std::string& pst, + OEMCryptoResult expected_result = OEMCrypto_SUCCESS, + Session* other = 0); + // Move this usage entry to a new index. + void MoveUsageEntry(uint32_t new_index, std::vector* header_buffer, + OEMCryptoResult expect_result = OEMCrypto_SUCCESS); + // PST used in FillSimpleMesage. + string pst() const { return pst_; } + // Returns a pointer-like thing to the usage report generated by the previous + // call to GenerateReport. + wvcdm::Unpacked_PST_Report pst_report() { + return wvcdm::Unpacked_PST_Report(&pst_report_buffer_[0]); + } + // Verify the values in the PST report. The signature should have been + // verified in GenerateReport, above. + void VerifyPST(const Test_PST_Report& report); + // Generate and Verify the Usage Report. If any time is greater than 10 + // minutes, it is assumed to be an absolute time, and time_since will be + // computed relative to now. + void GenerateVerifyReport(const std::string& pst, + OEMCrypto_Usage_Entry_Status status, + int64_t time_license_received = 0, + int64_t time_first_decrypt = 0, + int64_t time_last_decrypt = 0); + // Create an entry in the old usage table based on the given report. + void CreateOldEntry(const Test_PST_Report &report); + // Create a new entry and copy the old entry into it. Then very the report + // is right. + void CopyAndVerifyOldEntry(const Test_PST_Report &report, + std::vector* header_buffer); + + // The unencrypted license response or license renewal response. + MessageData& license() { return license_; } + // The encrypted license response or license renewal response. + MessageData& encrypted_license() { return padded_message_; } + + // A pointer to the buffer holding encrypted_license. + const uint8_t* message_ptr(); + + // An array of key objects for use in LoadKeys. + OEMCrypto_KeyObject* key_array() { return key_array_; } + // The last signature generated with the server's mac key. + std::vector& signature() { return signature_; } + + // Set the number of keys to use in the license(), encrypted_license() + // and key_array(). + void set_num_keys(int num_keys) { num_keys_ = num_keys; } + // The current number of keys to use in the license(), encrypted_license() + // and key_array(). + unsigned int num_keys() const { return num_keys_; } + + // Set the size of the buffer used the encrypted license. + // Must be between sizeof(MessageData) and kMaxMessageSize. + void set_message_size(size_t size); + // The size of the encrypted message. + size_t message_size() { return message_size_; } + + private: + // Generate mac and enc keys give the master key. + void DeriveKeys(const uint8_t* master_key, + const vector& mac_key_context, + const vector& enc_key_context); + // Internal utility function to derive key using CMAC-128 + void DeriveKey(const uint8_t* key, const vector& context, + int counter, vector* out); + + bool open_; + bool forced_session_id_; + OEMCrypto_SESSION session_id_; + vector mac_key_server_; + vector mac_key_client_; + vector enc_key_; + uint32_t nonce_; + RSA* public_rsa_; + vector pst_report_buffer_; + MessageData license_; + struct PaddedMessageData : public MessageData { + uint8_t padding[kMaxMessageSize - sizeof(MessageData)]; + } padded_message_; + size_t message_size_; // How much of the padded message to use. + OEMCrypto_KeyObject key_array_[kMaxNumKeys]; + std::vector signature_; + unsigned int num_keys_; + vector encrypted_usage_entry_; + uint32_t usage_entry_number_; + string pst_; + + // Clear Entitlement key data. This is the backing data for + // |entitled_key_array_|. + EntitledContentKeyData entitled_key_data_[kMaxNumKeys]; + // Entitled key object. Pointers are backed by |entitled_key_data_|. + OEMCrypto_EntitledContentKeyObject entitled_key_array_[kMaxNumKeys]; +}; + +} // namespace wvoec + +#endif // CDM_OEC_SESSION_UTIL_H_ diff --git a/test/oec_test_data.h b/test/oec_test_data.h new file mode 100644 index 0000000..cac31c7 --- /dev/null +++ b/test/oec_test_data.h @@ -0,0 +1,465 @@ +// Copyright 2016 Google Inc. All Rights Reserved. +// +// Test data for OEMCrypto unit tests. +// +#ifndef CDM_OEC_TEST_DATA_H_ +#define CDM_OEC_TEST_DATA_H_ + +#include + +#include "OEMCryptoCENC.h" +#include "wv_keybox.h" + +namespace wvoec { + +// These are test keyboxes. They will not be accepted by production systems. +// By using known keyboxes for these tests, the results for a given set of +// inputs to a test are predictable and can be compared to the actual results. +// The first keybox, kTestKeybox, with deviceID "TestKey01" is used for most of +// the tests. It should be loaded by OEMCrypto when OEMCrypto_LoadTestKeybox +// is called. +static const wvoec_mock::WidevineKeybox kTestKeybox = { + // Sample keybox used for test vectors + { + // deviceID = WidevineTestOnlyKeybox000 + 0x57, 0x69, 0x64, 0x65, 0x76, 0x69, 0x6e, 0x65, + 0x54, 0x65, 0x73, 0x74, 0x4f, 0x6e, 0x6c, 0x79, + 0x4b, 0x65, 0x79, 0x62, 0x6f, 0x78, 0x30, 0x30, + 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + }, { + // key + 0xe4, 0xff, 0x57, 0x4c, 0x32, 0x2e, 0xf5, 0x34, + 0x26, 0x21, 0x2c, 0xb3, 0xed, 0x37, 0xf3, 0x5e, + }, { + // data (system ID 7912). + 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x1e, 0xe8, + 0xca, 0x1e, 0x71, 0x7c, 0xfb, 0xe8, 0xa3, 0x94, + 0x52, 0x0a, 0x6b, 0x71, 0x37, 0xd2, 0x69, 0xfa, + 0x5a, 0xc6, 0xb5, 0x4c, 0x6b, 0x46, 0x63, 0x9b, + 0xbe, 0x80, 0x3d, 0xbb, 0x4f, 0xf7, 0x4c, 0x5f, + 0x6f, 0x55, 0x0e, 0x3d, 0x3d, 0x9a, 0xcf, 0x81, + 0x12, 0x5d, 0x52, 0xe0, 0x47, 0x8c, 0xda, 0x0b, + 0xf4, 0x31, 0x41, 0x13, 0xd0, 0xd5, 0x2d, 0xa0, + 0x5b, 0x20, 0x9a, 0xed, 0x51, 0x5d, 0x13, 0xd6, + }, { + // magic + 0x6b, 0x62, 0x6f, 0x78, + }, { + // Crc + 0x39, 0xf2, 0x94, 0xa7, + } +}; + +// These are old test keyboxes. The first keybox can be used to update an +// older OEMCrypto because it is the same keybox that was previously used in +// unit tests. +static const wvoec_mock::WidevineKeybox kValidKeybox01 = { + // Sample keybox used for test vectors + { + // deviceID + 0x54, 0x65, 0x73, 0x74, 0x4b, 0x65, 0x79, 0x30, // TestKey01 + 0x31, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ........ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ........ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ........ + }, { + // key + 0xfb, 0xda, 0x04, 0x89, 0xa1, 0x58, 0x16, 0x0e, + 0xa4, 0x02, 0xe9, 0x29, 0xe3, 0xb6, 0x8f, 0x04, + }, { + // data + 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x10, 0x19, + 0x07, 0xd9, 0xff, 0xde, 0x13, 0xaa, 0x95, 0xc1, + 0x22, 0x67, 0x80, 0x53, 0x36, 0x21, 0x36, 0xbd, + 0xf8, 0x40, 0x8f, 0x82, 0x76, 0xe4, 0xc2, 0xd8, + 0x7e, 0xc5, 0x2b, 0x61, 0xaa, 0x1b, 0x9f, 0x64, + 0x6e, 0x58, 0x73, 0x49, 0x30, 0xac, 0xeb, 0xe8, + 0x99, 0xb3, 0xe4, 0x64, 0x18, 0x9a, 0x14, 0xa8, + 0x72, 0x02, 0xfb, 0x02, 0x57, 0x4e, 0x70, 0x64, + 0x0b, 0xd2, 0x2e, 0xf4, 0x4b, 0x2d, 0x7e, 0x39, + }, { + // magic + 0x6b, 0x62, 0x6f, 0x78, + }, { + // Crc + 0x0a, 0x7a, 0x2c, 0x35, + } +}; + +static const wvoec_mock::WidevineKeybox kValidKeybox02 = { + // Sample keybox used for test vectors + { + // deviceID + 0x54, 0x65, 0x73, 0x74, 0x4b, 0x65, 0x79, 0x30, // TestKey02 + 0x32, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ........ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ........ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ........ + }, { + // key + 0x76, 0x5d, 0xce, 0x01, 0x04, 0x89, 0xb3, 0xd0, + 0xdf, 0xce, 0x54, 0x8a, 0x49, 0xda, 0xdc, 0xb6, + }, { + // data + 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x10, 0x19, + 0x92, 0x27, 0x0b, 0x1f, 0x1a, 0xd5, 0xc6, 0x93, + 0x19, 0x3f, 0xaa, 0x74, 0x1f, 0xdd, 0x5f, 0xb4, + 0xe9, 0x40, 0x2f, 0x34, 0xa4, 0x92, 0xf4, 0xae, + 0x9a, 0x52, 0x39, 0xbc, 0xb7, 0x24, 0x38, 0x13, + 0xab, 0xf4, 0x92, 0x96, 0xc4, 0x81, 0x60, 0x33, + 0xd8, 0xb8, 0x09, 0xc7, 0x55, 0x0e, 0x12, 0xfa, + 0xa8, 0x98, 0x62, 0x8a, 0xec, 0xea, 0x74, 0x8a, + 0x4b, 0xfa, 0x5a, 0x9e, 0xb6, 0x49, 0x0d, 0x80, + }, { + // magic + 0x6b, 0x62, 0x6f, 0x78, + }, { + // Crc + 0x2a, 0x3b, 0x3e, 0xe4, + } +}; + +static const wvoec_mock::WidevineKeybox kValidKeybox03 = { + // Sample keybox used for test vectors + { + // deviceID + 0x54, 0x65, 0x73, 0x74, 0x4b, 0x65, 0x79, 0x30, // TestKey03 + 0x33, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ........ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ........ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ........ + }, { + // key + 0x25, 0xe5, 0x2a, 0x02, 0x29, 0x68, 0x04, 0xa2, + 0x92, 0xfd, 0x7c, 0x67, 0x0b, 0x67, 0x1f, 0x31, + }, { + // data + 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x10, 0x19, + 0xf4, 0x0a, 0x0e, 0xa2, 0x0a, 0x71, 0xd5, 0x92, + 0xfa, 0xa3, 0x25, 0xc6, 0x4b, 0x76, 0xf1, 0x64, + 0xf4, 0x60, 0xa0, 0x30, 0x72, 0x23, 0xbe, 0x03, + 0xcd, 0xde, 0x7a, 0x06, 0xd4, 0x01, 0xeb, 0xdc, + 0xe0, 0x50, 0xc0, 0x53, 0x0a, 0x50, 0xb0, 0x37, + 0xe5, 0x05, 0x25, 0x0e, 0xa4, 0xc8, 0x5a, 0xff, + 0x46, 0x6e, 0xa5, 0x31, 0xf3, 0xdd, 0x94, 0xb7, + 0xe0, 0xd3, 0xf9, 0x04, 0xb2, 0x54, 0xb1, 0x64, + }, { + // magic + 0x6b, 0x62, 0x6f, 0x78, + }, { + // Crc + 0xa1, 0x99, 0x5f, 0x46, + } +}; + +// A 2048 bit RSA key in PKCS#8 PrivateKeyInfo format +// Used to verify the functions that manipulate RSA keys. +static const uint8_t kTestRSAPKCS8PrivateKeyInfo2_2048[] = { + 0x30, 0x82, 0x04, 0xbc, 0x02, 0x01, 0x00, 0x30, + 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, + 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00, 0x04, 0x82, + 0x04, 0xa6, 0x30, 0x82, 0x04, 0xa2, 0x02, 0x01, + 0x00, 0x02, 0x82, 0x01, 0x01, 0x00, 0xa7, 0x00, + 0x36, 0x60, 0x65, 0xdc, 0xbd, 0x54, 0x5a, 0x2a, + 0x40, 0xb4, 0xe1, 0x15, 0x94, 0x58, 0x11, 0x4f, + 0x94, 0x58, 0xdd, 0xde, 0xa7, 0x1f, 0x3c, 0x2c, + 0xe0, 0x88, 0x09, 0x29, 0x61, 0x57, 0x67, 0x5e, + 0x56, 0x7e, 0xee, 0x27, 0x8f, 0x59, 0x34, 0x9a, + 0x2a, 0xaa, 0x9d, 0xb4, 0x4e, 0xfa, 0xa7, 0x6a, + 0xd4, 0xc9, 0x7a, 0x53, 0xc1, 0x4e, 0x9f, 0xe3, + 0x34, 0xf7, 0x3d, 0xb7, 0xc9, 0x10, 0x47, 0x4f, + 0x28, 0xda, 0x3f, 0xce, 0x31, 0x7b, 0xfd, 0x06, + 0x10, 0xeb, 0xf7, 0xbe, 0x92, 0xf9, 0xaf, 0xfb, + 0x3e, 0x68, 0xda, 0xee, 0x1a, 0x64, 0x4c, 0xf3, + 0x29, 0xf2, 0x73, 0x9e, 0x39, 0xd8, 0xf6, 0x6f, + 0xd8, 0xb2, 0x80, 0x82, 0x71, 0x8e, 0xb5, 0xa4, + 0xf2, 0xc2, 0x3e, 0xcd, 0x0a, 0xca, 0xb6, 0x04, + 0xcd, 0x9a, 0x13, 0x8b, 0x54, 0x73, 0x54, 0x25, + 0x54, 0x8c, 0xbe, 0x98, 0x7a, 0x67, 0xad, 0xda, + 0xb3, 0x4e, 0xb3, 0xfa, 0x82, 0xa8, 0x4a, 0x67, + 0x98, 0x56, 0x57, 0x54, 0x71, 0xcd, 0x12, 0x7f, + 0xed, 0xa3, 0x01, 0xc0, 0x6a, 0x8b, 0x24, 0x03, + 0x96, 0x88, 0xbe, 0x97, 0x66, 0x2a, 0xbc, 0x53, + 0xc9, 0x83, 0x06, 0x51, 0x5a, 0x88, 0x65, 0x13, + 0x18, 0xe4, 0x3a, 0xed, 0x6b, 0xf1, 0x61, 0x5b, + 0x4c, 0xc8, 0x1e, 0xf4, 0xc2, 0xae, 0x08, 0x5e, + 0x2d, 0x5f, 0xf8, 0x12, 0x7f, 0xa2, 0xfc, 0xbb, + 0x21, 0x18, 0x30, 0xda, 0xfe, 0x40, 0xfb, 0x01, + 0xca, 0x2e, 0x37, 0x0e, 0xce, 0xdd, 0x76, 0x87, + 0x82, 0x46, 0x0b, 0x3a, 0x77, 0x8f, 0xc0, 0x72, + 0x07, 0x2c, 0x7f, 0x9d, 0x1e, 0x86, 0x5b, 0xed, + 0x27, 0x29, 0xdf, 0x03, 0x97, 0x62, 0xef, 0x44, + 0xd3, 0x5b, 0x3d, 0xdb, 0x9c, 0x5e, 0x1b, 0x7b, + 0x39, 0xb4, 0x0b, 0x6d, 0x04, 0x6b, 0xbb, 0xbb, + 0x2c, 0x5f, 0xcf, 0xb3, 0x7a, 0x05, 0x02, 0x03, + 0x01, 0x00, 0x01, 0x02, 0x82, 0x01, 0x00, 0x5e, + 0x79, 0x65, 0x49, 0xa5, 0x76, 0x79, 0xf9, 0x05, + 0x45, 0x0f, 0xf4, 0x03, 0xbd, 0xa4, 0x7d, 0x29, + 0xd5, 0xde, 0x33, 0x63, 0xd8, 0xb8, 0xac, 0x97, + 0xeb, 0x3f, 0x5e, 0x55, 0xe8, 0x7d, 0xf3, 0xe7, + 0x3b, 0x5c, 0x2d, 0x54, 0x67, 0x36, 0xd6, 0x1d, + 0x46, 0xf5, 0xca, 0x2d, 0x8b, 0x3a, 0x7e, 0xdc, + 0x45, 0x38, 0x79, 0x7e, 0x65, 0x71, 0x5f, 0x1c, + 0x5e, 0x79, 0xb1, 0x40, 0xcd, 0xfe, 0xc5, 0xe1, + 0xc1, 0x6b, 0x78, 0x04, 0x4e, 0x8e, 0x79, 0xf9, + 0x0a, 0xfc, 0x79, 0xb1, 0x5e, 0xb3, 0x60, 0xe3, + 0x68, 0x7b, 0xc6, 0xef, 0xcb, 0x71, 0x4c, 0xba, + 0xa7, 0x79, 0x5c, 0x7a, 0x81, 0xd1, 0x71, 0xe7, + 0x00, 0x21, 0x13, 0xe2, 0x55, 0x69, 0x0e, 0x75, + 0xbe, 0x09, 0xc3, 0x4f, 0xa9, 0xc9, 0x68, 0x22, + 0x0e, 0x97, 0x8d, 0x89, 0x6e, 0xf1, 0xe8, 0x88, + 0x7a, 0xd1, 0xd9, 0x09, 0x5d, 0xd3, 0x28, 0x78, + 0x25, 0x0b, 0x1c, 0x47, 0x73, 0x25, 0xcc, 0x21, + 0xb6, 0xda, 0xc6, 0x24, 0x5a, 0xd0, 0x37, 0x14, + 0x46, 0xc7, 0x94, 0x69, 0xe4, 0x43, 0x6f, 0x47, + 0xde, 0x00, 0x33, 0x4d, 0x8f, 0x95, 0x72, 0xfa, + 0x68, 0x71, 0x17, 0x66, 0x12, 0x1a, 0x87, 0x27, + 0xf7, 0xef, 0x7e, 0xe0, 0x35, 0x58, 0xf2, 0x4d, + 0x6f, 0x35, 0x01, 0xaa, 0x96, 0xe2, 0x3d, 0x51, + 0x13, 0x86, 0x9c, 0x79, 0xd0, 0xb7, 0xb6, 0x64, + 0xe8, 0x86, 0x65, 0x50, 0xbf, 0xcc, 0x27, 0x53, + 0x1f, 0x51, 0xd4, 0xca, 0xbe, 0xf5, 0xdd, 0x77, + 0x70, 0x98, 0x0f, 0xee, 0xa8, 0x96, 0x07, 0x5f, + 0x45, 0x6a, 0x7a, 0x0d, 0x03, 0x9c, 0x4f, 0x29, + 0xf6, 0x06, 0xf3, 0x5d, 0x58, 0x6c, 0x47, 0xd0, + 0x96, 0xa9, 0x03, 0x17, 0xbb, 0x4e, 0xc9, 0x21, + 0xe0, 0xac, 0xcd, 0x78, 0x78, 0xb2, 0xfe, 0x81, + 0xb2, 0x51, 0x53, 0xa6, 0x1f, 0x98, 0x45, 0x02, + 0x81, 0x81, 0x00, 0xcf, 0x73, 0x8c, 0xbe, 0x6d, + 0x45, 0x2d, 0x0c, 0x0b, 0x5d, 0x5c, 0x6c, 0x75, + 0x78, 0xcc, 0x35, 0x48, 0xb6, 0x98, 0xf1, 0xb9, + 0x64, 0x60, 0x8c, 0x43, 0xeb, 0x85, 0xab, 0x04, + 0xb6, 0x7d, 0x1b, 0x71, 0x75, 0x06, 0xe2, 0xda, + 0x84, 0x68, 0x2e, 0x7f, 0x4c, 0xe3, 0x73, 0xb4, + 0xde, 0x51, 0x4b, 0xb6, 0x51, 0x86, 0x7b, 0xd0, + 0xe6, 0x4d, 0xf3, 0xd1, 0xcf, 0x1a, 0xfe, 0x7f, + 0x3a, 0x83, 0xba, 0xb3, 0xe1, 0xff, 0x54, 0x13, + 0x93, 0xd7, 0x9c, 0x27, 0x80, 0xb7, 0x1e, 0x64, + 0x9e, 0xf7, 0x32, 0x2b, 0x46, 0x29, 0xf7, 0xf8, + 0x18, 0x6c, 0xf7, 0x4a, 0xbe, 0x4b, 0xee, 0x96, + 0x90, 0x8f, 0xa2, 0x16, 0x22, 0x6a, 0xcc, 0x48, + 0x06, 0x74, 0x63, 0x43, 0x7f, 0x27, 0x22, 0x44, + 0x3c, 0x2d, 0x3b, 0x62, 0xf1, 0x1c, 0xb4, 0x27, + 0x33, 0x85, 0x26, 0x60, 0x48, 0x16, 0xcb, 0xef, + 0xf8, 0xcd, 0x37, 0x02, 0x81, 0x81, 0x00, 0xce, + 0x15, 0x43, 0x6e, 0x4b, 0x0f, 0xf9, 0x3f, 0x87, + 0xc3, 0x41, 0x45, 0x97, 0xb1, 0x49, 0xc2, 0x19, + 0x23, 0x87, 0xe4, 0x24, 0x1c, 0x64, 0xe5, 0x28, + 0xcb, 0x43, 0x10, 0x14, 0x14, 0x0e, 0x19, 0xcb, + 0xbb, 0xdb, 0xfd, 0x11, 0x9d, 0x17, 0x68, 0x78, + 0x6d, 0x61, 0x70, 0x63, 0x3a, 0xa1, 0xb3, 0xf3, + 0xa7, 0x5b, 0x0e, 0xff, 0xb7, 0x61, 0x11, 0x54, + 0x91, 0x99, 0xe5, 0x91, 0x32, 0x2d, 0xeb, 0x3f, + 0xd8, 0x3e, 0xf7, 0xd4, 0xcb, 0xd2, 0xa3, 0x41, + 0xc1, 0xee, 0xc6, 0x92, 0x13, 0xeb, 0x7f, 0x42, + 0x58, 0xf4, 0xd0, 0xb2, 0x74, 0x1d, 0x8e, 0x87, + 0x46, 0xcd, 0x14, 0xb8, 0x16, 0xad, 0xb5, 0xbd, + 0x0d, 0x6c, 0x95, 0x5a, 0x16, 0xbf, 0xe9, 0x53, + 0xda, 0xfb, 0xed, 0x83, 0x51, 0x67, 0xa9, 0x55, + 0xab, 0x54, 0x02, 0x95, 0x20, 0xa6, 0x68, 0x17, + 0x53, 0xa8, 0xea, 0x43, 0xe5, 0xb0, 0xa3, 0x02, + 0x81, 0x80, 0x67, 0x9c, 0x32, 0x83, 0x39, 0x57, + 0xff, 0x73, 0xb0, 0x89, 0x64, 0x8b, 0xd6, 0xf0, + 0x0a, 0x2d, 0xe2, 0xaf, 0x30, 0x1c, 0x2a, 0x97, + 0xf3, 0x90, 0x9a, 0xab, 0x9b, 0x0b, 0x1b, 0x43, + 0x79, 0xa0, 0xa7, 0x3d, 0xe7, 0xbe, 0x8d, 0x9c, + 0xeb, 0xdb, 0xad, 0x40, 0xdd, 0xa9, 0x00, 0x80, + 0xb8, 0xe1, 0xb3, 0xa1, 0x6c, 0x25, 0x92, 0xe4, + 0x33, 0xb2, 0xbe, 0xeb, 0x4d, 0x74, 0x26, 0x5f, + 0x37, 0x43, 0x9c, 0x6c, 0x17, 0x76, 0x0a, 0x81, + 0x20, 0x82, 0xa1, 0x48, 0x2c, 0x2d, 0x45, 0xdc, + 0x0f, 0x62, 0x43, 0x32, 0xbb, 0xeb, 0x59, 0x41, + 0xf9, 0xca, 0x58, 0xce, 0x4a, 0x66, 0x53, 0x54, + 0xc8, 0x28, 0x10, 0x1e, 0x08, 0x71, 0x16, 0xd8, + 0x02, 0x71, 0x41, 0x58, 0xd4, 0x56, 0xcc, 0xf5, + 0xb1, 0x31, 0xa3, 0xed, 0x00, 0x85, 0x09, 0xbf, + 0x35, 0x95, 0x41, 0x29, 0x40, 0x19, 0x83, 0x35, + 0x24, 0x69, 0x02, 0x81, 0x80, 0x55, 0x10, 0x0b, + 0xcc, 0x3b, 0xa9, 0x75, 0x3d, 0x16, 0xe1, 0xae, + 0x50, 0x76, 0x63, 0x94, 0x49, 0x4c, 0xad, 0x10, + 0xcb, 0x47, 0x68, 0x7c, 0xf0, 0xe5, 0xdc, 0xb8, + 0x6a, 0xab, 0x8e, 0xf7, 0x9f, 0x08, 0x2c, 0x1b, + 0x8a, 0xa2, 0xb9, 0x8f, 0xce, 0xec, 0x5e, 0x61, + 0xa8, 0xcd, 0x1c, 0x87, 0x60, 0x4a, 0xc3, 0x1a, + 0x5f, 0xdf, 0x87, 0x26, 0xc6, 0xcb, 0x7c, 0x69, + 0xe4, 0x8b, 0x01, 0x06, 0x59, 0x22, 0xfa, 0x34, + 0x4b, 0x81, 0x87, 0x3c, 0x03, 0x6d, 0x02, 0x0a, + 0x77, 0xe6, 0x15, 0xd8, 0xcf, 0xa7, 0x68, 0x26, + 0x6c, 0xfa, 0x2b, 0xd9, 0x83, 0x5a, 0x2d, 0x0c, + 0x3b, 0x70, 0x1c, 0xd4, 0x48, 0xbe, 0xa7, 0x0a, + 0xd9, 0xbe, 0xdc, 0xc3, 0x0c, 0x21, 0x33, 0xb3, + 0x66, 0xff, 0x1c, 0x1b, 0xc8, 0x96, 0x76, 0xe8, + 0x6f, 0x44, 0x74, 0xbc, 0x9b, 0x1c, 0x7d, 0xc8, + 0xac, 0x21, 0xa8, 0x6e, 0x37, 0x02, 0x81, 0x80, + 0x2c, 0x7c, 0xad, 0x1e, 0x75, 0xf6, 0x69, 0x1d, + 0xe7, 0xa6, 0xca, 0x74, 0x7d, 0x67, 0xc8, 0x65, + 0x28, 0x66, 0xc4, 0x43, 0xa6, 0xbd, 0x40, 0x57, + 0xae, 0xb7, 0x65, 0x2c, 0x52, 0xf9, 0xe4, 0xc7, + 0x81, 0x7b, 0x56, 0xa3, 0xd2, 0x0d, 0xe8, 0x33, + 0x70, 0xcf, 0x06, 0x84, 0xb3, 0x4e, 0x44, 0x50, + 0x75, 0x61, 0x96, 0x86, 0x4b, 0xb6, 0x2b, 0xad, + 0xf0, 0xad, 0x57, 0xd0, 0x37, 0x0d, 0x1d, 0x35, + 0x50, 0xcb, 0x69, 0x22, 0x39, 0x29, 0xb9, 0x3a, + 0xd3, 0x29, 0x23, 0x02, 0x60, 0xf7, 0xab, 0x30, + 0x40, 0xda, 0x8e, 0x4d, 0x45, 0x70, 0x26, 0xf4, + 0xa2, 0x0d, 0xd0, 0x64, 0x5d, 0x47, 0x3c, 0x18, + 0xf4, 0xd4, 0x52, 0x95, 0x00, 0xae, 0x84, 0x6b, + 0x47, 0xb2, 0x3c, 0x82, 0xd3, 0x72, 0x53, 0xde, + 0x72, 0x2c, 0xf7, 0xc1, 0x22, 0x36, 0xd9, 0x18, + 0x56, 0xfe, 0x39, 0x28, 0x33, 0xe0, 0xdb, 0x03 }; + +// A 3072 bit RSA key in PKCS#8 PrivateKeyInfo format +// Used to verify the functions that manipulate RSA keys. +static const uint8_t kTestRSAPKCS8PrivateKeyInfo3_3072[] = { + 0x30, 0x82, 0x06, 0xfe, 0x02, 0x01, 0x00, 0x30, 0x0d, 0x06, 0x09, 0x2a, + 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00, 0x04, 0x82, + 0x06, 0xe8, 0x30, 0x82, 0x06, 0xe4, 0x02, 0x01, 0x00, 0x02, 0x82, 0x01, + 0x81, 0x00, 0xe3, 0x32, 0x2f, 0x0a, 0x94, 0x06, 0x46, 0x99, 0x0a, 0x58, + 0xda, 0xd0, 0x9b, 0x2b, 0xe4, 0x2a, 0x35, 0xdf, 0xb7, 0x9b, 0x5e, 0xbf, + 0xfb, 0xe5, 0x24, 0x47, 0x5a, 0x94, 0x06, 0x04, 0xe5, 0x43, 0xed, 0x37, + 0x33, 0x94, 0x09, 0xd0, 0xae, 0xad, 0x86, 0xb4, 0xc7, 0xc3, 0x56, 0x6f, + 0x88, 0x74, 0xfb, 0xab, 0xf7, 0xcf, 0xcb, 0xa6, 0x89, 0x48, 0x4a, 0x02, + 0x89, 0xcd, 0xfd, 0x83, 0x3d, 0x2a, 0x27, 0xc1, 0xa2, 0x99, 0x8e, 0xef, + 0xcf, 0x91, 0xd3, 0xb2, 0x96, 0xe7, 0x5f, 0x0c, 0xb3, 0x44, 0x6a, 0xcf, + 0xc1, 0x22, 0xb9, 0xe4, 0xd4, 0xc0, 0xf2, 0xc3, 0x8d, 0xe1, 0x43, 0x38, + 0x31, 0x9c, 0x56, 0x04, 0xd4, 0x9d, 0x41, 0x02, 0x31, 0xce, 0x7e, 0xc0, + 0x11, 0x24, 0x54, 0xb1, 0xa2, 0x99, 0x0e, 0xe2, 0x0c, 0x5b, 0x24, 0x94, + 0x85, 0xe8, 0x8c, 0x30, 0xbb, 0x12, 0x94, 0x74, 0x0f, 0x67, 0xe5, 0x69, + 0xa4, 0xc4, 0x59, 0xd6, 0x77, 0x96, 0xae, 0xc6, 0x00, 0xbe, 0xf5, 0xe6, + 0x1f, 0x71, 0x90, 0x6d, 0xdd, 0xfb, 0x7b, 0x42, 0xd0, 0xdf, 0x4b, 0x58, + 0xaf, 0x9c, 0xba, 0xcb, 0x35, 0x4b, 0xf3, 0x06, 0x3a, 0x20, 0x42, 0x97, + 0x96, 0x95, 0x47, 0xbe, 0x2d, 0xeb, 0x9a, 0xb6, 0xea, 0xe0, 0xc1, 0x1d, + 0x80, 0x61, 0x3e, 0x8e, 0x18, 0x66, 0xf4, 0x26, 0x77, 0xcf, 0x56, 0x27, + 0x8b, 0xde, 0x93, 0x94, 0x3e, 0x1d, 0xe4, 0x5f, 0x6d, 0xf2, 0x39, 0x03, + 0x15, 0x4f, 0x2e, 0x58, 0x59, 0x75, 0x19, 0xb9, 0x24, 0x87, 0xd4, 0xff, + 0x64, 0x82, 0x11, 0x10, 0x34, 0x30, 0x09, 0x39, 0x43, 0x9c, 0xd2, 0x3b, + 0x45, 0xdc, 0x85, 0x4f, 0x6d, 0xb7, 0xbb, 0x49, 0xda, 0x3b, 0x07, 0xa2, + 0x76, 0x56, 0xa0, 0xee, 0xa9, 0xa9, 0x52, 0xb7, 0xf1, 0xfd, 0xde, 0xa1, + 0x6f, 0x0e, 0x7f, 0x82, 0x3f, 0x9e, 0x3d, 0x46, 0xcd, 0x48, 0x55, 0xe8, + 0x59, 0x65, 0xd8, 0xc7, 0xe4, 0x6b, 0xe6, 0xc0, 0xdd, 0x6e, 0x5c, 0xb7, + 0x0c, 0xdb, 0x29, 0xad, 0x8e, 0xa4, 0x86, 0xe9, 0x4d, 0xad, 0x54, 0xf9, + 0x56, 0x06, 0x0e, 0xc4, 0x2b, 0x01, 0xd9, 0x86, 0x1f, 0x65, 0xbe, 0x0d, + 0x77, 0x8d, 0x9d, 0xff, 0x37, 0x97, 0x57, 0xc3, 0x06, 0x8a, 0x05, 0x80, + 0x78, 0xd3, 0xbd, 0x91, 0xa5, 0xc1, 0x11, 0x4d, 0x99, 0x1a, 0x83, 0xd7, + 0x30, 0x1c, 0x24, 0xac, 0xdf, 0x6c, 0xc1, 0x23, 0x60, 0x76, 0x54, 0xbf, + 0x2b, 0xac, 0x34, 0xf0, 0x35, 0x92, 0x0d, 0x36, 0x29, 0x09, 0x24, 0xd5, + 0x54, 0xe9, 0x68, 0x9c, 0x90, 0x07, 0x16, 0x86, 0xb1, 0xd0, 0x9b, 0xa5, + 0x86, 0x4e, 0xce, 0xbf, 0x30, 0x9d, 0x91, 0xd7, 0xd2, 0xa6, 0x4f, 0xbb, + 0xbb, 0x9d, 0x7c, 0x0f, 0x58, 0xaa, 0xf1, 0xd0, 0x90, 0x66, 0x20, 0x48, + 0x8f, 0x29, 0x02, 0x03, 0x01, 0x00, 0x01, 0x02, 0x82, 0x01, 0x81, 0x00, + 0x88, 0x41, 0x28, 0x85, 0x27, 0x91, 0x3b, 0xf5, 0xbc, 0x86, 0xdd, 0x74, + 0x0e, 0x1b, 0x9c, 0x92, 0xd4, 0x0c, 0x7f, 0x88, 0xe6, 0xa2, 0x2c, 0xe7, + 0x97, 0x82, 0x53, 0x88, 0x42, 0xb3, 0xdc, 0xeb, 0x87, 0xf0, 0x7b, 0x36, + 0x65, 0x4c, 0x89, 0xf5, 0xf7, 0xbb, 0xa3, 0xb2, 0x3a, 0xbc, 0x59, 0x12, + 0x0f, 0x7d, 0x6a, 0xf9, 0x6c, 0x21, 0x4c, 0x63, 0xd6, 0x3e, 0xff, 0x76, + 0x52, 0x7b, 0xca, 0xca, 0xe5, 0x5c, 0xf3, 0xaf, 0x34, 0x52, 0x0e, 0x22, + 0x5e, 0xdb, 0xd4, 0x34, 0x9e, 0x84, 0x77, 0x5e, 0xa8, 0xd0, 0x3f, 0xfc, + 0x1b, 0x90, 0x69, 0x27, 0xee, 0x6f, 0xe9, 0x3f, 0x17, 0x99, 0x33, 0xe7, + 0x96, 0x8e, 0xff, 0x13, 0xf0, 0x50, 0xe8, 0x9d, 0xf6, 0xd6, 0x29, 0x71, + 0xa8, 0x79, 0x80, 0x12, 0x5b, 0x22, 0xa6, 0x56, 0x62, 0xf1, 0xcf, 0xfd, + 0x4f, 0x56, 0x4a, 0x5b, 0x32, 0x3d, 0x08, 0xa0, 0x3e, 0xad, 0xc4, 0xeb, + 0x1d, 0x15, 0xca, 0x52, 0xcc, 0x2e, 0x63, 0x74, 0x22, 0xf5, 0x08, 0x16, + 0x8b, 0x8f, 0xd8, 0x79, 0x61, 0xcb, 0x08, 0x89, 0x62, 0x1e, 0xa5, 0xf3, + 0x50, 0xf3, 0x5d, 0xdb, 0x56, 0xbc, 0x7d, 0x4f, 0xab, 0xa0, 0x4d, 0xe6, + 0xe9, 0x47, 0xdd, 0x32, 0x57, 0x6f, 0x2c, 0x1d, 0xee, 0xb3, 0x4a, 0xb3, + 0x07, 0x59, 0x20, 0xb9, 0x5d, 0xe3, 0x54, 0x27, 0x3c, 0x7c, 0x2b, 0x1d, + 0x07, 0xff, 0x49, 0x93, 0xe2, 0xe3, 0xb2, 0x65, 0xf3, 0x69, 0xc1, 0x1c, + 0x2a, 0x75, 0x80, 0x16, 0x37, 0xe6, 0x00, 0x5b, 0xd3, 0x1b, 0xac, 0xca, + 0x8b, 0x8b, 0x98, 0x77, 0x81, 0x67, 0xe3, 0xdc, 0xbb, 0xc4, 0x3a, 0x45, + 0x15, 0xec, 0xd9, 0xad, 0xdb, 0x60, 0xcf, 0xe5, 0xd8, 0xd9, 0xfc, 0xcf, + 0xbe, 0x76, 0x2f, 0x5b, 0x60, 0xdb, 0x06, 0x62, 0x5b, 0x80, 0x7e, 0x53, + 0xde, 0x74, 0xb1, 0xa3, 0xb6, 0x9b, 0x14, 0xd7, 0x09, 0x65, 0x21, 0x1d, + 0xd5, 0xd3, 0x34, 0xca, 0x89, 0xe7, 0xbc, 0xf4, 0x48, 0x81, 0x6a, 0xcf, + 0x28, 0xbe, 0x74, 0x8b, 0x40, 0xad, 0x86, 0xcd, 0xa5, 0xd6, 0xfa, 0x64, + 0x9b, 0xd2, 0xd4, 0x17, 0x20, 0xd6, 0x0d, 0xbe, 0x95, 0xd4, 0xaf, 0xa5, + 0xde, 0x31, 0x0d, 0x6a, 0x90, 0xc6, 0xd0, 0x59, 0xd4, 0x8c, 0x81, 0x2d, + 0x9d, 0x09, 0xf1, 0x22, 0xf5, 0x30, 0x2d, 0xdf, 0x85, 0x54, 0x34, 0x8a, + 0xde, 0x3c, 0xce, 0xdb, 0x36, 0x9f, 0xcf, 0x12, 0x61, 0x0e, 0x99, 0x87, + 0x70, 0x51, 0x04, 0x91, 0x74, 0xc6, 0x88, 0x22, 0x75, 0x02, 0x8f, 0x7e, + 0xb5, 0x79, 0x48, 0x2f, 0xf3, 0x3b, 0xb8, 0x82, 0x3e, 0x7e, 0x45, 0xe5, + 0xb2, 0xc8, 0x4c, 0x12, 0x73, 0xb8, 0x92, 0x04, 0xd1, 0x9a, 0xae, 0xaa, + 0x08, 0xd9, 0x23, 0x54, 0x19, 0x46, 0xc8, 0x56, 0x5f, 0x5e, 0x10, 0xa1, + 0x02, 0x81, 0xc1, 0x00, 0xf6, 0x38, 0x88, 0x31, 0x06, 0x85, 0xd9, 0x00, + 0xf0, 0x6b, 0xd8, 0x7d, 0x76, 0x08, 0xc0, 0x69, 0x6a, 0xfb, 0xa4, 0xc8, + 0xdc, 0x6b, 0x00, 0xaf, 0xae, 0x52, 0x82, 0xe6, 0xba, 0xc9, 0x5e, 0xc9, + 0xb7, 0x7f, 0xa1, 0xc4, 0xcb, 0xa0, 0xbc, 0x66, 0x3c, 0x55, 0x6a, 0xea, + 0x6e, 0x42, 0xf1, 0x6b, 0xbd, 0xc4, 0xf2, 0x6b, 0x91, 0x11, 0x82, 0x20, + 0xc2, 0xe6, 0x9e, 0x96, 0x5c, 0x9a, 0x7e, 0xb3, 0x57, 0x45, 0x9c, 0x42, + 0x60, 0x4c, 0x04, 0x4f, 0x47, 0xfb, 0xa7, 0x68, 0x4e, 0x15, 0x43, 0x5a, + 0x97, 0xb3, 0xfc, 0xd2, 0x91, 0x3c, 0x11, 0x5e, 0xaf, 0x57, 0x2a, 0xa1, + 0x45, 0xa5, 0x60, 0xf0, 0xbe, 0x31, 0xe8, 0xc4, 0x0b, 0x35, 0xe3, 0x42, + 0x9b, 0x22, 0x6b, 0xa3, 0x6c, 0x49, 0x71, 0x20, 0x34, 0x3f, 0x46, 0x0b, + 0x79, 0xc9, 0xb8, 0xb4, 0xbd, 0x9c, 0xad, 0xd3, 0xd8, 0x7e, 0x95, 0x9f, + 0x9a, 0xd4, 0x03, 0xe9, 0x5a, 0x54, 0x46, 0x94, 0x39, 0x55, 0xf1, 0x28, + 0x0d, 0xd1, 0xaa, 0xc9, 0xf8, 0x28, 0x58, 0xef, 0xb0, 0x62, 0xb6, 0x2d, + 0xc7, 0xd2, 0x09, 0x3a, 0x21, 0x0f, 0x7d, 0xa1, 0xb9, 0x59, 0xd5, 0xa7, + 0x43, 0xa9, 0x51, 0xb7, 0xbf, 0x9d, 0xf3, 0x85, 0xec, 0xb3, 0xfb, 0x51, + 0x61, 0xca, 0x81, 0x4d, 0xfa, 0xf1, 0xc3, 0x94, 0x37, 0x45, 0x91, 0xf0, + 0x4b, 0xfc, 0x8e, 0xff, 0x02, 0x81, 0xc1, 0x00, 0xec, 0x38, 0x37, 0x3b, + 0xba, 0x1b, 0x83, 0xaf, 0x3a, 0x00, 0xb9, 0x5e, 0x1f, 0xc8, 0xad, 0x57, + 0xcf, 0x7c, 0xe2, 0x94, 0x95, 0xf1, 0xec, 0x0a, 0x4b, 0x40, 0xc4, 0x48, + 0xfb, 0x47, 0x5f, 0x66, 0xc6, 0xf0, 0x70, 0x14, 0xe9, 0x08, 0xe4, 0x50, + 0x29, 0x0a, 0x24, 0x57, 0x93, 0x97, 0x21, 0xd9, 0xfb, 0xc5, 0x52, 0x0a, + 0x38, 0xb9, 0x68, 0xa3, 0x4f, 0x4b, 0xf8, 0xb8, 0x24, 0xef, 0x0c, 0x42, + 0xda, 0x57, 0x32, 0x77, 0xed, 0x9c, 0x78, 0xeb, 0x10, 0x3e, 0x70, 0x67, + 0xe9, 0x01, 0x03, 0x19, 0x19, 0xdb, 0x48, 0x9e, 0x1e, 0x52, 0x23, 0x88, + 0xb6, 0x87, 0xb8, 0x0d, 0x2d, 0x0c, 0xfc, 0x90, 0x31, 0x9f, 0xa6, 0x96, + 0x0a, 0xe1, 0x34, 0x72, 0x86, 0x0e, 0x49, 0x7c, 0xfe, 0x21, 0xaa, 0x25, + 0xdd, 0x36, 0xbb, 0x1f, 0x85, 0xfe, 0x34, 0x18, 0xc2, 0x36, 0xa2, 0x7d, + 0xee, 0xd9, 0x4f, 0x8e, 0xcb, 0x49, 0x8e, 0x7a, 0x43, 0x3c, 0x52, 0x73, + 0x18, 0x60, 0xf6, 0xb7, 0x7a, 0xc4, 0x7a, 0x8a, 0x1c, 0xf0, 0xc9, 0x2e, + 0xad, 0x54, 0xb1, 0x7b, 0x8e, 0xcb, 0x4d, 0xc2, 0xbc, 0x2a, 0x72, 0xfe, + 0x61, 0x01, 0xd8, 0xff, 0x0a, 0x22, 0x6c, 0x51, 0x7e, 0x06, 0x9e, 0x9e, + 0x3c, 0xe8, 0x31, 0x98, 0xf5, 0x08, 0x34, 0x7e, 0xfa, 0x08, 0xd1, 0x14, + 0xdf, 0xfd, 0x26, 0x2f, 0x1f, 0x5a, 0x89, 0xd7, 0x02, 0x81, 0xc0, 0x76, + 0xdd, 0xed, 0xe9, 0xf5, 0x23, 0x33, 0x13, 0x3f, 0xfe, 0x60, 0xa2, 0x99, + 0x14, 0x3a, 0x87, 0xea, 0x0d, 0x18, 0x8d, 0x9b, 0xd3, 0xd0, 0x9d, 0xff, + 0xc3, 0x77, 0xcc, 0x9a, 0x0a, 0x53, 0x47, 0x80, 0xde, 0x0e, 0x23, 0xea, + 0xc6, 0x6b, 0x8d, 0xd3, 0xbc, 0xcd, 0x03, 0xe6, 0x3d, 0x4d, 0x3d, 0xdd, + 0x7c, 0xb2, 0x27, 0xf9, 0xfe, 0x00, 0xdb, 0x7e, 0x1c, 0x46, 0x1d, 0x83, + 0x11, 0x56, 0xef, 0x8f, 0xc7, 0x5c, 0x5b, 0xb3, 0x0f, 0x9f, 0xd9, 0x02, + 0x80, 0x5c, 0x5e, 0x7f, 0xab, 0xc6, 0x3b, 0x7b, 0x17, 0x7a, 0x8b, 0xd1, + 0x6f, 0xb5, 0x57, 0x07, 0xc1, 0x46, 0x24, 0x5b, 0x72, 0x2e, 0xad, 0xaa, + 0xb4, 0x7f, 0x91, 0xfd, 0x73, 0x83, 0x86, 0x89, 0x4c, 0x81, 0xb8, 0x80, + 0xb3, 0xa7, 0xf8, 0x8b, 0x20, 0xac, 0xd9, 0x27, 0x6f, 0x9a, 0x4b, 0x2f, + 0x6a, 0xef, 0x84, 0x61, 0x75, 0x23, 0x18, 0xcd, 0x6f, 0x63, 0x80, 0x09, + 0x8a, 0xbc, 0x14, 0x1c, 0xe5, 0xff, 0xa9, 0x7d, 0x9a, 0x66, 0x20, 0x61, + 0x3c, 0x61, 0x4b, 0x3d, 0xd5, 0x39, 0xec, 0x3a, 0x16, 0x8d, 0x3b, 0xd1, + 0xf0, 0x1f, 0x8f, 0xae, 0xe2, 0xce, 0xc1, 0x94, 0x69, 0xae, 0xb8, 0xcd, + 0xba, 0x1c, 0x71, 0xe0, 0x47, 0x37, 0xa2, 0x1f, 0x5a, 0xdb, 0x37, 0xe1, + 0x59, 0x4c, 0x39, 0x46, 0xc1, 0xc0, 0x65, 0xc8, 0xd9, 0x61, 0xd3, 0x02, + 0x81, 0xc0, 0x2f, 0x63, 0xe7, 0xd0, 0xd7, 0xb9, 0x85, 0x65, 0xb6, 0x21, + 0x47, 0x0f, 0x17, 0x19, 0x4f, 0x8d, 0x7a, 0x56, 0xf7, 0xae, 0x0f, 0x97, + 0x05, 0x5f, 0xdb, 0x51, 0x17, 0x0f, 0xfd, 0x39, 0x88, 0x6e, 0x3a, 0x23, + 0x2a, 0x99, 0x47, 0x57, 0x3d, 0x56, 0xc7, 0xa4, 0xfd, 0x3d, 0x84, 0xa2, + 0xa1, 0x6b, 0xf6, 0x12, 0xd4, 0x2e, 0xb0, 0xca, 0xa1, 0xaf, 0x81, 0xcd, + 0x20, 0x0c, 0xf1, 0x7b, 0xf3, 0xdd, 0xc5, 0xa8, 0x10, 0xbb, 0xf6, 0xb3, + 0x99, 0x9e, 0xaf, 0x17, 0x97, 0xbd, 0x81, 0x05, 0x6e, 0xf5, 0xae, 0x36, + 0x4c, 0x0f, 0x4c, 0xcd, 0xf5, 0xcb, 0x0b, 0xb3, 0x96, 0xbd, 0x2d, 0xf8, + 0x99, 0x02, 0xe4, 0xb1, 0xbe, 0xde, 0x03, 0x38, 0xc3, 0x28, 0xe6, 0xb4, + 0x1f, 0x12, 0x30, 0x79, 0xd8, 0x84, 0xd8, 0x28, 0x8e, 0xc9, 0xf8, 0x3b, + 0xd3, 0x7f, 0xd4, 0x16, 0xd9, 0xea, 0xa1, 0xec, 0x7f, 0x05, 0x8a, 0xcb, + 0x2b, 0x06, 0x64, 0x4e, 0xc9, 0xcb, 0xc5, 0x6c, 0x4e, 0x92, 0xe8, 0xd2, + 0x5a, 0x33, 0x33, 0x33, 0x2b, 0x69, 0x6d, 0xe4, 0xbb, 0xe6, 0xa9, 0xf3, + 0x27, 0x9a, 0x95, 0xdd, 0x7e, 0x4c, 0x82, 0x71, 0xb8, 0x73, 0x12, 0x39, + 0x6d, 0xb9, 0xbb, 0xaa, 0xe0, 0x4f, 0xa6, 0xb0, 0x7e, 0xa2, 0xcd, 0x25, + 0xe4, 0x42, 0x45, 0x2f, 0x57, 0xa2, 0xf4, 0x7c, 0xf9, 0x18, 0x23, 0x16, + 0x2a, 0xe9, 0x02, 0x81, 0xc1, 0x00, 0xab, 0x35, 0x0d, 0x35, 0x94, 0x9d, + 0x96, 0xb2, 0xb7, 0x45, 0x16, 0xef, 0xb7, 0xea, 0xba, 0xa4, 0x32, 0xec, + 0x43, 0x05, 0xb0, 0x14, 0xbd, 0x9e, 0xd2, 0xbe, 0x0a, 0x0c, 0x4f, 0xca, + 0x4f, 0xf3, 0x11, 0xb3, 0x1f, 0xdc, 0x04, 0x18, 0x38, 0x9d, 0xb0, 0x09, + 0xb8, 0xf1, 0xcf, 0x7a, 0x89, 0x03, 0xd8, 0xed, 0x28, 0x30, 0xe8, 0xe6, + 0xbc, 0x7c, 0x1c, 0x59, 0x12, 0xf8, 0x95, 0x9b, 0x36, 0xad, 0xf2, 0xea, + 0x4a, 0x34, 0x00, 0xcf, 0x94, 0x3e, 0xeb, 0xff, 0xe2, 0x5b, 0x6c, 0x72, + 0xe3, 0x04, 0xd1, 0x10, 0x2e, 0xdd, 0x18, 0x8d, 0x9a, 0x84, 0x93, 0x55, + 0x4a, 0x80, 0x6c, 0xb5, 0x82, 0xc4, 0x16, 0x19, 0xc4, 0xba, 0xad, 0x2e, + 0x40, 0x76, 0xb3, 0xc9, 0xd4, 0x26, 0x5d, 0xc9, 0xb1, 0x05, 0x0f, 0x1f, + 0x7d, 0x59, 0x8c, 0x7b, 0xbe, 0x34, 0x09, 0x3e, 0x71, 0x0b, 0xc8, 0xf9, + 0xb3, 0x77, 0x4e, 0x4b, 0xfb, 0xbf, 0x81, 0x55, 0xa4, 0x5e, 0xc6, 0xe9, + 0xa1, 0xc3, 0x16, 0xff, 0xc8, 0x37, 0x88, 0xd5, 0x2d, 0xfb, 0x06, 0x98, + 0xe9, 0x82, 0x1b, 0x5e, 0x1e, 0xdd, 0x48, 0x5d, 0x6c, 0x59, 0xee, 0x7a, + 0xa6, 0xa4, 0x29, 0x41, 0x20, 0xb4, 0xcd, 0xf4, 0x58, 0x95, 0xfd, 0x7d, + 0xbf, 0xfc, 0x83, 0xf5, 0xe1, 0x5a, 0x5d, 0xa8, 0x08, 0x66, 0xd8, 0xa0, + 0x7f, 0xad, 0x7d, 0xcd, 0x22, 0x06 +}; + +} // namespace wvoec + +#endif // CDM_OEC_TEST_DATA_H_ diff --git a/test/oemcrypto_session_tests_helper.cpp b/test/oemcrypto_session_tests_helper.cpp new file mode 100644 index 0000000..229a168 --- /dev/null +++ b/test/oemcrypto_session_tests_helper.cpp @@ -0,0 +1,159 @@ +#include "oemcrypto_session_tests_helper.h" + +#include +#include "oec_test_data.h" + +using namespace std; +using namespace wvoec; + +namespace wvoec { + +// Make this function available when in Fuzz mode because we are not inheriting +// from OEMCryptoClientTest. +const uint8_t* find(const vector& message, + const vector& substring) { + vector::const_iterator pos = search( + message.begin(), message.end(), substring.begin(), substring.end()); + if (pos == message.end()) { + return NULL; + } + return &(*pos); +} + +// If force is true, we assert that the key loads successfully. +void SessionUtil::CreateWrappedRSAKeyFromKeybox(uint32_t allowed_schemes, + bool force) { + 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.VerifyClientSignature()); + 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(NULL, find(wrapped_rsa_key_, encoded_rsa_key_)); +} + +// 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()); + s.GenerateNonce(); + struct RSAPrivateKeyMessage encrypted; + std::vector signature; + std::vector message_key; + std::vector encrypted_message_key; + s.GenerateRSASessionKey(&message_key, &encrypted_message_key); + ASSERT_NO_FATAL_FAILURE( + s.MakeRSACertificate(&encrypted, sizeof(encrypted), &signature, + allowed_schemes, encoded_rsa_key_, &message_key)); + 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(NULL, 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); + } +} + +void SessionUtil::InstallKeybox(const wvoec_mock::WidevineKeybox& keybox, + bool good) { + uint8_t wrapped[sizeof(wvoec_mock::WidevineKeybox)]; + size_t length = sizeof(wvoec_mock::WidevineKeybox); + keybox_ = keybox; + ASSERT_EQ( + OEMCrypto_SUCCESS, + OEMCrypto_WrapKeybox(reinterpret_cast(&keybox), + sizeof(keybox), wrapped, &length, NULL, 0)); + OEMCryptoResult sts = OEMCrypto_InstallKeybox(wrapped, sizeof(keybox)); + if (good) { + ASSERT_EQ(OEMCrypto_SUCCESS, sts); + } else { + // Can return error now, or return error on IsKeyboxValid. + } +} + +void SessionUtil::EnsureTestKeys() { + switch (global_features.derive_key_method) { + case DeviceFeatures::LOAD_TEST_KEYBOX: + keybox_ = kTestKeybox; + /* Note: If you are upgrading from an older version, it may be easier to + * uncomment the following line. This uses the same test keybox as we + * used in older versions of this test. + */ + // keybox_ = kValidKeybox01; + ASSERT_EQ(OEMCrypto_SUCCESS, + OEMCrypto_LoadTestKeybox( + reinterpret_cast(&keybox_), + sizeof(keybox_))); + break; + case DeviceFeatures::LOAD_TEST_RSA_KEY: + ASSERT_EQ(OEMCrypto_SUCCESS, OEMCrypto_LoadTestRSAKey()); + break; + case DeviceFeatures::EXISTING_TEST_KEYBOX: + // already has test keybox. + keybox_ = kTestKeybox; + break; + case DeviceFeatures::FORCE_TEST_KEYBOX: + keybox_ = kTestKeybox; + InstallKeybox(keybox_, true); + break; + case DeviceFeatures::TEST_PROVISION_30: + // Can use oem certificate to install test rsa key. + break; + default: + FAIL() << "Cannot run test without test keybox or RSA key installed."; + } +} + +// 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_)); + } + // 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_)); + } +} + +} diff --git a/test/oemcrypto_session_tests_helper.h b/test/oemcrypto_session_tests_helper.h new file mode 100644 index 0000000..f0124c6 --- /dev/null +++ b/test/oemcrypto_session_tests_helper.h @@ -0,0 +1,40 @@ +#include +#include +#include +#include +#include + +#include "oec_session_util.h" +#include "oec_test_data.h" +#include "OEMCryptoCENC.h" + +namespace wvoec { + +class SessionUtil { +public: + 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); + + // If force is true, we assert that the key loads successfully. + void CreateWrappedRSAKeyFromOEMCert(uint32_t allowed_schemes, bool force); + + // If force is true, we assert that the key loads successfully. + void CreateWrappedRSAKey(uint32_t allowed_schemes, bool force); + + void InstallKeybox(const wvoec_mock::WidevineKeybox& keybox, bool good); + + void EnsureTestKeys(); + + void InstallTestSessionKeys(Session* s); + + std::vector encoded_rsa_key_; + std::vector wrapped_rsa_key_; + wvoec_mock::WidevineKeybox keybox_; +}; + +} // namespace wvoec diff --git a/test/oemcrypto_test.cpp b/test/oemcrypto_test.cpp new file mode 100644 index 0000000..7615540 --- /dev/null +++ b/test/oemcrypto_test.cpp @@ -0,0 +1,5714 @@ +// Copyright 2013 Google Inc. All Rights Reserved. +// +// OEMCrypto unit tests +// +#include // needed for ntoh() +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "OEMCryptoCENC.h" +#include "log.h" +#include "oec_device_features.h" +#include "oec_session_util.h" +#include "oec_test_data.h" +#include "oemcrypto_session_tests_helper.h" +#include "oemcrypto_key_mock.h" +#include "properties.h" +#include "string_conversions.h" +#include "wv_cdm_constants.h" +#include "wv_keybox.h" + +using ::testing::Bool; +using ::testing::Combine; +using ::testing::Range; +using ::testing::Values; +using ::testing::WithParamInterface; +using namespace std; +using std::tr1::tuple; + +namespace std { // GTest wants PrintTo to be in the std namespace. +void PrintTo(const tuple& param, + ostream* os) { + OEMCrypto_CENCEncryptPatternDesc pattern = std::tr1::get<0>(param); + OEMCryptoCipherMode mode = std::tr1::get<1>(param); + bool decrypt_inplace = std::tr1::get<2>(param); + *os << ((mode == OEMCrypto_CipherMode_CTR) ? "CTR mode" : "CBC mode") + << ", encrypt=" << pattern.encrypt << ", skip=" << pattern.skip + << ", decrypt in place = " << (decrypt_inplace ? "true" : "false"); +} +} // namespace std + +namespace { +int GetRandBytes(unsigned char* buf, int num) { + // returns 1 on success, -1 if not supported, or 0 if other failure. +#if (OPENSSL_VERSION_NUMBER < 0x10100000L) + return RAND_pseudo_bytes(buf, num); +#else + return RAND_bytes(buf, num); +#endif +} +} // namespace + +namespace wvoec { + +class OEMCryptoClientTest : public ::testing::Test, public SessionUtil { + protected: + OEMCryptoClientTest() {} + + virtual void SetUp() { + ::testing::Test::SetUp(); + wvcdm::Properties::Init(); + wvcdm::g_cutoff = wvcdm::LOG_INFO; + 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()); + ASSERT_EQ(OEMCrypto_SUCCESS, OEMCrypto_Initialize()); + } + + virtual void TearDown() { + OEMCrypto_Terminate(); + ::testing::Test::TearDown(); + } + + const uint8_t* find(const vector& message, + const vector& substring) { + vector::const_iterator pos = search( + message.begin(), message.end(), substring.begin(), substring.end()); + if (pos == message.end()) { + return NULL; + } + return &(*pos); + } +}; + +// +// General tests. +// This test is first, becuase it might give an idea why other +// tests are failing when the device has the wrong keybox installed. +TEST_F(OEMCryptoClientTest, VersionNumber) { + const char* level = OEMCrypto_SecurityLevel(); + ASSERT_NE((char*)NULL, level); + ASSERT_EQ('L', level[0]); + cout << " OEMCrypto Security Level is " << level << endl; + uint32_t version = OEMCrypto_APIVersion(); + cout << " OEMCrypto API version is " << version << endl; + if (OEMCrypto_SupportsUsageTable()) { + cout << " OEMCrypto supports usage tables." << endl; + } else { + cout << " OEMCrypto does not support usage tables." << endl; + } + ASSERT_GE(version, 8u); + ASSERT_LE(version, 14u); +} + +TEST_F(OEMCryptoClientTest, ProvisioningDeclaredAPI12) { + OEMCrypto_ProvisioningMethod provisioning_method = + OEMCrypto_GetProvisioningMethod(); + cout << " Provisioning method = " + << ProvisioningMethodName(provisioning_method) << endl; + ASSERT_NE(OEMCrypto_ProvisioningError, provisioning_method); +} + +const char* HDCPCapabilityAsString(OEMCrypto_HDCP_Capability value) { + switch (value) { + case HDCP_NONE: + return "No HDCP supported, no secure data path"; + case HDCP_V1: + return "HDCP version 1.0"; + case HDCP_V2: + return "HDCP version 2.0"; + case HDCP_V2_1: + return "HDCP version 2.1"; + case HDCP_V2_2: + return "HDCP version 2.2"; + case HDCP_NO_DIGITAL_OUTPUT: + return "No HDCP device attached/using local display with secure path"; + default: + return ""; + } +} + +TEST_F(OEMCryptoClientTest, CheckHDCPCapabilityAPI09) { + OEMCryptoResult sts; + OEMCrypto_HDCP_Capability current, maximum; + sts = OEMCrypto_GetHDCPCapability(¤t, &maximum); + ASSERT_EQ(OEMCrypto_SUCCESS, sts); + printf(" Current HDCP Capability: 0x%02x = %s.\n", current, + HDCPCapabilityAsString(current)); + printf(" Maximum HDCP Capability: 0x%02x = %s.\n", maximum, + HDCPCapabilityAsString(maximum)); +} + +TEST_F(OEMCryptoClientTest, CheckSRMCapabilityV13) { + // This just tests some trivial functionality of the SRM update functions. + bool supported = OEMCrypto_IsSRMUpdateSupported(); + printf(" Update SRM Supported: %s.\n", + supported ? "true" : "false"); + uint16_t version = 0; + OEMCryptoResult current_result = OEMCrypto_GetCurrentSRMVersion(&version); + if (current_result == OEMCrypto_SUCCESS) { + printf(" Current SRM Version: %d.\n", version); + EXPECT_NE(OEMCrypto_SUCCESS, OEMCrypto_GetCurrentSRMVersion(NULL)); + } else if (current_result == OEMCrypto_LOCAL_DISPLAY_ONLY) { + printf(" Current SRM Status: Local Display Only.\n"); + } else { + EXPECT_EQ(OEMCrypto_ERROR_NOT_IMPLEMENTED, current_result); + } + vector bad_srm(42); + GetRandBytes(&bad_srm[0], bad_srm.size()); + EXPECT_NE(OEMCrypto_SUCCESS, OEMCrypto_LoadSRM(&bad_srm[0], bad_srm.size())); +} + +TEST_F(OEMCryptoClientTest, CheckMaxNumberOfSessionsAPI10) { + size_t sessions_count; + ASSERT_EQ(OEMCrypto_SUCCESS, + OEMCrypto_GetNumberOfOpenSessions(&sessions_count)); + ASSERT_EQ(0u, sessions_count); + size_t maximum; + OEMCryptoResult sts = OEMCrypto_GetMaxNumberOfSessions(&maximum); + ASSERT_EQ(OEMCrypto_SUCCESS, sts); + printf(" Max Number of Sessions: %zu.\n", maximum); +} + +// +// initialization tests +// +TEST_F(OEMCryptoClientTest, NormalInitTermination) { + // Should be able to terminate OEMCrypto, and then restart it. + ASSERT_EQ(OEMCrypto_SUCCESS, OEMCrypto_Terminate()); + ASSERT_EQ(OEMCrypto_SUCCESS, OEMCrypto_Initialize()); +} + +// +// Session Tests +// +TEST_F(OEMCryptoClientTest, NormalSessionOpenClose) { + Session s; + ASSERT_NO_FATAL_FAILURE(s.open()); + ASSERT_NO_FATAL_FAILURE(s.close()); +} + +TEST_F(OEMCryptoClientTest, TwoSessionsOpenClose) { + Session s1; + Session s2; + ASSERT_NO_FATAL_FAILURE(s1.open()); + ASSERT_NO_FATAL_FAILURE(s2.open()); + ASSERT_NO_FATAL_FAILURE(s1.close()); + ASSERT_NO_FATAL_FAILURE(s2.close()); +} + +// This test should still pass for API v9. A better test is below, but it only +// works for API v10. +TEST_F(OEMCryptoClientTest, EightSessionsOpenClose) { + vector s(8); + for (int i = 0; i < 8; i++) { + ASSERT_NO_FATAL_FAILURE(s[i].open()); + } + for (int i = 0; i < 8; i++) { + ASSERT_NO_FATAL_FAILURE(s[i].close()); + } +} + +TEST_F(OEMCryptoClientTest, MaxSessionsOpenCloseAPI10) { + size_t sessions_count; + ASSERT_EQ(OEMCrypto_SUCCESS, + OEMCrypto_GetNumberOfOpenSessions(&sessions_count)); + ASSERT_EQ(0u, sessions_count); + size_t max_sessions; + ASSERT_EQ(OEMCrypto_SUCCESS, OEMCrypto_GetMaxNumberOfSessions(&max_sessions)); + // We expect OEMCrypto implementations support at least 10 sessions. + const size_t kMinimumSupportedMaxNumberOfSessions = 10u; + ASSERT_GE(max_sessions, kMinimumSupportedMaxNumberOfSessions); + // We allow GetMaxNumberOfSessions to return an estimate. This tests with a + // pad of 5%. Even if it's just an estimate, we still require 8 sessions. + size_t max_sessions_with_pad = + max(max_sessions * 19 / 20, kMinimumSupportedMaxNumberOfSessions); + vector sessions; + // Limit the number of sessions for testing. + const size_t kMaxNumberOfSessionsForTesting = 0x100u; + for (size_t i = 0; i < kMaxNumberOfSessionsForTesting; i++) { + OEMCrypto_SESSION session_id; + OEMCryptoResult sts = OEMCrypto_OpenSession(&session_id); + // GetMaxNumberOfSessions might be an estimate. We allow OEMCrypto to report + // a max that is less than what is actually supported. Assume the number + // returned is |max|. OpenSessions shall not fail if number of active + // sessions is less than |max|; OpenSessions should fail with + // OEMCrypto_ERROR_TOO_MANY_SESSIONS if too many sessions are open. + if (sts != OEMCrypto_SUCCESS) { + ASSERT_EQ(OEMCrypto_ERROR_TOO_MANY_SESSIONS, sts); + ASSERT_GE(i, max_sessions_with_pad); + break; + } + ASSERT_EQ(OEMCrypto_SUCCESS, + OEMCrypto_GetNumberOfOpenSessions(&sessions_count)); + ASSERT_EQ(i + 1, sessions_count); + sessions.push_back(session_id); + } + for (size_t i = 0; i < sessions.size(); i++) { + ASSERT_EQ(OEMCrypto_SUCCESS, OEMCrypto_CloseSession(sessions[i])); + ASSERT_EQ(OEMCrypto_SUCCESS, + OEMCrypto_GetNumberOfOpenSessions(&sessions_count)); + ASSERT_EQ(sessions.size() - i - 1, sessions_count); + } + if (sessions.size() == kMaxNumberOfSessionsForTesting) { + printf( + " MaxSessionsOpenClose: reaches " + "kMaxNumberOfSessionsForTesting(%zu). GetMaxNumberOfSessions = %zu. " + "ERROR_TOO_MANY_SESSIONS not tested.", + kMaxNumberOfSessionsForTesting, max_sessions); + } +} + +TEST_F(OEMCryptoClientTest, GetRandomLargeBuffer) { + // 32 bytes. Not very large, but that's all we really need in one call. + const size_t size = 32; + uint8_t data1[size]; + uint8_t data2[size]; + memset(data1, 0, size); + memset(data2, 0, size); + ASSERT_EQ(OEMCrypto_SUCCESS, OEMCrypto_GetRandom(data1, size)); + ASSERT_EQ(OEMCrypto_SUCCESS, OEMCrypto_GetRandom(data2, size)); + // We don't have enough data to see that the data is really random, + // so we'll just do a spot check that two calls don't return the same values. + int count = 0; + for (size_t i = 0; i < size; i++) { + if (data1[i] == data2[i]) count++; + } + ASSERT_LE(count, 3); // P(count > 3) = 1/256^3 = 6e-8. +} + +TEST_F(OEMCryptoClientTest, GenerateNonce) { + Session s; + ASSERT_NO_FATAL_FAILURE(s.open()); + s.GenerateNonce(); +} + +TEST_F(OEMCryptoClientTest, GenerateTwoNonces) { + Session s; + ASSERT_NO_FATAL_FAILURE(s.open()); + s.GenerateNonce(); + uint32_t nonce1 = s.get_nonce(); + s.GenerateNonce(); + uint32_t nonce2 = s.get_nonce(); + ASSERT_TRUE(nonce1 != nonce2); // Very unlikely to be equal. +} + +TEST_F(OEMCryptoClientTest, PreventNonceFloodAPI09) { + Session s; + ASSERT_NO_FATAL_FAILURE(s.open()); + int error_counter = 0; + time_t test_start = time(NULL); + // More than 20 nonces per second should generate an error. + // To allow for some slop, we actually test for more. + const int kFloodCount = 80; + for (int i = 0; i < kFloodCount; i++) { + s.GenerateNonce(&error_counter); + } + time_t test_end = time(NULL); + 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 + // limited. We add two seconds to allow for round off error in both + // 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. + s.GenerateNonce(&error_counter); + EXPECT_EQ(0, error_counter); +} + +// 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(NULL); + // More than 20 nonces per second should generate an error. + // To allow for some slop, we actually test for more. + const int kFloodCount = 80; + for (int i = 0; i < kFloodCount; i++) { + Session s; + ASSERT_NO_FATAL_FAILURE(s.open()); + s.GenerateNonce(&error_counter); + } + time_t test_end = time(NULL); + 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 + // limited. We add two seconds to allow for round off error in both + // 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. + Session s; + ASSERT_NO_FATAL_FAILURE(s.open()); + s.GenerateNonce(&error_counter); + EXPECT_EQ(0, error_counter); +} + +// Prevent a nonce flood even if some nonces are in a different session. This +// is different from the test above because there are several session open at +// the same time. We want to make sure you can't get a flood of nonces by +// opening a flood of sessions. +TEST_F(OEMCryptoClientTest, PreventNonceFlood3API09) { + int request_counter = 0; + int error_counter = 0; + time_t test_start = time(NULL); + // More than 20 nonces per second should generate an error. + // To allow for some slop, we actually test for more. + Session s[8]; + for (int i = 0; i < 8; i++) { + ASSERT_NO_FATAL_FAILURE(s[i].open()); + for (int j = 0; j < 10; j++) { + request_counter++; + s[i].GenerateNonce(&error_counter); + } + } + time_t test_end = time(NULL); + 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 + // limited. We add two seconds to allow for round off error in both + // 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. + s[0].GenerateNonce(&error_counter); + EXPECT_EQ(0, error_counter); +} + +TEST_F(OEMCryptoClientTest, ClearCopyTestAPI10) { + const int kDataSize = 256; + vector input_buffer(kDataSize); + GetRandBytes(&input_buffer[0], input_buffer.size()); + vector output_buffer(kDataSize); + OEMCrypto_DestBufferDesc dest_buffer; + dest_buffer.type = OEMCrypto_BufferType_Clear; + dest_buffer.buffer.clear.address = &output_buffer[0]; + dest_buffer.buffer.clear.max_length = output_buffer.size(); + ASSERT_EQ( + OEMCrypto_SUCCESS, + OEMCrypto_CopyBuffer(&input_buffer[0], input_buffer.size(), &dest_buffer, + OEMCrypto_FirstSubsample | OEMCrypto_LastSubsample)); + ASSERT_EQ(input_buffer, output_buffer); + ASSERT_EQ( + OEMCrypto_ERROR_INVALID_CONTEXT, + OEMCrypto_CopyBuffer(NULL, input_buffer.size(), &dest_buffer, + OEMCrypto_FirstSubsample | OEMCrypto_LastSubsample)); + ASSERT_EQ( + OEMCrypto_ERROR_INVALID_CONTEXT, + OEMCrypto_CopyBuffer(&input_buffer[0], input_buffer.size(), NULL, + OEMCrypto_FirstSubsample | OEMCrypto_LastSubsample)); + dest_buffer.buffer.clear.address = NULL; + ASSERT_EQ( + OEMCrypto_ERROR_INVALID_CONTEXT, + OEMCrypto_CopyBuffer(&input_buffer[0], input_buffer.size(), &dest_buffer, + OEMCrypto_FirstSubsample | OEMCrypto_LastSubsample)); + dest_buffer.buffer.clear.address = &output_buffer[0]; + dest_buffer.buffer.clear.max_length = output_buffer.size() - 1; + ASSERT_EQ( + OEMCrypto_ERROR_SHORT_BUFFER, + OEMCrypto_CopyBuffer(&input_buffer[0], input_buffer.size(), &dest_buffer, + OEMCrypto_FirstSubsample | OEMCrypto_LastSubsample)); +} + +TEST_F(OEMCryptoClientTest, ClearCopyTestLargeBufferAPI10) { + vector input_buffer(kMaxDecryptSize); + GetRandBytes(&input_buffer[0], input_buffer.size()); + vector output_buffer(kMaxDecryptSize); + OEMCrypto_DestBufferDesc dest_buffer; + dest_buffer.type = OEMCrypto_BufferType_Clear; + dest_buffer.buffer.clear.address = &output_buffer[0]; + dest_buffer.buffer.clear.max_length = output_buffer.size(); + ASSERT_EQ( + OEMCrypto_SUCCESS, + OEMCrypto_CopyBuffer(&input_buffer[0], input_buffer.size(), &dest_buffer, + OEMCrypto_FirstSubsample | OEMCrypto_LastSubsample)); + ASSERT_EQ(input_buffer, output_buffer); +} + +TEST_F(OEMCryptoClientTest, CanLoadTestKeys) { + ASSERT_NE(DeviceFeatures::NO_METHOD, global_features.derive_key_method) + << "Session tests cannot run with out a test keybox or RSA cert."; +} + +class OEMCryptoKeyboxTest : public OEMCryptoClientTest {}; + +TEST_F(OEMCryptoKeyboxTest, NormalGetDeviceId) { + OEMCryptoResult sts; + uint8_t dev_id[128] = {0}; + size_t dev_id_len = 128; + sts = OEMCrypto_GetDeviceID(dev_id, &dev_id_len); + cout << " NormalGetDeviceId: dev_id = " << dev_id + << " len = " << dev_id_len << endl; + ASSERT_EQ(OEMCrypto_SUCCESS, sts); +} + +TEST_F(OEMCryptoKeyboxTest, GetDeviceIdShortBuffer) { + OEMCryptoResult sts; + uint8_t dev_id[128]; + uint32_t req_len = 0; + for (int i = 0; i < 128; ++i) { + dev_id[i] = 0x55; + } + dev_id[127] = '\0'; + size_t dev_id_len = req_len; + sts = OEMCrypto_GetDeviceID(dev_id, &dev_id_len); + ASSERT_EQ(OEMCrypto_ERROR_SHORT_BUFFER, sts); + // On short buffer error, function should return minimum buffer length + ASSERT_TRUE(dev_id_len > req_len); + // Should also return short buffer if passed a zero length and a null buffer. + dev_id_len = req_len; + sts = OEMCrypto_GetDeviceID(NULL, &dev_id_len); + ASSERT_EQ(OEMCrypto_ERROR_SHORT_BUFFER, sts); + // On short buffer error, function should return minimum buffer length + ASSERT_TRUE(dev_id_len > req_len); +} + +TEST_F(OEMCryptoKeyboxTest, NormalGetKeyData) { + OEMCryptoResult sts; + uint8_t key_data[256]; + size_t key_data_len = sizeof(key_data); + sts = OEMCrypto_GetKeyData(key_data, &key_data_len); + + uint32_t* data = reinterpret_cast(key_data); + printf(" NormalGetKeyData: system_id = %d = 0x%04X, version=%d\n", + htonl(data[1]), htonl(data[1]), htonl(data[0])); + ASSERT_EQ(OEMCrypto_SUCCESS, sts); +} + +TEST_F(OEMCryptoKeyboxTest, GetKeyDataNullPointer) { + OEMCryptoResult sts; + uint8_t key_data[256]; + sts = OEMCrypto_GetKeyData(key_data, NULL); + ASSERT_NE(OEMCrypto_SUCCESS, sts); +} + +TEST_F(OEMCryptoKeyboxTest, ProductionKeyboxValid) { + ASSERT_EQ(OEMCrypto_SUCCESS, OEMCrypto_IsKeyboxValid()); +} + +// This tests GenerateDerivedKeys with an 8k context. +TEST_F(OEMCryptoKeyboxTest, GenerateDerivedKeysFromKeyboxLargeBuffer) { + Session s; + ASSERT_NO_FATAL_FAILURE(s.open()); + vector mac_context(kMaxMessageSize); + vector enc_context(kMaxMessageSize); + // Stripe the data so the two vectors are not identical, and not all zeroes. + for (size_t i = 0; i < kMaxMessageSize; i++) { + mac_context[i] = i % 0x100; + enc_context[i] = (3 * i) % 0x100; + } + ASSERT_EQ(OEMCrypto_SUCCESS, + OEMCrypto_GenerateDerivedKeys(s.session_id(), &mac_context[0], + mac_context.size(), &enc_context[0], + enc_context.size())); +} + +class OEMCryptoProv30Test : public OEMCryptoClientTest {}; + +TEST_F(OEMCryptoProv30Test, DeviceClaimsOEMCertificate) { + ASSERT_EQ(OEMCrypto_OEMCertificate, OEMCrypto_GetProvisioningMethod()); +} + +TEST_F(OEMCryptoProv30Test, OEMCertValid) { + Session s; + ASSERT_NO_FATAL_FAILURE(s.open()); + bool kVerify = true; + ASSERT_NO_FATAL_FAILURE(s.LoadOEMCert(kVerify)); // Load and verify. +} + +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[0], data.size()); + size_t signature_length = 0; + vector signature(1); + + sts = OEMCrypto_GenerateRSASignature(s.session_id(), &data[0], data.size(), + &signature[0], &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[0], data.size(), + &signature[0], &signature_length, + kSign_RSASSA_PSS); + + ASSERT_EQ(OEMCrypto_SUCCESS, sts); + ASSERT_NO_FATAL_FAILURE(s.VerifyRSASignature( + data, &signature[0], signature_length, kSign_RSASSA_PSS)); +} + +TEST_F(OEMCryptoProv30Test, OEMCertForbiddenPaddingScheme) { + Session s; + ASSERT_NO_FATAL_FAILURE(s.open()); + ASSERT_NO_FATAL_FAILURE(s.LoadOEMCert()); + OEMCryptoResult sts; + // Sign a Message + vector data(500); + GetRandBytes(&data[0], data.size()); + size_t signature_length = 0; + // We need a size one vector to pass as a pointer. + vector signature(1, 0); + vector zero(1, 0); + + sts = OEMCrypto_GenerateRSASignature(s.session_id(), &data[0], data.size(), + &signature[0], &signature_length, + kSign_PKCS1_Block1); + if (OEMCrypto_ERROR_SHORT_BUFFER == sts) { + // The OEMCrypto could complain about buffer length first, so let's + // resize and check if it's writing to the signature again. + signature.resize(signature_length, 0); + zero.resize(signature_length, 0); + sts = OEMCrypto_GenerateRSASignature(s.session_id(), &data[0], data.size(), + &signature[0], &signature_length, + kSign_PKCS1_Block1); + } + EXPECT_NE(OEMCrypto_SUCCESS, sts) + << "OEM Cert Signed with forbidden kSign_PKCS1_Block1."; + ASSERT_EQ(zero, signature); // signature should not be computed. +} + +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[0], data.size()); + size_t signature_length = 0; + vector signature(1); + + sts = OEMCrypto_GenerateRSASignature(s.session_id(), &data[0], data.size(), + &signature[0], &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[0], data.size(), + &signature[0], &signature_length, + kSign_RSASSA_PSS); + + ASSERT_EQ(OEMCrypto_SUCCESS, sts); + ASSERT_NO_FATAL_FAILURE(s.VerifyRSASignature( + data, &signature[0], signature_length, kSign_RSASSA_PSS)); +} + +// +// AddKey Tests +// +// These tests will use either a test keybox or a test certificate to derive +// session keys. +class OEMCryptoSessionTests : public OEMCryptoClientTest { + protected: + OEMCryptoSessionTests() {} + + virtual void SetUp() { + OEMCryptoClientTest::SetUp(); + EnsureTestKeys(); + if (global_features.usage_table) { + CreateUsageTableHeader(); + } + } + + void CreateUsageTableHeader(bool expect_success = true) { + size_t header_buffer_length = 0; + OEMCryptoResult sts = + OEMCrypto_CreateUsageTableHeader(NULL, &header_buffer_length); + if (expect_success) { + ASSERT_EQ(OEMCrypto_ERROR_SHORT_BUFFER, sts); + } else { + ASSERT_NE(OEMCrypto_SUCCESS, sts); + if (sts != OEMCrypto_ERROR_SHORT_BUFFER) return; + } + encrypted_usage_header_.resize(header_buffer_length); + sts = OEMCrypto_CreateUsageTableHeader(&encrypted_usage_header_[0], + &header_buffer_length); + if (expect_success) { + ASSERT_EQ(OEMCrypto_SUCCESS, sts); + } else { + ASSERT_NE(OEMCrypto_SUCCESS, sts); + } + } + + virtual void TearDown() { + // If we installed a bad keybox, end with a good one installed. + if (global_features.derive_key_method == DeviceFeatures::FORCE_TEST_KEYBOX) + InstallKeybox(kTestKeybox, true); + OEMCryptoClientTest::TearDown(); + } + + vector encrypted_usage_header_; +}; + +class OEMCryptoSessionTestKeyboxTest : public OEMCryptoSessionTests {}; + +TEST_F(OEMCryptoSessionTestKeyboxTest, TestKeyboxIsValid) { + ASSERT_EQ(OEMCrypto_SUCCESS, OEMCrypto_IsKeyboxValid()); +} + +TEST_F(OEMCryptoSessionTestKeyboxTest, GoodForceKeybox) { + ASSERT_EQ(DeviceFeatures::FORCE_TEST_KEYBOX, + global_features.derive_key_method) + << "ForceKeybox tests will modify the installed keybox."; + wvoec_mock::WidevineKeybox keybox = kValidKeybox02; + OEMCryptoResult sts; + InstallKeybox(keybox, true); + sts = OEMCrypto_IsKeyboxValid(); + ASSERT_EQ(OEMCrypto_SUCCESS, sts); + + keybox = kValidKeybox03; + InstallKeybox(keybox, true); + sts = OEMCrypto_IsKeyboxValid(); + ASSERT_EQ(OEMCrypto_SUCCESS, sts); +} + +TEST_F(OEMCryptoSessionTestKeyboxTest, BadCRCForceKeybox) { + ASSERT_EQ(DeviceFeatures::FORCE_TEST_KEYBOX, + global_features.derive_key_method) + << "ForceKeybox tests will modify the installed keybox."; + wvoec_mock::WidevineKeybox keybox = kValidKeybox02; + keybox.crc_[1] ^= 42; + OEMCryptoResult sts; + InstallKeybox(keybox, false); + sts = OEMCrypto_IsKeyboxValid(); + ASSERT_EQ(OEMCrypto_ERROR_BAD_CRC, sts); +} + +TEST_F(OEMCryptoSessionTestKeyboxTest, BadMagicForceKeybox) { + ASSERT_EQ(DeviceFeatures::FORCE_TEST_KEYBOX, + global_features.derive_key_method) + << "ForceKeybox tests will modify the installed keybox."; + wvoec_mock::WidevineKeybox keybox = kValidKeybox02; + keybox.magic_[1] ^= 42; + OEMCryptoResult sts; + InstallKeybox(keybox, false); + sts = OEMCrypto_IsKeyboxValid(); + ASSERT_EQ(OEMCrypto_ERROR_BAD_MAGIC, sts); +} + +TEST_F(OEMCryptoSessionTestKeyboxTest, BadDataForceKeybox) { + ASSERT_EQ(DeviceFeatures::FORCE_TEST_KEYBOX, + global_features.derive_key_method) + << "ForceKeybox tests will modify the installed keybox."; + wvoec_mock::WidevineKeybox keybox = kValidKeybox02; + keybox.data_[1] ^= 42; + OEMCryptoResult sts; + InstallKeybox(keybox, false); + sts = OEMCrypto_IsKeyboxValid(); + ASSERT_EQ(OEMCrypto_ERROR_BAD_CRC, sts); +} + +TEST_F(OEMCryptoSessionTestKeyboxTest, GenerateSignature) { + 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 = 256; + vector signature(SignatureBufferMaxLength); + size_t signature_length = signature.size(); + + OEMCryptoResult sts; + sts = OEMCrypto_GenerateSignature(s.session_id(), &context[0], context.size(), + &signature[0], &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.ClientSignMessage(context, &expected_signature); + ASSERT_EQ(expected_signature, signature); +} + +TEST_F(OEMCryptoSessionTests, LoadKeyNoNonce) { + Session s; + ASSERT_NO_FATAL_FAILURE(s.open()); + ASSERT_NO_FATAL_FAILURE(InstallTestSessionKeys(&s)); + ASSERT_NO_FATAL_FAILURE(s.FillSimpleMessage(kDuration, 0, 42)); + ASSERT_NO_FATAL_FAILURE(s.EncryptAndSign()); + ASSERT_NO_FATAL_FAILURE(s.LoadTestKeys()); +} + +TEST_F(OEMCryptoSessionTests, LoadKeyWithNonce) { + Session s; + ASSERT_NO_FATAL_FAILURE(s.open()); + ASSERT_NO_FATAL_FAILURE(InstallTestSessionKeys(&s)); + ASSERT_NO_FATAL_FAILURE( + s.FillSimpleMessage(0, wvoec_mock::kControlNonceEnabled, s.get_nonce())); + ASSERT_NO_FATAL_FAILURE(s.EncryptAndSign()); + ASSERT_NO_FATAL_FAILURE(s.LoadTestKeys()); +} + +// This asks for several nonce. This simulates several license requests being +// lost. OEMCrypto is required to keep up to four nonce in the nonce table. +TEST_F(OEMCryptoSessionTests, LoadKeySeveralNonce) { + Session s; + ASSERT_NO_FATAL_FAILURE(s.open()); + ASSERT_NO_FATAL_FAILURE(InstallTestSessionKeys(&s)); + uint32_t first_nonce = + s.get_nonce(); // Nonce generated when installing keys. + s.GenerateNonce(); // two. + s.GenerateNonce(); // three. + s.GenerateNonce(); // four. + ASSERT_NO_FATAL_FAILURE( + s.FillSimpleMessage(0, wvoec_mock::kControlNonceEnabled, first_nonce)); + ASSERT_NO_FATAL_FAILURE(s.EncryptAndSign()); + ASSERT_NO_FATAL_FAILURE(s.LoadTestKeys()); +} + +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 = 256; + vector signature(SignatureBufferMaxLength); + size_t signature_length = signature.size(); + + OEMCryptoResult sts; + sts = OEMCrypto_GenerateSignature(s.session_id(), &context[0], context.size(), + &signature[0], &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.ClientSignMessage(context, &expected_signature); + ASSERT_EQ(expected_signature, signature); +} + +TEST_F(OEMCryptoSessionTests, LoadEntitlementKeys) { + Session s; + ASSERT_NO_FATAL_FAILURE(s.open()); + ASSERT_NO_FATAL_FAILURE(InstallTestSessionKeys(&s)); + ASSERT_NO_FATAL_FAILURE(s.FillSimpleEntitlementMessage(0, 0, 0)); + ASSERT_NO_FATAL_FAILURE(s.EncryptAndSign()); + ASSERT_NO_FATAL_FAILURE(s.LoadEnitlementTestKeys()); + s.FillEntitledKeyArray(); + ASSERT_NO_FATAL_FAILURE(s.LoadEntitledContentKeys()); + s.FillEntitledKeyArray(); + ASSERT_NO_FATAL_FAILURE(s.LoadEntitledContentKeys()); +} + +TEST_F(OEMCryptoSessionTests, LoadEntitlementKeysNoEntitlementKeys) { + Session s; + ASSERT_NO_FATAL_FAILURE(s.open()); + ASSERT_NO_FATAL_FAILURE(InstallTestSessionKeys(&s)); + ASSERT_NO_FATAL_FAILURE(s.FillSimpleEntitlementMessage(0, 0, 0)); + ASSERT_NO_FATAL_FAILURE(s.EncryptAndSign()); + s.FillEntitledKeyArray(); + s.LoadEntitledContentKeys(OEMCrypto_ERROR_INVALID_CONTEXT); +} + +// 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.FillSimpleMessage(0, 0, 0)); + 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 = 256; + vector signature(SignatureBufferMaxLength); + size_t signature_length = signature.size(); + + OEMCryptoResult sts; + sts = OEMCrypto_GenerateSignature(s.session_id(), &context[0], context.size(), + &signature[0], &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.ClientSignMessage(context, &expected_signature); + ASSERT_EQ(expected_signature, signature); +} + +// 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)); + s.set_message_size(kMaxMessageSize); + ASSERT_NO_FATAL_FAILURE(s.FillSimpleMessage(0, 0, 0)); + ASSERT_NO_FATAL_FAILURE(s.EncryptAndSign()); + ASSERT_NO_FATAL_FAILURE(s.LoadTestKeys()); +} + +/* The Bad Range tests verify that OEMCrypto_LoadKeys checks the range + of all the pointers. It should reject a message if the pointer does + not point into the message buffer */ +TEST_F(OEMCryptoSessionTests, LoadKeyWithBadRange1) { + Session s; + ASSERT_NO_FATAL_FAILURE(s.open()); + ASSERT_NO_FATAL_FAILURE(InstallTestSessionKeys(&s)); + ASSERT_NO_FATAL_FAILURE(s.FillSimpleMessage(0, 0, 0)); + ASSERT_NO_FATAL_FAILURE(s.EncryptAndSign()); + vector mac_keys( + s.encrypted_license().mac_keys, + s.encrypted_license().mac_keys + sizeof(s.encrypted_license().mac_keys)); + + OEMCryptoResult sts = OEMCrypto_LoadKeys( + s.session_id(), s.message_ptr(), s.message_size(), &s.signature()[0], + s.signature().size(), s.encrypted_license().mac_key_iv, + &mac_keys[0], // Not pointing into buffer. + s.num_keys(), s.key_array(), NULL, 0, NULL, OEMCrypto_ContentLicense); + ASSERT_NE(OEMCrypto_SUCCESS, sts); +} + +TEST_F(OEMCryptoSessionTests, LoadKeyWithBadRange2) { + 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()); + vector mac_key_iv(s.encrypted_license().mac_key_iv, + s.encrypted_license().mac_key_iv + + sizeof(s.encrypted_license().mac_key_iv)); + + OEMCryptoResult sts = + OEMCrypto_LoadKeys(s.session_id(), s.message_ptr(), s.message_size(), + &s.signature()[0], s.signature().size(), + &mac_key_iv[0], // bad. + s.encrypted_license().mac_keys, s.num_keys(), + s.key_array(), NULL, 0, NULL, + OEMCrypto_ContentLicense); + ASSERT_NE(OEMCrypto_SUCCESS, sts); +} + +TEST_F(OEMCryptoSessionTests, LoadKeyWithBadRange3) { + 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()); + vector bad_buffer(s.encrypted_license().keys[0].key_id, + s.encrypted_license().keys[0].key_id + + s.encrypted_license().keys[0].key_id_length); + s.key_array()[0].key_id = &bad_buffer[0]; + + OEMCryptoResult sts = OEMCrypto_LoadKeys( + s.session_id(), s.message_ptr(), s.message_size(), &s.signature()[0], + s.signature().size(), s.encrypted_license().mac_key_iv, + s.encrypted_license().mac_keys, s.num_keys(), s.key_array(), NULL, 0, + NULL, OEMCrypto_ContentLicense); + ASSERT_NE(OEMCrypto_SUCCESS, sts); +} + +TEST_F(OEMCryptoSessionTests, LoadKeyWithBadRange4) { + 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()); + + vector bad_buffer( + s.encrypted_license().keys[1].key_data, + s.encrypted_license().keys[1].key_data + wvcdm::KEY_SIZE); + s.key_array()[1].key_data = &bad_buffer[0]; + + OEMCryptoResult sts = OEMCrypto_LoadKeys( + s.session_id(), s.message_ptr(), s.message_size(), &s.signature()[0], + s.signature().size(), s.encrypted_license().mac_key_iv, + s.encrypted_license().mac_keys, s.num_keys(), s.key_array(), NULL, 0, + NULL, OEMCrypto_ContentLicense); + ASSERT_NE(OEMCrypto_SUCCESS, sts); +} + +TEST_F(OEMCryptoSessionTests, LoadKeyWithBadRange5) { + 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()); + vector bad_buffer(s.encrypted_license().keys[1].key_iv, + s.encrypted_license().keys[1].key_iv + + sizeof(s.encrypted_license().keys[1].key_iv)); + s.key_array()[1].key_data_iv = &bad_buffer[0]; + OEMCryptoResult sts = OEMCrypto_LoadKeys( + s.session_id(), s.message_ptr(), s.message_size(), &s.signature()[0], + s.signature().size(), s.encrypted_license().mac_key_iv, + s.encrypted_license().mac_keys, s.num_keys(), s.key_array(), NULL, 0, + NULL, OEMCrypto_ContentLicense); + ASSERT_NE(OEMCrypto_SUCCESS, sts); +} + +TEST_F(OEMCryptoSessionTests, LoadKeyWithBadRange6) { + 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()); + + vector bad_buffer(s.key_array()[2].key_control, + s.key_array()[2].key_control + + sizeof(s.encrypted_license().keys[1].control)); + s.key_array()[2].key_control = &bad_buffer[0]; + + OEMCryptoResult sts = OEMCrypto_LoadKeys( + s.session_id(), s.message_ptr(), s.message_size(), &s.signature()[0], + s.signature().size(), s.encrypted_license().mac_key_iv, + s.encrypted_license().mac_keys, s.num_keys(), s.key_array(), NULL, 0, + NULL, OEMCrypto_ContentLicense); + ASSERT_NE(OEMCrypto_SUCCESS, sts); +} + +TEST_F(OEMCryptoSessionTests, LoadKeyWithBadRange7) { + 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()); + vector bad_buffer( + s.key_array()[2].key_control_iv, + s.key_array()[2].key_control_iv + + sizeof(s.encrypted_license().keys[1].control_iv)); + s.key_array()[2].key_control_iv = &bad_buffer[0]; + + OEMCryptoResult sts = OEMCrypto_LoadKeys( + s.session_id(), s.message_ptr(), s.message_size(), &s.signature()[0], + s.signature().size(), s.encrypted_license().mac_key_iv, + s.encrypted_license().mac_keys, s.num_keys(), s.key_array(), NULL, 0, + NULL, OEMCrypto_ContentLicense); + ASSERT_NE(OEMCrypto_SUCCESS, sts); +} + +TEST_F(OEMCryptoSessionTests, LoadKeyWithBadNonce) { + Session s; + ASSERT_NO_FATAL_FAILURE(s.open()); + ASSERT_NO_FATAL_FAILURE(InstallTestSessionKeys(&s)); + ASSERT_NO_FATAL_FAILURE(s.FillSimpleMessage(0, + wvoec_mock::kControlNonceEnabled, + 42)); // bad nonce. + ASSERT_NO_FATAL_FAILURE(s.EncryptAndSign()); + OEMCryptoResult sts = OEMCrypto_LoadKeys( + s.session_id(), s.message_ptr(), s.message_size(), &s.signature()[0], + s.signature().size(), s.encrypted_license().mac_key_iv, + s.encrypted_license().mac_keys, s.num_keys(), s.key_array(), NULL, 0, + NULL, OEMCrypto_ContentLicense); + + ASSERT_NE(OEMCrypto_SUCCESS, sts); +} + +TEST_F(OEMCryptoSessionTests, LoadKeyWithRepeatNonce) { + Session s; + ASSERT_NO_FATAL_FAILURE(s.open()); + ASSERT_NO_FATAL_FAILURE(InstallTestSessionKeys(&s)); + uint32_t nonce = s.get_nonce(); + ASSERT_NO_FATAL_FAILURE( + s.FillSimpleMessage(0, wvoec_mock::kControlNonceEnabled, nonce)); + ASSERT_NO_FATAL_FAILURE(s.EncryptAndSign()); + ASSERT_NO_FATAL_FAILURE(s.LoadTestKeys()); + ASSERT_NO_FATAL_FAILURE(s.close()); + + ASSERT_NO_FATAL_FAILURE(s.open()); + ASSERT_NO_FATAL_FAILURE(InstallTestSessionKeys(&s)); + ASSERT_NO_FATAL_FAILURE(s.FillSimpleMessage(0, + wvoec_mock::kControlNonceEnabled, + nonce)); // same old nonce. + ASSERT_NO_FATAL_FAILURE(s.EncryptAndSign()); + OEMCryptoResult sts = OEMCrypto_LoadKeys( + s.session_id(), s.message_ptr(), s.message_size(), &s.signature()[0], + s.signature().size(), s.encrypted_license().mac_key_iv, + s.encrypted_license().mac_keys, s.num_keys(), s.key_array(), NULL, 0, + NULL, OEMCrypto_ContentLicense); + + ASSERT_NE(OEMCrypto_SUCCESS, sts); +} + +TEST_F(OEMCryptoSessionTests, LoadKeyWithBadVerification) { + Session s; + ASSERT_NO_FATAL_FAILURE(s.open()); + ASSERT_NO_FATAL_FAILURE(InstallTestSessionKeys(&s)); + ASSERT_NO_FATAL_FAILURE(s.FillSimpleMessage(0, 0, 0)); + s.license().keys[1].control.verification[2] = 'Z'; + ASSERT_NO_FATAL_FAILURE(s.EncryptAndSign()); + OEMCryptoResult sts = OEMCrypto_LoadKeys( + s.session_id(), s.message_ptr(), s.message_size(), &s.signature()[0], + s.signature().size(), s.encrypted_license().mac_key_iv, + s.encrypted_license().mac_keys, s.num_keys(), s.key_array(), NULL, 0, + NULL, OEMCrypto_ContentLicense); + + ASSERT_NE(OEMCrypto_SUCCESS, sts); +} + +// This tests each key control block verification string in the range kc09-kc1?. +class SessionTestAlternateVerification : public OEMCryptoSessionTests, + public WithParamInterface { + public: + virtual void SetUp() { + OEMCryptoSessionTests::SetUp(); + target_api_ = static_cast(GetParam()); + } + + protected: + uint32_t target_api_; +}; + +TEST_P(SessionTestAlternateVerification, LoadKeys) { + Session s; + ASSERT_NO_FATAL_FAILURE(s.open()); + ASSERT_NO_FATAL_FAILURE(InstallTestSessionKeys(&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. + if (target_api_ > 8 && target_api_ < 100) { + snprintf(buffer, 5, "kc%02d", target_api_); + } + for (unsigned int i = 0; i < s.num_keys(); i++) { + memcpy(s.license().keys[i].control.verification, buffer, 4); + } + ASSERT_NO_FATAL_FAILURE(s.EncryptAndSign()); + OEMCryptoResult sts = OEMCrypto_LoadKeys( + s.session_id(), s.message_ptr(), s.message_size(), &s.signature()[0], + s.signature().size(), s.encrypted_license().mac_key_iv, + s.encrypted_license().mac_keys, s.num_keys(), s.key_array(), NULL, 0, + NULL, OEMCrypto_ContentLicense); + // If this is a future API, then LoadKeys should fail. + if (global_features.api_version < target_api_) { + ASSERT_NE(OEMCrypto_SUCCESS, sts); + } else { + // Otherwise, LoadKeys should succeed. + ASSERT_EQ(OEMCrypto_SUCCESS, sts) + << "LoadKeys failed for key control block kc" << target_api_; + } +} + +// Range of API versions to test. This should start at 8, and go to +// the current API + 2. We use +2 because we want to test at least 1 +// future API, and the ::testing::Range is not inclusive. +INSTANTIATE_TEST_CASE_P(TestAll, SessionTestAlternateVerification, + Range(8, 14 + 2)); + +TEST_F(OEMCryptoSessionTests, LoadKeysBadSignature) { + 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()); + s.signature()[0] ^= 42; // Bad signature. + OEMCryptoResult sts = OEMCrypto_LoadKeys( + s.session_id(), s.message_ptr(), s.message_size(), &s.signature()[0], + s.signature().size(), s.encrypted_license().mac_key_iv, + s.encrypted_license().mac_keys, s.num_keys(), s.key_array(), NULL, 0, + NULL, OEMCrypto_ContentLicense); + ASSERT_NE(OEMCrypto_SUCCESS, sts); +} + +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()[0], + s.signature().size(), s.encrypted_license().mac_key_iv, + s.encrypted_license().mac_keys, s.num_keys(), s.key_array(), NULL, 0, + NULL, OEMCrypto_ContentLicense); + ASSERT_NE(OEMCrypto_SUCCESS, sts); +} + +// To prevent initial loading shared licenses without usage table or nonce, +// LoadKeys should reject an empty list of keys. +TEST_F(OEMCryptoSessionTests, LoadKeyNoKeys) { + Session s; + ASSERT_NO_FATAL_FAILURE(s.open()); + ASSERT_NO_FATAL_FAILURE(InstallTestSessionKeys(&s)); + ASSERT_NO_FATAL_FAILURE(s.FillSimpleMessage(kDuration, 0, 42)); + ASSERT_NO_FATAL_FAILURE(s.EncryptAndSign()); + int kNoKeys = 0; + ASSERT_NE( + OEMCrypto_SUCCESS, + OEMCrypto_LoadKeys(s.session_id(), s.message_ptr(), s.message_size(), + &s.signature()[0], s.signature().size(), NULL, NULL, + kNoKeys, s.key_array(), NULL, 0, NULL, + OEMCrypto_ContentLicense)); +} + +TEST_F(OEMCryptoSessionTests, LoadKeyNoKeyWithNonce) { + Session s; + ASSERT_NO_FATAL_FAILURE(s.open()); + ASSERT_NO_FATAL_FAILURE(InstallTestSessionKeys(&s)); + ASSERT_NO_FATAL_FAILURE( + s.FillSimpleMessage(0, wvoec_mock::kControlNonceEnabled, s.get_nonce())); + ASSERT_NO_FATAL_FAILURE(s.EncryptAndSign()); + int kNoKeys = 0; + ASSERT_NE( + OEMCrypto_SUCCESS, + OEMCrypto_LoadKeys(s.session_id(), s.message_ptr(), s.message_size(), + &s.signature()[0], s.signature().size(), NULL, NULL, + kNoKeys, s.key_array(), NULL, 0, NULL, + OEMCrypto_ContentLicense)); +} + +TEST_F(OEMCryptoSessionTests, QueryKeyControl) { + Session s; + ASSERT_NO_FATAL_FAILURE(s.open()); + ASSERT_NO_FATAL_FAILURE(InstallTestSessionKeys(&s)); + ASSERT_NO_FATAL_FAILURE( + s.FillSimpleMessage(0, wvoec_mock::kControlNonceEnabled, s.get_nonce())); + ASSERT_NO_FATAL_FAILURE(s.EncryptAndSign()); + ASSERT_NO_FATAL_FAILURE(s.LoadTestKeys()); + // Note: successful cases are tested in VerifyTestKeys. + KeyControlBlock block; + size_t size = sizeof(block) - 1; + OEMCryptoResult sts = + OEMCrypto_QueryKeyControl(s.session_id(), s.license().keys[0].key_id, + s.license().keys[0].key_id_length, + reinterpret_cast(&block), &size); + if (sts == OEMCrypto_ERROR_NOT_IMPLEMENTED) { + return; + } + ASSERT_EQ(OEMCrypto_ERROR_SHORT_BUFFER, sts); + const char* key_id = "no_key"; + size = sizeof(block); + ASSERT_NE(OEMCrypto_SUCCESS, + OEMCrypto_QueryKeyControl( + s.session_id(), reinterpret_cast(key_id), + strlen(key_id), reinterpret_cast(&block), &size)); +} + +TEST_F(OEMCryptoSessionTests, AntiRollbackHardwareRequired) { + Session s; + ASSERT_NO_FATAL_FAILURE(s.open()); + ASSERT_NO_FATAL_FAILURE(InstallTestSessionKeys(&s)); + ASSERT_NO_FATAL_FAILURE(s.FillSimpleMessage( + 0, wvoec_mock::kControlRequireAntiRollbackHardware, 0)); + ASSERT_NO_FATAL_FAILURE(s.EncryptAndSign()); + OEMCryptoResult sts = OEMCrypto_LoadKeys( + s.session_id(), s.message_ptr(), s.message_size(), &s.signature()[0], + s.signature().size(), s.encrypted_license().mac_key_iv, + s.encrypted_license().mac_keys, s.num_keys(), s.key_array(), NULL, 0, + NULL, OEMCrypto_ContentLicense); + if (OEMCrypto_IsAntiRollbackHwPresent()) { + ASSERT_EQ(OEMCrypto_SUCCESS, sts); + } else { + ASSERT_EQ(OEMCrypto_ERROR_UNKNOWN_FAILURE, sts); + } +} + +TEST_F(OEMCryptoSessionTests, CheckMinimumPatchLevel) { + uint8_t patch_level = OEMCrypto_Security_Patch_Level(); + printf(" Current Patch Level: %u.\n", patch_level); + { + Session s; + ASSERT_NO_FATAL_FAILURE(s.open()); + ASSERT_NO_FATAL_FAILURE(InstallTestSessionKeys(&s)); + ASSERT_NO_FATAL_FAILURE(s.FillSimpleMessage( + 0, patch_level << wvoec_mock::kControlSecurityPatchLevelShift, 0)); + ASSERT_NO_FATAL_FAILURE(s.EncryptAndSign()); + ASSERT_EQ( + OEMCrypto_SUCCESS, + OEMCrypto_LoadKeys(s.session_id(), s.message_ptr(), s.message_size(), + &s.signature()[0], s.signature().size(), + s.encrypted_license().mac_key_iv, + s.encrypted_license().mac_keys, s.num_keys(), + s.key_array(), NULL, 0, NULL, + OEMCrypto_ContentLicense)); + } + if (patch_level < 0x3F) { + Session s; + ASSERT_NO_FATAL_FAILURE(s.open()); + ASSERT_NO_FATAL_FAILURE(InstallTestSessionKeys(&s)); + ASSERT_NO_FATAL_FAILURE(s.FillSimpleMessage( + 0, (patch_level + 1) << wvoec_mock::kControlSecurityPatchLevelShift, + 0)); + ASSERT_NO_FATAL_FAILURE(s.EncryptAndSign()); + ASSERT_EQ( + OEMCrypto_ERROR_UNKNOWN_FAILURE, + OEMCrypto_LoadKeys(s.session_id(), s.message_ptr(), s.message_size(), + &s.signature()[0], s.signature().size(), + s.encrypted_license().mac_key_iv, + s.encrypted_license().mac_keys, s.num_keys(), + s.key_array(), NULL, 0, NULL, + OEMCrypto_ContentLicense)); + } + if (patch_level > 0) { + Session s; + ASSERT_NO_FATAL_FAILURE(s.open()); + ASSERT_NO_FATAL_FAILURE(InstallTestSessionKeys(&s)); + ASSERT_NO_FATAL_FAILURE(s.FillSimpleMessage( + 0, (patch_level - 1) << wvoec_mock::kControlSecurityPatchLevelShift, + 0)); + ASSERT_NO_FATAL_FAILURE(s.EncryptAndSign()); + ASSERT_EQ( + OEMCrypto_SUCCESS, + OEMCrypto_LoadKeys(s.session_id(), s.message_ptr(), s.message_size(), + &s.signature()[0], s.signature().size(), + s.encrypted_license().mac_key_iv, + s.encrypted_license().mac_keys, s.num_keys(), + s.key_array(), NULL, 0, NULL, + OEMCrypto_ContentLicense)); + } +} + +TEST_F(OEMCryptoSessionTests, Minimum20KeysAPI12) { + Session s; + ASSERT_NO_FATAL_FAILURE(s.open()); + ASSERT_NO_FATAL_FAILURE(InstallTestSessionKeys(&s)); + s.set_num_keys(kMaxNumKeys); + ASSERT_NO_FATAL_FAILURE(s.FillSimpleMessage(0, 0, 0)); + ASSERT_NO_FATAL_FAILURE(s.EncryptAndSign()); + ASSERT_NO_FATAL_FAILURE(s.LoadTestKeys()); + for (size_t key_index = 0; key_index < kMaxNumKeys; key_index++) { + bool kSelectKeyFirst = true; + ASSERT_NO_FATAL_FAILURE( + s.TestDecryptCTR(kSelectKeyFirst, OEMCrypto_SUCCESS, key_index)); + } +} + +class SessionTestDecryptWithHDCP : public OEMCryptoSessionTests, + public WithParamInterface { + public: + void DecryptWithHDCP(OEMCrypto_HDCP_Capability version) { + OEMCryptoResult sts; + OEMCrypto_HDCP_Capability current, maximum; + sts = OEMCrypto_GetHDCPCapability(¤t, &maximum); + ASSERT_EQ(OEMCrypto_SUCCESS, sts); + Session s; + ASSERT_NO_FATAL_FAILURE(s.open()); + ASSERT_NO_FATAL_FAILURE(InstallTestSessionKeys(&s)); + ASSERT_NO_FATAL_FAILURE(s.FillSimpleMessage( + 0, + (version << wvoec_mock::kControlHDCPVersionShift) | + wvoec_mock::kControlObserveHDCP | wvoec_mock::kControlHDCPRequired, + 0)); + ASSERT_NO_FATAL_FAILURE(s.EncryptAndSign()); + ASSERT_NO_FATAL_FAILURE(s.LoadTestKeys()); + + if (version > current) { + ASSERT_NO_FATAL_FAILURE( + s.TestDecryptCTR(true, OEMCrypto_ERROR_INSUFFICIENT_HDCP)); + } else { + ASSERT_NO_FATAL_FAILURE(s.TestDecryptCTR(true, OEMCrypto_SUCCESS)); + } + } +}; + +TEST_P(SessionTestDecryptWithHDCP, DecryptAPI09) { + // Test parameterized by HDCP version. + DecryptWithHDCP(static_cast(GetParam())); +} +INSTANTIATE_TEST_CASE_P(TestHDCP, SessionTestDecryptWithHDCP, Range(1, 5)); + +// +// Load, Refresh Keys Test +// +class SessionTestRefreshKeyTest + : public OEMCryptoSessionTests, + public WithParamInterface > { + public: + virtual void SetUp() { + OEMCryptoSessionTests::SetUp(); + new_mac_keys_ = + GetParam().first; // Whether to put new mac keys in LoadKeys. + num_keys_ = static_cast(GetParam().second); // # keys in refresh. + } + + protected: + bool new_mac_keys_; + size_t num_keys_; // Number of keys to refresh. +}; + +TEST_P(SessionTestRefreshKeyTest, RefreshWithNonce) { + Session s; + ASSERT_NO_FATAL_FAILURE(s.open()); + ASSERT_NO_FATAL_FAILURE(InstallTestSessionKeys(&s)); + ASSERT_NO_FATAL_FAILURE(s.FillSimpleMessage( + kDuration, wvoec_mock::kControlNonceEnabled, s.get_nonce())); + ASSERT_NO_FATAL_FAILURE(s.EncryptAndSign()); + ASSERT_NO_FATAL_FAILURE(s.LoadTestKeys("", new_mac_keys_)); + s.GenerateNonce(); + // License renewal message is signed by client and verified by the server. + ASSERT_NO_FATAL_FAILURE(s.VerifyClientSignature()); + ASSERT_NO_FATAL_FAILURE(s.RefreshTestKeys(num_keys_, + wvoec_mock::kControlNonceEnabled, + s.get_nonce(), OEMCrypto_SUCCESS)); +} + +TEST_P(SessionTestRefreshKeyTest, RefreshNoNonce) { + Session s; + ASSERT_NO_FATAL_FAILURE(s.open()); + ASSERT_NO_FATAL_FAILURE(InstallTestSessionKeys(&s)); + ASSERT_NO_FATAL_FAILURE(s.FillSimpleMessage(kDuration, 0, 0)); + ASSERT_NO_FATAL_FAILURE(s.EncryptAndSign()); + ASSERT_NO_FATAL_FAILURE(s.LoadTestKeys("", new_mac_keys_)); + // License renewal message is signed by client and verified by the server. + ASSERT_NO_FATAL_FAILURE(s.VerifyClientSignature()); + ASSERT_NO_FATAL_FAILURE( + s.RefreshTestKeys(num_keys_, 0, 0, OEMCrypto_SUCCESS)); +} + +TEST_P(SessionTestRefreshKeyTest, RefreshOldNonceAPI11) { + Session s; + ASSERT_NO_FATAL_FAILURE(s.open()); + ASSERT_NO_FATAL_FAILURE(InstallTestSessionKeys(&s)); + uint32_t nonce = s.get_nonce(); + ASSERT_NO_FATAL_FAILURE( + s.FillSimpleMessage(kDuration, wvoec_mock::kControlNonceEnabled, nonce)); + ASSERT_NO_FATAL_FAILURE(s.EncryptAndSign()); + ASSERT_NO_FATAL_FAILURE(s.LoadTestKeys("", new_mac_keys_)); + // License renewal message is signed by client and verified by the server. + ASSERT_NO_FATAL_FAILURE(s.VerifyClientSignature()); + // Tryinng to reuse the same nonce. + ASSERT_NO_FATAL_FAILURE( + s.RefreshTestKeys(num_keys_, wvoec_mock::kControlNonceEnabled, nonce, + OEMCrypto_ERROR_INVALID_NONCE)); +} + +TEST_P(SessionTestRefreshKeyTest, RefreshBadNonceAPI11) { + Session s; + ASSERT_NO_FATAL_FAILURE(s.open()); + ASSERT_NO_FATAL_FAILURE(InstallTestSessionKeys(&s)); + ASSERT_NO_FATAL_FAILURE(s.FillSimpleMessage( + kDuration, wvoec_mock::kControlNonceEnabled, s.get_nonce())); + ASSERT_NO_FATAL_FAILURE(s.EncryptAndSign()); + ASSERT_NO_FATAL_FAILURE(s.LoadTestKeys("", new_mac_keys_)); + s.GenerateNonce(); + // License renewal message is signed by client and verified by the server. + ASSERT_NO_FATAL_FAILURE(s.VerifyClientSignature()); + uint32_t nonce = s.get_nonce() ^ 42; + ASSERT_NO_FATAL_FAILURE( + s.RefreshTestKeys(num_keys_, wvoec_mock::kControlNonceEnabled, nonce, + OEMCrypto_ERROR_INVALID_NONCE)); +} + +TEST_P(SessionTestRefreshKeyTest, RefreshLargeBuffer) { + Session s; + s.set_message_size(kMaxMessageSize); + ASSERT_NO_FATAL_FAILURE(s.open()); + ASSERT_NO_FATAL_FAILURE(InstallTestSessionKeys(&s)); + ASSERT_NO_FATAL_FAILURE(s.FillSimpleMessage( + kDuration, wvoec_mock::kControlNonceEnabled, s.get_nonce())); + ASSERT_NO_FATAL_FAILURE(s.EncryptAndSign()); + ASSERT_NO_FATAL_FAILURE(s.LoadTestKeys("", new_mac_keys_)); + s.GenerateNonce(); + // License renewal message is signed by client and verified by the server. + // This uses a large buffer for the renewal message. + ASSERT_NO_FATAL_FAILURE(s.VerifyClientSignature(kMaxMessageSize)); + ASSERT_NO_FATAL_FAILURE(s.RefreshTestKeys(num_keys_, + wvoec_mock::kControlNonceEnabled, + s.get_nonce(), OEMCrypto_SUCCESS)); +} + +// 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(SessionTestRefreshKeyTest, RefreshWithNoSelectKey) { + Session s; + ASSERT_NO_FATAL_FAILURE(s.open()); + ASSERT_NO_FATAL_FAILURE(InstallTestSessionKeys(&s)); + ASSERT_NO_FATAL_FAILURE(s.FillSimpleMessage( + kDuration, wvoec_mock::kControlNonceEnabled, s.get_nonce())); + ASSERT_NO_FATAL_FAILURE(s.EncryptAndSign()); + ASSERT_NO_FATAL_FAILURE(s.LoadTestKeys("", new_mac_keys_)); + // Call select key before the refresh. No calls below to TestDecryptCTR with + // select key set to true. + ASSERT_NO_FATAL_FAILURE(s.TestDecryptCTR(true)); + + s.GenerateNonce(); + // License renewal message is signed by client and verified by the server. + ASSERT_NO_FATAL_FAILURE(s.VerifyClientSignature()); + // Note: we store the message in encrypted_license_, but the refresh key + // message is not actually encrypted. It is, however, signed. + // FillRefreshMessage fills the message with a duration of kLongDuration. + ASSERT_NO_FATAL_FAILURE(s.FillRefreshMessage( + num_keys_, wvoec_mock::kControlNonceEnabled, s.get_nonce())); + s.ServerSignBuffer(reinterpret_cast(&s.encrypted_license()), + s.message_size(), &s.signature()); + OEMCrypto_KeyRefreshObject key_array[num_keys_]; + s.FillRefreshArray(key_array, num_keys_); + ASSERT_EQ(OEMCrypto_SUCCESS, + OEMCrypto_RefreshKeys(s.session_id(), s.message_ptr(), + s.message_size(), &s.signature()[0], + s.signature().size(), num_keys_, key_array)); + 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); + 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); + ASSERT_NO_FATAL_FAILURE(s.TestDecryptCTR(false)); +} + +// Of only one key control block in the refesh, we update all the keys. +INSTANTIATE_TEST_CASE_P(TestRefreshAllKeys, SessionTestRefreshKeyTest, + Values(std::make_pair(true, 1), + std::make_pair(false, 1))); + +// If multiple key control blocks, we update each key separately. +INSTANTIATE_TEST_CASE_P(TestRefreshEachKeys, SessionTestRefreshKeyTest, + Values(std::make_pair(true, 4), + std::make_pair(false, 4))); + +// +// Decrypt Tests +// +TEST_F(OEMCryptoSessionTests, Decrypt) { + Session s; + ASSERT_NO_FATAL_FAILURE(s.open()); + ASSERT_NO_FATAL_FAILURE(InstallTestSessionKeys(&s)); + ASSERT_NO_FATAL_FAILURE(s.FillSimpleMessage(kDuration, 0, 0)); + ASSERT_NO_FATAL_FAILURE(s.EncryptAndSign()); + ASSERT_NO_FATAL_FAILURE(s.LoadTestKeys()); + ASSERT_NO_FATAL_FAILURE(s.TestDecryptCTR()); +} + +TEST_F(OEMCryptoSessionTests, DecryptZeroDuration) { + 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()); + ASSERT_NO_FATAL_FAILURE(s.TestDecryptCTR()); +} + +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])); + } + for (int i = 0; i < 8; i++) { + ASSERT_NO_FATAL_FAILURE( + s[i].FillSimpleMessage(kLongDuration, 0, s[i].get_nonce())); + ASSERT_NO_FATAL_FAILURE(s[i].EncryptAndSign()); + } + for (int i = 0; i < 8; i++) { + ASSERT_NO_FATAL_FAILURE(s[i].LoadTestKeys()); + } + for (int i = 0; i < 8; i++) { + ASSERT_NO_FATAL_FAILURE(s[i].TestDecryptCTR()); + } + // Second call to decrypt for each session. + for (int i = 0; i < 8; i++) { + ASSERT_NO_FATAL_FAILURE(s[i].TestDecryptCTR()); + } +} + +// This test generates several test keys, as if a license request was lost. +// This is only valid for (obsolete) devices that use a keybox to talk to a +// license server. +TEST_F(OEMCryptoSessionTests, SimultaneousDecryptWithLostMessageKeyboxTest) { + vector s(8); + for (int i = 0; i < 8; i++) { + ASSERT_NO_FATAL_FAILURE(s[i].open()); + ASSERT_NO_FATAL_FAILURE(InstallTestSessionKeys(&s[i])); + } + for (int i = 0; i < 8; i++) { + ASSERT_NO_FATAL_FAILURE(s[i].GenerateDerivedKeysFromKeybox(keybox_)); + ASSERT_NO_FATAL_FAILURE( + s[i].FillSimpleMessage(kLongDuration, 0, s[i].get_nonce())); + ASSERT_NO_FATAL_FAILURE(s[i].EncryptAndSign()); + } + // First set of messages are lost. Generate second set. + for (int i = 0; i < 8; i++) { + ASSERT_NO_FATAL_FAILURE(s[i].GenerateDerivedKeysFromKeybox(keybox_)); + ASSERT_NO_FATAL_FAILURE( + s[i].FillSimpleMessage(kLongDuration, 0, s[i].get_nonce())); + ASSERT_NO_FATAL_FAILURE(s[i].EncryptAndSign()); + } + for (int i = 0; i < 8; i++) { + ASSERT_NO_FATAL_FAILURE(s[i].LoadTestKeys()); + } + for (int i = 0; i < 8; i++) { + ASSERT_NO_FATAL_FAILURE(s[i].TestDecryptCTR()); + } + // Second call to decrypt for each session. + for (int i = 0; i < 8; i++) { + ASSERT_NO_FATAL_FAILURE(s[i].TestDecryptCTR()); + } +} + +struct SampleSize { + size_t clear_size; + size_t encrypted_size; + SampleSize(size_t clear, size_t encrypted) + : clear_size(clear), encrypted_size(encrypted) {} +}; + +struct SampleInitData { + uint8_t iv[AES_BLOCK_SIZE]; + size_t block_offset; +}; + +class OEMCryptoSessionTestsDecryptTests + : public OEMCryptoSessionTests, + public WithParamInterface > { + protected: + virtual void SetUp() { + OEMCryptoSessionTests::SetUp(); + pattern_ = std::tr1::get<0>(GetParam()); + cipher_mode_ = std::tr1::get<1>(GetParam()); + decrypt_inplace_ = std::tr1::get<2>(GetParam()); + } + + void FindTotalSize() { + total_size_ = 0; + for (size_t i = 0; i < subsample_size_.size(); i++) { + total_size_ += + subsample_size_[i].clear_size + subsample_size_[i].encrypted_size; + } + } + + void EncryptData(const vector& key, + const vector& starting_iv, + const vector& in_buffer, + vector* out_buffer) { + AES_KEY aes_key; + AES_set_encrypt_key(&key[0], AES_BLOCK_SIZE * 8, &aes_key); + out_buffer->resize(in_buffer.size()); + + uint8_t iv[AES_BLOCK_SIZE]; // Current iv. + + memcpy(iv, &starting_iv[0], AES_BLOCK_SIZE); + + size_t buffer_index = 0; // byte index into in and out. + size_t block_offset = 0; // byte index into current block. + for (size_t i = 0; i < subsample_size_.size(); i++) { + // Copy clear content. + if (subsample_size_[i].clear_size > 0) { + memcpy(&(*out_buffer)[buffer_index], &in_buffer[buffer_index], + subsample_size_[i].clear_size); + buffer_index += subsample_size_[i].clear_size; + } + // Save the current iv and offsets for call to DecryptCENC. + sample_init_data_.push_back(SampleInitData()); + memcpy(sample_init_data_[i].iv, iv, AES_BLOCK_SIZE); + // Note: final CENC spec specifies the pattern_offset = 0 at the + // start of each subsample. + size_t pattern_offset = 0; + sample_init_data_[i].block_offset = block_offset; + + size_t subsample_end = buffer_index + subsample_size_[i].encrypted_size; + while (buffer_index < subsample_end) { + size_t size = + min(subsample_end - buffer_index, AES_BLOCK_SIZE - block_offset); + size_t pattern_length = pattern_.encrypt + pattern_.skip; + bool skip_block = + (pattern_offset >= pattern_.encrypt) && (pattern_length > 0); + if (pattern_length > 0) { + pattern_offset = (pattern_offset + 1) % pattern_length; + } + // CBC mode should just copy a partial block at the end. If there + // is a partial block at the beginning, an error is returned, so we + // can put whatever we want in the output buffer. + if (skip_block || ((cipher_mode_ == OEMCrypto_CipherMode_CBC) && + (size < AES_BLOCK_SIZE))) { + memcpy(&(*out_buffer)[buffer_index], &in_buffer[buffer_index], size); + block_offset = 0; // Next block should be complete. + } else { + if (cipher_mode_ == OEMCrypto_CipherMode_CTR) { + uint8_t aes_output[AES_BLOCK_SIZE]; + AES_encrypt(iv, aes_output, &aes_key); + for (size_t n = 0; n < size; n++) { + (*out_buffer)[buffer_index + n] = + aes_output[n + block_offset] ^ in_buffer[buffer_index + n]; + } + if (size + block_offset < AES_BLOCK_SIZE) { + // Partial block. Don't increment iv. Compute next block offset. + block_offset = block_offset + size; + } else { + EXPECT_EQ(static_cast(AES_BLOCK_SIZE), + block_offset + size); + // Full block. Increment iv, and set offset to 0 for next block. + ctr128_inc64(1, iv); + block_offset = 0; + } + } else { + uint8_t aes_input[AES_BLOCK_SIZE]; + for (size_t n = 0; n < size; n++) { + aes_input[n] = in_buffer[buffer_index + n] ^ iv[n]; + } + AES_encrypt(aes_input, &(*out_buffer)[buffer_index], &aes_key); + memcpy(iv, &(*out_buffer)[buffer_index], AES_BLOCK_SIZE); + // CBC mode should always start on block boundary. + block_offset = 0; + } + } + buffer_index += size; + } + } + } + + void TestDecryptCENC(const vector& key, + const vector& /* encryptionIv */, + const vector& encryptedData, + const vector& unencryptedData) { + OEMCryptoResult sts; + Session s; + ASSERT_NO_FATAL_FAILURE(s.open()); + ASSERT_NO_FATAL_FAILURE(InstallTestSessionKeys(&s)); + ASSERT_NO_FATAL_FAILURE(s.FillSimpleMessage(kDuration, 0, 0)); + memcpy(s.license().keys[0].key_data, &key[0], key.size()); + s.license().keys[0].cipher_mode = cipher_mode_; + ASSERT_NO_FATAL_FAILURE(s.EncryptAndSign()); + ASSERT_NO_FATAL_FAILURE(s.LoadTestKeys()); + sts = OEMCrypto_SelectKey(s.session_id(), s.license().keys[0].key_id, + s.license().keys[0].key_id_length, + cipher_mode_); + ASSERT_EQ(OEMCrypto_SUCCESS, sts); + + // We decrypt each subsample. + vector output_buffer(total_size_ + 16, 0xaa); + const uint8_t *input_buffer = NULL; + if (decrypt_inplace_) { // Use same buffer for input and output. + // Copy the useful data from encryptedData to output_buffer, which + // will be the same as input_buffer. Leave the 0xaa padding at the end. + for(size_t i=0; i < total_size_; i++) output_buffer[i] = encryptedData[i]; + // Now let input_buffer point to the same data. + input_buffer = &output_buffer[0]; + } else { + input_buffer = &encryptedData[0]; + } + 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. + bool is_encrypted = false; + OEMCrypto_DestBufferDesc destBuffer; + size_t block_offset = 0; + uint8_t subsample_flags = 0; + if (subsample_size_[i].clear_size > 0) { + destBuffer.type = OEMCrypto_BufferType_Clear; + destBuffer.buffer.clear.address = &output_buffer[buffer_offset]; + destBuffer.buffer.clear.max_length = total_size_ - buffer_offset; + if (i == 0) subsample_flags |= OEMCrypto_FirstSubsample; + if ((i == subsample_size_.size() - 1) && + (subsample_size_[i].encrypted_size == 0)) { + subsample_flags |= OEMCrypto_LastSubsample; + } + sts = + OEMCrypto_DecryptCENC(s.session_id(), input_buffer + buffer_offset, + subsample_size_[i].clear_size, is_encrypted, + sample_init_data_[i].iv, block_offset, + &destBuffer, &pattern, subsample_flags); + ASSERT_EQ(OEMCrypto_SUCCESS, sts); + buffer_offset += subsample_size_[i].clear_size; + } + if (subsample_size_[i].encrypted_size > 0) { + destBuffer.type = OEMCrypto_BufferType_Clear; + destBuffer.buffer.clear.address = &output_buffer[buffer_offset]; + destBuffer.buffer.clear.max_length = total_size_ - buffer_offset; + is_encrypted = true; + block_offset = sample_init_data_[i].block_offset; + subsample_flags = 0; + if ((i == 0) && (subsample_size_[i].clear_size == 0)) { + subsample_flags |= OEMCrypto_FirstSubsample; + } + if (i == subsample_size_.size() - 1) { + subsample_flags |= OEMCrypto_LastSubsample; + } + sts = OEMCrypto_DecryptCENC( + s.session_id(), input_buffer + buffer_offset, + subsample_size_[i].encrypted_size, is_encrypted, + sample_init_data_[i].iv, block_offset, &destBuffer, &pattern, + subsample_flags); + // CBC mode should not accept a block offset. + if ((block_offset > 0) && (cipher_mode_ == OEMCrypto_CipherMode_CBC)) { + ASSERT_EQ(OEMCrypto_ERROR_INVALID_CONTEXT, sts) + << "CBC Mode should reject a non-zero block offset."; + return; + } + ASSERT_EQ(OEMCrypto_SUCCESS, sts); + buffer_offset += subsample_size_[i].encrypted_size; + } + } + EXPECT_EQ(0xaa, output_buffer[total_size_]) << "Buffer overrun."; + output_buffer.resize(total_size_); + EXPECT_EQ(unencryptedData, output_buffer); + } + + OEMCrypto_CENCEncryptPatternDesc pattern_; + OEMCryptoCipherMode cipher_mode_; + bool decrypt_inplace_; // If true, input and output buffers are the same. + vector subsample_size_; + size_t total_size_; + vector sample_init_data_; +}; + +// Tests that generate partial ending blocks. These tests should not be used +// with CTR mode and pattern encrypt. +class OEMCryptoSessionTestsPartialBlockTests + : public OEMCryptoSessionTestsDecryptTests {}; + +TEST_P(OEMCryptoSessionTestsDecryptTests, SingleLargeSubsample) { + // This subsample size should be larger a few encrypt/skip patterns. Most + // test cases use a pattern length of 160, so we'll run through at least two + // full patterns. + subsample_size_.push_back(SampleSize(0, 400)); + FindTotalSize(); + vector unencryptedData(total_size_); + vector encryptedData(total_size_); + vector encryptionIv(AES_BLOCK_SIZE); + vector key(AES_BLOCK_SIZE); + EXPECT_EQ(1, GetRandBytes(&encryptionIv[0], AES_BLOCK_SIZE)); + EXPECT_EQ(1, GetRandBytes(&key[0], AES_BLOCK_SIZE)); + for (size_t i = 0; i < total_size_; i++) unencryptedData[i] = i % 256; + EncryptData(key, encryptionIv, unencryptedData, &encryptedData); + TestDecryptCENC(key, encryptionIv, encryptedData, unencryptedData); +} + +TEST_P(OEMCryptoSessionTestsDecryptTests, PatternPlusOneBlock) { + // When the pattern length is 10 blocks, there is a discrepancy between the + // HLS and the CENC standards for samples of size 160*N+16, for N = 1, 2, 3... + // We require the CENC standard for OEMCrypto, and let a layer above us break + // samples into pieces if they wish to use the HLS standard. + subsample_size_.push_back(SampleSize(0, 160 + 16)); + FindTotalSize(); + vector unencryptedData(total_size_); + vector encryptedData(total_size_); + vector encryptionIv(AES_BLOCK_SIZE); + vector key(AES_BLOCK_SIZE); + EXPECT_EQ(1, GetRandBytes(&encryptionIv[0], AES_BLOCK_SIZE)); + EXPECT_EQ(1, GetRandBytes(&key[0], AES_BLOCK_SIZE)); + for (size_t i = 0; i < total_size_; i++) unencryptedData[i] = i % 256; + EncryptData(key, encryptionIv, unencryptedData, &encryptedData); + TestDecryptCENC(key, encryptionIv, encryptedData, unencryptedData); +} + +TEST_P(OEMCryptoSessionTestsDecryptTests, OneBlock) { + subsample_size_.push_back(SampleSize(0, 16)); + FindTotalSize(); + vector unencryptedData(total_size_); + vector encryptedData(total_size_); + vector encryptionIv(AES_BLOCK_SIZE); + vector key(AES_BLOCK_SIZE); + EXPECT_EQ(1, GetRandBytes(&encryptionIv[0], AES_BLOCK_SIZE)); + EXPECT_EQ(1, GetRandBytes(&key[0], AES_BLOCK_SIZE)); + for (size_t i = 0; i < total_size_; i++) unencryptedData[i] = i % 256; + EncryptData(key, encryptionIv, unencryptedData, &encryptedData); + TestDecryptCENC(key, encryptionIv, encryptedData, unencryptedData); +} + +// This tests the ability to decrypt multiple subsamples with no offset. +// There is no offset within the block, used by CTR mode. However, there might +// be an offset in the encrypt/skip pattern. +TEST_P(OEMCryptoSessionTestsDecryptTests, NoOffset) { + subsample_size_.push_back(SampleSize(25, 160)); + subsample_size_.push_back(SampleSize(50, 256)); + subsample_size_.push_back(SampleSize(25, 160)); + FindTotalSize(); + vector unencryptedData(total_size_); + vector encryptedData(total_size_); + vector encryptionIv(AES_BLOCK_SIZE); + vector key(AES_BLOCK_SIZE); + EXPECT_EQ(1, GetRandBytes(&encryptionIv[0], AES_BLOCK_SIZE)); + EXPECT_EQ(1, GetRandBytes(&key[0], AES_BLOCK_SIZE)); + for (size_t i = 0; i < total_size_; i++) unencryptedData[i] = i % 256; + EncryptData(key, encryptionIv, unencryptedData, &encryptedData); + TestDecryptCENC(key, encryptionIv, encryptedData, unencryptedData); +} + +// This tests an offset into the block for the second encrypted subsample. +// This should only work for CTR mode, for CBC mode an error is expected in +// the decrypt step. +// If this test fails for CTR mode, then it is probably handleing the +// block_offset incorrectly. +TEST_P(OEMCryptoSessionTestsPartialBlockTests, EvenOffset) { + subsample_size_.push_back(SampleSize(25, 8)); + subsample_size_.push_back(SampleSize(25, 32)); + subsample_size_.push_back(SampleSize(25, 50)); + FindTotalSize(); + vector unencryptedData(total_size_); + vector encryptedData(total_size_); + vector encryptionIv(AES_BLOCK_SIZE); + vector key(AES_BLOCK_SIZE); + EXPECT_EQ(1, GetRandBytes(&key[0], AES_BLOCK_SIZE)); + // CTR Mode is self-inverse -- i.e. We can pick the encrypted data and + // compute the unencrypted data. By picking the encrypted data to be all 0, + // it is easier to re-encrypt the data and debug problems. Similarly, we + // pick an iv = 0. + EncryptData(key, encryptionIv, encryptedData, &unencryptedData); + // Run EncryptData again to correctly compute intermediate IV vectors. + // For CBC mode, this also computes the real encrypted data. + EncryptData(key, encryptionIv, unencryptedData, &encryptedData); + TestDecryptCENC(key, encryptionIv, encryptedData, unencryptedData); +} + +// If the EvenOffset test passes, but this one doesn't, then DecryptCTR might +// be using the wrong definition of block offset. Adding the block offset to +// the block boundary should give you the beginning of the encrypted data. +// This should only work for CTR mode, for CBC mode, the block offset must be +// 0, so an error is expected in the decrypt step. +// Another way to view the block offset is with the formula: +// block_boundary + block_offset = beginning of subsample. +TEST_P(OEMCryptoSessionTestsPartialBlockTests, OddOffset) { + subsample_size_.push_back(SampleSize(10, 50)); + subsample_size_.push_back(SampleSize(10, 75)); + subsample_size_.push_back(SampleSize(10, 25)); + FindTotalSize(); + vector unencryptedData(total_size_); + vector encryptedData(total_size_); + vector encryptionIv(AES_BLOCK_SIZE); + vector key(AES_BLOCK_SIZE); + EXPECT_EQ(1, GetRandBytes(&encryptionIv[0], AES_BLOCK_SIZE)); + EXPECT_EQ(1, GetRandBytes(&key[0], AES_BLOCK_SIZE)); + for (size_t i = 0; i < total_size_; i++) unencryptedData[i] = i % 256; + EncryptData(key, encryptionIv, unencryptedData, &encryptedData); + TestDecryptCENC(key, encryptionIv, encryptedData, unencryptedData); +} + +// This tests that the algorithm used to increment the counter for +// AES-CTR mode is correct. There are two possible implementations: +// 1) increment the counter as if it were a 128 bit number, +// 2) increment the low 64 bits as a 64 bit number and leave the high bits +// alone. +// For CENC, the algorithm we should use is the second one. OpenSSL defaults to +// the first. If this test is not passing, you should look at the way you +// increment the counter. Look at the example code in ctr128_inc64 above. +// If you start with an IV of 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE, after you +// increment twice, you should get 0xFFFFFFFFFFFFFFFF0000000000000000. +TEST_P(OEMCryptoSessionTestsDecryptTests, DecryptWithNearWrap) { + subsample_size_.push_back(SampleSize(0, 256)); + FindTotalSize(); + vector unencryptedData(total_size_); + vector encryptedData(total_size_); + vector encryptionIv(AES_BLOCK_SIZE); + vector key(AES_BLOCK_SIZE); + EXPECT_EQ(1, GetRandBytes(&key[0], AES_BLOCK_SIZE)); + encryptionIv = wvcdm::a2b_hex("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE"); + for (size_t i = 0; i < total_size_; i++) unencryptedData[i] = i % 256; + EncryptData(key, encryptionIv, unencryptedData, &encryptedData); + TestDecryptCENC(key, encryptionIv, encryptedData, unencryptedData); +} + +// This tests the case where an encrypted sample is not an even number of +// blocks. For CTR mode, the partial block is encrypted. For CBC mode the +// partial block should be a copy of the clear data. +TEST_P(OEMCryptoSessionTestsPartialBlockTests, PartialBlock) { + // Note: for more complete test coverage, we want a sample size that is in + // the encrypted range for some tests, e.g. (3,7), and in the skip range for + // other tests, e.g. (7, 3). 3*16 < 50 and 7*16 > 50. + subsample_size_.push_back(SampleSize(0, 50)); + FindTotalSize(); + vector unencryptedData(total_size_); + vector encryptedData(total_size_); + vector encryptionIv(AES_BLOCK_SIZE); + vector key(AES_BLOCK_SIZE); + EXPECT_EQ(1, GetRandBytes(&encryptionIv[0], AES_BLOCK_SIZE)); + EXPECT_EQ(1, GetRandBytes(&key[0], AES_BLOCK_SIZE)); + for (size_t i = 0; i < total_size_; i++) unencryptedData[i] = i % 256; + EncryptData(key, encryptionIv, unencryptedData, &encryptedData); + TestDecryptCENC(key, encryptionIv, encryptedData, unencryptedData); +} + +TEST_P(OEMCryptoSessionTestsDecryptTests, DecryptLargeBuffer) { + subsample_size_.push_back(SampleSize(kMaxDecryptSize, 0)); + subsample_size_.push_back(SampleSize(kMaxDecryptSize, 0)); + subsample_size_.push_back(SampleSize(0, kMaxDecryptSize)); + subsample_size_.push_back(SampleSize(0, kMaxDecryptSize)); + subsample_size_.push_back(SampleSize(kMaxDecryptSize, 0)); + subsample_size_.push_back(SampleSize(kMaxDecryptSize, 0)); + subsample_size_.push_back(SampleSize(0, kMaxDecryptSize)); + subsample_size_.push_back(SampleSize(0, kMaxDecryptSize)); + FindTotalSize(); + vector unencryptedData(total_size_); + vector encryptedData(total_size_); + vector encryptionIv(AES_BLOCK_SIZE); + vector key(AES_BLOCK_SIZE); + EXPECT_EQ(1, GetRandBytes(&encryptionIv[0], AES_BLOCK_SIZE)); + EXPECT_EQ(1, GetRandBytes(&key[0], AES_BLOCK_SIZE)); + for (size_t i = 0; i < total_size_; i++) unencryptedData[i] = i % 256; + EncryptData(key, encryptionIv, unencryptedData, &encryptedData); + TestDecryptCENC(key, encryptionIv, encryptedData, unencryptedData); +} + +TEST_P(OEMCryptoSessionTestsDecryptTests, DecryptUnencrypted) { + subsample_size_.push_back(SampleSize(256, 0)); + FindTotalSize(); + vector unencryptedData(total_size_); + vector encryptedData(total_size_); + vector encryptionIv(AES_BLOCK_SIZE); + vector key(AES_BLOCK_SIZE); + EXPECT_EQ(1, GetRandBytes(&encryptionIv[0], AES_BLOCK_SIZE)); + EXPECT_EQ(1, GetRandBytes(&key[0], AES_BLOCK_SIZE)); + for (size_t i = 0; i < total_size_; i++) unencryptedData[i] = i % 256; + EncryptData(key, encryptionIv, unencryptedData, &encryptedData); + TestDecryptCENC(key, encryptionIv, encryptedData, unencryptedData); +} + +TEST_F(OEMCryptoSessionTests, DecryptUnencryptedNoKey) { + OEMCryptoResult sts; + Session s; + ASSERT_NO_FATAL_FAILURE(s.open()); + // Clear data should be copied even if there is no key selected. + // Set up our expected input and output + // This is dummy decrypted data. + vector in_buffer(256); + for (size_t i = 0; i < in_buffer.size(); i++) in_buffer[i] = i % 256; + vector encryptionIv(AES_BLOCK_SIZE); + EXPECT_EQ(1, GetRandBytes(&encryptionIv[0], AES_BLOCK_SIZE)); + // Describe the output + vector out_buffer(in_buffer.size()); + OEMCrypto_DestBufferDesc destBuffer; + destBuffer.type = OEMCrypto_BufferType_Clear; + destBuffer.buffer.clear.address = &out_buffer[0]; + destBuffer.buffer.clear.max_length = out_buffer.size(); + OEMCrypto_CENCEncryptPatternDesc pattern; + pattern.encrypt = 0; + pattern.skip = 0; + pattern.offset = 0; + + // Decrypt the data + sts = + OEMCrypto_DecryptCENC(s.session_id(), &in_buffer[0], in_buffer.size(), + false, &encryptionIv[0], 0, &destBuffer, &pattern, + OEMCrypto_FirstSubsample | OEMCrypto_LastSubsample); + ASSERT_EQ(OEMCrypto_SUCCESS, sts); + ASSERT_EQ(in_buffer, out_buffer); +} + +// Used to construct a specific pattern. +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; +} + +INSTANTIATE_TEST_CASE_P(CTRTests, OEMCryptoSessionTestsPartialBlockTests, + Combine(Values(MakePattern(0,0)), + Values(OEMCrypto_CipherMode_CTR), + Bool())); + +// Decrypt in place for CBC tests was only required in v13. +INSTANTIATE_TEST_CASE_P( + CBCTestsAPI14, OEMCryptoSessionTestsPartialBlockTests, + Combine( + Values(MakePattern(0, 0), + MakePattern(3, 7), + // HLS Edge case. We should follow the CENC spec, not HLS spec. + MakePattern(9, 1), + MakePattern(1, 9), + MakePattern(1, 3), + MakePattern(2, 1)), + Values(OEMCrypto_CipherMode_CBC), Bool())); + +INSTANTIATE_TEST_CASE_P( + CTRTestsAPI11, OEMCryptoSessionTestsDecryptTests, + Combine( + Values(MakePattern(0, 0), + MakePattern(3, 7), + // Pattern length should be 10, but that is not guaranteed. + MakePattern(1, 3), + MakePattern(2, 1)), + Values(OEMCrypto_CipherMode_CTR), Bool())); + +// Decrypt in place for CBC tests was only required in v13. +INSTANTIATE_TEST_CASE_P( + CBCTestsAPI14, OEMCryptoSessionTestsDecryptTests, + Combine( + Values(MakePattern(0, 0), + MakePattern(3, 7), + // HLS Edge case. We should follow the CENC spec, not HLS spec. + MakePattern(9, 1), + MakePattern(1, 9), + // Pattern length should be 10, but that is not guaranteed. + MakePattern(1, 3), + MakePattern(2, 1)), + Values(OEMCrypto_CipherMode_CBC), Bool())); + +TEST_F(OEMCryptoSessionTests, DecryptSecureToClear) { + Session s; + ASSERT_NO_FATAL_FAILURE(s.open()); + ASSERT_NO_FATAL_FAILURE(InstallTestSessionKeys(&s)); + ASSERT_NO_FATAL_FAILURE(s.FillSimpleMessage( + kDuration, + wvoec_mock::kControlObserveDataPath | wvoec_mock::kControlDataPathSecure, + 0)); + ASSERT_NO_FATAL_FAILURE(s.EncryptAndSign()); + ASSERT_NO_FATAL_FAILURE(s.LoadTestKeys()); + ASSERT_NO_FATAL_FAILURE( + s.TestDecryptCTR(true, OEMCrypto_ERROR_UNKNOWN_FAILURE)); +} + +TEST_F(OEMCryptoSessionTests, DecryptNoAnalogToClearAPI13) { + Session s; + ASSERT_NO_FATAL_FAILURE(s.open()); + ASSERT_NO_FATAL_FAILURE(InstallTestSessionKeys(&s)); + ASSERT_NO_FATAL_FAILURE(s.FillSimpleMessage( + kDuration, wvoec_mock::kControlDisableAnalogOutput, 0)); + ASSERT_NO_FATAL_FAILURE(s.EncryptAndSign()); + ASSERT_NO_FATAL_FAILURE(s.LoadTestKeys()); + ASSERT_NO_FATAL_FAILURE( + s.TestDecryptCTR(true, OEMCrypto_ERROR_ANALOG_OUTPUT)); +} + +TEST_F(OEMCryptoSessionTests, KeyDuration) { + Session s; + ASSERT_NO_FATAL_FAILURE(s.open()); + ASSERT_NO_FATAL_FAILURE(InstallTestSessionKeys(&s)); + ASSERT_NO_FATAL_FAILURE(s.FillSimpleMessage( + kDuration, wvoec_mock::kControlNonceEnabled, s.get_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. + ASSERT_NO_FATAL_FAILURE(s.TestDecryptCTR(false, OEMCrypto_SUCCESS)); + sleep(kLongSleep); // Should be expired key. + ASSERT_NO_FATAL_FAILURE(s.TestDecryptCTR(false, OEMCrypto_ERROR_KEY_EXPIRED)); + ASSERT_NO_FATAL_FAILURE(s.TestSelectExpired(0)); +} + +// +// Certificate Root of Trust Tests +// +class OEMCryptoLoadsCertificate : public OEMCryptoSessionTestKeyboxTest {}; + +TEST_F(OEMCryptoLoadsCertificate, LoadRSASessionKey) { + CreateWrappedRSAKey(kSign_RSASSA_PSS, true); + Session s; + ASSERT_NO_FATAL_FAILURE(s.open()); + ASSERT_NO_FATAL_FAILURE(s.InstallRSASessionTestKey(wrapped_rsa_key_)); +} + +TEST_F(OEMCryptoLoadsCertificate, CertificateProvision) { + CreateWrappedRSAKey(kSign_RSASSA_PSS, true); + // We should not be able to find the rsa key in the wrapped key. It should + // be encrypted. + ASSERT_EQ(NULL, find(wrapped_rsa_key_, encoded_rsa_key_)); +} + +TEST_F(OEMCryptoLoadsCertificate, CertificateProvisionBadRange1KeyboxTest) { + Session s; + ASSERT_NO_FATAL_FAILURE(s.open()); + ASSERT_NO_FATAL_FAILURE(s.GenerateDerivedKeysFromKeybox(keybox_)); + ASSERT_NO_FATAL_FAILURE(s.VerifyClientSignature()); + 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[0], + signature.size(), &encrypted.nonce, encrypted.rsa_key, + encrypted.rsa_key_length, encrypted.rsa_key_iv, NULL, + &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[0], + signature.size(), &nonce, encrypted.rsa_key, encrypted.rsa_key_length, + encrypted.rsa_key_iv, &(wrapped_key.front()), &wrapped_key_length)); +} + +TEST_F(OEMCryptoLoadsCertificate, CertificateProvisionBadRange2KeyboxTest) { + 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.VerifyClientSignature()); + 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[0], + signature.size(), &encrypted.nonce, encrypted.rsa_key, + encrypted.rsa_key_length, encrypted.rsa_key_iv, NULL, + &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[0], + signature.size(), &encrypted.nonce, &bad_buffer[0], + encrypted.rsa_key_length, encrypted.rsa_key_iv, + &(wrapped_key.front()), &wrapped_key_length)); +} + +TEST_F(OEMCryptoLoadsCertificate, CertificateProvisionBadRange3KeyboxTest) { + 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.VerifyClientSignature()); + 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; + + size_t wrapped_key_length = 0; + ASSERT_EQ(OEMCrypto_ERROR_SHORT_BUFFER, + OEMCrypto_RewrapDeviceRSAKey( + s.session_id(), message_ptr, sizeof(encrypted), &signature[0], + signature.size(), &encrypted.nonce, encrypted.rsa_key, + encrypted.rsa_key_length, encrypted.rsa_key_iv, NULL, + &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[0], + signature.size(), &encrypted.nonce, encrypted.rsa_key, + encrypted.rsa_key_length, &bad_buffer[0], + &(wrapped_key.front()), &wrapped_key_length)); +} + +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.VerifyClientSignature()); + 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[0], + signature.size(), &encrypted.nonce, encrypted.rsa_key, + encrypted.rsa_key_length, encrypted.rsa_key_iv, NULL, + &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[0], + signature.size(), &encrypted.nonce, encrypted.rsa_key, + encrypted.rsa_key_length, encrypted.rsa_key_iv, + &(wrapped_key.front()), &wrapped_key_length)); +} + +TEST_F(OEMCryptoLoadsCertificate, CertificateProvisionBadNonceKeyboxTest) { + 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.VerifyClientSignature()); + 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[0], + signature.size(), &encrypted.nonce, encrypted.rsa_key, + encrypted.rsa_key_length, encrypted.rsa_key_iv, NULL, + &wrapped_key_length)); + wrapped_key.clear(); + wrapped_key.assign(wrapped_key_length, 0); + encrypted.nonce ^= 42; // Almost surely a bad nonce. + ASSERT_EQ(OEMCrypto_ERROR_INVALID_NONCE, + OEMCrypto_RewrapDeviceRSAKey( + s.session_id(), message_ptr, sizeof(encrypted), &signature[0], + signature.size(), &encrypted.nonce, encrypted.rsa_key, + encrypted.rsa_key_length, encrypted.rsa_key_iv, + &(wrapped_key.front()), &wrapped_key_length)); +} + +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.VerifyClientSignature()); + 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[0], + signature.size(), &encrypted.nonce, encrypted.rsa_key, + encrypted.rsa_key_length, encrypted.rsa_key_iv, NULL, + &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[0], + signature.size(), &encrypted.nonce, encrypted.rsa_key, + encrypted.rsa_key_length, encrypted.rsa_key_iv, + &(wrapped_key.front()), &wrapped_key_length)); +} + +TEST_F(OEMCryptoLoadsCertificate, CertificateProvisionLargeBufferKeyboxTest) { + 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.VerifyClientSignature()); + 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(NULL, find(wrapped_key, encoded_rsa_key_)); +} + +TEST_F(OEMCryptoLoadsCertificate, CertificateProvisionBadNonceProv30Test) { + Session s; + ASSERT_NO_FATAL_FAILURE(s.open()); + ASSERT_NO_FATAL_FAILURE(s.LoadOEMCert()); + s.GenerateNonce(); + uint32_t bad_nonce = s.get_nonce() ^ 42; + struct RSAPrivateKeyMessage encrypted; + std::vector signature; + std::vector message_key; + std::vector encrypted_message_key; + s.GenerateRSASessionKey(&message_key, &encrypted_message_key); + ASSERT_NO_FATAL_FAILURE(s.MakeRSACertificate(&encrypted, sizeof(encrypted), + &signature, kSign_RSASSA_PSS, + encoded_rsa_key_, &message_key)); + size_t wrapped_key_length = 0; + ASSERT_EQ(OEMCrypto_ERROR_SHORT_BUFFER, + OEMCrypto_RewrapDeviceRSAKey30( + s.session_id(), &bad_nonce, &encrypted_message_key[0], + encrypted_message_key.size(), encrypted.rsa_key, + encrypted.rsa_key_length, encrypted.rsa_key_iv, NULL, + &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[0], + encrypted_message_key.size(), encrypted.rsa_key, + encrypted.rsa_key_length, encrypted.rsa_key_iv, &wrapped_key[0], + &wrapped_key_length)); +} + +TEST_F(OEMCryptoLoadsCertificate, CertificateProvisionBadRSAKeyProv30Test) { + Session s; + ASSERT_NO_FATAL_FAILURE(s.open()); + ASSERT_NO_FATAL_FAILURE(s.LoadOEMCert()); + s.GenerateNonce(); + struct RSAPrivateKeyMessage encrypted; + std::vector signature; + std::vector message_key; + std::vector encrypted_message_key; + s.GenerateRSASessionKey(&message_key, &encrypted_message_key); + ASSERT_NO_FATAL_FAILURE(s.MakeRSACertificate(&encrypted, sizeof(encrypted), + &signature, kSign_RSASSA_PSS, + encoded_rsa_key_, &message_key)); + size_t wrapped_key_length = 0; + uint32_t nonce = s.get_nonce(); + ASSERT_EQ(OEMCrypto_ERROR_SHORT_BUFFER, + OEMCrypto_RewrapDeviceRSAKey30( + s.session_id(), &nonce, &encrypted_message_key[0], + encrypted_message_key.size(), encrypted.rsa_key, + encrypted.rsa_key_length, encrypted.rsa_key_iv, NULL, + &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[0], + encrypted_message_key.size(), encrypted.rsa_key, + encrypted.rsa_key_length, encrypted.rsa_key_iv, &wrapped_key[0], + &wrapped_key_length)); +} + +TEST_F(OEMCryptoLoadsCertificate, LoadWrappedRSAKey) { + OEMCryptoResult sts; + CreateWrappedRSAKey(kSign_RSASSA_PSS, true); + + Session s; + ASSERT_NO_FATAL_FAILURE(s.open()); + sts = OEMCrypto_LoadDeviceRSAKey(s.session_id(), &wrapped_rsa_key_[0], + wrapped_rsa_key_.size()); + 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); + Session s; + ASSERT_NO_FATAL_FAILURE(s.open()); + ASSERT_NO_FATAL_FAILURE(s.PreparePublicKey(&encoded_rsa_key_[0], + encoded_rsa_key_.size())); + ASSERT_NO_FATAL_FAILURE(s.InstallRSASessionTestKey(wrapped_rsa_key_)); + ASSERT_NO_FATAL_FAILURE(s.FillSimpleMessage(kDuration, 0, 0)); + ASSERT_NO_FATAL_FAILURE(s.EncryptAndSign()); + ASSERT_NO_FATAL_FAILURE(s.LoadTestKeys()); + ASSERT_NO_FATAL_FAILURE(s.TestDecryptCTR()); +} + +// Devices that load certificates, should at least support RSA 2048 keys. +TEST_F(OEMCryptoLoadsCertificate, SupportsCertificatesAPI13) { + ASSERT_NE(0u, + OEMCrypto_Supports_RSA_2048bit & OEMCrypto_SupportedCertificates()) + << "Supported certificates is only " << OEMCrypto_SupportedCertificates(); +} + +class OEMCryptoUsesCertificate : public OEMCryptoLoadsCertificate { + protected: + virtual void SetUp() { + OEMCryptoLoadsCertificate::SetUp(); + 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_[0], + wrapped_rsa_key_.size())); + } + } + + virtual void TearDown() { + session_.close(); + OEMCryptoLoadsCertificate::TearDown(); + } + + Session session_; +}; + +// This test is not run by default, because it takes a long time and +// is used to measure RSA performance, not test functionality. +TEST_F(OEMCryptoLoadsCertificate, RSAPerformance) { + OEMCryptoResult sts; + sleep(2); // Make sure are not nonce limited. + + const uint32_t TestDuration = 5000; // milliseconds. + struct timeval start_time, end_time; + gettimeofday(&start_time, NULL); + gettimeofday(&end_time, NULL); + double mtime = 0; + long count = 0; + for (int i = 0; i < 15; i++) { // Only 20 nonce available. + CreateWrappedRSAKey(kSign_RSASSA_PSS, true); + count++; + gettimeofday(&end_time, NULL); + long seconds = end_time.tv_sec - start_time.tv_sec; + long useconds = end_time.tv_usec - start_time.tv_usec; + mtime = seconds * 1e3 + useconds * 1e-3; + } + double provision_time = mtime / count; + + Session session; + CreateWrappedRSAKey(kSign_RSASSA_PSS, true); + gettimeofday(&start_time, NULL); + gettimeofday(&end_time, NULL); + mtime = 0; + count = 0; + do { + Session s; + ASSERT_NO_FATAL_FAILURE(s.open()); + sts = OEMCrypto_LoadDeviceRSAKey(s.session_id(), &wrapped_rsa_key_[0], + wrapped_rsa_key_.size()); + ASSERT_EQ(OEMCrypto_SUCCESS, sts); + const size_t size = 50; + vector licenseRequest(size); + GetRandBytes(&licenseRequest[0], licenseRequest.size()); + size_t signature_length = 0; + sts = OEMCrypto_GenerateRSASignature(s.session_id(), &licenseRequest[0], + licenseRequest.size(), NULL, + &signature_length, kSign_RSASSA_PSS); + ASSERT_EQ(OEMCrypto_ERROR_SHORT_BUFFER, sts); + ASSERT_NE(static_cast(0), signature_length); + uint8_t* signature = new uint8_t[signature_length]; + sts = OEMCrypto_GenerateRSASignature(s.session_id(), &licenseRequest[0], + licenseRequest.size(), signature, + &signature_length, kSign_RSASSA_PSS); + delete[] signature; + ASSERT_EQ(OEMCrypto_SUCCESS, sts); + count++; + gettimeofday(&end_time, NULL); + long seconds = end_time.tv_sec - start_time.tv_sec; + long useconds = end_time.tv_usec - start_time.tv_usec; + mtime = seconds * 1e3 + useconds * 1e-3; + } while (mtime < TestDuration); + double license_request_time = mtime / count; + + Session s; + ASSERT_NO_FATAL_FAILURE(s.open()); + ASSERT_EQ(OEMCrypto_SUCCESS, + OEMCrypto_LoadDeviceRSAKey(s.session_id(), &wrapped_rsa_key_[0], + wrapped_rsa_key_.size())); + vector session_key; + vector enc_session_key; + ASSERT_NO_FATAL_FAILURE(s.PreparePublicKey(&encoded_rsa_key_[0], + encoded_rsa_key_.size())); + ASSERT_TRUE(s.GenerateRSASessionKey(&session_key, &enc_session_key)); + vector mac_context; + vector enc_context; + s.FillDefaultContext(&mac_context, &enc_context); + gettimeofday(&start_time, NULL); + gettimeofday(&end_time, NULL); + mtime = 0; + count = 0; + + enc_session_key = wvcdm::a2b_hex( + "7789c619aa3b9fa3c0a53f57a4abc6" + "02157c8aa57e3c6fb450b0bea22667fb" + "0c3200f9d9d618e397837c720dc2dadf" + "486f33590744b2a4e54ca134ae7dbf74" + "434c2fcf6b525f3e132262f05ea3b3c1" + "198595c0e52b573335b2e8a3debd0d0d" + "d0306f8fcdde4e76476be71342957251" + "e1688c9ca6c1c34ed056d3b989394160" + "cf6937e5ce4d39cc73d11a2e93da21a2" + "fa019d246c852fe960095b32f120c3c2" + "7085f7b64aac344a68d607c0768676ce" + "d4c5b2d057f7601921b453a451e1dea0" + "843ebfef628d9af2784d68e86b730476" + "e136dfe19989de4be30a4e7878efcde5" + "ad2b1254f80c0c5dd3cf111b56572217" + "b9f58fc1dacbf74b59d354a1e62cfa0e" + "bf"); + do { + ASSERT_EQ(OEMCrypto_SUCCESS, + OEMCrypto_DeriveKeysFromSessionKey( + s.session_id(), &enc_session_key[0], enc_session_key.size(), + &mac_context[0], mac_context.size(), &enc_context[0], + enc_context.size())); + count++; + gettimeofday(&end_time, NULL); + long seconds = end_time.tv_sec - start_time.tv_sec; + long useconds = end_time.tv_usec - start_time.tv_usec; + mtime = seconds * 1e3 + useconds * 1e-3; + } while (mtime < TestDuration); + double derive_keys_time = mtime / count; + + const char* level = OEMCrypto_SecurityLevel(); + printf("PERF:head, security, provision (ms), lic req(ms), derive keys(ms)\n"); + printf("PERF:stat, %s, %8.3f, %8.3f, %8.3f\n", level, provision_time, + license_request_time, derive_keys_time); +} + +TEST_F(OEMCryptoUsesCertificate, RSASignature) { + OEMCryptoResult sts; + // Sign a Message + vector licenseRequest(500); + GetRandBytes(&licenseRequest[0], licenseRequest.size()); + size_t signature_length = 0; + uint8_t signature[500]; + + sts = OEMCrypto_GenerateRSASignature( + session_.session_id(), &licenseRequest[0], 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[0], 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_[0], + encoded_rsa_key_.size())); + ASSERT_NO_FATAL_FAILURE(session_.VerifyRSASignature( + licenseRequest, signature, signature_length, kSign_RSASSA_PSS)); +} + +TEST_F(OEMCryptoUsesCertificate, RSASignatureLargeBuffer) { + OEMCryptoResult sts; + // Sign a Message + vector licenseRequest(kMaxMessageSize); + GetRandBytes(&licenseRequest[0], licenseRequest.size()); + size_t signature_length = 0; + uint8_t signature[500]; + + sts = OEMCrypto_GenerateRSASignature( + session_.session_id(), &licenseRequest[0], 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[0], 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_[0], + encoded_rsa_key_.size())); + ASSERT_NO_FATAL_FAILURE(session_.VerifyRSASignature( + licenseRequest, signature, signature_length, kSign_RSASSA_PSS)); +} + +TEST_F(OEMCryptoUsesCertificate, GenerateDerivedKeysLargeBuffer) { + vector session_key; + vector enc_session_key; + ASSERT_NO_FATAL_FAILURE(session_.PreparePublicKey(&encoded_rsa_key_[0], + encoded_rsa_key_.size())); + ASSERT_TRUE(session_.GenerateRSASessionKey(&session_key, &enc_session_key)); + vector mac_context(kMaxMessageSize); + vector enc_context(kMaxMessageSize); + // Stripe the data so the two vectors are not identical, and not all zeroes. + for (size_t i = 0; i < kMaxMessageSize; i++) { + mac_context[i] = i % 0x100; + enc_context[i] = (3 * i) % 0x100; + } + ASSERT_EQ(OEMCrypto_SUCCESS, + OEMCrypto_DeriveKeysFromSessionKey( + session_.session_id(), &enc_session_key[0], + enc_session_key.size(), &mac_context[0], mac_context.size(), + &enc_context[0], enc_context.size())); +} + +// This test attempts to use alternate algorithms for loaded device certs. +class OEMCryptoLoadsCertificateAlternates : public OEMCryptoLoadsCertificate { + protected: + void DisallowForbiddenPadding(RSA_Padding_Scheme scheme, size_t size) { + OEMCryptoResult sts; + Session s; + ASSERT_NO_FATAL_FAILURE(s.open()); + sts = OEMCrypto_LoadDeviceRSAKey(s.session_id(), &wrapped_rsa_key_[0], + wrapped_rsa_key_.size()); + ASSERT_EQ(OEMCrypto_SUCCESS, sts); + + // Sign a Message + vector licenseRequest(size); + GetRandBytes(&licenseRequest[0], licenseRequest.size()); + size_t signature_length = 256; + vector signature(signature_length); + sts = OEMCrypto_GenerateRSASignature(s.session_id(), &licenseRequest[0], + licenseRequest.size(), &signature[0], + &signature_length, scheme); + // Allow OEMCrypto to request a full buffer. + if (sts == OEMCrypto_ERROR_SHORT_BUFFER) { + ASSERT_NE(static_cast(0), signature_length); + signature.assign(signature_length, 0); + sts = OEMCrypto_GenerateRSASignature(s.session_id(), &licenseRequest[0], + licenseRequest.size(), &signature[0], + &signature_length, scheme); + } + + EXPECT_NE(OEMCrypto_SUCCESS, sts) + << "Signed with forbidden padding scheme=" << (int)scheme + << ", size=" << (int)size; + vector zero(signature_length, 0); + ASSERT_EQ(zero, signature); // signature should not be computed. + } + + void TestSignature(RSA_Padding_Scheme scheme, size_t size) { + OEMCryptoResult sts; + Session s; + ASSERT_NO_FATAL_FAILURE(s.open()); + sts = OEMCrypto_LoadDeviceRSAKey(s.session_id(), &wrapped_rsa_key_[0], + wrapped_rsa_key_.size()); + ASSERT_EQ(OEMCrypto_SUCCESS, sts); + + vector licenseRequest(size); + GetRandBytes(&licenseRequest[0], licenseRequest.size()); + size_t signature_length = 0; + sts = OEMCrypto_GenerateRSASignature(s.session_id(), &licenseRequest[0], + licenseRequest.size(), NULL, + &signature_length, scheme); + ASSERT_EQ(OEMCrypto_ERROR_SHORT_BUFFER, sts); + ASSERT_NE(static_cast(0), signature_length); + + uint8_t* signature = new uint8_t[signature_length]; + sts = OEMCrypto_GenerateRSASignature(s.session_id(), &licenseRequest[0], + licenseRequest.size(), signature, + &signature_length, scheme); + + ASSERT_EQ(OEMCrypto_SUCCESS, sts) + << "Failed to sign with padding scheme=" << (int)scheme + << ", size=" << (int)size; + ASSERT_NO_FATAL_FAILURE(s.PreparePublicKey(&encoded_rsa_key_[0], + encoded_rsa_key_.size())); + ASSERT_NO_FATAL_FAILURE(s.VerifyRSASignature(licenseRequest, signature, + signature_length, scheme)); + delete[] signature; + } + + void DisallowDeriveKeys() { + OEMCryptoResult sts; + Session s; + ASSERT_NO_FATAL_FAILURE(s.open()); + sts = OEMCrypto_LoadDeviceRSAKey(s.session_id(), &wrapped_rsa_key_[0], + wrapped_rsa_key_.size()); + ASSERT_EQ(OEMCrypto_SUCCESS, sts); + s.GenerateNonce(); + vector session_key; + vector enc_session_key; + ASSERT_NO_FATAL_FAILURE(s.PreparePublicKey(&encoded_rsa_key_[0], + encoded_rsa_key_.size())); + ASSERT_TRUE(s.GenerateRSASessionKey(&session_key, &enc_session_key)); + vector mac_context; + vector enc_context; + s.FillDefaultContext(&mac_context, &enc_context); + ASSERT_NE(OEMCrypto_SUCCESS, + OEMCrypto_DeriveKeysFromSessionKey( + s.session_id(), &enc_session_key[0], enc_session_key.size(), + &mac_context[0], mac_context.size(), &enc_context[0], + enc_context.size())); + } + + // If force is true, we assert that the key loads successfully. + void LoadWithAllowedSchemes(uint32_t schemes, bool force) { + CreateWrappedRSAKey(schemes, force); + key_loaded_ = (wrapped_rsa_key_.size() > 0); + if (force) ASSERT_TRUE(key_loaded_); + } + + bool key_loaded_; +}; + +// The alternate padding is only required for cast receivers, but all devices +// should forbid the alternate padding for regular certificates. +TEST_F(OEMCryptoLoadsCertificateAlternates, DisallowForbiddenPaddingAPI09) { + LoadWithAllowedSchemes(kSign_RSASSA_PSS, true); // Use default padding scheme + DisallowForbiddenPadding(kSign_PKCS1_Block1, 50); +} + +// The alternate padding is only required for cast receivers, but if a device +// does load an alternate certificate, it should NOT use it for generating +// a license request signature. +TEST_F(OEMCryptoLoadsCertificateAlternates, TestSignaturePKCS1) { + // Try to load an RSA key with alternative padding schemes. This signing + // scheme is used by cast receivers. + LoadWithAllowedSchemes(kSign_PKCS1_Block1, false); + // If the device is a cast receiver, then this scheme is required. + if (global_features.cast_receiver) ASSERT_TRUE(key_loaded_); + // If the key loaded with no error, then we will verify that it is not used + // for forbidden padding schemes. + if (key_loaded_) { + // The other padding scheme should fail. + DisallowForbiddenPadding(kSign_RSASSA_PSS, 83); + DisallowDeriveKeys(); + if (global_features.cast_receiver) { + // A signature with a valid size should succeed. + TestSignature(kSign_PKCS1_Block1, 83); + TestSignature(kSign_PKCS1_Block1, 50); + } + // A signature with padding that is too big should fail. + DisallowForbiddenPadding(kSign_PKCS1_Block1, 84); // too big. + } +} + +TEST_F(OEMCryptoLoadsCertificateAlternates, TestSignatureBoth) { + // 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. + 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 +// those devices. +class OEMCryptoCastReceiverTest : public OEMCryptoLoadsCertificateAlternates { + protected: + vector encode(uint8_t type, const vector& substring) { + vector result; + result.push_back(type); + if (substring.size() < 0x80) { + uint8_t length = substring.size(); + result.push_back(length); + } else if (substring.size() < 0x100) { + result.push_back(0x81); + uint8_t length = substring.size(); + result.push_back(length); + } else { + result.push_back(0x82); + uint16_t length = substring.size(); + result.push_back(length >> 8); + result.push_back(length & 0xFF); + } + result.insert(result.end(), substring.begin(), substring.end()); + return result; + } + vector concat(const vector& a, const vector& b) { + vector result = a; + result.insert(result.end(), b.begin(), b.end()); + return result; + } + + // This encodes the RSA key used in the PKCS#1 signing tests below. + void BuildRSAKey() { + vector field_n = + encode(0x02, wvcdm::a2b_hex("00" + "df271fd25f8644496b0c81be4bd50297" + "ef099b002a6fd67727eb449cea566ed6" + "a3981a71312a141cabc9815c1209e320" + "a25b32464e9999f18ca13a9fd3892558" + "f9e0adefdd3650dd23a3f036d60fe398" + "843706a40b0b8462c8bee3bce12f1f28" + "60c2444cdc6a44476a75ff4aa24273cc" + "be3bf80248465f8ff8c3a7f3367dfc0d" + "f5b6509a4f82811cedd81cdaaa73c491" + "da412170d544d4ba96b97f0afc806549" + "8d3a49fd910992a1f0725be24f465cfe" + "7e0eabf678996c50bc5e7524abf73f15" + "e5bef7d518394e3138ce4944506aaaaf" + "3f9b236dcab8fc00f87af596fdc3d9d6" + "c75cd508362fae2cbeddcc4c7450b17b" + "776c079ecca1f256351a43b97dbe2153")); + vector field_e = encode(0x02, wvcdm::a2b_hex("010001")); + vector field_d = + encode(0x02, wvcdm::a2b_hex("5bd910257830dce17520b03441a51a8c" + "ab94020ac6ecc252c808f3743c95b7c8" + "3b8c8af1a5014346ebc4242cdfb5d718" + "e30a733e71f291e4d473b61bfba6daca" + "ed0a77bd1f0950ae3c91a8f901118825" + "89e1d62765ee671e7baeea309f64d447" + "bbcfa9ea12dce05e9ea8939bc5fe6108" + "581279c982b308794b3448e7f7b95229" + "2df88c80cb40142c4b5cf5f8ddaa0891" + "678d610e582fcb880f0d707caf47d09a" + "84e14ca65841e5a3abc5e9dba94075a9" + "084341f0edad9b68e3b8e082b80b6e6e" + "8a0547b44fb5061b6a9131603a5537dd" + "abd01d8e863d8922e9aa3e4bfaea0b39" + "d79283ad2cbc8a59cce7a6ecf4e4c81e" + "d4c6591c807defd71ab06866bb5e7745")); + vector field_p = + encode(0x02, wvcdm::a2b_hex("00" + "f44f5e4246391f482b2f5296e3602eb3" + "4aa136427710f7c0416d403fd69d4b29" + "130cfebef34e885abdb1a8a0a5f0e9b5" + "c33e1fc3bfc285b1ae17e40cc67a1913" + "dd563719815ebaf8514c2a7aa0018e63" + "b6c631dc315a46235716423d11ff5803" + "4e610645703606919f5c7ce2660cd148" + "bd9efc123d9c54b6705590d006cfcf3f")); + vector field_q = + encode(0x02, wvcdm::a2b_hex("00" + "e9d49841e0e0a6ad0d517857133e36dc" + "72c1bdd90f9174b52e26570f373640f1" + "c185e7ea8e2ed7f1e4ebb951f70a5802" + "3633b0097aec67c6dcb800fc1a67f9bb" + "0563610f08ebc8746ad129772136eb1d" + "daf46436450d318332a84982fe5d28db" + "e5b3e912407c3e0e03100d87d436ee40" + "9eec1cf85e80aba079b2e6106b97bced")); + vector field_exp1 = + encode(0x02, wvcdm::a2b_hex("00" + "ed102acdb26871534d1c414ecad9a4d7" + "32fe95b10eea370da62f05de2c393b1a" + "633303ea741b6b3269c97f704b352702" + "c9ae79922f7be8d10db67f026a8145de" + "41b30c0a42bf923bac5f7504c248604b" + "9faa57ed6b3246c6ba158e36c644f8b9" + "548fcf4f07e054a56f768674054440bc" + "0dcbbc9b528f64a01706e05b0b91106f")); + vector field_exp2 = + encode(0x02, wvcdm::a2b_hex("6827924a85e88b55ba00f8219128bd37" + "24c6b7d1dfe5629ef197925fecaff5ed" + "b9cdf3a7befd8ea2e8dd3707138b3ff8" + "7c3c39c57f439e562e2aa805a39d7cd7" + "9966d2ece7845f1dbc16bee99999e4d0" + "bf9eeca45fcda8a8500035fe6b5f03bc" + "2f6d1bfc4d4d0a3723961af0cdce4a01" + "eec82d7f5458ec19e71b90eeef7dff61")); + vector field_invq = + encode(0x02, wvcdm::a2b_hex("57b73888d183a99a6307422277551a3d" + "9e18adf06a91e8b55ceffef9077c8496" + "948ecb3b16b78155cb2a3a57c119d379" + "951c010aa635edcf62d84c5a122a8d67" + "ab5fa9e5a4a8772a1e943bafc70ae3a4" + "c1f0f3a4ddffaefd1892c8cb33bb0d0b" + "9590e963a69110fb34db7b906fc4ba28" + "36995aac7e527490ac952a02268a4f18")); + + // Header of rsa key is constant. + encoded_rsa_key_ = wvcdm::a2b_hex( + // 0x02 0x01 0x00 == integer, size 1 byte, value = 0 (field=version) + "020100" + // 0x30, sequence, size = d = 13 (field=pkeyalg) AlgorithmIdentifier + "300d" + // 0x06 = object identifier. length = 9 + // (this should be 1.2.840.113549.1.1.1) (field=algorithm) + "0609" + "2a" // 1*0x40 + 2 = 42 = 0x2a. + "8648" // 840 = 0x348, 0x03 *2 + 0x80 + (0x48>>15) = 0x86. + // 0x48 -> 0x48 + "86f70d" // 113549 = 0x1668d -> (110 , 1110111, 0001101) + // -> (0x80+0x06, 0x80+0x77, 0x0d) + "01" // 1 + "01" // 1 + "01" // 1 + "05" // null object. (field=parameter?) + "00" // size of null object + ); + + vector pkey = wvcdm::a2b_hex("020100"); // integer, version = 0. + pkey = concat(pkey, field_n); + pkey = concat(pkey, field_e); + pkey = concat(pkey, field_d); + pkey = concat(pkey, field_p); + pkey = concat(pkey, field_q); + pkey = concat(pkey, field_exp1); + pkey = concat(pkey, field_exp2); + pkey = concat(pkey, field_invq); + pkey = encode(0x30, pkey); + pkey = encode(0x04, pkey); + + encoded_rsa_key_ = concat(encoded_rsa_key_, pkey); + encoded_rsa_key_ = encode(0x30, encoded_rsa_key_); // 0x30=sequence + } + + // This is used to test a signature from the file pkcs1v15sign-vectors.txt. + void TestSignature(RSA_Padding_Scheme scheme, const vector& message, + const vector& correct_signature) { + OEMCryptoResult sts; + Session s; + ASSERT_NO_FATAL_FAILURE(s.open()); + sts = OEMCrypto_LoadDeviceRSAKey(s.session_id(), &wrapped_rsa_key_[0], + wrapped_rsa_key_.size()); + ASSERT_EQ(OEMCrypto_SUCCESS, sts); + + // The application will compute the SHA-1 Hash of the message, so this + // test must do that also. + uint8_t hash[SHA_DIGEST_LENGTH]; + if (!SHA1(&message[0], message.size(), hash)) { + dump_boringssl_error(); + FAIL() << "boringssl error creating SHA1 hash."; + } + + // The application will prepend the digest info to the hash. + // SHA-1 digest info prefix = 0x30 0x21 0x30 ... + vector digest = wvcdm::a2b_hex("3021300906052b0e03021a05000414"); + digest.insert(digest.end(), hash, hash + SHA_DIGEST_LENGTH); + + // OEMCrypto will apply the padding, and encrypt to generate the signature. + size_t signature_length = 0; + sts = OEMCrypto_GenerateRSASignature(s.session_id(), &digest[0], + digest.size(), NULL, &signature_length, + scheme); + ASSERT_EQ(OEMCrypto_ERROR_SHORT_BUFFER, sts); + ASSERT_NE(static_cast(0), signature_length); + + vector signature(signature_length); + sts = OEMCrypto_GenerateRSASignature(s.session_id(), &digest[0], + digest.size(), &signature[0], + &signature_length, scheme); + + ASSERT_EQ(OEMCrypto_SUCCESS, sts) + << "Failed to sign with padding scheme=" << (int)scheme + << ", size=" << (int)message.size(); + ASSERT_NO_FATAL_FAILURE(s.PreparePublicKey(&encoded_rsa_key_[0], + encoded_rsa_key_.size())); + + // Verify that the signature matches the official test vector. + ASSERT_EQ(correct_signature.size(), signature_length); + signature.resize(signature_length); + ASSERT_EQ(correct_signature, signature); + + // Also verify that our verification algorithm agrees. This is not needed + // to test OEMCrypto, but it does verify that this test is valid. + ASSERT_NO_FATAL_FAILURE( + s.VerifyRSASignature(digest, &signature[0], signature_length, scheme)); + ASSERT_NO_FATAL_FAILURE(s.VerifyRSASignature( + digest, &correct_signature[0], correct_signature.size(), scheme)); + } +}; + +// CAST Receivers should report that they support cast certificates. +TEST_F(OEMCryptoCastReceiverTest, SupportsCertificatesAPI13) { + ASSERT_NE(0u, OEMCrypto_Supports_RSA_CAST & OEMCrypto_SupportedCertificates()); +} + +// # PKCS#1 v1.5 Signature Example 15.1 +TEST_F(OEMCryptoCastReceiverTest, TestSignaturePKCS1_15_1) { + BuildRSAKey(); + LoadWithAllowedSchemes(kSign_PKCS1_Block1, true); + vector message = wvcdm::a2b_hex( + "f45d55f35551e975d6a8dc7ea9f48859" + "3940cc75694a278f27e578a163d839b3" + "4040841808cf9c58c9b8728bf5f9ce8e" + "e811ea91714f47bab92d0f6d5a26fcfe" + "ea6cd93b910c0a2c963e64eb1823f102" + "753d41f0335910ad3a977104f1aaf6c3" + "742716a9755d11b8eed690477f445c5d" + "27208b2e284330fa3d301423fa7f2d08" + "6e0ad0b892b9db544e456d3f0dab85d9" + "53c12d340aa873eda727c8a649db7fa6" + "3740e25e9af1533b307e61329993110e" + "95194e039399c3824d24c51f22b26bde" + "1024cd395958a2dfeb4816a6e8adedb5" + "0b1f6b56d0b3060ff0f1c4cb0d0e001d" + "d59d73be12"); + vector signature = wvcdm::a2b_hex( + "b75a5466b65d0f300ef53833f2175c8a" + "347a3804fc63451dc902f0b71f908345" + "9ed37a5179a3b723a53f1051642d7737" + "4c4c6c8dbb1ca20525f5c9f32db77695" + "3556da31290e22197482ceb69906c46a" + "758fb0e7409ba801077d2a0a20eae7d1" + "d6d392ab4957e86b76f0652d68b83988" + "a78f26e11172ea609bf849fbbd78ad7e" + "dce21de662a081368c040607cee29db0" + "627227f44963ad171d2293b633a392e3" + "31dca54fe3082752f43f63c161b447a4" + "c65a6875670d5f6600fcc860a1caeb0a" + "88f8fdec4e564398a5c46c87f68ce070" + "01f6213abe0ab5625f87d19025f08d81" + "dac7bd4586bc9382191f6d2880f6227e" + "5df3eed21e7792d249480487f3655261"); + TestSignature(kSign_PKCS1_Block1, message, signature); +} +// # PKCS#1 v1.5 Signature Example 15.2 +TEST_F(OEMCryptoCastReceiverTest, TestSignaturePKCS1_15_2) { + BuildRSAKey(); + LoadWithAllowedSchemes(kSign_PKCS1_Block1, true); + vector message = wvcdm::a2b_hex( + "c14b4c6075b2f9aad661def4ecfd3cb9" + "33c623f4e63bf53410d2f016d1ab98e2" + "729eccf8006cd8e08050737d95fdbf29" + "6b66f5b9792a902936c4f7ac69f51453" + "ce4369452dc22d96f037748114662000" + "dd9cd3a5e179f4e0f81fa6a0311ca1ae" + "e6519a0f63cec78d27bb726393fb7f1f" + "88cde7c97f8a66cd66301281dac3f3a4" + "33248c75d6c2dcd708b6a97b0a3f325e" + "0b2964f8a5819e479b"); + vector signature = wvcdm::a2b_hex( + "afa7343462bea122cc149fca70abdae7" + "9446677db5373666af7dc313015f4de7" + "86e6e394946fad3cc0e2b02bedba5047" + "fe9e2d7d099705e4a39f28683279cf0a" + "c85c1530412242c0e918953be000e939" + "cf3bf182525e199370fa7907eba69d5d" + "b4631017c0e36df70379b5db8d4c695a" + "979a8e6173224065d7dc15132ef28cd8" + "22795163063b54c651141be86d36e367" + "35bc61f31fca574e5309f3a3bbdf91ef" + "f12b99e9cc1744f1ee9a1bd22c5bad96" + "ad481929251f0343fd36bcf0acde7f11" + "e5ad60977721202796fe061f9ada1fc4" + "c8e00d6022a8357585ffe9fdd59331a2" + "8c4aa3121588fb6cf68396d8ac054659" + "9500c9708500a5972bd54f72cf8db0c8"); + TestSignature(kSign_PKCS1_Block1, message, signature); +} + +// # PKCS#1 v1.5 Signature Example 15.3 +TEST_F(OEMCryptoCastReceiverTest, TestSignaturePKCS1_15_3) { + BuildRSAKey(); + LoadWithAllowedSchemes(kSign_PKCS1_Block1, true); + vector message = wvcdm::a2b_hex( + "d02371ad7ee48bbfdb2763de7a843b94" + "08ce5eb5abf847ca3d735986df84e906" + "0bdbcdd3a55ba55dde20d4761e1a21d2" + "25c1a186f4ac4b3019d3adf78fe63346" + "67f56f70c901a0a2700c6f0d56add719" + "592dc88f6d2306c7009f6e7a635b4cb3" + "a502dfe68ddc58d03be10a1170004fe7" + "4dd3e46b82591ff75414f0c4a03e605e" + "20524f2416f12eca589f111b75d639c6" + "1baa80cafd05cf3500244a219ed9ced9" + "f0b10297182b653b526f400f2953ba21" + "4d5bcd47884132872ae90d4d6b1f4215" + "39f9f34662a56dc0e7b4b923b6231e30" + "d2676797817f7c337b5ac824ba93143b" + "3381fa3dce0e6aebd38e67735187b1eb" + "d95c02"); + vector signature = wvcdm::a2b_hex( + "3bac63f86e3b70271203106b9c79aabd" + "9f477c56e4ee58a4fce5baf2cab4960f" + "88391c9c23698be75c99aedf9e1abf17" + "05be1dac33140adb48eb31f450bb9efe" + "83b7b90db7f1576d33f40c1cba4b8d6b" + "1d3323564b0f1774114fa7c08e6d1e20" + "dd8fbba9b6ac7ad41e26b4568f4a8aac" + "bfd178a8f8d2c9d5f5b88112935a8bc9" + "ae32cda40b8d20375510735096536818" + "ce2b2db71a9772c9b0dda09ae10152fa" + "11466218d091b53d92543061b7294a55" + "be82ff35d5c32fa233f05aaac7585030" + "7ecf81383c111674397b1a1b9d3bf761" + "2ccbe5bacd2b38f0a98397b24c83658f" + "b6c0b4140ef11970c4630d44344e76ea" + "ed74dcbee811dbf6575941f08a6523b8"); + TestSignature(kSign_PKCS1_Block1, message, signature); +}; + +// # PKCS#1 v1.5 Signature Example 15.4 +TEST_F(OEMCryptoCastReceiverTest, TestSignaturePKCS1_15_4) { + BuildRSAKey(); + LoadWithAllowedSchemes(kSign_PKCS1_Block1, true); + vector message = wvcdm::a2b_hex( + "29035584ab7e0226a9ec4b02e8dcf127" + "2dc9a41d73e2820007b0f6e21feccd5b" + "d9dbb9ef88cd6758769ee1f956da7ad1" + "8441de6fab8386dbc693"); + vector signature = wvcdm::a2b_hex( + "28d8e3fcd5dddb21ffbd8df1630d7377" + "aa2651e14cad1c0e43ccc52f907f946d" + "66de7254e27a6c190eb022ee89ecf622" + "4b097b71068cd60728a1aed64b80e545" + "7bd3106dd91706c937c9795f2b36367f" + "f153dc2519a8db9bdf2c807430c451de" + "17bbcd0ce782b3e8f1024d90624dea7f" + "1eedc7420b7e7caa6577cef43141a726" + "4206580e44a167df5e41eea0e69a8054" + "54c40eefc13f48e423d7a32d02ed42c0" + "ab03d0a7cf70c5860ac92e03ee005b60" + "ff3503424b98cc894568c7c56a023355" + "1cebe588cf8b0167b7df13adcad82867" + "6810499c704da7ae23414d69e3c0d2db" + "5dcbc2613bc120421f9e3653c5a87672" + "97643c7e0740de016355453d6c95ae72"); + TestSignature(kSign_PKCS1_Block1, message, signature); +} + +// # PKCS#1 v1.5 Signature Example 15.5 +TEST_F(OEMCryptoCastReceiverTest, TestSignaturePKCS1_15_5) { + BuildRSAKey(); + LoadWithAllowedSchemes(kSign_PKCS1_Block1, true); + vector message = wvcdm::a2b_hex("bda3a1c79059eae598308d3df609"); + vector signature = wvcdm::a2b_hex( + "a156176cb96777c7fb96105dbd913bc4" + "f74054f6807c6008a1a956ea92c1f81c" + "b897dc4b92ef9f4e40668dc7c556901a" + "cb6cf269fe615b0fb72b30a513386923" + "14b0e5878a88c2c7774bd16939b5abd8" + "2b4429d67bd7ac8e5ea7fe924e20a6ec" + "662291f2548d734f6634868b039aa5f9" + "d4d906b2d0cb8585bf428547afc91c6e" + "2052ddcd001c3ef8c8eefc3b6b2a82b6" + "f9c88c56f2e2c3cb0be4b80da95eba37" + "1d8b5f60f92538743ddbb5da2972c71f" + "e7b9f1b790268a0e770fc5eb4d5dd852" + "47d48ae2ec3f26255a3985520206a1f2" + "68e483e9dbb1d5cab190917606de31e7" + "c5182d8f151bf41dfeccaed7cde690b2" + "1647106b490c729d54a8fe2802a6d126"); + TestSignature(kSign_PKCS1_Block1, message, signature); +} + +// # PKCS#1 v1.5 Signature Example 15.6 +TEST_F(OEMCryptoCastReceiverTest, TestSignaturePKCS1_15_6) { + BuildRSAKey(); + LoadWithAllowedSchemes(kSign_PKCS1_Block1, true); + vector message = wvcdm::a2b_hex( + "c187915e4e87da81c08ed4356a0cceac" + "1c4fb5c046b45281b387ec28f1abfd56" + "7e546b236b37d01ae71d3b2834365d3d" + "f380b75061b736b0130b070be58ae8a4" + "6d12166361b613dbc47dfaeb4ca74645" + "6c2e888385525cca9dd1c3c7a9ada76d" + "6c"); + vector signature = wvcdm::a2b_hex( + "9cab74163608669f7555a333cf196fe3" + "a0e9e5eb1a32d34bb5c85ff689aaab0e" + "3e65668ed3b1153f94eb3d8be379b8ee" + "f007c4a02c7071ce30d8bb341e58c620" + "f73d37b4ecbf48be294f6c9e0ecb5e63" + "fec41f120e5553dfa0ebebbb72640a95" + "37badcb451330229d9f710f62e3ed8ec" + "784e50ee1d9262b42671340011d7d098" + "c6f2557b2131fa9bd0254636597e88ec" + "b35a240ef0fd85957124df8080fee1e1" + "49af939989e86b26c85a5881fae8673d" + "9fd40800dd134eb9bdb6410f420b0aa9" + "7b20efcf2eb0c807faeb83a3ccd9b51d" + "4553e41dfc0df6ca80a1e81dc234bb83" + "89dd195a38b42de4edc49d346478b9f1" + "1f0557205f5b0bd7ffe9c850f396d7c4"); + TestSignature(kSign_PKCS1_Block1, message, signature); +} + +// # PKCS#1 v1.5 Signature Example 15.7 +TEST_F(OEMCryptoCastReceiverTest, TestSignaturePKCS1_15_7) { + BuildRSAKey(); + LoadWithAllowedSchemes(kSign_PKCS1_Block1, true); + vector message = wvcdm::a2b_hex( + "abfa2ecb7d29bd5bcb9931ce2bad2f74" + "383e95683cee11022f08e8e7d0b8fa05" + "8bf9eb7eb5f98868b5bb1fb5c31ceda3" + "a64f1a12cdf20fcd0e5a246d7a1773d8" + "dba0e3b277545babe58f2b96e3f4edc1" + "8eabf5cd2a560fca75fe96e07d859def" + "b2564f3a34f16f11e91b3a717b41af53" + "f6605323001aa406c6"); + vector signature = wvcdm::a2b_hex( + "c4b437bcf703f352e1faf74eb9622039" + "426b5672caf2a7b381c6c4f0191e7e4a" + "98f0eebcd6f41784c2537ff0f99e7498" + "2c87201bfbc65eae832db71d16dacadb" + "0977e5c504679e40be0f9db06ffd848d" + "d2e5c38a7ec021e7f68c47dfd38cc354" + "493d5339b4595a5bf31e3f8f13816807" + "373df6ad0dc7e731e51ad19eb4754b13" + "4485842fe709d378444d8e36b1724a4f" + "da21cafee653ab80747f7952ee804dea" + "b1039d84139945bbf4be82008753f3c5" + "4c7821a1d241f42179c794ef7042bbf9" + "955656222e45c34369a384697b6ae742" + "e18fa5ca7abad27d9fe71052e3310d0f" + "52c8d12ea33bf053a300f4afc4f098df" + "4e6d886779d64594d369158fdbc1f694"); + TestSignature(kSign_PKCS1_Block1, message, signature); +} + +// # PKCS#1 v1.5 Signature Example 15.8 +TEST_F(OEMCryptoCastReceiverTest, TestSignaturePKCS1_15_8) { + BuildRSAKey(); + LoadWithAllowedSchemes(kSign_PKCS1_Block1, true); + vector message = wvcdm::a2b_hex( + "df4044a89a83e9fcbf1262540ae3038b" + "bc90f2b2628bf2a4467ac67722d8546b" + "3a71cb0ea41669d5b4d61859c1b4e47c" + "ecc5933f757ec86db0644e311812d00f" + "b802f03400639c0e364dae5aebc5791b" + "c655762361bc43c53d3c7886768f7968" + "c1c544c6f79f7be820c7e2bd2f9d73e6" + "2ded6d2e937e6a6daef90ee37a1a52a5" + "4f00e31addd64894cf4c02e16099e29f" + "9eb7f1a7bb7f84c47a2b594813be02a1" + "7b7fc43b34c22c91925264126c89f86b" + "b4d87f3ef131296c53a308e0331dac8b" + "af3b63422266ecef2b90781535dbda41" + "cbd0cf22a8cbfb532ec68fc6afb2ac06"); + vector signature = wvcdm::a2b_hex( + "1414b38567ae6d973ede4a06842dcc0e" + "0559b19e65a4889bdbabd0fd02806829" + "13bacd5dc2f01b30bb19eb810b7d9ded" + "32b284f147bbe771c930c6052aa73413" + "90a849f81da9cd11e5eccf246dbae95f" + "a95828e9ae0ca3550325326deef9f495" + "30ba441bed4ac29c029c9a2736b1a419" + "0b85084ad150426b46d7f85bd702f48d" + "ac5f71330bc423a766c65cc1dcab20d3" + "d3bba72b63b3ef8244d42f157cb7e3a8" + "ba5c05272c64cc1ad21a13493c3911f6" + "0b4e9f4ecc9900eb056ee59d6fe4b8ff" + "6e8048ccc0f38f2836fd3dfe91bf4a38" + "6e1ecc2c32839f0ca4d1b27a568fa940" + "dd64ad16bd0125d0348e383085f08894" + "861ca18987227d37b42b584a8357cb04"); + TestSignature(kSign_PKCS1_Block1, message, signature); +} + +// # PKCS#1 v1.5 Signature Example 15.9 +TEST_F(OEMCryptoCastReceiverTest, TestSignaturePKCS1_15_9) { + BuildRSAKey(); + LoadWithAllowedSchemes(kSign_PKCS1_Block1, true); + vector message = wvcdm::a2b_hex( + "ea941ff06f86c226927fcf0e3b11b087" + "2676170c1bfc33bda8e265c77771f9d0" + "850164a5eecbcc5ce827fbfa07c85214" + "796d8127e8caa81894ea61ceb1449e72" + "fea0a4c943b2da6d9b105fe053b9039a" + "9cc53d420b7539fab2239c6b51d17e69" + "4c957d4b0f0984461879a0759c4401be" + "ecd4c606a0afbd7a076f50a2dfc2807f" + "24f1919baa7746d3a64e268ed3f5f8e6" + "da83a2a5c9152f837cb07812bd5ba7d3" + "a07985de88113c1796e9b466ec299c5a" + "c1059e27f09415"); + vector signature = wvcdm::a2b_hex( + "ceeb84ccb4e9099265650721eea0e8ec" + "89ca25bd354d4f64564967be9d4b08b3" + "f1c018539c9d371cf8961f2291fbe0dc" + "2f2f95fea47b639f1e12f4bc381cef0c" + "2b7a7b95c3adf27605b7f63998c3cbad" + "542808c3822e064d4ad14093679e6e01" + "418a6d5c059684cd56e34ed65ab605b8" + "de4fcfa640474a54a8251bbb7326a42d" + "08585cfcfc956769b15b6d7fdf7da84f" + "81976eaa41d692380ff10eaecfe0a579" + "682909b5521fade854d797b8a0345b9a" + "864e0588f6caddbf65f177998e180d1f" + "102443e6dca53a94823caa9c3b35f322" + "583c703af67476159ec7ec93d1769b30" + "0af0e7157dc298c6cd2dee2262f8cddc" + "10f11e01741471bbfd6518a175734575"); + TestSignature(kSign_PKCS1_Block1, message, signature); +} + +// # PKCS#1 v1.5 Signature Example 15.10 +TEST_F(OEMCryptoCastReceiverTest, TestSignaturePKCS1_15_10) { + BuildRSAKey(); + LoadWithAllowedSchemes(kSign_PKCS1_Block1, true); + vector message = wvcdm::a2b_hex( + "d8b81645c13cd7ecf5d00ed2c91b9acd" + "46c15568e5303c4a9775ede76b48403d" + "6be56c05b6b1cf77c6e75de096c5cb35" + "51cb6fa964f3c879cf589d28e1da2f9d" + "ec"); + vector signature = wvcdm::a2b_hex( + "2745074ca97175d992e2b44791c323c5" + "7167165cdd8da579cdef4686b9bb404b" + "d36a56504eb1fd770f60bfa188a7b24b" + "0c91e881c24e35b04dc4dd4ce38566bc" + "c9ce54f49a175fc9d0b22522d9579047" + "f9ed42eca83f764a10163997947e7d2b" + "52ff08980e7e7c2257937b23f3d279d4" + "cd17d6f495546373d983d536efd7d1b6" + "7181ca2cb50ac616c5c7abfbb9260b91" + "b1a38e47242001ff452f8de10ca6eaea" + "dcaf9edc28956f28a711291fc9a80878" + "b8ba4cfe25b8281cb80bc9cd6d2bd182" + "5246eebe252d9957ef93707352084e6d" + "36d423551bf266a85340fb4a6af37088" + "0aab07153d01f48d086df0bfbec05e7b" + "443b97e71718970e2f4bf62023e95b67"); + TestSignature(kSign_PKCS1_Block1, message, signature); +} + +// # PKCS#1 v1.5 Signature Example 15.11 +TEST_F(OEMCryptoCastReceiverTest, TestSignaturePKCS1_15_11) { + BuildRSAKey(); + LoadWithAllowedSchemes(kSign_PKCS1_Block1, true); + vector message = wvcdm::a2b_hex( + "e5739b6c14c92d510d95b826933337ff" + "0d24ef721ac4ef64c2bad264be8b44ef" + "a1516e08a27eb6b611d3301df0062dae" + "fc73a8c0d92e2c521facbc7b26473876" + "7ea6fc97d588a0baf6ce50adf79e600b" + "d29e345fcb1dba71ac5c0289023fe4a8" + "2b46a5407719197d2e958e3531fd54ae" + "f903aabb4355f88318994ed3c3dd62f4" + "20a7"); + vector signature = wvcdm::a2b_hex( + "be40a5fb94f113e1b3eff6b6a33986f2" + "02e363f07483b792e68dfa5554df0466" + "cc32150950783b4d968b639a04fd2fb9" + "7f6eb967021f5adccb9fca95acc8f2cd" + "885a380b0a4e82bc760764dbab88c1e6" + "c0255caa94f232199d6f597cc9145b00" + "e3d4ba346b559a8833ad1516ad5163f0" + "16af6a59831c82ea13c8224d84d0765a" + "9d12384da460a8531b4c407e04f4f350" + "709eb9f08f5b220ffb45abf6b75d1579" + "fd3f1eb55fc75b00af8ba3b087827fe9" + "ae9fb4f6c5fa63031fe582852fe2834f" + "9c89bff53e2552216bc7c1d4a3d5dc2b" + "a6955cd9b17d1363e7fee8ed7629753f" + "f3125edd48521ae3b9b03217f4496d0d" + "8ede57acbc5bd4deae74a56f86671de2"); + TestSignature(kSign_PKCS1_Block1, message, signature); +} + +// # PKCS#1 v1.5 Signature Example 15.12 +TEST_F(OEMCryptoCastReceiverTest, TestSignaturePKCS1_15_12) { + BuildRSAKey(); + LoadWithAllowedSchemes(kSign_PKCS1_Block1, true); + vector message = wvcdm::a2b_hex( + "7af42835917a88d6b3c6716ba2f5b0d5" + "b20bd4e2e6e574e06af1eef7c81131be" + "22bf8128b9cbc6ec00275ba80294a5d1" + "172d0824a79e8fdd830183e4c00b9678" + "2867b1227fea249aad32ffc5fe007bc5" + "1f21792f728deda8b5708aa99cabab20" + "a4aa783ed86f0f27b5d563f42e07158c" + "ea72d097aa6887ec411dd012912a5e03" + "2bbfa678507144bcc95f39b58be7bfd1" + "759adb9a91fa1d6d8226a8343a8b849d" + "ae76f7b98224d59e28f781f13ece605f" + "84f6c90bae5f8cf378816f4020a7dda1" + "bed90c92a23634d203fac3fcd86d68d3" + "182a7d9ccabe7b0795f5c655e9acc4e3" + "ec185140d10cef053464ab175c83bd83" + "935e3dabaf3462eebe63d15f573d269a"); + vector signature = wvcdm::a2b_hex( + "4e78c5902b807914d12fa537ae6871c8" + "6db8021e55d1adb8eb0ccf1b8f36ab7d" + "ad1f682e947a627072f03e627371781d" + "33221d174abe460dbd88560c22f69011" + "6e2fbbe6e964363a3e5283bb5d946ef1" + "c0047eba038c756c40be7923055809b0" + "e9f34a03a58815ebdde767931f018f6f" + "1878f2ef4f47dd374051dd48685ded6e" + "fb3ea8021f44be1d7d149398f98ea9c0" + "8d62888ebb56192d17747b6b8e170954" + "31f125a8a8e9962aa31c285264e08fb2" + "1aac336ce6c38aa375e42bc92ab0ab91" + "038431e1f92c39d2af5ded7e43bc151e" + "6ebea4c3e2583af3437e82c43c5e3b5b" + "07cf0359683d2298e35948ed806c063c" + "606ea178150b1efc15856934c7255cfe"); + TestSignature(kSign_PKCS1_Block1, message, signature); +} + +// # PKCS#1 v1.5 Signature Example 15.13 +TEST_F(OEMCryptoCastReceiverTest, TestSignaturePKCS1_15_13) { + BuildRSAKey(); + LoadWithAllowedSchemes(kSign_PKCS1_Block1, true); + vector message = wvcdm::a2b_hex( + "ebaef3f9f23bdfe5fa6b8af4c208c189" + "f2251bf32f5f137b9de4406378686b3f" + "0721f62d24cb8688d6fc41a27cbae21d" + "30e429feacc7111941c277"); + vector signature = wvcdm::a2b_hex( + "c48dbef507114f03c95fafbeb4df1bfa" + "88e0184a33cc4f8a9a1035ff7f822a5e" + "38cda18723915ff078244429e0f6081c" + "14fd83331fa65c6ba7bb9a12dbf66223" + "74cd0ca57de3774e2bd7ae823677d061" + "d53ae9c4040d2da7ef7014f3bbdc95a3" + "61a43855c8ce9b97ecabce174d926285" + "142b534a3087f9f4ef74511ec742b0d5" + "685603faf403b5072b985df46adf2d25" + "29a02d40711e2190917052371b79b749" + "b83abf0ae29486c3f2f62477b2bd362b" + "039c013c0c5076ef520dbb405f42cee9" + "5425c373a975e1cdd032c49622c85079" + "b09e88dab2b13969ef7a723973781040" + "459f57d5013638483de2d91cb3c490da" + "81c46de6cd76ea8a0c8f6fe331712d24"); + TestSignature(kSign_PKCS1_Block1, message, signature); +} + +// # PKCS#1 v1.5 Signature Example 15.14 +TEST_F(OEMCryptoCastReceiverTest, TestSignaturePKCS1_15_14) { + BuildRSAKey(); + LoadWithAllowedSchemes(kSign_PKCS1_Block1, true); + vector message = wvcdm::a2b_hex( + "c5a2711278761dfcdd4f0c99e6f5619d" + "6c48b5d4c1a80982faa6b4cf1cf7a60f" + "f327abef93c801429efde08640858146" + "1056acc33f3d04f5ada21216cacd5fd1" + "f9ed83203e0e2fe6138e3eae8424e591" + "5a083f3f7ab76052c8be55ae882d6ec1" + "482b1e45c5dae9f41015405327022ec3" + "2f0ea2429763b255043b1958ee3cf6d6" + "3983596eb385844f8528cc9a9865835d" + "c5113c02b80d0fca68aa25e72bcaaeb3" + "cf9d79d84f984fd417"); + vector signature = wvcdm::a2b_hex( + "6bd5257aa06611fb4660087cb4bc4a9e" + "449159d31652bd980844daf3b1c7b353" + "f8e56142f7ea9857433b18573b4deede" + "818a93b0290297783f1a2f23cbc72797" + "a672537f01f62484cd4162c3214b9ac6" + "28224c5de01f32bb9b76b27354f2b151" + "d0e8c4213e4615ad0bc71f515e300d6a" + "64c6743411fffde8e5ff190e54923043" + "126ecfc4c4539022668fb675f25c07e2" + "0099ee315b98d6afec4b1a9a93dc3349" + "6a15bd6fde1663a7d49b9f1e639d3866" + "4b37a010b1f35e658682d9cd63e57de0" + "f15e8bdd096558f07ec0caa218a8c06f" + "4788453940287c9d34b6d40a3f09bf77" + "99fe98ae4eb49f3ff41c5040a50cefc9" + "bdf2394b749cf164480df1ab6880273b"); + TestSignature(kSign_PKCS1_Block1, message, signature); +} + +// # PKCS#1 v1.5 Signature Example 15.15 +TEST_F(OEMCryptoCastReceiverTest, TestSignaturePKCS1_15_15) { + BuildRSAKey(); + LoadWithAllowedSchemes(kSign_PKCS1_Block1, true); + vector message = wvcdm::a2b_hex( + "9bf8aa253b872ea77a7e23476be26b23" + "29578cf6ac9ea2805b357f6fc3ad130d" + "baeb3d869a13cce7a808bbbbc969857e" + "03945c7bb61df1b5c2589b8e046c2a5d" + "7e4057b1a74f24c711216364288529ec" + "9570f25197213be1f5c2e596f8bf8b2c" + "f3cb38aa56ffe5e31df7395820e94ecf" + "3b1189a965dcf9a9cb4298d3c88b2923" + "c19fc6bc34aacecad4e0931a7c4e5d73" + "dc86dfa798a8476d82463eefaa90a8a9" + "192ab08b23088dd58e1280f7d72e4548" + "396baac112252dd5c5346adb2004a2f7" + "101ccc899cc7fafae8bbe295738896a5" + "b2012285014ef6"); + vector signature = wvcdm::a2b_hex( + "27f7f4da9bd610106ef57d32383a448a" + "8a6245c83dc1309c6d770d357ba89e73" + "f2ad0832062eb0fe0ac915575bcd6b8b" + "cadb4e2ba6fa9da73a59175152b2d4fe" + "72b070c9b7379e50000e55e6c269f665" + "8c937972797d3add69f130e34b85bdec" + "9f3a9b392202d6f3e430d09caca82277" + "59ab825f7012d2ff4b5b62c8504dbad8" + "55c05edd5cab5a4cccdc67f01dd6517c" + "7d41c43e2a4957aff19db6f18b17859a" + "f0bc84ab67146ec1a4a60a17d7e05f8b" + "4f9ced6ad10908d8d78f7fc88b76adc8" + "290f87daf2a7be10ae408521395d54ed" + "2556fb7661854a730ce3d82c71a8d493" + "ec49a378ac8a3c74439f7cc555ba13f8" + "59070890ee18ff658fa4d741969d70a5"); + TestSignature(kSign_PKCS1_Block1, message, signature); +} + +// # PKCS#1 v1.5 Signature Example 15.16 +TEST_F(OEMCryptoCastReceiverTest, TestSignaturePKCS1_15_16) { + BuildRSAKey(); + LoadWithAllowedSchemes(kSign_PKCS1_Block1, true); + vector message = wvcdm::a2b_hex( + "32474830e2203754c8bf0681dc4f842a" + "fe360930378616c108e833656e5640c8" + "6856885bb05d1eb9438efede679263de" + "07cb39553f6a25e006b0a52311a063ca" + "088266d2564ff6490c46b5609818548f" + "88764dad34a25e3a85d575023f0b9e66" + "5048a03c350579a9d32446c7bb96cc92" + "e065ab94d3c8952e8df68ef0d9fa456b" + "3a06bb80e3bbc4b28e6a94b6d0ff7696" + "a64efe05e735fea025d7bdbc4139f3a3" + "b546075cba7efa947374d3f0ac80a68d" + "765f5df6210bca069a2d88647af7ea04" + "2dac690cb57378ec0777614fb8b65ff4" + "53ca6b7dce6098451a2f8c0da9bfecf1" + "fdf391bbaa4e2a91ca18a1121a7523a2" + "abd42514f489e8"); + vector signature = wvcdm::a2b_hex( + "6917437257c22ccb5403290c3dee82d9" + "cf7550b31bd31c51bd57bfd35d452ab4" + "db7c4be6b2e25ac9a59a1d2a7feb627f" + "0afd4976b3003cc9cffd8896505ec382" + "f265104d4cf8c932fa9fe86e00870795" + "9912389da4b2d6b369b36a5e72e29d24" + "c9a98c9d31a3ab44e643e6941266a47a" + "45e3446ce8776abe241a8f5fc6423b24" + "b1ff250dc2c3a8172353561077e850a7" + "69b25f0325dac88965a3b9b472c494e9" + "5f719b4eac332caa7a65c7dfe46d9aa7" + "e6e00f525f303dd63ab7919218901868" + "f9337f8cd26aafe6f33b7fb2c98810af" + "19f7fcb282ba1577912c1d368975fd5d" + "440b86e10c199715fa0b6f4250b53373" + "2d0befe1545150fc47b876de09b00a94"); + TestSignature(kSign_PKCS1_Block1, message, signature); +} + +// # PKCS#1 v1.5 Signature Example 15.17 +TEST_F(OEMCryptoCastReceiverTest, TestSignaturePKCS1_15_17) { + BuildRSAKey(); + LoadWithAllowedSchemes(kSign_PKCS1_Block1, true); + vector message = wvcdm::a2b_hex( + "008e59505eafb550aae5e845584cebb0" + "0b6de1733e9f95d42c882a5bbeb5ce1c" + "57e119e7c0d4daca9f1ff7870217f7cf" + "d8a6b373977cac9cab8e71e420"); + vector signature = wvcdm::a2b_hex( + "922503b673ee5f3e691e1ca85e9ff417" + "3cf72b05ac2c131da5603593e3bc259c" + "94c1f7d3a06a5b9891bf113fa39e59ff" + "7c1ed6465e908049cb89e4e125cd37d2" + "ffd9227a41b4a0a19c0a44fbbf3de55b" + "ab802087a3bb8d4ff668ee6bbb8ad89e" + "6857a79a9c72781990dfcf92cd519404" + "c950f13d1143c3184f1d250c90e17ac6" + "ce36163b9895627ad6ffec1422441f55" + "e4499dba9be89546ae8bc63cca01dd08" + "463ae7f1fce3d893996938778c1812e6" + "74ad9c309c5acca3fde44e7dd8695993" + "e9c1fa87acda99ece5c8499e468957ad" + "66359bf12a51adbe78d3a213b449bf0b" + "5f8d4d496acf03d3033b7ccd196bc22f" + "68fb7bef4f697c5ea2b35062f48a36dd"); + TestSignature(kSign_PKCS1_Block1, message, signature); +} + +// # PKCS#1 v1.5 Signature Example 15.18 +TEST_F(OEMCryptoCastReceiverTest, TestSignaturePKCS1_15_18) { + BuildRSAKey(); + LoadWithAllowedSchemes(kSign_PKCS1_Block1, true); + vector message = wvcdm::a2b_hex( + "6abc54cf8d1dff1f53b17d8160368878" + "a8788cc6d22fa5c2258c88e660b09a89" + "33f9f2c0504ddadc21f6e75e0b833beb" + "555229dee656b9047b92f62e76b8ffcc" + "60dab06b80"); + vector signature = wvcdm::a2b_hex( + "0b6daf42f7a862147e417493c2c401ef" + "ae32636ab4cbd44192bbf5f195b50ae0" + "96a475a1614f0a9fa8f7a026cb46c650" + "6e518e33d83e56477a875aca8c7e714c" + "e1bdbd61ef5d535239b33f2bfdd61771" + "bab62776d78171a1423cea8731f82e60" + "766d6454265620b15f5c5a584f55f95b" + "802fe78c574ed5dacfc831f3cf2b0502" + "c0b298f25ccf11f973b31f85e4744219" + "85f3cff702df3946ef0a6605682111b2" + "f55b1f8ab0d2ea3a683c69985ead93ed" + "449ea48f0358ddf70802cb41de2fd83f" + "3c808082d84936948e0c84a131b49278" + "27460527bb5cd24bfab7b48e071b2417" + "1930f99763272f9797bcb76f1d248157" + "5558fcf260b1f0e554ebb3df3cfcb958"); + TestSignature(kSign_PKCS1_Block1, message, signature); +} + +// # PKCS#1 v1.5 Signature Example 15.19 +TEST_F(OEMCryptoCastReceiverTest, TestSignaturePKCS1_15_19) { + BuildRSAKey(); + LoadWithAllowedSchemes(kSign_PKCS1_Block1, true); + vector message = wvcdm::a2b_hex( + "af2d78152cf10efe01d274f217b177f6" + "b01b5e749f1567715da324859cd3dd88" + "db848ec79f48dbba7b6f1d33111ef31b" + "64899e7391c2bffd69f49025cf201fc5" + "85dbd1542c1c778a2ce7a7ee108a309f" + "eca26d133a5ffedc4e869dcd7656596a" + "c8427ea3ef6e3fd78fe99d8ddc71d839" + "f6786e0da6e786bd62b3a4f19b891a56" + "157a554ec2a2b39e25a1d7c7d37321c7" + "a1d946cf4fbe758d9276f08563449d67" + "414a2c030f4251cfe2213d04a5410637" + "87"); + vector signature = wvcdm::a2b_hex( + "209c61157857387b71e24bf3dd564145" + "50503bec180ff53bdd9bac062a2d4995" + "09bf991281b79527df9136615b7a6d9d" + "b3a103b535e0202a2caca197a7b74e53" + "56f3dd595b49acfd9d30049a98ca88f6" + "25bca1d5f22a392d8a749efb6eed9b78" + "21d3110ac0d244199ecb4aa3d735a83a" + "2e8893c6bf8581383ccaee834635b7fa" + "1faffa45b13d15c1da33af71e89303d6" + "8090ff62ee615fdf5a84d120711da53c" + "2889198ab38317a9734ab27d67924cea" + "74156ff99bef9876bb5c339e93745283" + "e1b34e072226b88045e017e9f05b2a8c" + "416740258e223b2690027491732273f3" + "229d9ef2b1b3807e321018920ad3e53d" + "ae47e6d9395c184b93a374c671faa2ce"); + TestSignature(kSign_PKCS1_Block1, message, signature); +} + +// # PKCS#1 v1.5 Signature Example 15.20 +TEST_F(OEMCryptoCastReceiverTest, TestSignaturePKCS1_15_20) { + BuildRSAKey(); + LoadWithAllowedSchemes(kSign_PKCS1_Block1, true); + vector message = wvcdm::a2b_hex( + "40ee992458d6f61486d25676a96dd2cb" + "93a37f04b178482f2b186cf88215270d" + "ba29d786d774b0c5e78c7f6e56a956e7" + "f73950a2b0c0c10a08dbcd67e5b210bb" + "21c58e2767d44f7dd4014e3966143bf7" + "e3d66ff0c09be4c55f93b39994b8518d" + "9c1d76d5b47374dea08f157d57d70634" + "978f3856e0e5b481afbbdb5a3ac48d48" + "4be92c93de229178354c2de526e9c65a" + "31ede1ef68cb6398d7911684fec0babc" + "3a781a66660783506974d0e14825101c" + "3bfaea"); + vector signature = wvcdm::a2b_hex( + "927502b824afc42513ca6570de338b8a" + "64c3a85eb828d3193624f27e8b1029c5" + "5c119c9733b18f5849b3500918bcc005" + "51d9a8fdf53a97749fa8dc480d6fe974" + "2a5871f973926528972a1af49e3925b0" + "adf14a842719b4a5a2d89fa9c0b6605d" + "212bed1e6723b93406ad30e86829a5c7" + "19b890b389306dc5506486ee2f36a8df" + "e0a96af678c9cbd6aff397ca200e3edc" + "1e36bd2f08b31d540c0cb282a9559e4a" + "dd4fc9e6492eed0ccbd3a6982e5faa2d" + "dd17be47417c80b4e5452d31f72401a0" + "42325109544d954c01939079d409a5c3" + "78d7512dfc2d2a71efcc3432a765d1c6" + "a52cfce899cd79b15b4fc3723641ef6b" + "d00acc10407e5df58dd1c3c5c559a506"); + TestSignature(kSign_PKCS1_Block1, message, signature); +} + +class GenericCryptoTest : public OEMCryptoSessionTests { + protected: + // buffer_size_ must be a multiple of encryption block size, 16. We'll use a + // reasonable number of blocks for most of the tests. + GenericCryptoTest() : buffer_size_(160) {} + + virtual void SetUp() { + OEMCryptoSessionTests::SetUp(); + ASSERT_NO_FATAL_FAILURE(session_.open()); + ASSERT_NO_FATAL_FAILURE(InstallTestSessionKeys(&session_)); + ASSERT_NO_FATAL_FAILURE(MakeFourKeys()); + } + + virtual void TearDown() { + session_.close(); + OEMCryptoSessionTests::TearDown(); + } + + void MakeFourKeys(uint32_t duration = kDuration, uint32_t control = 0, + uint32_t nonce = 0, const std::string& pst = "") { + ASSERT_NO_FATAL_FAILURE( + session_.FillSimpleMessage(duration, control, nonce, pst)); + session_.license().keys[0].control.control_bits |= + htonl(wvoec_mock::kControlAllowEncrypt); + session_.license().keys[1].control.control_bits |= + htonl(wvoec_mock::kControlAllowDecrypt); + session_.license().keys[2].control.control_bits |= + htonl(wvoec_mock::kControlAllowSign); + session_.license().keys[3].control.control_bits |= + htonl(wvoec_mock::kControlAllowVerify); + + session_.license().keys[2].key_data_length = wvcdm::MAC_KEY_SIZE; + session_.license().keys[3].key_data_length = wvcdm::MAC_KEY_SIZE; + + clear_buffer_.assign(buffer_size_, 0); + for (size_t i = 0; i < clear_buffer_.size(); i++) { + clear_buffer_[i] = 1 + i % 250; + } + for (size_t i = 0; i < wvcdm::KEY_IV_SIZE; i++) { + iv_[i] = i; + } + } + + void EncryptAndLoadKeys() { + ASSERT_NO_FATAL_FAILURE(session_.EncryptAndSign()); + session_.LoadTestKeys(); + } + + void EncryptBuffer(unsigned int key_index, const vector& in_buffer, + vector* out_buffer) { + AES_KEY aes_key; + ASSERT_EQ(0, + AES_set_encrypt_key(session_.license().keys[key_index].key_data, + AES_BLOCK_SIZE * 8, &aes_key)); + uint8_t iv_buffer[wvcdm::KEY_IV_SIZE]; + memcpy(iv_buffer, iv_, wvcdm::KEY_IV_SIZE); + out_buffer->resize(in_buffer.size()); + ASSERT_GT(in_buffer.size(), 0u); + ASSERT_EQ(0u, in_buffer.size() % AES_BLOCK_SIZE); + AES_cbc_encrypt(&in_buffer[0], out_buffer->data(), in_buffer.size(), + &aes_key, iv_buffer, AES_ENCRYPT); + } + + // Sign the buffer with the specified key. + void SignBuffer(unsigned int key_index, const vector& in_buffer, + vector* signature) { + unsigned int md_len = SHA256_DIGEST_LENGTH; + signature->resize(SHA256_DIGEST_LENGTH); + HMAC(EVP_sha256(), session_.license().keys[key_index].key_data, + wvcdm::MAC_KEY_SIZE, &in_buffer[0], in_buffer.size(), + signature->data(), &md_len); + } + + void BadEncrypt(unsigned int key_index, OEMCrypto_Algorithm algorithm, + size_t buffer_length) { + OEMCryptoResult sts; + vector expected_encrypted; + EncryptBuffer(key_index, clear_buffer_, &expected_encrypted); + sts = OEMCrypto_SelectKey(session_.session_id(), + session_.license().keys[key_index].key_id, + session_.license().keys[key_index].key_id_length, + OEMCrypto_CipherMode_CTR); + ASSERT_EQ(OEMCrypto_SUCCESS, sts); + vector encrypted(buffer_length); + sts = + OEMCrypto_Generic_Encrypt(session_.session_id(), &clear_buffer_[0], + buffer_length, iv_, algorithm, &encrypted[0]); + EXPECT_NE(OEMCrypto_SUCCESS, sts); + expected_encrypted.resize(buffer_length); + EXPECT_NE(encrypted, expected_encrypted); + } + + void BadDecrypt(unsigned int key_index, OEMCrypto_Algorithm algorithm, + size_t buffer_length) { + OEMCryptoResult sts; + vector encrypted; + EncryptBuffer(key_index, clear_buffer_, &encrypted); + sts = OEMCrypto_SelectKey(session_.session_id(), + session_.license().keys[key_index].key_id, + session_.license().keys[key_index].key_id_length, + OEMCrypto_CipherMode_CTR); + ASSERT_EQ(OEMCrypto_SUCCESS, sts); + vector resultant(encrypted.size()); + sts = + OEMCrypto_Generic_Decrypt(session_.session_id(), &encrypted[0], + buffer_length, iv_, algorithm, &resultant[0]); + EXPECT_NE(OEMCrypto_SUCCESS, sts); + EXPECT_NE(clear_buffer_, resultant); + } + + void BadSign(unsigned int key_index, OEMCrypto_Algorithm algorithm) { + OEMCryptoResult sts; + vector expected_signature; + SignBuffer(key_index, clear_buffer_, &expected_signature); + + sts = OEMCrypto_SelectKey(session_.session_id(), + session_.license().keys[key_index].key_id, + session_.license().keys[key_index].key_id_length, + OEMCrypto_CipherMode_CTR); + ASSERT_EQ(OEMCrypto_SUCCESS, sts); + size_t signature_length = (size_t)SHA256_DIGEST_LENGTH; + vector signature(SHA256_DIGEST_LENGTH); + sts = OEMCrypto_Generic_Sign(session_.session_id(), &clear_buffer_[0], + clear_buffer_.size(), algorithm, &signature[0], + &signature_length); + EXPECT_NE(OEMCrypto_SUCCESS, sts); + EXPECT_NE(signature, expected_signature); + } + + void BadVerify(unsigned int key_index, OEMCrypto_Algorithm algorithm, + size_t signature_size, bool alter_data) { + OEMCryptoResult sts; + vector signature; + SignBuffer(key_index, clear_buffer_, &signature); + if (alter_data) { + signature[0] ^= 42; + } + + sts = OEMCrypto_SelectKey(session_.session_id(), + session_.license().keys[key_index].key_id, + session_.license().keys[key_index].key_id_length, + OEMCrypto_CipherMode_CTR); + ASSERT_EQ(OEMCrypto_SUCCESS, sts); + sts = OEMCrypto_Generic_Verify(session_.session_id(), &clear_buffer_[0], + clear_buffer_.size(), algorithm, + &signature[0], signature_size); + EXPECT_NE(OEMCrypto_SUCCESS, sts); + } + + // This must be a multiple of encryption block size. + size_t buffer_size_; + vector clear_buffer_; + vector encrypted_buffer_; + uint8_t iv_[wvcdm::KEY_IV_SIZE]; + Session session_; +}; + +TEST_F(GenericCryptoTest, GenericKeyLoad) { EncryptAndLoadKeys(); } + +TEST_F(GenericCryptoTest, GenericKeyEncrypt) { + EncryptAndLoadKeys(); + unsigned int key_index = 0; + vector expected_encrypted; + EncryptBuffer(key_index, clear_buffer_, &expected_encrypted); + ASSERT_EQ( + OEMCrypto_SUCCESS, + OEMCrypto_SelectKey(session_.session_id(), + session_.license().keys[key_index].key_id, + session_.license().keys[key_index].key_id_length, + OEMCrypto_CipherMode_CTR)); + vector encrypted(clear_buffer_.size()); + ASSERT_EQ(OEMCrypto_SUCCESS, + OEMCrypto_Generic_Encrypt( + session_.session_id(), &clear_buffer_[0], clear_buffer_.size(), + iv_, OEMCrypto_AES_CBC_128_NO_PADDING, &encrypted[0])); + ASSERT_EQ(expected_encrypted, encrypted); +} + +TEST_F(GenericCryptoTest, GenericKeyBadEncrypt) { + EncryptAndLoadKeys(); + BadEncrypt(0, OEMCrypto_HMAC_SHA256, buffer_size_); + // The buffer size must be a multiple of 16, so subtracting 10 is bad. + BadEncrypt(0, OEMCrypto_AES_CBC_128_NO_PADDING, buffer_size_ - 10); + BadEncrypt(1, OEMCrypto_AES_CBC_128_NO_PADDING, buffer_size_); + BadEncrypt(2, OEMCrypto_AES_CBC_128_NO_PADDING, buffer_size_); + BadEncrypt(3, OEMCrypto_AES_CBC_128_NO_PADDING, buffer_size_); +} + +TEST_F(GenericCryptoTest, GenericKeyEncryptSameBufferAPI12) { + EncryptAndLoadKeys(); + unsigned int key_index = 0; + vector expected_encrypted; + EncryptBuffer(key_index, clear_buffer_, &expected_encrypted); + ASSERT_EQ( + OEMCrypto_SUCCESS, + OEMCrypto_SelectKey(session_.session_id(), + session_.license().keys[key_index].key_id, + session_.license().keys[key_index].key_id_length, + OEMCrypto_CipherMode_CTR)); + // Input and output are same buffer: + vector buffer = clear_buffer_; + ASSERT_EQ(OEMCrypto_SUCCESS, + OEMCrypto_Generic_Encrypt( + session_.session_id(), &buffer[0], buffer.size(), + iv_, OEMCrypto_AES_CBC_128_NO_PADDING, &buffer[0])); + ASSERT_EQ(expected_encrypted, buffer); +} + +TEST_F(GenericCryptoTest, GenericKeyDecrypt) { + EncryptAndLoadKeys(); + unsigned int key_index = 1; + vector encrypted; + EncryptBuffer(key_index, clear_buffer_, &encrypted); + ASSERT_EQ( + OEMCrypto_SUCCESS, + OEMCrypto_SelectKey(session_.session_id(), + session_.license().keys[key_index].key_id, + session_.license().keys[key_index].key_id_length, + OEMCrypto_CipherMode_CTR)); + vector resultant(encrypted.size()); + ASSERT_EQ(OEMCrypto_SUCCESS, + OEMCrypto_Generic_Decrypt( + session_.session_id(), &encrypted[0], encrypted.size(), iv_, + OEMCrypto_AES_CBC_128_NO_PADDING, &resultant[0])); + ASSERT_EQ(clear_buffer_, resultant); +} + +TEST_F(GenericCryptoTest, GenericKeyDecryptSameBufferAPI12) { + EncryptAndLoadKeys(); + unsigned int key_index = 1; + vector encrypted; + EncryptBuffer(key_index, clear_buffer_, &encrypted); + ASSERT_EQ( + OEMCrypto_SUCCESS, + OEMCrypto_SelectKey(session_.session_id(), + session_.license().keys[key_index].key_id, + session_.license().keys[key_index].key_id_length, + OEMCrypto_CipherMode_CTR)); + vector buffer = encrypted; + ASSERT_EQ(OEMCrypto_SUCCESS, + OEMCrypto_Generic_Decrypt( + session_.session_id(), &buffer[0], buffer.size(), iv_, + OEMCrypto_AES_CBC_128_NO_PADDING, &buffer[0])); + ASSERT_EQ(clear_buffer_, buffer); +} + +TEST_F(GenericCryptoTest, GenericSecureToClear) { + session_.license().keys[1].control.control_bits |= htonl( + wvoec_mock::kControlObserveDataPath | wvoec_mock::kControlDataPathSecure); + EncryptAndLoadKeys(); + unsigned int key_index = 1; + vector encrypted; + EncryptBuffer(key_index, clear_buffer_, &encrypted); + ASSERT_EQ( + OEMCrypto_SUCCESS, + OEMCrypto_SelectKey(session_.session_id(), + session_.license().keys[key_index].key_id, + session_.license().keys[key_index].key_id_length, + OEMCrypto_CipherMode_CTR)); + vector resultant(encrypted.size()); + ASSERT_NE(OEMCrypto_SUCCESS, + OEMCrypto_Generic_Decrypt( + session_.session_id(), &encrypted[0], encrypted.size(), iv_, + OEMCrypto_AES_CBC_128_NO_PADDING, &resultant[0])); + ASSERT_NE(clear_buffer_, resultant); +} + +TEST_F(GenericCryptoTest, GenericKeyBadDecrypt) { + EncryptAndLoadKeys(); + BadDecrypt(1, OEMCrypto_HMAC_SHA256, buffer_size_); + // The buffer size must be a multiple of 16, so subtracting 10 is bad. + BadDecrypt(1, OEMCrypto_AES_CBC_128_NO_PADDING, buffer_size_ - 10); + BadDecrypt(0, OEMCrypto_AES_CBC_128_NO_PADDING, buffer_size_); + BadDecrypt(2, OEMCrypto_AES_CBC_128_NO_PADDING, buffer_size_); + BadDecrypt(3, OEMCrypto_AES_CBC_128_NO_PADDING, buffer_size_); +} + +TEST_F(GenericCryptoTest, GenericKeySign) { + EncryptAndLoadKeys(); + unsigned int key_index = 2; + vector expected_signature; + SignBuffer(key_index, clear_buffer_, &expected_signature); + + ASSERT_EQ( + OEMCrypto_SUCCESS, + OEMCrypto_SelectKey(session_.session_id(), + session_.license().keys[key_index].key_id, + session_.license().keys[key_index].key_id_length, + OEMCrypto_CipherMode_CTR)); + size_t gen_signature_length = 0; + ASSERT_EQ(OEMCrypto_ERROR_SHORT_BUFFER, + OEMCrypto_Generic_Sign(session_.session_id(), &clear_buffer_[0], + clear_buffer_.size(), OEMCrypto_HMAC_SHA256, + NULL, &gen_signature_length)); + ASSERT_EQ(static_cast(SHA256_DIGEST_LENGTH), gen_signature_length); + vector signature(SHA256_DIGEST_LENGTH); + ASSERT_EQ(OEMCrypto_SUCCESS, + OEMCrypto_Generic_Sign(session_.session_id(), &clear_buffer_[0], + clear_buffer_.size(), OEMCrypto_HMAC_SHA256, + &signature[0], &gen_signature_length)); + ASSERT_EQ(expected_signature, signature); +} + +TEST_F(GenericCryptoTest, GenericKeyBadSign) { + EncryptAndLoadKeys(); + BadSign(0, OEMCrypto_HMAC_SHA256); // Can't sign with encrypt key. + BadSign(1, OEMCrypto_HMAC_SHA256); // Can't sign with decrypt key. + BadSign(3, OEMCrypto_HMAC_SHA256); // Can't sign with verify key. + BadSign(2, OEMCrypto_AES_CBC_128_NO_PADDING); // Bad signing algorithm. +} + +TEST_F(GenericCryptoTest, GenericKeyVerify) { + EncryptAndLoadKeys(); + unsigned int key_index = 3; + vector signature; + SignBuffer(key_index, clear_buffer_, &signature); + + ASSERT_EQ( + OEMCrypto_SUCCESS, + OEMCrypto_SelectKey(session_.session_id(), + session_.license().keys[key_index].key_id, + session_.license().keys[key_index].key_id_length, + OEMCrypto_CipherMode_CTR)); + ASSERT_EQ(OEMCrypto_SUCCESS, + OEMCrypto_Generic_Verify( + session_.session_id(), &clear_buffer_[0], clear_buffer_.size(), + OEMCrypto_HMAC_SHA256, &signature[0], signature.size())); +} + +TEST_F(GenericCryptoTest, GenericKeyBadVerify) { + EncryptAndLoadKeys(); + BadVerify(0, OEMCrypto_HMAC_SHA256, SHA256_DIGEST_LENGTH, false); + BadVerify(1, OEMCrypto_HMAC_SHA256, SHA256_DIGEST_LENGTH, false); + BadVerify(2, OEMCrypto_HMAC_SHA256, SHA256_DIGEST_LENGTH, false); + BadVerify(3, OEMCrypto_HMAC_SHA256, SHA256_DIGEST_LENGTH, true); + BadVerify(3, OEMCrypto_HMAC_SHA256, SHA256_DIGEST_LENGTH - 1, false); + BadVerify(3, OEMCrypto_HMAC_SHA256, SHA256_DIGEST_LENGTH + 1, false); + BadVerify(3, OEMCrypto_AES_CBC_128_NO_PADDING, SHA256_DIGEST_LENGTH, false); +} + +TEST_F(GenericCryptoTest, GenericKeyEncryptLargeBuffer) { + buffer_size_ = kMaxDecryptSize; + EncryptAndLoadKeys(); + unsigned int key_index = 0; + vector expected_encrypted; + EncryptBuffer(key_index, clear_buffer_, &expected_encrypted); + ASSERT_EQ( + OEMCrypto_SUCCESS, + OEMCrypto_SelectKey(session_.session_id(), + session_.license().keys[key_index].key_id, + session_.license().keys[key_index].key_id_length, + OEMCrypto_CipherMode_CTR)); + vector encrypted(clear_buffer_.size()); + ASSERT_EQ(OEMCrypto_SUCCESS, + OEMCrypto_Generic_Encrypt( + session_.session_id(), &clear_buffer_[0], clear_buffer_.size(), + iv_, OEMCrypto_AES_CBC_128_NO_PADDING, &encrypted[0])); + ASSERT_EQ(expected_encrypted, encrypted); +} + +TEST_F(GenericCryptoTest, GenericKeyDecryptLargeBuffer) { + // Some applications are known to pass in a block that is almost 400k. + buffer_size_ = kMaxDecryptSize; + EncryptAndLoadKeys(); + unsigned int key_index = 1; + vector encrypted; + EncryptBuffer(key_index, clear_buffer_, &encrypted); + ASSERT_EQ( + OEMCrypto_SUCCESS, + OEMCrypto_SelectKey(session_.session_id(), + session_.license().keys[key_index].key_id, + session_.license().keys[key_index].key_id_length, + OEMCrypto_CipherMode_CTR)); + vector resultant(encrypted.size()); + ASSERT_EQ(OEMCrypto_SUCCESS, + OEMCrypto_Generic_Decrypt( + session_.session_id(), &encrypted[0], encrypted.size(), iv_, + OEMCrypto_AES_CBC_128_NO_PADDING, &resultant[0])); + ASSERT_EQ(clear_buffer_, resultant); +} + +TEST_F(GenericCryptoTest, GenericKeySignLargeBuffer) { + buffer_size_ = kMaxDecryptSize; + EncryptAndLoadKeys(); + unsigned int key_index = 2; + vector expected_signature; + SignBuffer(key_index, clear_buffer_, &expected_signature); + + ASSERT_EQ( + OEMCrypto_SUCCESS, + OEMCrypto_SelectKey(session_.session_id(), + session_.license().keys[key_index].key_id, + session_.license().keys[key_index].key_id_length, + OEMCrypto_CipherMode_CTR)); + size_t gen_signature_length = 0; + ASSERT_EQ(OEMCrypto_ERROR_SHORT_BUFFER, + OEMCrypto_Generic_Sign(session_.session_id(), &clear_buffer_[0], + clear_buffer_.size(), OEMCrypto_HMAC_SHA256, + NULL, &gen_signature_length)); + ASSERT_EQ(static_cast(SHA256_DIGEST_LENGTH), gen_signature_length); + vector signature(SHA256_DIGEST_LENGTH); + ASSERT_EQ(OEMCrypto_SUCCESS, + OEMCrypto_Generic_Sign(session_.session_id(), &clear_buffer_[0], + clear_buffer_.size(), OEMCrypto_HMAC_SHA256, + &signature[0], &gen_signature_length)); + ASSERT_EQ(expected_signature, signature); +} + +TEST_F(GenericCryptoTest, GenericKeyVerifyLargeBuffer) { + buffer_size_ = kMaxDecryptSize; + EncryptAndLoadKeys(); + unsigned int key_index = 3; + vector signature; + SignBuffer(key_index, clear_buffer_, &signature); + + ASSERT_EQ( + OEMCrypto_SUCCESS, + OEMCrypto_SelectKey(session_.session_id(), + session_.license().keys[key_index].key_id, + session_.license().keys[key_index].key_id_length, + OEMCrypto_CipherMode_CTR)); + ASSERT_EQ(OEMCrypto_SUCCESS, + OEMCrypto_Generic_Verify( + session_.session_id(), &clear_buffer_[0], clear_buffer_.size(), + OEMCrypto_HMAC_SHA256, &signature[0], signature.size())); +} + +TEST_F(GenericCryptoTest, KeyDurationEncrypt) { + EncryptAndLoadKeys(); + vector expected_encrypted; + EncryptBuffer(0, clear_buffer_, &expected_encrypted); + unsigned int key_index = 0; + vector encrypted(clear_buffer_.size()); + + sleep(kShortSleep); // Should still be valid key. + + ASSERT_EQ( + OEMCrypto_SUCCESS, + OEMCrypto_SelectKey(session_.session_id(), + session_.license().keys[key_index].key_id, + session_.license().keys[key_index].key_id_length, + OEMCrypto_CipherMode_CTR)); + ASSERT_EQ(OEMCrypto_SUCCESS, + OEMCrypto_Generic_Encrypt( + session_.session_id(), &clear_buffer_[0], clear_buffer_.size(), + iv_, OEMCrypto_AES_CBC_128_NO_PADDING, &encrypted[0])); + ASSERT_EQ(expected_encrypted, encrypted); + + sleep(kLongSleep); // Should be expired key. + + encrypted.assign(clear_buffer_.size(), 0); + OEMCryptoResult status = OEMCrypto_Generic_Encrypt( + session_.session_id(), &clear_buffer_[0], clear_buffer_.size(), iv_, + OEMCrypto_AES_CBC_128_NO_PADDING, &encrypted[0]); + ASSERT_NO_FATAL_FAILURE( + session_.TestDecryptResult(OEMCrypto_ERROR_KEY_EXPIRED, status)); + ASSERT_NE(encrypted, expected_encrypted); + ASSERT_NO_FATAL_FAILURE(session_.TestSelectExpired(key_index)); +} + +TEST_F(GenericCryptoTest, KeyDurationDecrypt) { + EncryptAndLoadKeys(); + + unsigned int key_index = 1; + vector encrypted; + EncryptBuffer(key_index, clear_buffer_, &encrypted); + ASSERT_EQ( + OEMCrypto_SUCCESS, + OEMCrypto_SelectKey(session_.session_id(), + session_.license().keys[key_index].key_id, + session_.license().keys[key_index].key_id_length, + OEMCrypto_CipherMode_CTR)); + + sleep(kShortSleep); // Should still be valid key. + + vector resultant(encrypted.size()); + ASSERT_EQ(OEMCrypto_SUCCESS, + OEMCrypto_Generic_Decrypt( + session_.session_id(), &encrypted[0], encrypted.size(), iv_, + OEMCrypto_AES_CBC_128_NO_PADDING, &resultant[0])); + ASSERT_EQ(clear_buffer_, resultant); + + sleep(kLongSleep); // Should be expired key. + + resultant.assign(encrypted.size(), 0); + OEMCryptoResult status = OEMCrypto_Generic_Decrypt( + session_.session_id(), &encrypted[0], encrypted.size(), iv_, + OEMCrypto_AES_CBC_128_NO_PADDING, &resultant[0]); + ASSERT_NO_FATAL_FAILURE( + session_.TestDecryptResult(OEMCrypto_ERROR_KEY_EXPIRED, status)); + ASSERT_NE(clear_buffer_, resultant); + ASSERT_NO_FATAL_FAILURE(session_.TestSelectExpired(key_index)); +} + +TEST_F(GenericCryptoTest, KeyDurationSign) { + EncryptAndLoadKeys(); + + unsigned int key_index = 2; + vector expected_signature; + vector signature(SHA256_DIGEST_LENGTH); + size_t signature_length = signature.size(); + SignBuffer(key_index, clear_buffer_, &expected_signature); + + ASSERT_EQ( + OEMCrypto_SUCCESS, + OEMCrypto_SelectKey(session_.session_id(), + session_.license().keys[key_index].key_id, + session_.license().keys[key_index].key_id_length, + OEMCrypto_CipherMode_CTR)); + + sleep(kShortSleep); // Should still be valid key. + + ASSERT_EQ(OEMCrypto_SUCCESS, + OEMCrypto_Generic_Sign(session_.session_id(), &clear_buffer_[0], + clear_buffer_.size(), OEMCrypto_HMAC_SHA256, + &signature[0], &signature_length)); + ASSERT_EQ(expected_signature, signature); + + sleep(kLongSleep); // Should be expired key. + + signature.assign(SHA256_DIGEST_LENGTH, 0); + OEMCryptoResult status = OEMCrypto_Generic_Sign( + session_.session_id(), &clear_buffer_[0], clear_buffer_.size(), + OEMCrypto_HMAC_SHA256, &signature[0], &signature_length); + ASSERT_NO_FATAL_FAILURE( + session_.TestDecryptResult(OEMCrypto_ERROR_KEY_EXPIRED, status)); + ASSERT_NE(expected_signature, signature); + ASSERT_NO_FATAL_FAILURE(session_.TestSelectExpired(key_index)); +} + +TEST_F(GenericCryptoTest, KeyDurationVerify) { + EncryptAndLoadKeys(); + + unsigned int key_index = 3; + vector signature; + SignBuffer(key_index, clear_buffer_, &signature); + + ASSERT_EQ( + OEMCrypto_SUCCESS, + OEMCrypto_SelectKey(session_.session_id(), + session_.license().keys[key_index].key_id, + session_.license().keys[key_index].key_id_length, + OEMCrypto_CipherMode_CTR)); + + sleep(kShortSleep); // Should still be valid key. + + ASSERT_EQ(OEMCrypto_SUCCESS, + OEMCrypto_Generic_Verify( + session_.session_id(), &clear_buffer_[0], clear_buffer_.size(), + OEMCrypto_HMAC_SHA256, &signature[0], signature.size())); + + sleep(kLongSleep); // Should be expired key. + + OEMCryptoResult status = OEMCrypto_Generic_Verify( + session_.session_id(), &clear_buffer_[0], clear_buffer_.size(), + OEMCrypto_HMAC_SHA256, &signature[0], signature.size()); + ASSERT_NO_FATAL_FAILURE( + session_.TestDecryptResult(OEMCrypto_ERROR_KEY_EXPIRED, status)); + ASSERT_NO_FATAL_FAILURE(session_.TestSelectExpired(key_index)); +} + +const unsigned int kLongKeyId = 2; + +class GenericCryptoKeyIdLengthTest : public GenericCryptoTest { + protected: + virtual void SetUp() { + GenericCryptoTest::SetUp(); + const uint32_t kNoNonce = 0; + session_.set_num_keys(5); + ASSERT_NO_FATAL_FAILURE(session_.FillSimpleMessage( + kDuration, wvoec_mock::kControlAllowDecrypt, kNoNonce)); + SetUniformKeyIdLength(16); // Start with all key ids being 16 bytes. + // But, we are testing that the key ids do not have to have the same length. + session_.SetKeyId(0, "123456789012"); // 12 bytes (common key id length). + session_.SetKeyId(1, "12345"); // short key id. + session_.SetKeyId(2, "1234567890123456"); // 16 byte key id. (default) + session_.SetKeyId(3, "12345678901234"); // 14 byte. (uncommon) + session_.SetKeyId(4, "1"); // very short key id. + ASSERT_EQ(2u, kLongKeyId); + } + + // Make all four keys have the same length. + void SetUniformKeyIdLength(size_t key_id_length) { + for (unsigned int i = 0; i < session_.num_keys(); i++) { + string key_id; + key_id.resize(key_id_length, i + 'a'); + session_.SetKeyId(i, key_id); + } + } + + void TestWithKey(unsigned int key_index) { + ASSERT_LT(key_index, session_.num_keys()); + EncryptAndLoadKeys(); + vector encrypted; + // To make sure OEMCrypto is not expecting the key_id to be zero padded, we + // will create a buffer that is padded with 'Z'. + // Then, we use fill the buffer with the longer of the three keys. If + // OEMCrypto is paying attention to the key id length, it should pick out + // the correct key. + vector key_id_buffer( + session_.license().keys[kLongKeyId].key_id_length + 5, + 'Z'); // Fill a bigger buffer with letter 'Z'. + memcpy(key_id_buffer.data(), session_.license().keys[kLongKeyId].key_id, + session_.license().keys[kLongKeyId].key_id_length); + EncryptBuffer(key_index, clear_buffer_, &encrypted); + ASSERT_EQ( + OEMCrypto_SUCCESS, + OEMCrypto_SelectKey(session_.session_id(), key_id_buffer.data(), + session_.license().keys[key_index].key_id_length, + OEMCrypto_CipherMode_CTR)); + vector resultant(encrypted.size()); + ASSERT_EQ(OEMCrypto_SUCCESS, + OEMCrypto_Generic_Decrypt( + session_.session_id(), &encrypted[0], encrypted.size(), iv_, + OEMCrypto_AES_CBC_128_NO_PADDING, &resultant[0])); + ASSERT_EQ(clear_buffer_, resultant); + } +}; + +TEST_F(GenericCryptoKeyIdLengthTest, MediumKeyId) { TestWithKey(0); } + +TEST_F(GenericCryptoKeyIdLengthTest, ShortKeyId) { TestWithKey(1); } + +TEST_F(GenericCryptoKeyIdLengthTest, LongKeyId) { TestWithKey(2); } + +TEST_F(GenericCryptoKeyIdLengthTest, FourteenByteKeyId) { TestWithKey(3); } + +TEST_F(GenericCryptoKeyIdLengthTest, VeryShortKeyId) { TestWithKey(4); } + +TEST_F(GenericCryptoKeyIdLengthTest, UniformShortKeyId) { + SetUniformKeyIdLength(5); + TestWithKey(2); +} + +TEST_F(GenericCryptoKeyIdLengthTest, UniformLongKeyId) { + SetUniformKeyIdLength(kTestKeyIdMaxLength); + TestWithKey(2); +} + +class UsageTableTest : public GenericCryptoTest { + public: + virtual void SetUp() { + GenericCryptoTest::SetUp(); + new_mac_keys_ = true; + } + + virtual void ShutDown() { + ASSERT_NO_FATAL_FAILURE(session_.close()); + ASSERT_EQ(OEMCrypto_SUCCESS, OEMCrypto_Terminate()); + } + + virtual void Restart() { + ASSERT_EQ(OEMCrypto_SUCCESS, OEMCrypto_Initialize()); + EnsureTestKeys(); + ASSERT_NO_FATAL_FAILURE(session_.open()); + } + + void LoadOfflineLicense(Session& s, const std::string& pst) { + ASSERT_NO_FATAL_FAILURE(s.open()); + ASSERT_NO_FATAL_FAILURE(InstallTestSessionKeys(&s)); + ASSERT_NO_FATAL_FAILURE(s.FillSimpleMessage( + 0, wvoec_mock::kControlNonceOrEntry, s.get_nonce(), pst)); + ASSERT_NO_FATAL_FAILURE(s.EncryptAndSign()); + ASSERT_NO_FATAL_FAILURE(s.CreateNewUsageEntry()); + 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)); + 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(NULL); + do { + sleep(1); + elapsed_time = time(NULL) - start_time; + if (elapsed_time >= dot_time) { + cout << "."; + cout.flush(); + dot_time += interval_seconds; + } + } while (elapsed_time < total_seconds); + cout << endl; + } + + protected: + bool new_mac_keys_; +}; + +// Some usage tables we want to check a license either with or without a +// new pair of mac keys in the license response. This affects signatures after +// the license is loaded. +class UsageTableTestWithMAC : public UsageTableTest, + public WithParamInterface { + public: + virtual void SetUp() { + UsageTableTest::SetUp(); + new_mac_keys_ = GetParam(); + } +}; + +TEST_P(UsageTableTestWithMAC, OnlineLicense) { + std::string pst = "my_pst"; + Session s; + ASSERT_NO_FATAL_FAILURE(s.open()); + ASSERT_NO_FATAL_FAILURE(InstallTestSessionKeys(&s)); + ASSERT_NO_FATAL_FAILURE(s.FillSimpleMessage( + 0, wvoec_mock::kControlNonceEnabled | wvoec_mock::kControlNonceRequired, + s.get_nonce(), pst)); + ASSERT_NO_FATAL_FAILURE(s.EncryptAndSign()); + ASSERT_NO_FATAL_FAILURE(s.CreateNewUsageEntry()); + ASSERT_NO_FATAL_FAILURE(s.LoadTestKeys(pst, new_mac_keys_)); + ASSERT_NO_FATAL_FAILURE(s.UpdateUsageEntry(&encrypted_usage_header_)); + // test repeated report generation + ASSERT_NO_FATAL_FAILURE(s.GenerateVerifyReport(pst, kUnused)); + ASSERT_NO_FATAL_FAILURE(s.GenerateVerifyReport(pst, kUnused)); + ASSERT_NO_FATAL_FAILURE(s.GenerateVerifyReport(pst, kUnused)); + ASSERT_NO_FATAL_FAILURE(s.GenerateVerifyReport(pst, kUnused)); + ASSERT_NO_FATAL_FAILURE(s.TestDecryptCTR()); + ASSERT_NO_FATAL_FAILURE(s.UpdateUsageEntry(&encrypted_usage_header_)); + ASSERT_NO_FATAL_FAILURE(s.GenerateVerifyReport(pst, kActive)); + // Flag the entry as inactive. + ASSERT_NO_FATAL_FAILURE(s.DeactivateUsageEntry(pst)); + ASSERT_NO_FATAL_FAILURE(s.UpdateUsageEntry(&encrypted_usage_header_)); + // It should report as inactive. + ASSERT_NO_FATAL_FAILURE(s.GenerateVerifyReport(pst, kInactiveUsed)); + // Decrypt should fail. + ASSERT_NO_FATAL_FAILURE( + s.TestDecryptCTR(false, OEMCrypto_ERROR_UNKNOWN_FAILURE)); +} + +TEST_P(UsageTableTestWithMAC, ForbidReportWithNoUpdate) { + std::string pst = "my_pst"; + Session s; + ASSERT_NO_FATAL_FAILURE(s.open()); + ASSERT_NO_FATAL_FAILURE(InstallTestSessionKeys(&s)); + ASSERT_NO_FATAL_FAILURE(s.FillSimpleMessage( + 0, wvoec_mock::kControlNonceEnabled | wvoec_mock::kControlNonceRequired, + s.get_nonce(), pst)); + ASSERT_NO_FATAL_FAILURE(s.EncryptAndSign()); + ASSERT_NO_FATAL_FAILURE(s.CreateNewUsageEntry()); + 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)); + ASSERT_NO_FATAL_FAILURE(s.TestDecryptCTR()); + // Cannot generate a report without first updating the file. + ASSERT_NO_FATAL_FAILURE( + s.GenerateReport(pst, OEMCrypto_ERROR_ENTRY_NEEDS_UPDATE)); + ASSERT_NO_FATAL_FAILURE(s.UpdateUsageEntry(&encrypted_usage_header_)); + // Now it's OK. + ASSERT_NO_FATAL_FAILURE(s.GenerateVerifyReport(pst, kActive)); + // Flag the entry as inactive. + ASSERT_NO_FATAL_FAILURE(s.DeactivateUsageEntry(pst)); + // Cannot generate a report without first updating the file. + ASSERT_NO_FATAL_FAILURE( + s.GenerateReport(pst, OEMCrypto_ERROR_ENTRY_NEEDS_UPDATE)); + // Decrypt should fail. + ASSERT_NO_FATAL_FAILURE( + s.TestDecryptCTR(false, OEMCrypto_ERROR_UNKNOWN_FAILURE)); +} + +TEST_P(UsageTableTestWithMAC, OnlineLicenseWithRefresh) { + std::string pst = "my_pst"; + Session s; + ASSERT_NO_FATAL_FAILURE(s.open()); + ASSERT_NO_FATAL_FAILURE(InstallTestSessionKeys(&s)); + ASSERT_NO_FATAL_FAILURE(s.FillSimpleMessage( + 0, wvoec_mock::kControlNonceEnabled | wvoec_mock::kControlNonceRequired, + s.get_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(NULL); + ASSERT_NO_FATAL_FAILURE(s.TestDecryptCTR()); + s.GenerateNonce(); + // License renewal message is signed by client and verified by the server. + ASSERT_NO_FATAL_FAILURE(s.VerifyClientSignature()); + size_t kAllKeys = 1; + ASSERT_NO_FATAL_FAILURE(s.RefreshTestKeys( + kAllKeys, + wvoec_mock::kControlNonceEnabled | wvoec_mock::kControlNonceRequired, + s.get_nonce(), OEMCrypto_SUCCESS)); + ASSERT_NO_FATAL_FAILURE(s.UpdateUsageEntry(&encrypted_usage_header_)); + ASSERT_NO_FATAL_FAILURE( + s.GenerateVerifyReport(pst, kActive, + loaded, // when license loaded. (not refreshed) + loaded, // first decrypt. + 0)); // last decrypt is now. +} + +TEST_F(UsageTableTest, RepeatOnlineLicense) { + std::string pst = "my_pst"; + Session s; + ASSERT_NO_FATAL_FAILURE(s.open()); + ASSERT_NO_FATAL_FAILURE(InstallTestSessionKeys(&s)); + ASSERT_NO_FATAL_FAILURE(s.FillSimpleMessage( + 0, wvoec_mock::kControlNonceEnabled | wvoec_mock::kControlNonceRequired, + s.get_nonce(), pst)); + ASSERT_NO_FATAL_FAILURE(s.EncryptAndSign()); + ASSERT_NO_FATAL_FAILURE(s.CreateNewUsageEntry()); + 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()); + Session s2; + ASSERT_NO_FATAL_FAILURE(s2.open()); + ASSERT_NO_FATAL_FAILURE(InstallTestSessionKeys(&s2)); + uint8_t* pst_ptr = s.encrypted_license().pst; + 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. + ASSERT_NE( + OEMCrypto_SUCCESS, + OEMCrypto_LoadKeys(s2.session_id(), s.message_ptr(), s.message_size(), + &s.signature()[0], s.signature().size(), + s.encrypted_license().mac_key_iv, + s.encrypted_license().mac_keys, s.num_keys(), + s.key_array(), pst_ptr, pst.length(), NULL, + OEMCrypto_ContentLicense)); + ASSERT_NO_FATAL_FAILURE(s2.close()); +} + +// A license with non-zero replay control bits needs a valid pst.. +TEST_F(UsageTableTest, OnlineEmptyPST) { + Session s; + ASSERT_NO_FATAL_FAILURE(s.open()); + ASSERT_NO_FATAL_FAILURE(InstallTestSessionKeys(&s)); + ASSERT_NO_FATAL_FAILURE(s.FillSimpleMessage( + 0, wvoec_mock::kControlNonceEnabled | wvoec_mock::kControlNonceRequired, + s.get_nonce())); + ASSERT_NO_FATAL_FAILURE(s.EncryptAndSign()); + ASSERT_NO_FATAL_FAILURE(s.CreateNewUsageEntry()); + OEMCryptoResult sts = OEMCrypto_LoadKeys( + s.session_id(), s.message_ptr(), s.message_size(), &s.signature()[0], + s.signature().size(), s.encrypted_license().mac_key_iv, + s.encrypted_license().mac_keys, s.num_keys(), s.key_array(), NULL, 0, + NULL, OEMCrypto_ContentLicense); + ASSERT_NE(OEMCrypto_SUCCESS, sts); + ASSERT_NO_FATAL_FAILURE(s.close()); +} + +// A license with non-zero replay control bits needs a valid pst.. +TEST_F(UsageTableTest, OnlineMissingEntry) { + std::string pst = "my_pst"; + Session s; + ASSERT_NO_FATAL_FAILURE(s.open()); + ASSERT_NO_FATAL_FAILURE(InstallTestSessionKeys(&s)); + ASSERT_NO_FATAL_FAILURE(s.FillSimpleMessage( + 0, wvoec_mock::kControlNonceEnabled | wvoec_mock::kControlNonceRequired, + s.get_nonce(), pst)); + ASSERT_NO_FATAL_FAILURE(s.EncryptAndSign()); + // ENTRY NOT CREATED: ASSERT_NO_FATAL_FAILURE(s.CreateNewUsageEntry()); + OEMCryptoResult sts = OEMCrypto_LoadKeys( + s.session_id(), s.message_ptr(), s.message_size(), &s.signature()[0], + s.signature().size(), s.encrypted_license().mac_key_iv, + s.encrypted_license().mac_keys, s.num_keys(), s.key_array(), + s.encrypted_license().pst, pst.length(), NULL, OEMCrypto_ContentLicense); + ASSERT_NE(OEMCrypto_SUCCESS, sts); + ASSERT_NO_FATAL_FAILURE(s.close()); +} + +TEST_F(UsageTableTest, TwoHundredEntries) { + Session s1; + ASSERT_NO_FATAL_FAILURE(s1.open()); + ASSERT_NO_FATAL_FAILURE(InstallTestSessionKeys(&s1)); + std::string pst1 = "pst saved"; + ASSERT_NO_FATAL_FAILURE(s1.FillSimpleMessage( + 0, wvoec_mock::kControlNonceEnabled | wvoec_mock::kControlNonceRequired, + s1.get_nonce(), pst1)); + ASSERT_NO_FATAL_FAILURE(s1.EncryptAndSign()); + ASSERT_NO_FATAL_FAILURE(s1.CreateNewUsageEntry()); + ASSERT_EQ(0u, s1.usage_entry_number()); + time_t start = time(NULL); + ASSERT_NO_FATAL_FAILURE(s1.LoadTestKeys(pst1, new_mac_keys_)); + ASSERT_NO_FATAL_FAILURE(s1.UpdateUsageEntry(&encrypted_usage_header_)); + ASSERT_NO_FATAL_FAILURE(s1.close()); + + const size_t ENTRY_COUNT = 200; // API says should hold at least 200 entries. + vector sessions(ENTRY_COUNT); + for (size_t i = 0; i < ENTRY_COUNT; i++) { + ASSERT_NO_FATAL_FAILURE(sessions[i].open()); + ASSERT_NO_FATAL_FAILURE(InstallTestSessionKeys(&sessions[i])); + std::string pst = "pst "; + char c1 = 'A' + (i/26); + char c2 = 'A' + (i%26); + pst = pst + c1 + c2; + ASSERT_NO_FATAL_FAILURE(sessions[i].FillSimpleMessage( + 0, wvoec_mock::kControlNonceOrEntry, sessions[i].get_nonce(), pst)); + ASSERT_NO_FATAL_FAILURE(sessions[i].EncryptAndSign()); + ASSERT_NO_FATAL_FAILURE(sessions[i].CreateNewUsageEntry()); + ASSERT_EQ(sessions[i].usage_entry_number(), i + 1); + ASSERT_NO_FATAL_FAILURE(sessions[i].LoadTestKeys(pst, new_mac_keys_)); + ASSERT_NO_FATAL_FAILURE( + sessions[i].UpdateUsageEntry(&encrypted_usage_header_)); + ASSERT_NO_FATAL_FAILURE(sessions[i].close()); + } + sleep(kShortSleep); + for (size_t i = 0; i < ENTRY_COUNT; i++) { + ASSERT_NO_FATAL_FAILURE(sessions[i].open()); + ASSERT_NO_FATAL_FAILURE(InstallTestSessionKeys(&sessions[i])); + std::string pst = "pst "; + char c1 = 'A' + (i/26); + char c2 = 'A' + (i%26); + pst = pst + c1 + c2; + // Reuse license message created above. + ASSERT_NO_FATAL_FAILURE(sessions[i].ReloadUsageEntry()); + ASSERT_NO_FATAL_FAILURE(sessions[i].LoadTestKeys(pst, new_mac_keys_)) + << "Failed to reload license " << i << " with pst = " << pst; + ASSERT_NO_FATAL_FAILURE( + sessions[i].UpdateUsageEntry(&encrypted_usage_header_)); + ASSERT_NO_FATAL_FAILURE(sessions[i].close()); + } + // Make sure s1's entry is still in the table. + ASSERT_NO_FATAL_FAILURE(s1.open()); + ASSERT_NO_FATAL_FAILURE(s1.ReloadUsageEntry()); + ASSERT_NO_FATAL_FAILURE(s1.UpdateUsageEntry(&encrypted_usage_header_)); + ASSERT_NO_FATAL_FAILURE(s1.GenerateVerifyReport(pst1, kUnused, start)); + ASSERT_NO_FATAL_FAILURE(s1.close()); +} + +TEST_P(UsageTableTestWithMAC, GenericCryptoEncrypt) { + std::string pst = "A PST"; + uint32_t nonce = session_.get_nonce(); + MakeFourKeys( + 0, wvoec_mock::kControlNonceEnabled | wvoec_mock::kControlNonceRequired, + nonce, pst); + ASSERT_NO_FATAL_FAILURE(session_.EncryptAndSign()); + ASSERT_NO_FATAL_FAILURE(session_.CreateNewUsageEntry()); + ASSERT_NO_FATAL_FAILURE(session_.LoadTestKeys(pst, new_mac_keys_)); + OEMCryptoResult sts; + unsigned int key_index = 0; + vector expected_encrypted; + EncryptBuffer(key_index, clear_buffer_, &expected_encrypted); + sts = OEMCrypto_SelectKey(session_.session_id(), + session_.license().keys[key_index].key_id, + session_.license().keys[key_index].key_id_length, + OEMCrypto_CipherMode_CTR); + ASSERT_EQ(OEMCrypto_SUCCESS, sts); + vector encrypted(clear_buffer_.size()); + sts = OEMCrypto_Generic_Encrypt( + session_.session_id(), &clear_buffer_[0], clear_buffer_.size(), iv_, + OEMCrypto_AES_CBC_128_NO_PADDING, &encrypted[0]); + ASSERT_EQ(OEMCrypto_SUCCESS, sts); + EXPECT_EQ(expected_encrypted, encrypted); + ASSERT_NO_FATAL_FAILURE(session_.UpdateUsageEntry(&encrypted_usage_header_)); + ASSERT_NO_FATAL_FAILURE(session_.GenerateVerifyReport(pst, kActive)); + ASSERT_NO_FATAL_FAILURE(session_.DeactivateUsageEntry(pst)); + ASSERT_NO_FATAL_FAILURE(session_.UpdateUsageEntry(&encrypted_usage_header_)); + ASSERT_NO_FATAL_FAILURE(session_.GenerateVerifyReport(pst, kInactiveUsed)); + encrypted.assign(clear_buffer_.size(), 0); + sts = OEMCrypto_Generic_Encrypt( + session_.session_id(), &clear_buffer_[0], clear_buffer_.size(), iv_, + OEMCrypto_AES_CBC_128_NO_PADDING, &encrypted[0]); + ASSERT_NE(OEMCrypto_SUCCESS, sts); + EXPECT_NE(encrypted, expected_encrypted); +} + +TEST_P(UsageTableTestWithMAC, GenericCryptoDecrypt) { + std::string pst = "my_pst"; + uint32_t nonce = session_.get_nonce(); + MakeFourKeys( + 0, wvoec_mock::kControlNonceEnabled | wvoec_mock::kControlNonceRequired, + nonce, pst); + ASSERT_NO_FATAL_FAILURE(session_.EncryptAndSign()); + ASSERT_NO_FATAL_FAILURE(session_.CreateNewUsageEntry()); + ASSERT_NO_FATAL_FAILURE(session_.LoadTestKeys(pst, new_mac_keys_)); + OEMCryptoResult sts; + unsigned int key_index = 1; + vector encrypted; + EncryptBuffer(key_index, clear_buffer_, &encrypted); + sts = OEMCrypto_SelectKey(session_.session_id(), + session_.license().keys[key_index].key_id, + session_.license().keys[key_index].key_id_length, + OEMCrypto_CipherMode_CTR); + ASSERT_EQ(OEMCrypto_SUCCESS, sts); + vector resultant(encrypted.size()); + sts = OEMCrypto_Generic_Decrypt( + session_.session_id(), &encrypted[0], encrypted.size(), iv_, + OEMCrypto_AES_CBC_128_NO_PADDING, &resultant[0]); + ASSERT_EQ(OEMCrypto_SUCCESS, sts); + EXPECT_EQ(clear_buffer_, resultant); + ASSERT_NO_FATAL_FAILURE(session_.UpdateUsageEntry(&encrypted_usage_header_)); + ASSERT_NO_FATAL_FAILURE(session_.GenerateVerifyReport(pst, kActive)); + ASSERT_NO_FATAL_FAILURE(session_.DeactivateUsageEntry(pst)); + ASSERT_NO_FATAL_FAILURE(session_.UpdateUsageEntry(&encrypted_usage_header_)); + ASSERT_NO_FATAL_FAILURE(session_.GenerateVerifyReport(pst, kInactiveUsed)); + resultant.assign(encrypted.size(), 0); + sts = OEMCrypto_Generic_Decrypt( + session_.session_id(), &encrypted[0], encrypted.size(), iv_, + OEMCrypto_AES_CBC_128_NO_PADDING, &resultant[0]); + ASSERT_NE(OEMCrypto_SUCCESS, sts); + EXPECT_NE(clear_buffer_, resultant); +} + +TEST_P(UsageTableTestWithMAC, GenericCryptoSign) { + std::string pst = "my_pst"; + uint32_t nonce = session_.get_nonce(); + MakeFourKeys( + 0, wvoec_mock::kControlNonceEnabled | wvoec_mock::kControlNonceRequired, + nonce, pst); + ASSERT_NO_FATAL_FAILURE(session_.EncryptAndSign()); + ASSERT_NO_FATAL_FAILURE(session_.CreateNewUsageEntry()); + ASSERT_NO_FATAL_FAILURE(session_.LoadTestKeys(pst, new_mac_keys_)); + + OEMCryptoResult sts; + unsigned int key_index = 2; + vector expected_signature; + ASSERT_NO_FATAL_FAILURE( + SignBuffer(key_index, clear_buffer_, &expected_signature)); + + sts = OEMCrypto_SelectKey(session_.session_id(), + session_.license().keys[key_index].key_id, + session_.license().keys[key_index].key_id_length, + OEMCrypto_CipherMode_CTR); + ASSERT_EQ(OEMCrypto_SUCCESS, sts); + size_t gen_signature_length = 0; + sts = OEMCrypto_Generic_Sign(session_.session_id(), &clear_buffer_[0], + clear_buffer_.size(), OEMCrypto_HMAC_SHA256, + NULL, &gen_signature_length); + ASSERT_EQ(OEMCrypto_ERROR_SHORT_BUFFER, sts); + ASSERT_EQ(static_cast(SHA256_DIGEST_LENGTH), gen_signature_length); + vector signature(SHA256_DIGEST_LENGTH); + sts = OEMCrypto_Generic_Sign(session_.session_id(), &clear_buffer_[0], + clear_buffer_.size(), OEMCrypto_HMAC_SHA256, + &signature[0], &gen_signature_length); + ASSERT_EQ(OEMCrypto_SUCCESS, sts); + ASSERT_EQ(expected_signature, signature); + + ASSERT_NO_FATAL_FAILURE(session_.UpdateUsageEntry(&encrypted_usage_header_)); + ASSERT_NO_FATAL_FAILURE(session_.GenerateVerifyReport(pst, kActive)); + ASSERT_NO_FATAL_FAILURE(session_.DeactivateUsageEntry(pst)); + ASSERT_NO_FATAL_FAILURE(session_.UpdateUsageEntry(&encrypted_usage_header_)); + ASSERT_NO_FATAL_FAILURE(session_.GenerateVerifyReport(pst, kInactiveUsed)); + signature.assign(SHA256_DIGEST_LENGTH, 0); + gen_signature_length = SHA256_DIGEST_LENGTH; + sts = OEMCrypto_Generic_Sign(session_.session_id(), &clear_buffer_[0], + clear_buffer_.size(), OEMCrypto_HMAC_SHA256, + &signature[0], &gen_signature_length); + ASSERT_NE(OEMCrypto_SUCCESS, sts); + ASSERT_NE(signature, expected_signature); +} + +TEST_P(UsageTableTestWithMAC, GenericCryptoVerify) { + std::string pst = "my_pst"; + uint32_t nonce = session_.get_nonce(); + MakeFourKeys( + 0, wvoec_mock::kControlNonceEnabled | wvoec_mock::kControlNonceRequired, + nonce, pst); + ASSERT_NO_FATAL_FAILURE(session_.EncryptAndSign()); + ASSERT_NO_FATAL_FAILURE(session_.CreateNewUsageEntry()); + ASSERT_NO_FATAL_FAILURE(session_.LoadTestKeys(pst, new_mac_keys_)); + + OEMCryptoResult sts; + unsigned int key_index = 3; + vector signature; + SignBuffer(key_index, clear_buffer_, &signature); + + sts = OEMCrypto_SelectKey(session_.session_id(), + session_.license().keys[key_index].key_id, + session_.license().keys[key_index].key_id_length, + OEMCrypto_CipherMode_CTR); + ASSERT_EQ(OEMCrypto_SUCCESS, sts); + sts = OEMCrypto_Generic_Verify(session_.session_id(), &clear_buffer_[0], + clear_buffer_.size(), OEMCrypto_HMAC_SHA256, + &signature[0], signature.size()); + ASSERT_EQ(OEMCrypto_SUCCESS, sts); + ASSERT_NO_FATAL_FAILURE(session_.UpdateUsageEntry(&encrypted_usage_header_)); + ASSERT_NO_FATAL_FAILURE(session_.GenerateVerifyReport(pst, kActive)); + ASSERT_NO_FATAL_FAILURE(session_.DeactivateUsageEntry(pst)); + ASSERT_NO_FATAL_FAILURE(session_.UpdateUsageEntry(&encrypted_usage_header_)); + ASSERT_NO_FATAL_FAILURE(session_.GenerateVerifyReport(pst, kInactiveUsed)); + sts = OEMCrypto_Generic_Verify(session_.session_id(), &clear_buffer_[0], + clear_buffer_.size(), OEMCrypto_HMAC_SHA256, + &signature[0], signature.size()); + ASSERT_NE(OEMCrypto_SUCCESS, sts); +} + +TEST_P(UsageTableTestWithMAC, OfflineLicense) { + std::string pst = "my_pst"; + Session s; + ASSERT_NO_FATAL_FAILURE(LoadOfflineLicense(s, pst)); +} + +TEST_P(UsageTableTestWithMAC, OfflineLicenseRefresh) { + std::string pst = "my_pst"; + Session s; + ASSERT_NO_FATAL_FAILURE(s.open()); + ASSERT_NO_FATAL_FAILURE(InstallTestSessionKeys(&s)); + ASSERT_NO_FATAL_FAILURE(s.FillSimpleMessage( + 0, wvoec_mock::kControlNonceOrEntry, s.get_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(NULL); + ASSERT_NO_FATAL_FAILURE(s.TestDecryptCTR()); + s.GenerateNonce(); + // License renewal message is signed by client and verified by the server. + ASSERT_NO_FATAL_FAILURE(s.VerifyClientSignature()); + size_t kAllKeys = 1; + ASSERT_NO_FATAL_FAILURE(s.RefreshTestKeys( + kAllKeys, wvoec_mock::kControlNonceOrEntry, 0, OEMCrypto_SUCCESS)); + ASSERT_NO_FATAL_FAILURE(s.TestDecryptCTR()); + ASSERT_NO_FATAL_FAILURE(s.UpdateUsageEntry(&encrypted_usage_header_)); + ASSERT_NO_FATAL_FAILURE( + s.GenerateVerifyReport(pst, kActive, + loaded, // license recieved. + loaded, // First decrypt when loaded, not refresh. + 0)); // last decrypt now. +} + +TEST_P(UsageTableTestWithMAC, ReloadOfflineLicense) { + std::string pst = "my_pst"; + Session s; + ASSERT_NO_FATAL_FAILURE(LoadOfflineLicense(s, pst)); + + ASSERT_NO_FATAL_FAILURE(s.open()); + ASSERT_NO_FATAL_FAILURE(InstallTestSessionKeys(&s)); + // 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(s.LoadTestKeys(pst, new_mac_keys_)); + ASSERT_NO_FATAL_FAILURE(s.UpdateUsageEntry(&encrypted_usage_header_)); + ASSERT_NO_FATAL_FAILURE(s.GenerateVerifyReport(pst, kUnused)); + ASSERT_NO_FATAL_FAILURE(s.TestDecryptCTR()); + ASSERT_NO_FATAL_FAILURE(s.UpdateUsageEntry(&encrypted_usage_header_)); + ASSERT_NO_FATAL_FAILURE(s.GenerateVerifyReport(pst, kActive)); + ASSERT_NO_FATAL_FAILURE(s.close()); +} + +TEST_P(UsageTableTestWithMAC, ReloadOfflineLicenseWithRefresh) { + std::string pst = "my_pst"; + Session s; + ASSERT_NO_FATAL_FAILURE(LoadOfflineLicense(s, pst)); + time_t loaded = time(NULL); + + ASSERT_NO_FATAL_FAILURE(s.open()); + ASSERT_NO_FATAL_FAILURE(InstallTestSessionKeys(&s)); + // 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(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(NULL); + ASSERT_NO_FATAL_FAILURE(s.UpdateUsageEntry(&encrypted_usage_header_)); + ASSERT_NO_FATAL_FAILURE( + s.GenerateVerifyReport(pst, kActive, + loaded, // license received. + decrypt_time, // first decrypt + decrypt_time)); // last decrypt + size_t kAllKeys = 1; + ASSERT_NO_FATAL_FAILURE(s.RefreshTestKeys( + kAllKeys, wvoec_mock::kControlNonceOrEntry, 0, OEMCrypto_SUCCESS)); + ASSERT_NO_FATAL_FAILURE(s.TestDecryptCTR()); + ASSERT_NO_FATAL_FAILURE(s.UpdateUsageEntry(&encrypted_usage_header_)); + ASSERT_NO_FATAL_FAILURE(s.GenerateVerifyReport(pst, kActive, + loaded, // license loaded. + decrypt_time, // first decrypt + 0)); // last decrypt + ASSERT_NO_FATAL_FAILURE(s.close()); +} + +TEST_P(UsageTableTestWithMAC, ReloadOfflineLicenseWithTerminate) { + std::string pst = "my_pst"; + Session s; + ASSERT_NO_FATAL_FAILURE(LoadOfflineLicense(s, pst)); + ShutDown(); + Restart(); + ASSERT_EQ(OEMCrypto_SUCCESS, + OEMCrypto_LoadUsageTableHeader(&encrypted_usage_header_[0], + encrypted_usage_header_.size())); + + ASSERT_NO_FATAL_FAILURE(s.open()); + ASSERT_NO_FATAL_FAILURE(InstallTestSessionKeys(&s)); + // 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(s.LoadTestKeys(pst, new_mac_keys_)); + ASSERT_NO_FATAL_FAILURE(s.UpdateUsageEntry(&encrypted_usage_header_)); + ASSERT_NO_FATAL_FAILURE(s.GenerateVerifyReport(pst, kUnused)); + ASSERT_NO_FATAL_FAILURE(s.TestDecryptCTR()); + ASSERT_NO_FATAL_FAILURE(s.UpdateUsageEntry(&encrypted_usage_header_)); + ASSERT_NO_FATAL_FAILURE(s.GenerateVerifyReport(pst, kActive)); + ASSERT_NO_FATAL_FAILURE(s.close()); +} + +TEST_P(UsageTableTestWithMAC, BadReloadOfflineLicense) { + std::string pst = "my_pst"; + Session s; + ASSERT_NO_FATAL_FAILURE(LoadOfflineLicense(s, pst)); + + // 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(s2.FillSimpleMessage( + 0, wvoec_mock::kControlNonceOrEntry, s2.get_nonce(), pst)); + ASSERT_NO_FATAL_FAILURE(s2.EncryptAndSign()); + ASSERT_NO_FATAL_FAILURE(s2.LoadUsageEntry(s)); + uint8_t* pst_ptr = s2.encrypted_license().pst; + ASSERT_NE( + OEMCrypto_SUCCESS, + OEMCrypto_LoadKeys(s2.session_id(), s2.message_ptr(), s2.message_size(), + &s2.signature()[0], s2.signature().size(), + s2.encrypted_license().mac_key_iv, + s2.encrypted_license().mac_keys, s.num_keys(), + s2.key_array(), pst_ptr, pst.length(), NULL, + OEMCrypto_ContentLicense)); + ASSERT_NO_FATAL_FAILURE(s2.close()); + + // Offline license with same mac keys should still be OK. + ASSERT_NO_FATAL_FAILURE(s.open()); + ASSERT_NO_FATAL_FAILURE(InstallTestSessionKeys(&s)); + ASSERT_NO_FATAL_FAILURE(s.ReloadUsageEntry()); + 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)); +} + +// An offline license should not load on the first call if the nonce is bad. +TEST_P(UsageTableTestWithMAC, OfflineBadNonce) { + std::string pst = "my_pst"; + Session s; + ASSERT_NO_FATAL_FAILURE(s.open()); + ASSERT_NO_FATAL_FAILURE(InstallTestSessionKeys(&s)); + ASSERT_NO_FATAL_FAILURE(s.CreateNewUsageEntry()); + ASSERT_NO_FATAL_FAILURE( + s.FillSimpleMessage(0, wvoec_mock::kControlNonceOrEntry, 42, pst)); + ASSERT_NO_FATAL_FAILURE(s.EncryptAndSign()); + uint8_t* pst_ptr = s.encrypted_license().pst; + OEMCryptoResult sts = OEMCrypto_LoadKeys( + s.session_id(), s.message_ptr(), s.message_size(), &s.signature()[0], + s.signature().size(), s.encrypted_license().mac_key_iv, + s.encrypted_license().mac_keys, s.num_keys(), s.key_array(), pst_ptr, + pst.length(), NULL, OEMCrypto_ContentLicense); + ASSERT_NE(OEMCrypto_SUCCESS, sts); + ASSERT_NO_FATAL_FAILURE(s.close()); +} + +// An offline license needs a valid pst. +TEST_P(UsageTableTestWithMAC, OfflineEmptyPST) { + Session s; + ASSERT_NO_FATAL_FAILURE(s.open()); + ASSERT_NO_FATAL_FAILURE(InstallTestSessionKeys(&s)); + ASSERT_NO_FATAL_FAILURE(s.CreateNewUsageEntry()); + ASSERT_NO_FATAL_FAILURE( + s.FillSimpleMessage(0, wvoec_mock::kControlNonceOrEntry, s.get_nonce())); + ASSERT_NO_FATAL_FAILURE(s.EncryptAndSign()); + OEMCryptoResult sts = OEMCrypto_LoadKeys( + s.session_id(), s.message_ptr(), s.message_size(), &s.signature()[0], + s.signature().size(), s.encrypted_license().mac_key_iv, + s.encrypted_license().mac_keys, s.num_keys(), s.key_array(), NULL, 0, + NULL, OEMCrypto_ContentLicense); + ASSERT_NE(OEMCrypto_SUCCESS, sts); + ASSERT_NO_FATAL_FAILURE(s.close()); +} + +TEST_P(UsageTableTestWithMAC, ReloadOfflineWrongPST) { + std::string pst = "my_pst1"; + Session s; + ASSERT_NO_FATAL_FAILURE(LoadOfflineLicense(s, pst)); + + std::string bad_pst = "my_pst2"; + ASSERT_NO_FATAL_FAILURE(s.open()); + ASSERT_NO_FATAL_FAILURE(InstallTestSessionKeys(&s)); + ASSERT_NO_FATAL_FAILURE(s.ReloadUsageEntry()); + memcpy(s.license().pst, bad_pst.c_str(), bad_pst.length()); + ASSERT_NO_FATAL_FAILURE(s.EncryptAndSign()); + uint8_t* pst_ptr = s.encrypted_license().pst; + ASSERT_NE( + OEMCrypto_SUCCESS, + OEMCrypto_LoadKeys(s.session_id(), s.message_ptr(), s.message_size(), + &s.signature()[0], s.signature().size(), NULL, NULL, + s.num_keys(), s.key_array(), + pst_ptr, bad_pst.length(), NULL, + OEMCrypto_ContentLicense)); +} + +TEST_P(UsageTableTestWithMAC, DeactivateOfflineLicense) { + std::string pst = "my_pst"; + Session s; + ASSERT_NO_FATAL_FAILURE(LoadOfflineLicense(s, pst)); + + ASSERT_NO_FATAL_FAILURE(s.open()); + ASSERT_NO_FATAL_FAILURE(InstallTestSessionKeys(&s)); + ASSERT_NO_FATAL_FAILURE(s.ReloadUsageEntry()); + ASSERT_NO_FATAL_FAILURE( + s.LoadTestKeys(pst, new_mac_keys_)); // Reload the license + ASSERT_NO_FATAL_FAILURE(s.TestDecryptCTR()); // Should be able to decrypt. + ASSERT_NO_FATAL_FAILURE(s.DeactivateUsageEntry(pst)); // Then deactivate. + // After deactivate, should not be able to decrypt. + ASSERT_NO_FATAL_FAILURE( + s.TestDecryptCTR(false, OEMCrypto_ERROR_UNKNOWN_FAILURE)); + ASSERT_NO_FATAL_FAILURE(s.UpdateUsageEntry(&encrypted_usage_header_)); + ASSERT_NO_FATAL_FAILURE(s.GenerateVerifyReport(pst, kInactiveUsed)); + ASSERT_NO_FATAL_FAILURE(s.close()); + + Session s2; + ASSERT_NO_FATAL_FAILURE(s2.open()); + ASSERT_NO_FATAL_FAILURE(InstallTestSessionKeys(&s2)); + ASSERT_NO_FATAL_FAILURE(s2.LoadUsageEntry(s)); + // Offile license can not be reused if it has been deactivated. + uint8_t* pst_ptr = s.encrypted_license().pst; + EXPECT_NE( + OEMCrypto_SUCCESS, + OEMCrypto_LoadKeys(s2.session_id(), s.message_ptr(), s.message_size(), + &s.signature()[0], s.signature().size(), + s.encrypted_license().mac_key_iv, + s.encrypted_license().mac_keys, s.num_keys(), + s.key_array(), pst_ptr, pst.length(), NULL, + OEMCrypto_ContentLicense)); + s2.close(); + // But we can still generate a report. + Session s3; + ASSERT_NO_FATAL_FAILURE(s3.open()); + ASSERT_NO_FATAL_FAILURE(s3.LoadUsageEntry(s)); + ASSERT_NO_FATAL_FAILURE(s3.UpdateUsageEntry(&encrypted_usage_header_)); + ASSERT_NO_FATAL_FAILURE(s3.GenerateReport(pst, OEMCrypto_SUCCESS, &s)); + EXPECT_EQ(kInactiveUsed, s3.pst_report().status()); +} + +TEST_P(UsageTableTestWithMAC, BadRange) { + std::string pst = "my_pst"; + Session s; + ASSERT_NO_FATAL_FAILURE(s.open()); + ASSERT_NO_FATAL_FAILURE(InstallTestSessionKeys(&s)); + ASSERT_NO_FATAL_FAILURE(session_.CreateNewUsageEntry()); + ASSERT_NO_FATAL_FAILURE(s.FillSimpleMessage( + 0, wvoec_mock::kControlNonceOrEntry, s.get_nonce(), pst)); + ASSERT_NO_FATAL_FAILURE(s.EncryptAndSign()); + uint8_t* pst_ptr = s.license().pst; // Bad: not in encrypted_license. + ASSERT_NE( + OEMCrypto_SUCCESS, + OEMCrypto_LoadKeys(s.session_id(), s.message_ptr(), s.message_size(), + &s.signature()[0], s.signature().size(), + s.encrypted_license().mac_key_iv, + s.encrypted_license().mac_keys, s.num_keys(), + s.key_array(), pst_ptr, pst.length(), NULL, + OEMCrypto_ContentLicense)); +} + +TEST_F(UsageTableTest, UpdateFailsWithNullPtr) { + std::string pst = "my_pst"; + Session s; + ASSERT_NO_FATAL_FAILURE(s.open()); + ASSERT_NO_FATAL_FAILURE(InstallTestSessionKeys(&s)); + ASSERT_NO_FATAL_FAILURE(s.FillSimpleMessage( + 0, wvoec_mock::kControlNonceEnabled | wvoec_mock::kControlNonceRequired, + s.get_nonce(), pst)); + ASSERT_NO_FATAL_FAILURE(s.EncryptAndSign()); + ASSERT_NO_FATAL_FAILURE(s.CreateNewUsageEntry()); + ASSERT_NO_FATAL_FAILURE(s.LoadTestKeys(pst, new_mac_keys_)); + ASSERT_NO_FATAL_FAILURE(s.UpdateUsageEntry(&encrypted_usage_header_)); + size_t header_buffer_length = encrypted_usage_header_.size(); + size_t entry_buffer_length = s.encrypted_usage_entry().size(); + vector buffer(entry_buffer_length); + // Now try to pass in null pointers for the buffers. This should fail. + ASSERT_NE(OEMCrypto_SUCCESS, + OEMCrypto_UpdateUsageEntry( + s.session_id(), NULL, &header_buffer_length, + &buffer[0], &entry_buffer_length)); + ASSERT_NE(OEMCrypto_SUCCESS, + OEMCrypto_UpdateUsageEntry( + s.session_id(), &encrypted_usage_header_[0], + &header_buffer_length, NULL, &entry_buffer_length)); +} + +class UsageTableDefragTest : public UsageTableTest { + protected: + void LoadFirstLicense(Session* s, uint32_t index) { + ASSERT_NO_FATAL_FAILURE(s->open()); + ASSERT_NO_FATAL_FAILURE(InstallTestSessionKeys(s)); + std::string pst = "pst "; + char c1 = 'A' + (index / 26); + char c2 = 'A' + (index % 26); + pst = pst + c1 + c2; + ASSERT_NO_FATAL_FAILURE(s->FillSimpleMessage( + 0, wvoec_mock::kControlNonceOrEntry, s->get_nonce(), pst)); + ASSERT_NO_FATAL_FAILURE(s->EncryptAndSign()); + ASSERT_NO_FATAL_FAILURE(s->CreateNewUsageEntry()); + ASSERT_EQ(s->usage_entry_number(), index); + ASSERT_NO_FATAL_FAILURE(s->LoadTestKeys(pst, new_mac_keys_)); + ASSERT_NO_FATAL_FAILURE(s->TestDecryptCTR()); + ASSERT_NO_FATAL_FAILURE(s->UpdateUsageEntry(&encrypted_usage_header_)); + ASSERT_NO_FATAL_FAILURE(s->close()); + } + + void ReloadLicense(Session* s, time_t start) { + ASSERT_NO_FATAL_FAILURE(s->open()); + ASSERT_NO_FATAL_FAILURE(InstallTestSessionKeys(s)); + ASSERT_NO_FATAL_FAILURE(s->ReloadUsageEntry()); + 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()); + ASSERT_NO_FATAL_FAILURE(s->UpdateUsageEntry(&encrypted_usage_header_)); + ASSERT_NO_FATAL_FAILURE(s->GenerateVerifyReport(s->pst(), kActive, + start, start, 0)); + ASSERT_NO_FATAL_FAILURE(s->close()); + } + + void FailReload(Session* s, OEMCryptoResult expected_result) { + ASSERT_NO_FATAL_FAILURE(s->open()); + ASSERT_NO_FATAL_FAILURE(InstallTestSessionKeys(s)); + ASSERT_EQ(expected_result, + OEMCrypto_LoadUsageEntry(s->session_id(), s->usage_entry_number(), + &s->encrypted_usage_entry()[0], + s->encrypted_usage_entry().size())); + + uint8_t* pst_ptr = s->encrypted_license().pst; + ASSERT_NE( + OEMCrypto_SUCCESS, + OEMCrypto_LoadKeys(s->session_id(), s->message_ptr(), s->message_size(), + &s->signature()[0], s->signature().size(), + s->encrypted_license().mac_key_iv, + s->encrypted_license().mac_keys, s->num_keys(), + s->key_array(), pst_ptr, s->pst().length(), NULL, + OEMCrypto_ContentLicense)); + ASSERT_NO_FATAL_FAILURE(s->close()); + } + + void ShrinkHeader(uint32_t new_size, + OEMCryptoResult expected_result = OEMCrypto_SUCCESS) { + size_t header_buffer_length = 0; + OEMCryptoResult sts = + OEMCrypto_ShrinkUsageTableHeader(new_size, NULL, &header_buffer_length); + if (expected_result == OEMCrypto_SUCCESS) { + ASSERT_EQ(OEMCrypto_ERROR_SHORT_BUFFER, sts); + } else { + ASSERT_NE(OEMCrypto_SUCCESS, sts); + if (sts != OEMCrypto_ERROR_SHORT_BUFFER) return; + } + ASSERT_LT(0u, header_buffer_length); + encrypted_usage_header_.resize(header_buffer_length); + sts = OEMCrypto_ShrinkUsageTableHeader( + new_size, &encrypted_usage_header_[0], + &header_buffer_length); + ASSERT_EQ(expected_result, sts); + } +}; + +TEST_F(UsageTableDefragTest, MoveUsageEntries) { + const size_t ENTRY_COUNT = 10; + vector sessions(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(NULL); + } + for (size_t i = 0; i < ENTRY_COUNT; i++) { + ASSERT_NO_FATAL_FAILURE(ReloadLicense(&sessions[i], start[i])) + << "On license " << i << " pst=" << sessions[i].pst(); + } + // Move 4 to 1. + ASSERT_NO_FATAL_FAILURE( + sessions[4].MoveUsageEntry(1, &encrypted_usage_header_)); + // Shrink header to 3 entries 0, 1 was 4, 2. + ASSERT_NO_FATAL_FAILURE(ShrinkHeader(3)); + ShutDown(); + Restart(); + ASSERT_EQ(OEMCrypto_SUCCESS, + OEMCrypto_LoadUsageTableHeader(&encrypted_usage_header_[0], + encrypted_usage_header_.size())); + ASSERT_NO_FATAL_FAILURE(ReloadLicense(&sessions[0], start[0])); + // Now has index 1. + ASSERT_NO_FATAL_FAILURE(ReloadLicense(&sessions[4], start[4])); + ASSERT_NO_FATAL_FAILURE(ReloadLicense(&sessions[2], start[2])); + // When 4 was moved to 1, it increased the gen. number in the header. + ASSERT_NO_FATAL_FAILURE( + FailReload(&sessions[1], OEMCrypto_ERROR_GENERATION_SKEW)); + // Index 3 is beyond the end of the table. + ASSERT_NO_FATAL_FAILURE( + FailReload(&sessions[3], OEMCrypto_ERROR_UNKNOWN_FAILURE)); +} + +TEST_F(UsageTableDefragTest, MoveUsageEntriesToOpenSession) { + Session s0; + Session s1; + LoadFirstLicense(&s0, 0); + LoadFirstLicense(&s1, 1); + s0.open(); + ASSERT_NO_FATAL_FAILURE(s0.ReloadUsageEntry()); + // s0 currently open on index 0. Expect this to fail: + ASSERT_NO_FATAL_FAILURE(s1.MoveUsageEntry(0, &encrypted_usage_header_, + OEMCrypto_ERROR_ENTRY_IN_USE)); +} + +TEST_F(UsageTableDefragTest, ShrinkOverOpenSessions) { + Session s0; + Session s1; + LoadFirstLicense(&s0, 0); + LoadFirstLicense(&s1, 1); + s0.open(); + ASSERT_NO_FATAL_FAILURE(s0.ReloadUsageEntry()); + s1.open(); + ASSERT_NO_FATAL_FAILURE(s1.ReloadUsageEntry()); + // Since s0 and s1 are open, we can't shrink. + ASSERT_NO_FATAL_FAILURE(ShrinkHeader(1, OEMCrypto_ERROR_ENTRY_IN_USE)); + s1.close(); // Can shrink after closing s1, even if s0 is open. + ASSERT_NO_FATAL_FAILURE(ShrinkHeader(1, OEMCrypto_SUCCESS)); +} + +TEST_F(UsageTableDefragTest, EnlargeHeader) { + Session s0; + Session s1; + LoadFirstLicense(&s0, 0); + LoadFirstLicense(&s1, 1); + // Can only shrink the header -- not make it bigger. + ASSERT_NO_FATAL_FAILURE(ShrinkHeader(4, OEMCrypto_ERROR_UNKNOWN_FAILURE)); +} + +TEST_F(UsageTableDefragTest, CreateNewHeaderWhileUsingOldOne) { + Session s0; + Session s1; + LoadFirstLicense(&s0, 0); + LoadFirstLicense(&s1, 1); + s0.open(); + ASSERT_NO_FATAL_FAILURE(s0.ReloadUsageEntry()); + const bool kExpectFailure = false; + ASSERT_NO_FATAL_FAILURE(CreateUsageTableHeader(kExpectFailure)); +} + +TEST_F(UsageTableDefragTest, ReloadUsageEntryWrongIndex) { + Session s0; + Session s1; + LoadFirstLicense(&s0, 0); + LoadFirstLicense(&s1, 1); + s0.set_usage_entry_number(1); + ASSERT_NO_FATAL_FAILURE( + FailReload(&s0, OEMCrypto_ERROR_INVALID_SESSION)); +} + +TEST_F(UsageTableDefragTest, ReloadUsageEntryBadData) { + Session s; + LoadFirstLicense(&s, 0); + ASSERT_NO_FATAL_FAILURE(s.open()); + ASSERT_NO_FATAL_FAILURE(InstallTestSessionKeys(&s)); + vector data = s.encrypted_usage_entry(); + ASSERT_LT(0, data.size()); + data[0] ^= 42; + // Error could be signature or verification error. + ASSERT_NE(OEMCrypto_SUCCESS, + OEMCrypto_LoadUsageEntry(s.session_id(), s.usage_entry_number(), + &data[0], data.size())); +} + +TEST_F(UsageTableTest, CopyOldEntries) { + // First create three old entries. We open sessions first to force creation + // of the mac keys. + + Session s1; + ASSERT_NO_FATAL_FAILURE(s1.open()); + ASSERT_NO_FATAL_FAILURE(InstallTestSessionKeys(&s1)); + ASSERT_NO_FATAL_FAILURE(s1.FillSimpleMessage(0, 0, 0, "pst number 1")); + ASSERT_NO_FATAL_FAILURE(s1.EncryptAndSign()); + + Test_PST_Report report1(s1.pst(), kUnused); + report1.seconds_since_license_received = 30; + report1.seconds_since_first_decrypt = 20; + report1.seconds_since_last_decrypt = 10; + ASSERT_NO_FATAL_FAILURE(s1.CreateOldEntry(report1)); + + Session s2; + ASSERT_NO_FATAL_FAILURE(s2.open()); + ASSERT_NO_FATAL_FAILURE(InstallTestSessionKeys(&s2)); + ASSERT_NO_FATAL_FAILURE(s2.FillSimpleMessage(0, 0, 0, "pst number 2")); + ASSERT_NO_FATAL_FAILURE(s2.EncryptAndSign()); + + Test_PST_Report report2(s2.pst(), kActive); + report2.seconds_since_license_received = 60; + report2.seconds_since_first_decrypt = 50; + report2.seconds_since_last_decrypt = 40; + ASSERT_NO_FATAL_FAILURE(s2.CreateOldEntry(report2)); + + Session s3; + ASSERT_NO_FATAL_FAILURE(s3.open()); + ASSERT_NO_FATAL_FAILURE(InstallTestSessionKeys(&s3)); + ASSERT_NO_FATAL_FAILURE(s3.FillSimpleMessage(0, 0, 0, "pst number 3")); + ASSERT_NO_FATAL_FAILURE(s3.EncryptAndSign()); + + Test_PST_Report report3(s3.pst(), kInactive); + report3.seconds_since_license_received = 90; + report3.seconds_since_first_decrypt = 80; + report3.seconds_since_last_decrypt = 70; + ASSERT_NO_FATAL_FAILURE(s3.CreateOldEntry(report3)); + + // Now we copy and verify each one. The order is changed to make + // sure there are no order dependecies. + ASSERT_NO_FATAL_FAILURE( + s2.CopyAndVerifyOldEntry(report2, &encrypted_usage_header_)); + ASSERT_NO_FATAL_FAILURE( + s1.CopyAndVerifyOldEntry(report1, &encrypted_usage_header_)); + ASSERT_NO_FATAL_FAILURE( + s3.CopyAndVerifyOldEntry(report3, &encrypted_usage_header_)); +} + +TEST_F(UsageTableTest, ReloadUsageTableWithSkew) { + // This also tests a few other error conditions with usage table headers. + std::string pst = "my_pst"; + Session s; + ASSERT_NO_FATAL_FAILURE(LoadOfflineLicense(s, pst)); + + // Reload the license, and save the header. + ASSERT_NO_FATAL_FAILURE(s.open()); + ASSERT_NO_FATAL_FAILURE(InstallTestSessionKeys(&s)); + // 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(s.LoadTestKeys(pst, new_mac_keys_)); + ASSERT_NO_FATAL_FAILURE(s.UpdateUsageEntry(&encrypted_usage_header_)); + vector old_usage_header_2_ = encrypted_usage_header_; + ASSERT_NO_FATAL_FAILURE(s.UpdateUsageEntry(&encrypted_usage_header_)); + vector old_usage_header_1_ = encrypted_usage_header_; + ASSERT_NO_FATAL_FAILURE(s.UpdateUsageEntry(&encrypted_usage_header_)); + ASSERT_NO_FATAL_FAILURE(s.close()); + + ShutDown(); + Restart(); + // Null pointer generates error. + ASSERT_NE(OEMCrypto_SUCCESS, + OEMCrypto_LoadUsageTableHeader(NULL, + old_usage_header_2_.size())); + ASSERT_NO_FATAL_FAILURE(s.open()); + ASSERT_NO_FATAL_FAILURE(InstallTestSessionKeys(&s)); + // Cannot load an entry with if header didn't load. + ASSERT_EQ( + OEMCrypto_ERROR_UNKNOWN_FAILURE, + OEMCrypto_LoadUsageEntry(s.session_id(), s.usage_entry_number(), + &s.encrypted_usage_entry()[0], + s.encrypted_usage_entry().size())); + + ASSERT_NO_FATAL_FAILURE(s.close()); + + // Modified header generates error. + vector bad_header = encrypted_usage_header_; + bad_header[3] ^= 42; + ASSERT_NE(OEMCrypto_SUCCESS, + OEMCrypto_LoadUsageTableHeader(&bad_header[0], + bad_header.size())); + ASSERT_NO_FATAL_FAILURE(s.open()); + ASSERT_NO_FATAL_FAILURE(InstallTestSessionKeys(&s)); + // Cannot load an entry with if header didn't load. + ASSERT_EQ( + OEMCrypto_ERROR_UNKNOWN_FAILURE, + OEMCrypto_LoadUsageEntry(s.session_id(), s.usage_entry_number(), + &s.encrypted_usage_entry()[0], + s.encrypted_usage_entry().size())); + + ASSERT_NO_FATAL_FAILURE(s.close()); + + // Old by 2 generation numbers is error. + ASSERT_EQ(OEMCrypto_ERROR_GENERATION_SKEW, + OEMCrypto_LoadUsageTableHeader(&old_usage_header_2_[0], + old_usage_header_2_.size())); + ASSERT_NO_FATAL_FAILURE(s.open()); + ASSERT_NO_FATAL_FAILURE(InstallTestSessionKeys(&s)); + // Cannot load an entry with if header didn't load. + ASSERT_NE( + OEMCrypto_SUCCESS, + OEMCrypto_LoadUsageEntry(s.session_id(), s.usage_entry_number(), + &s.encrypted_usage_entry()[0], + s.encrypted_usage_entry().size())); + + ASSERT_NO_FATAL_FAILURE(s.close()); + + // Old by 1 generation numbers is just warning. + ASSERT_EQ(OEMCrypto_WARNING_GENERATION_SKEW, + OEMCrypto_LoadUsageTableHeader(&old_usage_header_1_[0], + old_usage_header_1_.size())); + // Everything else should still work. Skew by 1 is just a warning. + ASSERT_NO_FATAL_FAILURE(s.open()); + ASSERT_NO_FATAL_FAILURE(InstallTestSessionKeys(&s)); + ASSERT_EQ( + OEMCrypto_WARNING_GENERATION_SKEW, + OEMCrypto_LoadUsageEntry(s.session_id(), s.usage_entry_number(), + &s.encrypted_usage_entry()[0], + s.encrypted_usage_entry().size())); + 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()); +} + +TEST_F(UsageTableTest, GenerateReportWrongPST) { + std::string pst = "my_pst"; + Session s; + ASSERT_NO_FATAL_FAILURE(s.open()); + ASSERT_NO_FATAL_FAILURE(InstallTestSessionKeys(&s)); + ASSERT_NO_FATAL_FAILURE(s.FillSimpleMessage( + 0, wvoec_mock::kControlNonceOrEntry, s.get_nonce(), pst)); + ASSERT_NO_FATAL_FAILURE(s.EncryptAndSign()); + ASSERT_NO_FATAL_FAILURE(s.CreateNewUsageEntry()); + ASSERT_NO_FATAL_FAILURE(s.LoadTestKeys(pst, new_mac_keys_)); + ASSERT_NO_FATAL_FAILURE(s.UpdateUsageEntry(&encrypted_usage_header_)); + ASSERT_NO_FATAL_FAILURE(s.GenerateReport("wrong_pst", + OEMCrypto_ERROR_WRONG_PST)); +} + +TEST_F(UsageTableTest, TimingTest) { + std::string pst1 = "my_pst_1"; + std::string pst2 = "my_pst_2"; + std::string pst3 = "my_pst_3"; + Session s1; + Session s2; + Session s3; + ASSERT_NO_FATAL_FAILURE(LoadOfflineLicense(s1, pst1)); + time_t loaded1 = time(NULL); + ASSERT_NO_FATAL_FAILURE(LoadOfflineLicense(s2, pst2)); + time_t loaded2 = time(NULL); + ASSERT_NO_FATAL_FAILURE(LoadOfflineLicense(s3, pst3)); + time_t loaded3 = time(NULL); + + sleep(kLongSleep); + ASSERT_NO_FATAL_FAILURE(s1.open()); + ASSERT_NO_FATAL_FAILURE(InstallTestSessionKeys(&s1)); + ASSERT_NO_FATAL_FAILURE(s1.ReloadUsageEntry()); + ASSERT_NO_FATAL_FAILURE(s1.LoadTestKeys(pst1, new_mac_keys_)); + time_t first_decrypt1 = time(NULL); + ASSERT_NO_FATAL_FAILURE(s1.TestDecryptCTR()); + + ASSERT_NO_FATAL_FAILURE(s2.open()); + ASSERT_NO_FATAL_FAILURE(InstallTestSessionKeys(&s2)); + ASSERT_NO_FATAL_FAILURE(s2.ReloadUsageEntry()); + ASSERT_NO_FATAL_FAILURE(s2.LoadTestKeys(pst2, new_mac_keys_)); + time_t first_decrypt2 = time(NULL); + ASSERT_NO_FATAL_FAILURE(s2.TestDecryptCTR()); + + sleep(kLongSleep); + time_t second_decrypt = time(NULL); + ASSERT_NO_FATAL_FAILURE(s1.TestDecryptCTR()); + ASSERT_NO_FATAL_FAILURE(s2.TestDecryptCTR()); + + 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); + // This is as close to reboot as we can simulate in code. + ShutDown(); + sleep(kShortSleep); + Restart(); + ASSERT_EQ(OEMCrypto_SUCCESS, + OEMCrypto_LoadUsageTableHeader(&encrypted_usage_header_[0], + encrypted_usage_header_.size())); + + // After a reboot, we should be able to reload keys, and generate reports. + sleep(kLongSleep); + time_t third_decrypt = time(NULL); + ASSERT_NO_FATAL_FAILURE(s2.open()); + ASSERT_NO_FATAL_FAILURE(InstallTestSessionKeys(&s2)); + ASSERT_NO_FATAL_FAILURE(s2.ReloadUsageEntry()); + 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_)); + ASSERT_NO_FATAL_FAILURE(s2.close()); + + ASSERT_NO_FATAL_FAILURE(s1.open()); + ASSERT_NO_FATAL_FAILURE(s2.open()); + ASSERT_NO_FATAL_FAILURE(s3.open()); + ASSERT_NO_FATAL_FAILURE(s1.ReloadUsageEntry()); + ASSERT_NO_FATAL_FAILURE(s2.ReloadUsageEntry()); + ASSERT_NO_FATAL_FAILURE(s3.ReloadUsageEntry()); + sleep(kLongSleep); + ASSERT_NO_FATAL_FAILURE(s1.UpdateUsageEntry(&encrypted_usage_header_)); + ASSERT_NO_FATAL_FAILURE(s1.GenerateVerifyReport(pst1, kInactiveUsed, + loaded1, + first_decrypt1, + second_decrypt)); + ASSERT_NO_FATAL_FAILURE(s2.UpdateUsageEntry(&encrypted_usage_header_)); + ASSERT_NO_FATAL_FAILURE(s2.GenerateVerifyReport(pst2, kActive, + loaded2, + first_decrypt2, + third_decrypt)); + ASSERT_NO_FATAL_FAILURE(s3.UpdateUsageEntry(&encrypted_usage_header_)); + ASSERT_NO_FATAL_FAILURE(s3.GenerateVerifyReport(pst3, kUnused, loaded3)); +} + +TEST_F(UsageTableTest, VerifyUsageTimes) { + std::string pst = "my_pst"; + Session s; + ASSERT_NO_FATAL_FAILURE(s.open()); + ASSERT_NO_FATAL_FAILURE(InstallTestSessionKeys(&s)); + ASSERT_NO_FATAL_FAILURE(s.FillSimpleMessage( + 0, wvoec_mock::kControlNonceEnabled | wvoec_mock::kControlNonceRequired, + s.get_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 load_time = time(NULL); + + 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; + + cout << "This test verifies the elapsed time reported in the usage table " + "for a 2 minute simulated playback." + << endl; + cout << "The total time for this test is about " + << kPlaybackLoopInSeconds + 2 * kIdleInSeconds << " seconds." << endl; + cout << "Wait " << kIdleInSeconds + << " seconds to verify usage table time before playback." << endl; + + PrintDotsWhileSleep(kIdleInSeconds, kDotIntervalInSeconds); + + ASSERT_NO_FATAL_FAILURE(s.UpdateUsageEntry(&encrypted_usage_header_)); + 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(NULL); + do { + ASSERT_NO_FATAL_FAILURE(s.TestDecryptCTR()); + ASSERT_NO_FATAL_FAILURE(s.UpdateUsageEntry(&encrypted_usage_header_)); + ASSERT_NO_FATAL_FAILURE(s.GenerateVerifyReport(pst, kActive, + load_time, + start_time, + 0)); // last decrypt = now. + playback_time = time(NULL) - start_time; + ASSERT_LE(0, playback_time); + if (playback_time >= dot_time) { + cout << "."; + cout.flush(); + dot_time += kDotIntervalInSeconds; + } + } while (playback_time < kPlaybackLoopInSeconds); + cout << "\nSimulated playback time = " << playback_time << " seconds.\n"; + + ASSERT_NO_FATAL_FAILURE(s.UpdateUsageEntry(&encrypted_usage_header_)); + ASSERT_NO_FATAL_FAILURE(s.GenerateVerifyReport(pst, kActive, + load_time, + start_time, + 0)); // last decrypt = now. + EXPECT_NEAR(s.pst_report().seconds_since_first_decrypt() - + s.pst_report().seconds_since_last_decrypt(), + playback_time, kUsageTableTimeTolerance); + + cout << "Wait another " << kIdleInSeconds + << " seconds " + "to verify usage table time since playback ended." + << endl; + PrintDotsWhileSleep(kIdleInSeconds, kDotIntervalInSeconds); + + // At this point, this is what we expect: + // idle playback loop idle + // |-----|-------------------------|-----| + // |<--->| = seconds_since_last_decrypt + // |<----------------------------->| = seconds_since_first_decrypt + // |<------------------------------------| = seconds_since_license_received + ASSERT_NO_FATAL_FAILURE(s.UpdateUsageEntry(&encrypted_usage_header_)); + ASSERT_NO_FATAL_FAILURE(s.GenerateVerifyReport(pst, kActive, + load_time, + start_time, + kIdleInSeconds)); + ASSERT_NO_FATAL_FAILURE(s.DeactivateUsageEntry(pst)); + ASSERT_NO_FATAL_FAILURE(s.UpdateUsageEntry(&encrypted_usage_header_)); + ASSERT_NO_FATAL_FAILURE(s.GenerateVerifyReport(pst, kInactiveUsed, + load_time, + start_time, + kIdleInSeconds)); + ASSERT_NO_FATAL_FAILURE( + s.TestDecryptCTR(false, OEMCrypto_ERROR_UNKNOWN_FAILURE)); +} + +// This is a special case where a group of assets can be licensed with a master +// key. In order for this to work, a single session must first load a device +// specific license, and then a shared content license. This shared license is +// sometimes called an embedded license. +TEST_F(UsageTableTest, LoadSharedLicense) { + std::string pst = "my_pst"; + Session s; + ASSERT_NO_FATAL_FAILURE(LoadOfflineLicense(s, pst)); + + ASSERT_NO_FATAL_FAILURE(s.open()); + ASSERT_NO_FATAL_FAILURE(InstallTestSessionKeys(&s)); + // 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(s.LoadTestKeys(pst, true)); + + // The second set of keys are in the shared license. They will have the + // same mac keys as the original license, so we leave that alone. + // They are given different key ids so we can test that they were loaded. + // For this test, we leave the key content the same -- in real life it + // will be different. + for (unsigned int i = 0; i < s.num_keys(); i++) { + memset(s.license().keys[i].key_id, 'A' + i, + s.license().keys[i].key_id_length); + s.license().keys[i].control.nonce = 0; + s.license().keys[i].control.control_bits = + htonl(wvoec_mock::kSharedLicense); + } + ASSERT_NO_FATAL_FAILURE(s.EncryptAndSign()); + ASSERT_NO_FATAL_FAILURE(s.LoadTestKeys(pst, false)); + ASSERT_NO_FATAL_FAILURE(s.TestDecryptCTR()); + ASSERT_NO_FATAL_FAILURE(s.close()); +} + +TEST_F(UsageTableTest, LoadSharedLicenseWithNoMaster) { + std::string pst = "my_pst"; + Session s; + ASSERT_NO_FATAL_FAILURE(LoadOfflineLicense(s, pst)); + + ASSERT_NO_FATAL_FAILURE(s.open()); + ASSERT_NO_FATAL_FAILURE(InstallTestSessionKeys(&s)); + ASSERT_NO_FATAL_FAILURE(s.ReloadUsageEntry()); + // This time, we do NOT load the master license. This should + // generate an error below. + // ASSERT_NO_FATAL_FAILURE(s.LoadTestKeys(pst, true)); + + // The second set of keys are in the shared license. + // We given them different key ids so we can test that they were loaded. + // For this test, we leave the key content the same -- in real life it + // will be different. + for (unsigned int i = 0; i < s.num_keys(); i++) { + memset(s.license().keys[i].key_id, 'A' + i, + s.license().keys[i].key_id_length); + s.license().keys[i].control.nonce = 0; + s.license().keys[i].control.control_bits = + htonl(wvoec_mock::kSharedLicense); + } + ASSERT_NO_FATAL_FAILURE(s.EncryptAndSign()); + uint8_t* pst_ptr = s.encrypted_license().pst; + ASSERT_EQ(OEMCrypto_ERROR_MISSING_MASTER, + OEMCrypto_LoadKeys( + s.session_id(), s.message_ptr(), s.message_size(), + &s.signature()[0], s.signature().size(), + s.encrypted_license().mac_key_iv, + s.encrypted_license().mac_keys, + s.num_keys(), s.key_array(), pst_ptr, pst.length(), NULL, + OEMCrypto_ContentLicense)); + ASSERT_NO_FATAL_FAILURE(s.close()); +} + +TEST_F(UsageTableTest, PSTLargeBuffer) { + std::string pst(kMaxPSTLength, 'a'); // A large PST. + Session s; + ASSERT_NO_FATAL_FAILURE(LoadOfflineLicense(s, pst)); + + ASSERT_NO_FATAL_FAILURE(s.open()); + ASSERT_NO_FATAL_FAILURE(InstallTestSessionKeys(&s)); + ASSERT_NO_FATAL_FAILURE(s.ReloadUsageEntry()); + ASSERT_NO_FATAL_FAILURE( + s.LoadTestKeys(pst, new_mac_keys_)); // Reload the license + ASSERT_NO_FATAL_FAILURE(s.TestDecryptCTR()); // Should be able to decrypt. + ASSERT_NO_FATAL_FAILURE(s.DeactivateUsageEntry(pst)); // Then deactivate. + // After deactivate, should not be able to decrypt. + ASSERT_NO_FATAL_FAILURE( + s.TestDecryptCTR(false, OEMCrypto_ERROR_UNKNOWN_FAILURE)); + ASSERT_NO_FATAL_FAILURE(s.UpdateUsageEntry(&encrypted_usage_header_)); + ASSERT_NO_FATAL_FAILURE(s.GenerateVerifyReport(pst, kInactiveUsed)); + ASSERT_NO_FATAL_FAILURE(s.close()); + + Session s2; + ASSERT_NO_FATAL_FAILURE(s2.open()); + ASSERT_NO_FATAL_FAILURE(InstallTestSessionKeys(&s2)); + // Offile license can not be reused if it has been deactivated. + uint8_t* pst_ptr = s.encrypted_license().pst; + ASSERT_NO_FATAL_FAILURE(s2.LoadUsageEntry(s)); + EXPECT_NE( + OEMCrypto_SUCCESS, + OEMCrypto_LoadKeys(s2.session_id(), s.message_ptr(), s.message_size(), + &s.signature()[0], s.signature().size(), + s.encrypted_license().mac_key_iv, + s.encrypted_license().mac_keys, s.num_keys(), + s.key_array(), pst_ptr, pst.length(), NULL, + OEMCrypto_ContentLicense)); + s2.close(); + // But we can still generate a report. + Session s3; + ASSERT_NO_FATAL_FAILURE(s3.open()); + ASSERT_NO_FATAL_FAILURE(s3.LoadUsageEntry(s)); + ASSERT_NO_FATAL_FAILURE(s3.UpdateUsageEntry(&encrypted_usage_header_)); + ASSERT_NO_FATAL_FAILURE(s3.GenerateReport(pst, OEMCrypto_SUCCESS, &s)); + EXPECT_EQ(kInactiveUsed, s3.pst_report().status()); +} + +INSTANTIATE_TEST_CASE_P(TestUsageTables, UsageTableTestWithMAC, + Values(true, false)); // With and without new_mac_keys. +} // namespace wvoec diff --git a/test/oemcrypto_test_android.cpp b/test/oemcrypto_test_android.cpp new file mode 100644 index 0000000..6ccff63 --- /dev/null +++ b/test/oemcrypto_test_android.cpp @@ -0,0 +1,151 @@ +// Copyright 2013 Google Inc. All Rights Reserved. +// +// OEMCrypto unit tests - extra tests required for Android platform. +// +// The Widevine CDM system can be built on many platforms, with different +// capabilities. For example, some platforms do not require usage tables, +// and some can have a pre-installed certificate and do not need a keybox. +// On Android, these features are not optional. This set of unit tests +// verify that these features are implemented. +// +// In the file oemcrypto_test.cpp, the unit tests only verify correct +// functionality for functions that are implemented. Android devices must pass +// unit tests in both files. + +#include +#include "oec_test_data.h" +#include "OEMCryptoCENC.h" + +namespace wvoec { + +// These tests are required for LollyPop Android devices. +class OEMCryptoAndroidLMPTest : public ::testing::Test { + protected: + virtual void SetUp() { ASSERT_EQ(OEMCrypto_SUCCESS, OEMCrypto_Initialize()); } + + virtual void TearDown() { OEMCrypto_Terminate(); } +}; + +// Android devices must have a keybox, or use provisioning 3.0. +TEST_F(OEMCryptoAndroidLMPTest, GetKeyDataImplemented) { + uint8_t key_data[256]; + size_t key_data_len = sizeof(key_data); + if (OEMCrypto_Keybox == OEMCrypto_GetProvisioningMethod()) { + ASSERT_NE(OEMCrypto_ERROR_NOT_IMPLEMENTED, + OEMCrypto_GetKeyData(key_data, &key_data_len)); + } else { + ASSERT_EQ(OEMCrypto_OEMCertificate, OEMCrypto_GetProvisioningMethod()); + } +} + +TEST_F(OEMCryptoAndroidLMPTest, MinVersionNumber9) { + uint32_t version = OEMCrypto_APIVersion(); + ASSERT_LE(9u, version); +} + +TEST_F(OEMCryptoAndroidLMPTest, ValidKeyboxTest) { + ASSERT_EQ(OEMCrypto_SUCCESS, OEMCrypto_IsKeyboxValid()); +} + +TEST_F(OEMCryptoAndroidLMPTest, RewrapDeviceRSAKeyImplemented) { + if (OEMCrypto_Keybox == OEMCrypto_GetProvisioningMethod()) { + ASSERT_NE(OEMCrypto_ERROR_NOT_IMPLEMENTED, + OEMCrypto_RewrapDeviceRSAKey(0, NULL, 0, NULL, 0, NULL, NULL, 0, + NULL, NULL, NULL)); + } else { + ASSERT_NE(OEMCrypto_ERROR_NOT_IMPLEMENTED, + OEMCrypto_RewrapDeviceRSAKey30(0, NULL, NULL, 0, NULL, 0, NULL, + NULL, NULL)); + } +} + +TEST_F(OEMCryptoAndroidLMPTest, RSASignatureImplemented) { + ASSERT_NE( + OEMCrypto_ERROR_NOT_IMPLEMENTED, + OEMCrypto_GenerateRSASignature(0, NULL, 0, NULL, NULL, kSign_RSASSA_PSS)); +} + +TEST_F(OEMCryptoAndroidLMPTest, GenericCryptoImplemented) { + ASSERT_NE(OEMCrypto_ERROR_NOT_IMPLEMENTED, + OEMCrypto_Generic_Encrypt(0, NULL, 0, NULL, + OEMCrypto_AES_CBC_128_NO_PADDING, NULL)); + ASSERT_NE(OEMCrypto_ERROR_NOT_IMPLEMENTED, + OEMCrypto_Generic_Decrypt(0, NULL, 0, NULL, + OEMCrypto_AES_CBC_128_NO_PADDING, NULL)); + ASSERT_NE( + OEMCrypto_ERROR_NOT_IMPLEMENTED, + OEMCrypto_Generic_Sign(0, NULL, 0, OEMCrypto_HMAC_SHA256, NULL, NULL)); + ASSERT_NE( + OEMCrypto_ERROR_NOT_IMPLEMENTED, + OEMCrypto_Generic_Verify(0, NULL, 0, OEMCrypto_HMAC_SHA256, NULL, 0)); +} + +TEST_F(OEMCryptoAndroidLMPTest, SupportsUsageTable) { + ASSERT_TRUE(OEMCrypto_SupportsUsageTable()); +} + +TEST_F(OEMCryptoAndroidLMPTest, Level1Required) { + const char* char_level = OEMCrypto_SecurityLevel(); + std::string security_level(char_level ? char_level : ""); + EXPECT_EQ("L1", security_level) + << "The security level is " << security_level << ". but we expect L1.\n" + << "If you are testing a device that should be L3 or L2, please\n" + << "repeat the tests with the flag --gtest_filter=\"*-*Level1Required\""; +} + +// These tests are required for M Android devices. +class OEMCryptoAndroidMNCTest : public OEMCryptoAndroidLMPTest {}; + +TEST_F(OEMCryptoAndroidMNCTest, MinVersionNumber10) { + uint32_t version = OEMCrypto_APIVersion(); + ASSERT_GE(version, 10u); +} + +TEST_F(OEMCryptoAndroidMNCTest, LoadsTestKeyboxImplemented) { + if (OEMCrypto_Keybox == OEMCrypto_GetProvisioningMethod()) { + ASSERT_EQ(OEMCrypto_SUCCESS, OEMCrypto_LoadTestKeybox( + reinterpret_cast(&kTestKeybox), + sizeof(kTestKeybox))); + } else { + // Android should use keybox or provisioning 3.0. + ASSERT_EQ(OEMCrypto_OEMCertificate, OEMCrypto_GetProvisioningMethod()); + } +} + +TEST_F(OEMCryptoAndroidMNCTest, NumberOfSessionsImplemented) { + ASSERT_NE(OEMCrypto_ERROR_NOT_IMPLEMENTED, + OEMCrypto_GetNumberOfOpenSessions(NULL)); + ASSERT_NE(OEMCrypto_ERROR_NOT_IMPLEMENTED, + OEMCrypto_GetMaxNumberOfSessions(NULL)); +} + +TEST_F(OEMCryptoAndroidMNCTest, QueryKeyControlImplemented) { + ASSERT_NE(OEMCrypto_ERROR_NOT_IMPLEMENTED, + OEMCrypto_QueryKeyControl(0, NULL, 0, NULL, NULL)); +} + +// These tests are required for N Android devices. +class OEMCryptoAndroidNYCTest : public OEMCryptoAndroidMNCTest {}; + +TEST_F(OEMCryptoAndroidNYCTest, MinVersionNumber11) { + uint32_t version = OEMCrypto_APIVersion(); + ASSERT_GE(version, 11u); +} + +// These tests are required for O MR1 Android devices. +class OEMCryptoAndroidOCTest : public OEMCryptoAndroidNYCTest {}; + +TEST_F(OEMCryptoAndroidOCTest, MinVersionNumber13) { + uint32_t version = OEMCrypto_APIVersion(); + ASSERT_GE(version, 13u); +} + +// These tests are required for Pi Android devices. +class OEMCryptoAndroidPiTest : public OEMCryptoAndroidOCTest {}; + +TEST_F(OEMCryptoAndroidPiTest, MinVersionNumber14) { + uint32_t version = OEMCrypto_APIVersion(); + ASSERT_GE(version, 14u); +} + +} // namespace wvoec diff --git a/test/oemcrypto_test_main.cpp b/test/oemcrypto_test_main.cpp new file mode 100644 index 0000000..631fbfc --- /dev/null +++ b/test/oemcrypto_test_main.cpp @@ -0,0 +1,43 @@ +#include +#include + +#include "OEMCryptoCENC.h" +#include "log.h" +#include "oec_device_features.h" +#include "properties.h" + +static void acknowledge_cast() { + std::cout + << "==================================================================\n" + << "= This device is expected to load x509 certs as a cast receiver. =\n" + << "==================================================================\n"; +} + +int main(int argc, char** argv) { + ::testing::InitGoogleTest(&argc, argv); + wvcdm::Properties::Init(); + wvcdm::g_cutoff = wvcdm::LOG_INFO; + bool is_cast_receiver = false; + bool force_load_test_keybox = false; + bool filter_tests = true; + for (int i = 0; i < argc; i++) { + if (!strcmp(argv[i], "--cast")) { + acknowledge_cast(); + is_cast_receiver = true; + } + if (!strcmp(argv[i], "--force_load_test_keybox")) { + force_load_test_keybox = true; + } + if (!strcmp(argv[i], "--no_filter")) { + filter_tests = false; + } + } + wvoec::global_features.Initialize(is_cast_receiver, force_load_test_keybox); + // If the user requests --no_filter, we don't change the filter, otherwise, we + // filter out features that are not supported. + if (filter_tests) { + ::testing::GTEST_FLAG(filter) = + wvoec::global_features.RestrictFilter(::testing::GTEST_FLAG(filter)); + } + return RUN_ALL_TESTS(); +}