Initial import of Widevine Common Encryption DRM engine

Builds libwvmdrmengine.so, which is loaded by the new
MediaDrm APIs to support playback of Widevine/CENC
protected content.

Change-Id: I6f57dd37083dfd96c402cb9dd137c7d74edc8f1c
This commit is contained in:
Jeff Tinker
2013-03-21 17:39:02 -07:00
parent 38334efbe7
commit 1a8aa0dd05
211 changed files with 51913 additions and 144 deletions

View File

@@ -0,0 +1,109 @@
/*********************************************************************
* L3CryptoCENC.h
*
* (c) Copyright 2013 Google, Inc.
*
* Reference APIs needed to support Widevine's crypto algorithms.
*********************************************************************/
#ifndef L3CRYPTO_CENC_H_
#define L3CRYPTO_CENC_H_
#include "OEMCryptoCENC.h"
#ifdef __cplusplus
extern "C" {
#endif
#define L3CRYPTO_VERSION "5.0"
static const char l3c_version[] = L3CRYPTO_VERSION;
typedef uint32_t OEMCrypto_SESSION;
#define L3Crypto_Initialize _l3cc01
#define L3Crypto_Terminate _l3cc02
#define L3Crypto_InstallKeybox _l3cc03
#define L3Crypto_GetKeyData _l3cc04
#define L3Crypto_IsKeyboxValid _l3cc05
#define L3Crypto_GetRandom _l3cc06
#define L3Crypto_GetDeviceID _l3cc07
#define L3Crypto_WrapKeybox _l3cc08
#define L3Crypto_OpenSession _l3cc09
#define L3Crypto_CloseSession _l3cc10
#define L3Crypto_DecryptCTR _l3cc11
#define L3Crypto_GenerateDerivedKeys _l3cc12
#define L3Crypto_GenerateSignature _l3cc13
#define L3Crypto_GenerateNonce _l3cc14
#define L3Crypto_LoadKeys _l3cc15
#define L3Crypto_RefreshKeys _l3cc16
#define L3Crypto_SelectKey _l3cc17
OEMCryptoResult L3Crypto_Initialize(void);
OEMCryptoResult L3Crypto_Terminate(void);
OEMCryptoResult L3Crypto_OpenSession(OEMCrypto_SESSION *session);
OEMCryptoResult L3Crypto_CloseSession(OEMCrypto_SESSION session);
OEMCryptoResult L3Crypto_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 L3Crypto_GenerateNonce(
OEMCrypto_SESSION session,
uint32_t* nonce);
OEMCryptoResult L3Crypto_GenerateSignature(
OEMCrypto_SESSION session,
const uint8_t* message,
size_t message_length,
uint8_t* signature,
size_t* signature_length);
OEMCryptoResult L3Crypto_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_key,
size_t num_keys,
const OEMCrypto_KeyObject* key_array);
OEMCryptoResult
L3Crypto_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 L3Crypto_SelectKey(const OEMCrypto_SESSION session,
const uint8_t* key_id,
size_t key_id_length);
OEMCryptoResult
L3Crypto_DecryptCTR(OEMCrypto_SESSION session,
const uint8_t *data_addr,
size_t data_length,
bool is_encrypted,
const uint8_t *iv,
size_t offset,
const OEMCrypto_DestBufferDesc* out_buffer);
OEMCryptoResult OEMCrypto_InstallKeybox(const uint8_t *keybox,
size_t keyBoxLength);
OEMCryptoResult L3Crypto_IsKeyboxValid(void);
OEMCryptoResult L3Crypto_GetDeviceID(uint8_t* deviceID,
size_t *idLength);
OEMCryptoResult L3Crypto_GetKeyData(uint8_t* keyData,
size_t *keyDataLength);
OEMCryptoResult L3Crypto_GetRandom(uint8_t* randomData,
size_t dataLength);
OEMCryptoResult L3Crypto_WrapKeybox(const uint8_t *keybox,
size_t keyBoxLength,
uint8_t *wrappedKeybox,
size_t *wrappedKeyBoxLength,
const uint8_t *transportKey,
size_t transportKeyLength);
#ifdef __cplusplus
}
#endif
#endif // L3CRYPTO_CENC_H_

View File

@@ -0,0 +1,982 @@
/*********************************************************************
* OEMCryptoCENC.h
*
* (c) Copyright 2013 Google, Inc.
*
* Reference APIs needed to support Widevine's crypto algorithms.
*********************************************************************/
#ifndef OEMCRYPTO_CENC_H_
#define OEMCRYPTO_CENC_H_
#include<stddef.h>
#include<stdint.h>
#ifdef __cplusplus
extern "C" {
#endif
#define OEMCRYPTO_VERSION "5.0"
static const char oec_version[] = OEMCRYPTO_VERSION;
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,
OEMCrypto_ERROR_EXIT_SECURE_PLAYBACK_FAILED = 6,
OEMCrypto_ERROR_SHORT_BUFFER = 7,
OEMCrypto_ERROR_NO_DEVICE_KEY = 8,
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,
} 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.
* 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.
* (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
} OEMCrytoBufferType;
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;
} secure;
struct { // type == OEMCrypto_BufferType_Direct
bool is_video;
} direct;
} buffer;
} OEMCrypto_DestBufferDesc;
/*
* 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.
* 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;
const uint8_t* key_control_iv;
const uint8_t* key_control;
} OEMCrypto_KeyObject;
/*
* 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. All fields are 128 bits (16 bytes):
* key_id - the unique id of this key.
* 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 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;
#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 _oecc11
#define OEMCrypto_GenerateDerivedKeys _oecc12
#define OEMCrypto_GenerateSignature _oecc13
#define OEMCrypto_GenerateNonce _oecc14
#define OEMCrypto_LoadKeys _oecc15
#define OEMCrypto_RefreshKeys _oecc16
#define OEMCrypto_SelectKey _oecc17
#define OEMCrypto_RewrapDeviceRSAKey _oecc18
#define OEMCrypto_LoadDeviceRSAKey _oecc19
#define OEMCrypto_GenerateRSASignature _oecc20
#define OEMCrypto_DeriveKeysFromSessionKey _oecc21
/*
* 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
*/
OEMCryptoResult OEMCrypto_Initialize(void);
/*
* OEMCrypto_Terminate
*
* Description:
* The API closes the crypto operation and releases all resources used.
*
* 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
*/
OEMCryptoResult OEMCrypto_Terminate(void);
/*
* OEMCrypto_OpenSession
*
* Description:
* The API provides for session based crypto initialization for AES CTR mode.
*
* Parameters:
* session (out) - pointer to crypto session identifier.
*
* Threading:
* No other Open/Close session calls will be made while this function is
* running. Functions on 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
*/
OEMCryptoResult OEMCrypto_OpenSession(OEMCrypto_SESSION *session);
/*
* OEMCrypto_CloseSession
*
* Description:
* The API provides for session based crypto termination for AES CTR mode.
*
* Parameters:
* session (in) - crypto session identifier.
*
* Threading:
* No other Open/Close session calls will be made while this function is
* running. Functions on 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
*/
OEMCryptoResult OEMCrypto_CloseSession(OEMCrypto_SESSION session);
/*
* OEMCrypto_GenerateDerivedKeys
*
* Description:
* Generates a pair of secondary keys, mac_key and encrypt_key, for handling
* signing and content key decryption under the license server protocol
* for AES CTR mode.
*
* Refer to document "OEMCrypto Changes for V2 License Protocol" 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 two cycles of AES-128-CMAC of the mac_key_context and
* stores it in the mac_key. These two keys will be stored until the next
* call to LoadKeys.
*
* Parameters:
* session (in) - crypto session identifier.
* 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.
* 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.
*
* Results:
* mac_key: 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_UNKNOWN_FAILURE
* OEMCrypto_ERROR_INVALID_CONTEXT
*/
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.
*
* Refer to documents "OEMCrypto Changes for V2 License Protocol" and "Key
* Control Block Definition" for details.
*
* 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.
*
* 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_UNKNOWN_FAILURE
* OEMCrypto_ERROR_INVALID_CONTEXT
*/
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.
*
* NOTE: OEMCrypto_GenerateDerivedKeys() must be called first to establish the
* mac_key
*
* Refer to document "OEMCrypto Changes for V2 License Protocol" for details.
*
* Parameters:
* session (in) - crypto session identifier.
* message (in) - pointer to memory containing message to be signed.
* message_length (in) - length of the message.
* signature (out) - pointer to memory to received the computed signature.
* signature_length (in/out) - (in) length of the signature buffer.
* (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_NO_DEVICE_KEY
* OEMCrypto_ERROR_INVALID_SESSION
* OEMCrypto_ERROR_UNKNOWN_FAILURE
* OEMCrypto_ERROR_INVALID_CONTEXT
*/
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).
* 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 corresponding content key (AES-128-CBC) and the IV given in the
* KeyObject.
*
* If any key's control block does not have valid verification fields, return
* OEMCrypto_ERROR_INVALID_CONTEXT and do not install any keys.
*
* If any key's control block requires a nonce, and the nonce in the control
* block is different from the current nonce, return
* OEMCrypto_ERROR_INVALID_NONCE. In that case, do not install any keys.
*
* The new mac_key is decrypted with the current encrypt_key and the offered
* IV. It replaces the current mac_key.
*
* 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 sessions elapsed time clock is started at 0. The clock will be used
* in OEMCrypto_DecryptCTR.
*
* NOTE: OEMCrypto_GenerateDerivedKeys() must be called first to establish the
* mac_key and encrypt_key.
*
* Refer to document "OEMCrypto Changes for V2 License Protocol" for details.
*
* Parameters:
* session (in) - crypto session identifier.
* message (in) - pointer to memory containing message to be verified.
* message_length (in) - length of the message.
* signature (in) - pointer to memory containing the signature.
* signature_length (in) - length of the signature.
* enc_mac_key_iv (in) - IV for decrypting new mac_key. Size is 128 bits.
* enc_mac_key (in) - encrypted mac_key for generating new mac_key. Size is
* 256 bits.
* num_keys (in) - number of keys present.
* key_array (in) - set of keys to be installed.
*
* 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_UNKNOWN_FAILURE
* OEMCrypto_ERROR_INVALID_CONTEXT
* OEMCrypto_ERROR_SIGNATURE_FAILURE
* OEMCrypto_ERROR_INVALID_NONCE
* OEMCrypto_ERROR_TOO_MANY_KEYS
*/
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_key,
size_t num_keys,
const OEMCrypto_KeyObject* 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).
* If the signature verification fails, ignore all other arguments and return
* OEMCrypto_ERROR_SIGNATURE_FAILURE. Otherwise, add the keys to the session
* context.
*
* NOTE: OEMCrypto_GenerateDerivedKeys() or OEMCrypto_LoadKeys() must be called
* first to establish the mac_key
*
* Refer to document OEMCrypto Changes for V2 License Protocol for details.
*
* Parameters:
* session (in) - crypto session identifier.
* message (in) - pointer to memory containing message to be verified.
* message_length (in) - length of the message.
* signature (in) - pointer to memory containing the signature.
* signature_length (in) - length of the signature.
* num_keys (in) - number of keys present.
* key_array (in) - set of keys to be installed.
*
* 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_UNKNOWN_FAILURE
* OEMCrypto_ERROR_INVALID_CONTEXT
* OEMCrypto_ERROR_INVALID_NONCE
* OEMCrypto_ERROR_SIGNATURE_FAILURE
*/
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_SelectKey
*
* Description:
* Select a content key and install it in the hardware key ladder for
* subsequent decryption operations (OEMCrypto_DecryptCTR()) for this session.
* The specified key must have been previously "installed" via
* OEMCrypto_LoadKeys() or OEMCrypto_RefreshKeys().
*
* This sessions elapsed time clock is started at 0. The clock will be used
* in OEMCrypto_DecryptCTR.
*
* 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, the content key IV, the key control
* block, and the key control block IV.
*
* 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)
* to decrypt buffers passed in via OEMCrypto_DecryptCTR(). Continue
* to use this key until OEMCrypto_SelectKey() is called again, or
* until OEMCrypto_CloseSession() is called.
*
* Parameters:
* session (in) - crypto session identifier
* key_id (in) - pointer to the Key ID
* key_id_length (in) - length of the Key ID 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 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
*/
OEMCryptoResult OEMCrypto_SelectKey(const OEMCrypto_SESSION session,
const uint8_t* key_id,
size_t key_id_length);
/*
* OEMCrypto_DecryptCTR
*
* Description:
*
* The API decrypts (AES-CTR) or copies the payload in the buffer referenced by
* the data_addr parameter to the buffer determined by out_buffer, using the key
* previously set by a call to OEMCrypto_SelectKey for the specified session.
*
* 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.
* 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.
* This is discussed further below.
* offset (in) - If non-zero, the decryption block boundary is different
* from the start of the data. 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.
* out_buffer (in) - A caller-owned descriptor that specifies the
* handling of the decrypted byte stream. See OEMCrypto_DestbufferDesc
* for details.
*
* AES CTR is a stream cipher. The stream may be composed of arbitrary-
* length clear and encrypted segments. The encrypted portions of a sample
* are collectively treated as a continuous sequence of decryption
* block-sized blocks even though the sequence is interrupted by clear blocks.
* This means a given encrypted segment may not start or end on a decryption
* block boundary.
*
* If data_addr is not aligned with a decryption block boundary (offset != 0),
* the additional offset bytes before data_addr (pre-padding) are included in
* the decrypt operation, and they are dropped after decryption. If
* data_length + offset is not a multiple of the decryption block size, the
* extra bytes in the final decryption block (post-padding) are also dropped
* after decryption. The caller is responsible for guaranteeing that all
* memory addresses from (data-addr - pre-padding) to (data-addr +
* data-length + post-padding) are valid memory addresses.
*
* After decrypting the entire buffer including any pre-padding and
* post-padding, send data_length bytes starting at data_addr to the decoder.
*
* NOTES:
* 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.
*
* 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_UNKNOWN_FAILURE
* OEMCrypto_ERROR_INVALID_CONTEXT
* OEMCrypto_ERROR_DECRYPT_FAILED
*/
OEMCryptoResult
OEMCrypto_DecryptCTR(OEMCrypto_SESSION session,
const uint8_t *data_addr,
size_t data_length,
bool is_encrypted,
const uint8_t *iv,
size_t offset,
const OEMCrypto_DestBufferDesc* out_buffer);
/*
* OEMCrypto_InstallKeybox
*
* Description:
* Unwrap and store the keybox to persistent memory.
* The device key must be stored securely.
*
* This function is used once to load the keybox onto the device at
* provisioning time.
*
* Parameters:
* keybox (in) - Pointer to clear keybox data. Must have been originally
* wrapped with OEMCrypto_WrapKeybox.
* 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_WRITE_KEYBOX failed to handle and store Keybox
*/
OEMCryptoResult OEMCrypto_InstallKeybox(const uint8_t *keybox,
size_t keyBoxLength);
/*
* 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
*/
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
*/
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
*/
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
*/
OEMCryptoResult OEMCrypto_GetRandom(uint8_t* randomData,
size_t dataLength);
/*
* OEMCrypto_WrapKeybox
*
* Description:
* Wrap the Keybox with a key derived for the device key. If transportKey
* is not NULL, the input keybox is encrypted with transportKey. If so,
* decrypt the input keybox before wrapping it, using transportKey in AES-CBC
* mode with an IV of all zeroes. This function is only needed if the
* provisioning method involves saving the keybox to the file system.
*
* Parameters:
* keybox (in) - Pointer to keybox data.
* keyboxLength - Length of 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) - An optional AES transport key. If provided, the input
* keybox is encrypted with this transport key with AES-CBC
* and a null IV.
* transportKeyLength - number of bytes in the transportKey
*
* Returns:
* OEMCrypto_SUCCESS success
* OEMCrypto_ERROR_WRAP_KEYBOX failed to wrap Keybox
* OEMCrypto_ERROR_NOT_IMPLEMENTED
*/
OEMCryptoResult OEMCrypto_WrapKeybox(const uint8_t *keybox,
size_t keyBoxLength,
uint8_t *wrappedKeybox,
size_t *wrappedKeyBoxLength,
const uint8_t *transportKey,
size_t transportKeyLength);
/*
* OEMCrypto_RewrapDeviceRSAKey
*
* Description:
* 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 for storage on the filesystem. The OEM may either encrypt it
* with the private key from the Widevine Keybox, or with an OEM specific
* device key.
*
* 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.
* nonce (in) - The nonce provided in the provisioning response.
* enc_rsa_key (in) - Encrypted device private RSA key received from
* - the provisioning server. Format is PKCS#1, binary
* - DER encoded, and 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.
* wrapped_rsa_key_iv (out) - IV for encrypting/decrypting the RSA private key.
* - Size is 128 bits.
*
* Returns:
* OEMCrypto_SUCCESS success
* OEMCrypto_ERROR_NO_DEVICE_KEY
* OEMCrypto_ERROR_INVALID_SESSION
* OEMCrypto_ERROR_UNKNOWN_FAILURE
* OEMCrypto_ERROR_INVALID_RSA_KEY
* OEMCrypto_ERROR_SIGNATURE_FAILURE
* OEMCrypto_ERROR_INVALID_NONCE
* OEMCrypto_ERROR_SHORT_BUFFER
*/
OEMCryptoResult OEMCrypto_RewrapDeviceRSAKey(OEMCrypto_SESSION session,
const uint8_t* message,
size_t message_length,
const uint8_t* signature,
size_t signature_length,
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);
/*
* 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 one verified and wrapped by OEMCrypto_RewrapDeviceRSAKey. The RSA
* private key should be stored in secure memory.
*
* Parameters:
* session (in) - crypto session identifier.
* wrapped_rsa_key (in) - wrapped device RSA key stored on the device.
* - Format is PKCS#1, 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.
* wrapped_rsa_key_iv (in) - The initialization vector used to encrypt
* - wrapped_rsa_key.
*
* Returns:
* OEMCrypto_SUCCESS success
* OEMCrypto_ERROR_NO_DEVICE_KEY
* OEMCrypto_ERROR_INVALID_SESSION
* OEMCrypto_ERROR_UNKNOWN_FAILURE
* OEMCrypto_ERROR_INVALID_RSA_KEY
*/
OEMCryptoResult OEMCrypto_LoadDeviceRSAKey(OEMCrypto_SESSION session,
const uint8_t* wrapped_rsa_key,
size_t wrapped_rsa_key_length);
/*
* 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 document "Widevine Security Integration Guide for DASH" for
* more details.
*
* 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
*
* Returns:
* OEMCrypto_SUCCESS success
* OEMCrypto_ERROR_INVALID_SESSION
* OEMCrypto_ERROR_SHORT_BUFFER if the signature buffer is too small.
* OEMCrypto_ERROR_CLOSE_SESSION_FAILED illegal/unrecognized handle or the
* security engine is not properly initialized.
*/
OEMCryptoResult OEMCrypto_GenerateRSASignature(OEMCrypto_SESSION session,
const uint8_t* message,
size_t message_length,
uint8_t* signature,
size_t *signature_length);
/*
* OEMCrypto_DeriveKeysFromSessionKey
*
* Description:
* Generates a pair of secondary keys, mac_key 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_key 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.
*
* 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.
*
* Returns:
* OEMCrypto_SUCCESS success
* OEMCrypto_ERROR_DEVICE_NOT_RSA_PROVISIONED
* OEMCrypto_ERROR_INVALID_SESSION
* OEMCrypto_ERROR_UNKNOWN_FAILURE
* OEMCrypto_ERROR_INVALID_CONTEXT
*/
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);
#ifdef __cplusplus
}
#endif
#endif // OEMCRYPTO_CENC_H_

View File

@@ -0,0 +1,38 @@
LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS)
LOCAL_SRC_FILES:= \
src/oemcrypto_engine_mock.cpp \
src/oemcrypto_key_mock.cpp \
src/oemcrypto_keybox_mock.cpp \
src/oemcrypto_mock.cpp \
src/lock.cpp \
src/log.cpp \
src/string_conversions.cpp \
src/wvcrc.cpp \
LOCAL_MODULE_TAGS := tests
LOCAL_C_INCLUDES += \
$(LOCAL_PATH)/../include \
$(LOCAL_PATH)/src \
bionic \
external/gtest/include \
external/openssl/include \
external/openssl/include/openssl \
external/stlport/stlport \
LOCAL_SHARED_LIBRARIES := \
libcrypto \
libcutils \
libdl \
liblog \
libstlport \
libutils \
libz \
LOCAL_MODULE := liboemcrypto
include $(BUILD_SHARED_LIBRARY)

View File

@@ -0,0 +1,109 @@
#
# Builds liboemcrypto_mock.so
#
#PROJECTS_ROOT = ~projects
#
ifndef PROJECTS_ROOT
PROJECTS_ROOT = ../../../..
endif
CDM_ROOT = $(PROJECTS_ROOT)/cdm
CDM_SRC_PATH = $(CDM_ROOT)/cdm
CDM_INCLUDE_PATH = $(CDM_SRC_PATH)/include
EUREKA_ROOT = $(PROJECTS_ROOT)/eureka/eureka/src
CHROME_ROOT = $(EUREKA_ROOT)/chromium/src
#
# build outputs should go into Chrome repository, such as ../chromium/src/out
# or some local equivalent.
# WARNING: splitting outputs from CHROME_ROOT can lead to build errors
ifndef CHROME_ROOT
CHROME_ROOT = $(CDM_ROOT)/out
endif
# TARGET_PLATFORM from {x86,eureka}
ifndef TARGET_PLATFORM
TARGET_PLATFORM = x86
endif
# TARGET_BUILD from {debug,release}
ifndef TARGET_BUILD
TARGET_BUILD = debug
endif
ifeq ($(TARGET_PLATFORM),x86)
BUILDPLATFORM = out_x86_linux
else ifeq ($(TARGET_PLATFORM),eureka)
BUILDPLATFORM = out_arm_eureka
else
BUILDPLATFORM = UNKNOWN
endif
ifeq ($(TARGET_BUILD),debug)
BUILDTYPE = Debug
else ifeq ($(TARGET_BUILD),release)
BUILDTYPE = Release
else
BUILDTYPE = UNKNOWN
endif
BUILDPATH = $(CHROME_ROOT)/$(BUILDPLATFORM)/$(BUILDTYPE)
OBJPATH = $(BUILDPATH)/obj
#primary build target
TARGET_LIB = liboemcrypto_mock.so
LIB_OBJECTS = \
oemcrypto_mock.o \
oemcrypto_engine_mock.o \
oemcrypto_key_mock.o \
oemcrypto_keybox_mock.o
INCLUDES = \
-I$(CDM_INCLUDE_PATH)
OBJECTDIR = $(OBJPATH)/mock
INSTALLDIR = $(BUILDPATH)
OBJECTS := $(patsubst %.o,$(OBJECTDIR)/%.o,$(LIB_OBJECTS))
CXXFLAGS = -m64 -fPIC -W -Wall -g -DCDM_TEST
LINK = $(CXX)
MKDIR = mkdir -p
$(INSTALLDIR)/$(TARGET_LIB): $(OBJECTDIR) $(INSTALLDIR) $(OBJECTS)
$(LINK) -v -fPIC -m64 -shared -static-libgcc $(SHLIBFLAGS) \
$(OBJECTS) \
--retain-symbols-file=OECsymbols.txt \
-Wl,-Bstatic \
-Wl,-Bdynamic -lcrypto \
-o $@
$(OBJECTDIR)/%.o: src/%.cpp
$(CXX) -c -DCDM_TEST $(CXXFLAGS) $(INCLUDES) $< -o $@
$(OBJECTDIR)/%.o: src/%.cc
$(CXX) -c -DCDM_TEST $(CXXFLAGS) $(INCLUDES) $< -o $@
test:
make -C test
clean:
$(RM) -rf $(OBJECTDIR)
$(RM) -rf $(INSTALLDIR)/$(TARGET_LIB)
$(OBJECTDIR):
@$(MKDIR) $@
$(INSTALLDIR):
@$(MKDIR) $@
.PHONY: $(OBJECTDIR)
.PHONY: $(INSTALLDIR)
.PHONY: clean
.PHONY: test

View File

@@ -0,0 +1,54 @@
// Copyright 2013 Google Inc. All Rights Reserved.
//
// Lock class - provides a simple android specific mutex implementation
#include "lock.h"
#include "utils/Mutex.h"
namespace wvcdm {
class Lock::Impl {
public:
android::Mutex lock_;
};
Lock::Lock() : impl_(new Lock::Impl()) {
}
Lock::~Lock() {
delete impl_;
impl_ = NULL;
}
void Lock::Acquire() {
impl_->lock_.lock();
}
void Lock::Release() {
impl_->lock_.unlock();
}
bool Lock::Try() {
return (impl_->lock_.tryLock() == 0);
}
class AutoLock::Impl {
public:
android::Mutex::Autolock* autolock_;
};
AutoLock::AutoLock(Lock& lock) : impl_(new AutoLock::Impl()) {
impl_->autolock_ = new android::Mutex::Autolock(lock.impl_->lock_);
}
AutoLock::AutoLock(Lock* lock) : impl_(new AutoLock::Impl()) {
impl_->autolock_ = new android::Mutex::Autolock(lock->impl_->lock_);
}
AutoLock::~AutoLock() {
delete impl_->autolock_;
delete impl_;
impl_ = NULL;
}
}; // namespace wvcdm

View File

@@ -0,0 +1,54 @@
// Copyright 2013 Google Inc. All Rights Reserved.
//
// Lock - Platform independent interface for a Mutex class
//
#ifndef OEMCRYPTO_LOCK_H_
#define OEMCRYPTO_LOCK_H_
#include "wv_cdm_types.h"
namespace wvcdm {
// Simple lock class. The implementation is platform dependent.
//
// The lock must be unlocked by the thread that locked it.
// The lock is also not recursive (ie. cannot be taken multiple times).
class Lock {
public:
Lock();
~Lock();
void Acquire();
void Release();
// Acquires a lock if not held and returns true.
// Returns false if the lock is held by another thread.
bool Try();
friend class AutoLock;
private:
class Impl;
Impl* impl_;
CORE_DISALLOW_COPY_AND_ASSIGN(Lock);
};
// Manages the lock automatically. It will be locked when AutoLock
// is constructed and release when AutoLock goes out of scope
class AutoLock {
public:
explicit AutoLock(Lock& lock);
explicit AutoLock(Lock* lock);
~AutoLock();
private:
class Impl;
Impl* impl_;
CORE_DISALLOW_COPY_AND_ASSIGN(AutoLock);
};
}; // namespace wvcdm
#endif // OEMCRYPTO_LOCK_H_

View File

@@ -0,0 +1,33 @@
// Copyright 2013 Google Inc. All Rights Reserved.
//
// Log - implemented using the standard Android logging mechanism
#define LOG_TAG "WVCdm"
#define LOG_BUF_SIZE 1024
#include "log.h"
#include "utils/Log.h"
namespace wvcdm {
void log_write(LogPriority level, const char* fmt, ...) {
va_list ap;
char buf[LOG_BUF_SIZE];
va_start(ap, fmt);
vsnprintf(buf, LOG_BUF_SIZE, fmt, ap);
va_end(ap);
android_LogPriority prio = ANDROID_LOG_VERBOSE;
switch(level) {
case LOG_ERROR: prio = ANDROID_LOG_ERROR; break;
case LOG_WARN: prio = ANDROID_LOG_WARN; break;
case LOG_INFO: prio = ANDROID_LOG_INFO; break;
case LOG_DEBUG: prio = ANDROID_LOG_DEBUG; break;
case LOG_VERBOSE: prio = ANDROID_LOG_VERBOSE; break;
}
__android_log_write(prio, LOG_TAG, buf);
}
}; // namespace wvcdm

View File

@@ -0,0 +1,31 @@
// Copyright 2013 Google Inc. All Rights Reserved.
//
// Log - Platform independent interface for a Logging class
//
#ifndef OEMCRYPTO_LOG_H_
#define OEMCRYPTO_LOG_H_
namespace wvcdm {
// Simple logging class. The implementation is platform dependent.
typedef enum {
LOG_ERROR,
LOG_WARN,
LOG_INFO,
LOG_DEBUG,
LOG_VERBOSE
} LogPriority;
void log_write(LogPriority priority, const char *fmt, ...);
// Log APIs
#define LOGE(...) ((void)log_write(wvcdm::LOG_ERROR, __VA_ARGS__))
#define LOGW(...) ((void)log_write(wvcdm::LOG_WARN, __VA_ARGS__))
#define LOGI(...) ((void)log_write(wvcdm::LOG_INFO, __VA_ARGS__))
#define LOGD(...) ((void)log_write(wvcdm::LOG_DEBUG, __VA_ARGS__))
#define LOGV(...) ((void)log_write(wvcdm::LOG_VERBOSE, __VA_ARGS__))
}; // namespace wvcdm
#endif // OEMCRYPTO_LOG_H_

View File

@@ -0,0 +1,742 @@
/*******************************************************************************
*
* Copyright 2013 Google Inc. All Rights Reserved.
*
* mock implementation of OEMCrypto APIs
*
******************************************************************************/
#include "oemcrypto_engine_mock.h"
#include <iostream>
#include <vector>
#include <string.h>
#include "log.h"
#include "oemcrypto_key_mock.h"
#include "openssl/aes.h"
#include "openssl/cmac.h"
#include "openssl/err.h"
#include "openssl/evp.h"
#include "openssl/hmac.h"
#include "openssl/rand.h"
#include <openssl/rsa.h>
#include "openssl/sha.h"
#include "string_conversions.h"
#include "wv_cdm_constants.h"
namespace {
// Increment counter for AES-CTR
void ctr128_inc(uint8_t* counter) {
uint32_t n = 16;
do {
if (++counter[--n] != 0) return;
} while (n);
}
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 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 SessionContext::Open() {
}
void SessionContext::Close() {
}
// Internal utility function to derive key using CMAC-128
bool SessionContext::DeriveKey(const std::vector<uint8_t>& key,
const std::vector<uint8_t>& context,
int counter,
std::vector<uint8_t>* out) {
if (key.empty() || counter > 2 || 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_Init(cmac_ctx, &key[0], key.size(), cipher, 0)) {
LOGE("[DeriveKey(): OEMCrypto_ERROR_CMAC_FAILURE]");
return false;
}
std::vector<uint8_t> 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]");
return false;
}
size_t reslen;
uint8_t res[128];
if (!CMAC_Final(cmac_ctx, res, &reslen)) {
LOGE("[DeriveKey(): OEMCrypto_ERROR_CMAC_FAILURE]");
return false;
}
out->assign(res, res + reslen);
CMAC_CTX_free(cmac_ctx);
return true;
}
bool SessionContext::DeriveKeys(const std::vector<uint8_t>& mac_key_context,
const std::vector<uint8_t>& enc_key_context) {
// Generate derived key for mac key
std::vector<uint8_t> device_key = ce_->keybox().device_key().value();
std::vector<uint8_t> mac_key;
std::vector<uint8_t> mac_key_part2;
if (!DeriveKey(device_key, mac_key_context, 1, &mac_key)) {
return false;
}
if (!DeriveKey(device_key, mac_key_context, 2, &mac_key_part2)) {
return false;
}
mac_key.insert(mac_key.end(), mac_key_part2.begin(), mac_key_part2.end());
// Generate derived key for encryption key
std::vector<uint8_t> enc_key;
if (!DeriveKey(device_key, enc_key_context, 1, &enc_key)) {
return false;
}
set_mac_key(mac_key);
set_encryption_key(enc_key);
return true;
}
bool SessionContext::RSADeriveKeys(const std::vector<uint8_t>& enc_session_key,
const std::vector<uint8_t>& mac_key_context,
const std::vector<uint8_t>& enc_key_context) {
if (!rsa_key_) {
LOGE("[RSADeriveKeys(): no RSA key set]");
return false;
}
session_key_.resize(wvcdm::KEY_SIZE);
if (-1 == RSA_private_decrypt(wvcdm::KEY_SIZE, &enc_session_key[0],
&session_key_[0], rsa_key_,
RSA_PKCS1_OAEP_PADDING)) {
LOGE("[RSADeriveKeys(): error decrypting session key.]");
dump_openssl_error();
return false;
}
// Generate derived key for mac key
std::vector<uint8_t> mac_key;
std::vector<uint8_t> mac_key_part2;
if (!DeriveKey(session_key_, mac_key_context, 1, &mac_key)) {
return false;
}
if (!DeriveKey(session_key_, mac_key_context, 2, &mac_key_part2)) {
return false;
}
mac_key.insert(mac_key.end(), mac_key_part2.begin(), mac_key_part2.end());
// Generate derived key for encryption key
std::vector<uint8_t> enc_key;
if (!DeriveKey(session_key_, enc_key_context, 1, &enc_key)) {
return false;
}
set_mac_key(mac_key);
set_encryption_key(enc_key);
return true;
}
// 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;
}
if (mac_key_.empty() || mac_key_.size() != wvcdm::MAC_KEY_SIZE) {
LOGE("[GenerateSignature(): No MAC Key]");
return false;
}
if (*signature_length < SHA256_DIGEST_LENGTH) {
*signature_length = SHA256_DIGEST_LENGTH;
return false;
}
unsigned int md_len = *signature_length;
if (HMAC(EVP_sha256(), &mac_key_[0], SHA256_DIGEST_LENGTH,
message, message_length, signature, &md_len)) {
*signature_length = md_len;
return true;
}
return false;
}
bool SessionContext::GenerateRSASignature(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("[GenerateRSASignature(): OEMCrypto_ERROR_INVALID_CONTEXT]");
return false;
}
if (!rsa_key_) {
LOGE("[GenerateRSASignature(): no RSA key set]");
return false;
}
if (*signature_length < static_cast<size_t>(RSA_size(rsa_key_))) {
*signature_length = RSA_size(rsa_key_);
return false;
}
// TODO(fredgc): This uses the wrong algorithm for signing.
// This code needs to be fixed!!
LOGE("COmputing signature using RSASSA-PKCS1v1.5 instead of RSASSA-PSS");
uint8_t hash[SHA_DIGEST_LENGTH];
if (!SHA1(message, message_length, hash)) {
LOGE("[GeneratRSASignature(): error creating signature hash.]");
dump_openssl_error();
return false;
}
int ret = RSA_sign(NID_sha1, hash, SHA_DIGEST_LENGTH,
signature, signature_length, rsa_key_);
if (ret != 1) {
LOGE("[GeneratRSASignature(): error signing signature hash.]");
dump_openssl_error();
return false;
}
return true;
}
// 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[signature_length];
if (! GenerateSignature(given_message, message_length,
computed_signature, &signature_length)) {
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;
}
bool SessionContext::ParseKeyControl(
const std::vector<uint8_t>& key_control_string,
KeyControlBlock& key_control_block) {
key_control_block.Invalidate();
if (key_control_string.size() < wvcdm::KEY_CONTROL_SIZE) {
return false;
}
if (!key_control_block.SetFromString(key_control_string)) {
LOGE("KCB: BAD Size or Structure");
return false;
}
if (!key_control_block.Validate()) {
LOGE("KCB: BAD Signature");
return false;
}
if (!CheckNonce(key_control_block.nonce())) {
LOGE("KCB: BAD Nonce");
return false;
}
LOGD("KCB:");
LOGD(" valid: %d", key_control_block.valid());
LOGD(" duration: %d", key_control_block.duration());
LOGD(" nonce: %08X", key_control_block.nonce());
LOGD(" bits: %08X", key_control_block.control_bits());
LOGD(" bit kControlObserveDataPath %s.",
(key_control_block.control_bits() & kControlObserveDataPath) ? "set" : "unset");
LOGD(" bit kControlObserveHDCP %s.",
(key_control_block.control_bits() & kControlObserveHDCP) ? "set" : "unset");
LOGD(" bit kControlObserveCGMS %s.",
(key_control_block.control_bits() & kControlObserveCGMS) ? "set" : "unset");
LOGD(" bit kControlDataPathSecure %s.",
(key_control_block.control_bits() & kControlDataPathSecure) ? "set" : "unset");
LOGD(" bit kControlNonceEnabled %s.",
(key_control_block.control_bits() & kControlNonceEnabled) ? "set" : "unset");
LOGD(" bit kControlHDCPRequired %s.",
(key_control_block.control_bits() & kControlHDCPRequired) ? "set" : "unset");
uint32_t cgms_bits = key_control_block.control_bits() & 0x3;
const char* cgms_values[4] = {"free", "BAD", "once", "never"};
LOGD(" CGMS = %s", cgms_values[cgms_bits]);
return true;
}
void SessionContext::StartTimer() {
timer_start_ = time(NULL);
}
uint32_t SessionContext::CurrentTimer() {
time_t now = time(NULL);
return now - timer_start_;
}
bool SessionContext::InstallKey(const KeyId& key_id,
const std::vector<uint8_t>& key_data,
const std::vector<uint8_t>& key_data_iv,
const std::vector<uint8_t>& key_control,
const std::vector<uint8_t>& key_control_iv) {
// Decrypt encrypted key_data using derived encryption key and offered iv
std::vector<uint8_t> content_key;
std::vector<uint8_t> key_control_str;
KeyControlBlock key_control_block;
if (!ce_->DecryptMessage(this, encryption_key_, key_data_iv,
key_data, &content_key)) {
LOGE("[Installkey(): Could not decrypt key data]");
return false;
}
#if 0 // Print content key to stdout.
std::cout << " InstallKey: key_id = "
<< wvcdm::b2a_hex(key_id) << std::endl;
std::cout << " InstallKey: content_key = "
<< wvcdm::b2a_hex(content_key) << std::endl;
std::cout << " InstallKey: key_control = "
<< wvcdm::b2a_hex(content_key) << std::endl;
#endif
// Key control must be supplied by license server
if (key_control.empty()) {
LOGE("[Installkey(): WARNING: No Key Control]");
key_control_block.Invalidate();
return false;
} else {
if (key_control_iv.empty()) {
LOGE("[Installkey(): ERROR: No Key Control IV]");
return false;
}
if (!ce_->DecryptMessage(this, content_key, key_control_iv,
key_control, &key_control_str)) {
LOGE("[Installkey(): ERROR: Could not decrypt content key]");
return false;
}
if (!ParseKeyControl(key_control_str, key_control_block)) {
return false;
}
}
Key key(KEYTYPE_CONTENT, content_key, key_control_block);
session_keys_.Insert(key_id, key);
return true;
}
bool SessionContext::EncryptRSAKey(uint8_t* wrapped_rsa_key,
size_t wrapped_rsa_key_length,
uint8_t* wrapped_rsa_key_iv) {
std::vector<uint8_t> buffer(wrapped_rsa_key_length);
uint8_t* p = &buffer[0];
int len = i2d_RSAPrivateKey(rsa_key_, &p);
if (len < 0) {
LOGE("[RewrapRSAKey(): Could not decode rsa key]");
dump_openssl_error();
return false;
}
// Encrypt rsa key with keybox.
uint8_t iv_buffer[ wvcdm::KEY_IV_SIZE];
memcpy(iv_buffer, wrapped_rsa_key_iv, wvcdm::KEY_IV_SIZE);
AES_KEY aes_key;
AES_set_encrypt_key(&encryption_key_[0], 128, &aes_key);
AES_cbc_encrypt(&buffer[0], wrapped_rsa_key, wrapped_rsa_key_length,
&aes_key, iv_buffer, AES_ENCRYPT);
return true;
}
bool SessionContext::LoadRSAKey(const uint8_t* enc_rsa_key,
size_t enc_rsa_key_length,
const uint8_t* enc_rsa_key_iv,
const uint8_t* message,
size_t message_length,
const uint8_t* signature,
size_t signature_length) {
std::vector<uint8_t> enc_rsa_key_v(enc_rsa_key,
enc_rsa_key + enc_rsa_key_length);
std::vector<uint8_t> iv(enc_rsa_key_iv, enc_rsa_key_iv + wvcdm::KEY_IV_SIZE);
std::vector<uint8_t> rsa_key; // unencrypted.
// Validate message signature
if (!ValidateMessage(message, message_length, signature, signature_length)) {
LOGE("[LoadRSAKey(): Could not verify signature]");
return false;
}
if (!ce_->DecryptMessage(this, encryption_key_, iv,
enc_rsa_key_v, &rsa_key)) {
LOGE("[LoadRSAKey(): Could not decrypt key data]");
return false;
}
if (rsa_key_) {
RSA_free(rsa_key_);
rsa_key_ = NULL;
}
uint8_t const* p = &rsa_key[0];
RSA* rsa = d2i_RSAPrivateKey(0, &p, rsa_key.size());
rsa_key_ = rsa;
if (! rsa_key_) {
LOGE("[LoadRSAKey(): Could decode unencrypted rsa key]");
dump_openssl_error();
return false;
}
switch (RSA_check_key(rsa_key_)) {
case 1: // valid.
return true;
case 0: // not valid.
LOGE("[LoadRSAKey(): rsa key not valid]");
dump_openssl_error();
return false;
default: // -1 == check failed.
LOGE("[LoadRSAKey(): error checking rsa key]");
dump_openssl_error();
return false;
}
}
bool SessionContext::RefreshKey(const KeyId& key_id,
const std::vector<uint8_t>& key_control,
const std::vector<uint8_t>& key_control_iv) {
if (key_id.empty()) {
return false;
}
Key* content_key = session_keys_.Find(key_id);
if (NULL == content_key) {
return false;
}
if (!key_control.empty()) {
const std::vector<uint8_t> content_key_value = content_key->value();
// Decrypt encrypted key control block
// We don't actually make use of it in Oemcrypto mock, just to verify its
// validity
std::vector<uint8_t> control;
if (key_control_iv.empty()) {
control = key_control;
} else if (!ce_->DecryptMessage(this, content_key_value, key_control_iv,
key_control, &control)) {
return false;
}
KeyControlBlock key_control_block;
if (!ParseKeyControl(control, key_control_block)) {
return false;
}
if (!content_key->UpdateControl(key_control_block)) {
return false;
}
}
return true;
}
bool SessionContext::UpdateMacKey(const std::vector<uint8_t>& enc_mac_key,
const std::vector<uint8_t>& iv) {
// Decrypt mac key from enc_mac_key using device_key
std::vector<uint8_t> mac_key;
if (!ce_->DecryptMessage(this, encryption_key_, iv,
enc_mac_key, &mac_key)) {
return false;
}
mac_key_ = mac_key;
return true;
}
bool SessionContext::SelectContentKey(const KeyId& key_id) {
const Key* content_key = session_keys_.Find(key_id);
if (NULL == content_key) {
LOGE("[SelectContentKey(): No key matches key id]");
return false;
}
current_content_key_ = content_key;
return true;
}
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();
}
CryptoEngine::CryptoEngine() :
ce_state_(CE_INITIALIZED), current_session_(NULL) {
valid_ = true;
ERR_load_crypto_strings();
}
CryptoEngine::~CryptoEngine() {
current_session_ = NULL;
sessions_.clear();
}
void CryptoEngine::Terminate() {
}
KeyboxError CryptoEngine::ValidateKeybox() { return keybox_.Validate(); }
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);
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;
}
// Internal utility function to decrypt the message
bool CryptoEngine::DecryptMessage(SessionContext* session,
const std::vector<uint8_t>& key,
const std::vector<uint8_t>& iv,
const std::vector<uint8_t>& message,
std::vector<uint8_t>* 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 CryptoEngine::DecryptCTR(SessionContext* session,
const std::vector<uint8_t>& iv,
size_t byte_offset,
const std::vector<uint8_t>& cipher_data,
bool is_encrypted,
void* clear_data,
BufferType buffer_type) {
// Check there is a content key
if (session->current_content_key() == NULL) {
LOGE("[DecryptCTR(): OEMCrypto_ERROR_NO_CONTENT_KEY]");
return false;
}
const KeyControlBlock& control = session->current_content_key()->control();
if (control.control_bits() & kControlDataPathSecure) {
if (buffer_type == kBufferTypeClear) {
LOGE("[DecryptCTR(): Secure key with insecure buffer]");
return false;
}
}
if (control.control_bits() & kControlHDCPRequired) {
// For reference implementation, we do not implement any HDCP.
return false;
}
if (control.duration() > 0) {
if (control.duration() < session->CurrentTimer()) {
return false;
}
}
const std::vector<uint8_t>& content_key = session->current_content_key()->value();
// Set the AES key.
if (static_cast<int>(content_key.size()) != AES_BLOCK_SIZE) {
LOGE("[DecryptCTR(): CONTENT_KEY has wrong size.");
return false;
}
const uint8_t* key_u8 = &content_key[0];
AES_KEY aes_key;
if (AES_set_encrypt_key(key_u8, AES_BLOCK_SIZE * 8, &aes_key) != 0) {
LOGE("[DecryptCTR(): FAILURE]");
return false;
}
if (buffer_type == kBufferTypeDirect) {
// For reference implementation, we quietly drop direct video.
return true;
}
if (buffer_type == kBufferTypeSecure) {
// For reference implementation, we also quietly drop secure data.
return true;
}
if (! is_encrypted) {
memcpy(reinterpret_cast<uint8_t*>(clear_data),
&cipher_data[0], cipher_data.size());
return true;
}
// Local copy (will be modified).
uint8_t aes_iv[AES_BLOCK_SIZE];
if (static_cast<int>(iv.size()) != AES_BLOCK_SIZE) {
LOGE("[DecryptCTR(): FAILURE: iv has wrong length]");
return false;
}
memcpy(aes_iv, &iv[0], AES_BLOCK_SIZE);
// Encrypt the IV.
uint8_t ecount_buf[AES_BLOCK_SIZE];
if (byte_offset != 0) {
// The context is needed only when not starting a new block.
AES_encrypt(aes_iv, ecount_buf, &aes_key);
ctr128_inc(aes_iv);
}
// Decryption.
unsigned int byte_offset_cur = byte_offset;
AES_ctr128_encrypt(
&cipher_data[0], reinterpret_cast<uint8_t*>(clear_data), cipher_data.size(),
&aes_key, aes_iv, ecount_buf, &byte_offset_cur);
if (byte_offset_cur != ((byte_offset + cipher_data.size()) % AES_BLOCK_SIZE)) {
LOGE("[DecryptCTR(): FAILURE: byte offset wrong.]");
return false;
}
return true;
}
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

View File

@@ -0,0 +1,232 @@
/*******************************************************************************
*
* Copyright 2013 Google Inc. All Rights Reserved.
*
* mock implementation of OEMCrypto APIs
*
******************************************************************************/
#ifndef OEMCRYPTO_ENGINE_MOCK_H_
#define OEMCRYPTO_ENGINE_MOCK_H_
#include <openssl/rsa.h>
#include <stdint.h>
#include <time.h>
#include <map>
#include <vector>
#include "lock.h"
#include "oemcrypto_key_mock.h"
#include "oemcrypto_keybox_mock.h"
#include "wv_cdm_types.h"
namespace wvoec_mock {
enum BufferType {
kBufferTypeClear,
kBufferTypeSecure,
kBufferTypeDirect
};
class SessionContext;
class CryptoEngine;
typedef uint32_t SessionId;
typedef std::map<SessionId, SessionContext*> ActiveSessions;
typedef std::vector<uint8_t> KeyId;
typedef std::map<KeyId, Key*> KeyMap;
// 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);
private:
KeyMap keys_;
CORE_DISALLOW_COPY_AND_ASSIGN(SessionKeyTable);
};
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];
};
class SessionContext {
private:
SessionContext() {}
public:
explicit SessionContext(CryptoEngine* ce, SessionId sid)
: valid_(true), ce_(ce), id_(sid), current_content_key_(NULL),
rsa_key_(NULL) {}
~SessionContext() {}
void Open();
void Close();
bool isValid() { return valid_; }
bool DeriveKeys(const std::vector<uint8_t>& mac_context,
const std::vector<uint8_t>& enc_context);
bool RSADeriveKeys(const std::vector<uint8_t>& enc_session_key,
const std::vector<uint8_t>& mac_context,
const std::vector<uint8_t>& enc_context);
bool GenerateSignature(const uint8_t* message,
size_t message_length,
uint8_t* signature,
size_t* signature_length);
bool GenerateRSASignature(const uint8_t* message,
size_t message_length,
uint8_t* signature,
size_t* signature_length);
bool ValidateMessage(const uint8_t* message,
size_t message_length,
const uint8_t* signature,
size_t signature_length);
void StartTimer();
uint32_t CurrentTimer(); // (seconds).
bool InstallKey(const KeyId& key_id,
const std::vector<uint8_t>& key_data,
const std::vector<uint8_t>& key_data_iv,
const std::vector<uint8_t>& key_control,
const std::vector<uint8_t>& key_control_iv);
bool EncryptRSAKey(uint8_t* wrapped_rsa_key,
size_t wrapped_rsa_key_length,
uint8_t* wrapped_rsa_key_iv);
bool LoadRSAKey(const uint8_t* enc_rsa_key,
size_t enc_rsa_key_length,
const uint8_t* enc_rsa_key_iv,
const uint8_t* message,
size_t message_length,
const uint8_t* signature,
size_t signature_length);
bool ParseKeyControl(const std::vector<uint8_t>& key_control_string,
KeyControlBlock& key_control_block);
bool RefreshKey(const KeyId& key_id,
const std::vector<uint8_t>& key_control,
const std::vector<uint8_t>& key_control_iv);
bool UpdateMacKey(const std::vector<uint8_t>& mac_key, const std::vector<uint8_t>& iv);
bool SelectContentKey(const KeyId& key_id);
const Key* current_content_key(void) {return current_content_key_;}
void set_mac_key(const std::vector<uint8_t>& mac_key) { mac_key_ = mac_key; }
const std::vector<uint8_t>& mac_key() { return mac_key_; }
void set_encryption_key(const std::vector<uint8_t>& enc_key) {
encryption_key_ = enc_key;
}
const std::vector<uint8_t>& encryption_key() { return encryption_key_; }
void AddNonce(uint32_t nonce);
bool CheckNonce(uint32_t nonce);
void FlushNonces();
private:
bool DeriveKey(const std::vector<uint8_t>& key, const std::vector<uint8_t>& context,
int counter, std::vector<uint8_t>* out);
bool valid_;
CryptoEngine* ce_;
SessionId id_;
std::vector<uint8_t> mac_key_;
std::vector<uint8_t> encryption_key_;
std::vector<uint8_t> session_key_;
const Key* current_content_key_;
SessionKeyTable session_keys_;
NonceTable nonce_table_;
RSA* rsa_key_;
time_t timer_start_;
CORE_DISALLOW_COPY_AND_ASSIGN(SessionContext);
};
class CryptoEngine {
private:
enum CryptoEngineState {
CE_ILLEGAL,
CE_INITIALIZED,
CE_HAS_KEYBOX,
CE_HAS_SESSIONS,
CE_ERROR
};
public:
CryptoEngine();
~CryptoEngine();
bool Initialized() { return (ce_state_ != CE_ILLEGAL); }
void Terminate();
bool isValid() { return valid_; }
KeyboxError ValidateKeybox();
WvKeybox& keybox() { return keybox_; }
SessionId CreateSession();
bool DestroySession(SessionId sid);
SessionContext* FindSession(SessionId sid);
void set_current_session_(SessionContext* current) {
current_session_ = current;
}
bool DecryptMessage(SessionContext* session,
const std::vector<uint8_t>& key,
const std::vector<uint8_t>& iv,
const std::vector<uint8_t>& message,
std::vector<uint8_t>* decrypted);
bool DecryptCTR(SessionContext* session,
const std::vector<uint8_t>& iv,
size_t byte_offset,
const std::vector<uint8_t>& cipher_data,
bool is_encrypted,
void* clear_data,
BufferType buffer_type);
private:
bool valid_;
CryptoEngineState ce_state_;
SessionContext* current_session_;
ActiveSessions sessions_;
WvKeybox keybox_;
wvcdm::Lock session_table_lock_;
CORE_DISALLOW_COPY_AND_ASSIGN(CryptoEngine);
};
}; // namespace wvoec_eng
#endif

View File

@@ -0,0 +1,106 @@
/*******************************************************************************
*
* Copyright 2013 Google Inc. All Rights Reserved.
*
* mock implementation of OEMCrypto APIs
*
******************************************************************************/
#include "oemcrypto_key_mock.h"
#include <cstring>
#include "log.h"
#include "wv_cdm_constants.h"
namespace wvoec_mock {
bool KeyControlBlock::Validate() {
valid_ = false;
if (0x6b63746c != verification_) { // kctl.
LOGE("KCB: BAD verification string: %08X (not %08X)", verification_,
0x6b63746c);
return false;
}
// TODO(gmorgan): validate control bits
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<uint8_t>& str, int idx) {
int bidx = idx * 4;
uint32_t t = static_cast<unsigned char>(str[bidx]) << 24;
t |= static_cast<unsigned char>(str[bidx + 1]) << 16;
t |= static_cast<unsigned char>(str[bidx + 2]) << 8;
t |= static_cast<unsigned char>(str[bidx + 3]);
return t;
}
bool KeyControlBlock::SetFromString(const std::vector<uint8_t>& 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 false;
}
verification_ = ExtractField(key_control_string, 0);
duration_ = ExtractField(key_control_string, 1);
nonce_ = ExtractField(key_control_string, 2);
control_bits_ = ExtractField(key_control_string, 3);
return Validate();
}
Key::Key(KeyType ktype, const std::vector<uint8_t>& key_string,
const KeyControlBlock& control) :
valid_(true), type_(ktype),
value_(key_string), has_control_(true),
control_(control) {
}
bool Key::setValue(const char* key_string, size_t key_string_length) {
valid_ = false;
if (!key_string || key_string_length == 0) {
return false;
}
value_.assign(key_string, key_string + key_string_length);
if (isValidType() && has_control_) {
valid_ = true;
}
return valid_;
}
bool Key::setType(KeyType ktype) {
valid_ = false;
type_ = ktype;
if (value_.empty()) {
return false;
}
if (isValidType() && has_control_) {
valid_ = true;
}
return valid_;
}
bool Key::setControl(const KeyControlBlock& control) {
valid_ = false;
if (!control.valid()) {
return false;
}
control_ = control;
has_control_ = true;
if (isValidType() && !value_.empty()) {
valid_ = true;
}
return valid_;
}
}; // namespace wvoec_eng

View File

@@ -0,0 +1,117 @@
/*******************************************************************************
*
* Copyright 2013 Google Inc. All Rights Reserved.
*
* mock implementation of OEMCrypto APIs
*
******************************************************************************/
#ifndef OEMCRYPTO_KEY_MOCK_H_
#define OEMCRYPTO_KEY_MOCK_H_
#include <stdint.h>
#include <string>
#include <vector>
namespace wvoec_mock {
enum KeyType {
KEYTYPE_UNKNOWN,
KEYTYPE_PREPROV,
KEYTYPE_ROOT,
KEYTYPE_DEVICE,
KEYTYPE_CONTENT,
KEYTYPE_CONTENT_AUDIO,
KEYTYPE_CONTENT_VIDEO,
KEYTYPE_MAX
};
const uint32_t kControlObserveDataPath = (1<<31);
const uint32_t kControlObserveHDCP = (1<<30);
const uint32_t kControlObserveCGMS = (1<<29);
const uint32_t kControlDataPathSecure = (1<<4);
const uint32_t kControlNonceEnabled = (1<<3);
const uint32_t kControlHDCPRequired = (1<<2);
const uint32_t kControlCGMSMask = (0x03);
const uint32_t kControlCGMSCopyFreely = (0x00);
const uint32_t kControlCGMSCopyOnce = (0x02);
const uint32_t kControlCGMSCopyNever = (0x03);
class KeyControlBlock {
public:
KeyControlBlock() {}
KeyControlBlock(const std::vector<uint8_t>& key_control_string) {
valid_ = SetFromString(key_control_string);
}
~KeyControlBlock() {}
bool SetFromString(const std::vector<uint8_t>& key_control_string);
bool Validate();
void Invalidate() { valid_ = false; }
bool valid() const { return valid_; }
uint32_t duration() const { return duration_; }
uint32_t nonce() const { return nonce_; }
uint32_t control_bits() const { return control_bits_; }
private:
uint32_t ExtractField(const std::vector<uint8_t>& str, int idx);
bool valid_;
uint32_t verification_;
uint32_t duration_;
uint32_t nonce_;
uint32_t control_bits_;
};
// AES-128 crypto key
class Key {
public:
Key() : valid_(false), type_(KEYTYPE_UNKNOWN), has_control_(false) {}
Key(const Key& key) : valid_(key.valid_), type_(key.type_),
value_(key.value_),
has_control_(key.has_control_),
control_(key.control_) {}
Key(KeyType type, const std::vector<uint8_t>& key_string,
const KeyControlBlock& control);
virtual ~Key() {};
// Key is valid iff setValue(), setType(), and setControl() have been called
bool setValue(const char* key_string, size_t key_string_length);
bool setType(KeyType ktype);
bool setControl(const KeyControlBlock& control);
bool UpdateControl(const KeyControlBlock& control) { return true; }
KeyType keyType() { return type_; }
const std::vector<uint8_t>& value() const { return value_; }
const KeyControlBlock& control() const { return control_; }
bool isDeviceKey() { return (KEYTYPE_DEVICE == type_); }
bool isRootKey() { return (KEYTYPE_ROOT == type_); }
bool isPreprovKey() { return (KEYTYPE_PREPROV == type_); }
bool isContentKey() {
bool ctypes = (KEYTYPE_CONTENT == type_) ||
(KEYTYPE_CONTENT_AUDIO == type_) ||
(KEYTYPE_CONTENT_VIDEO == type_);
return ctypes;
}
bool isValidType() {
return ((KEYTYPE_UNKNOWN < type_) && (KEYTYPE_MAX > type_));
}
bool isValid() { return valid_; }
void clear() { value_.clear(); valid_ = false; }
private:
bool valid_;
KeyType type_;
std::vector<uint8_t> value_;
bool has_control_;
KeyControlBlock control_;
};
}; // namespace wvoec_eng
#endif

View File

@@ -0,0 +1,109 @@
/*******************************************************************************
*
* Copyright 2013 Google Inc. All Rights Reserved.
*
* mock implementation of OEMCrypto APIs
*
******************************************************************************/
#include "oemcrypto_keybox_mock.h"
#include <arpa/inet.h> // TODO(fredgc): Add ntoh to wv_cdm_utilities.h
#include <string>
#include <cstring>
#include <sys/types.h>
#include "log.h"
#include "wvcrc32.h"
#include "wv_keybox.h"
namespace wvoec_mock {
const WidevineKeybox kDefaultKeybox = {
// 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,
}
};
WvKeybox::WvKeybox() : valid_(false) {
Prepare();
}
bool WvKeybox::Prepare() {
InstallKeybox(reinterpret_cast<const uint8_t*>(&kDefaultKeybox),
sizeof(kDefaultKeybox));
valid_ = true;
return valid_;
}
KeyboxError WvKeybox::Validate() {
if (!valid_) {
LOGE("[KEYBOX NOT LOADED]");
return OTHER_ERROR;
}
if (strncmp(reinterpret_cast<char*>(magic_), "kbox", 4) != 0) {
LOGE("[KEYBOX HAS BAD MAGIC]");
return BAD_MAGIC;
}
uint32_t crc_computed;
uint32_t* crc_stored = (uint32_t*)crc_;
WidevineKeybox keybox;
memset(&keybox, 0, sizeof(keybox));
memcpy(keybox.device_id_, &device_id_[0], device_id_.size());
memcpy(keybox.device_key_, &device_key_.value()[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<uint8_t*>(&keybox),
sizeof(keybox) - 4)); // Don't include 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<const WidevineKeybox*>(buffer);
device_id_.assign(keybox->device_id_,
keybox->device_id_ + sizeof(keybox->device_id_));
device_key_.setValue(reinterpret_cast<const char*>(keybox->device_key_),
sizeof(keybox->device_key_));
device_key_.setType(KEYTYPE_DEVICE);
memcpy(key_data_, keybox->data_, sizeof(keybox->data_));
memcpy(magic_, keybox->magic_, sizeof(keybox->magic_));
memcpy(crc_, keybox->crc_, sizeof(keybox->crc_));
return true;
}
}; // namespace wvoec_eng

View File

@@ -0,0 +1,50 @@
/*******************************************************************************
*
* 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<uint8_t>& device_id() { return device_id_; }
Key& 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<uint8_t> device_id_;
Key device_key_;
WvKeyboxKeyData key_data_;
uint8_t magic_[4];
uint8_t crc_[4];
};
}; // namespace wvoec_eng
#endif

View File

@@ -0,0 +1,893 @@
/*******************************************************************************
*
* Copyright 2013 Google Inc. All Rights Reserved.
*
* mock implementation of OEMCrypto APIs
*
******************************************************************************/
#include "OEMCryptoCENC.h"
#include <iostream>
#include <cstring>
#include <stdio.h>
#include <string>
#include "log.h"
#include "oemcrypto_engine_mock.h"
#include "openssl/rand.h"
#include "wv_cdm_constants.h"
namespace wvoec_mock {
static CryptoEngine* crypto_engine = NULL;
// Set this to true when you are generating test vectors.
const bool trace_all_calls = false;
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;
static void dump_hex(std::string name, const uint8_t* vector, size_t length) {
printf("%s = ", name.c_str());
if (vector == NULL) {
printf("NULL;\n");
return;
}
// TODO(fredgc): replace with HEXEncode.
for (size_t i = 0; i < length; i++) {
if (i == 0) {
printf("\n wvcdm::a2b_hex(\"");
} else if (i % 32 == 0) {
printf("\"\n \"");
}
printf("%02X", vector[i]);
}
printf("\");\n");
}
void dump_array_part(std::string array, size_t index,
std::string name, const uint8_t* vector, size_t length) {
if (vector == NULL) {
printf("%s[%zu].%s = NULL;\n", array.c_str(), index, name.c_str());
return;
}
printf("std::string s%zu_", index);
dump_hex(name, vector, length);
printf("%s[%zu].%s = message_ptr + message.find(s%zu_%s.data());\n",
array.c_str(), index, name.c_str(), index, name.c_str());
}
extern "C"
OEMCryptoResult OEMCrypto_Initialize(void) {
if (trace_all_calls) {
printf("------------------------- OEMCrypto_Initialize(void)\n");
}
crypto_engine = new CryptoEngine;
if (!crypto_engine || !crypto_engine->Initialized()) {
LOGE("[OEMCrypto_Initialize(): failed]");
return OEMCrypto_ERROR_INIT_FAILED;
}
LOGD("[OEMCrypto_Initialize(): success]");
return OEMCrypto_SUCCESS;
}
extern "C"
OEMCryptoResult OEMCrypto_Terminate(void) {
if (trace_all_calls) {
printf("----------------- OEMCryptoResult OEMCrypto_Terminate(void)\n");
}
if (!crypto_engine) {
LOGE("[OEMCrypto_Terminate(): failed]");
return OEMCrypto_ERROR_TERMINATE_FAILED;
}
if (crypto_engine->Initialized()) {
crypto_engine->Terminate();
}
delete crypto_engine;
crypto_engine = NULL;
LOGD("[OEMCrypto_Terminate(): success]");
return OEMCrypto_SUCCESS;
}
extern "C"
OEMCryptoResult OEMCrypto_OpenSession(OEMCrypto_SESSION* session) {
if (trace_all_calls) {
printf("-- OEMCryptoResult OEMCrypto_OpenSession(OEMCrypto_SESSION *session)\n");
}
SessionId sid = crypto_engine->CreateSession();
*session = (OEMCrypto_SESSION)sid;
LOGD("[OEMCrypto_OpenSession(): SID=%08x]", sid);
return OEMCrypto_SUCCESS;
}
extern "C"
OEMCryptoResult OEMCrypto_CloseSession(OEMCrypto_SESSION session) {
if (trace_all_calls) {
printf("-- OEMCryptoResult OEMCrypto_CloseSession(OEMCrypto_SESSION session)\n");
}
if (!crypto_engine->DestroySession((SessionId)session)) {
LOGD("[OEMCrypto_CloseSession(SID=%08X): failed]", session);
return OEMCrypto_ERROR_CLOSE_SESSION_FAILED;
} else {
LOGD("[OEMCrypto_CloseSession(SID=%08X): success]", session);
return OEMCrypto_SUCCESS;
}
}
extern "C"
OEMCryptoResult OEMCrypto_GenerateNonce(OEMCrypto_SESSION session,
uint32_t* nonce) {
if (trace_all_calls) {
printf("-- OEMCryptoResult OEMCrypto_GenerateNonce(OEMCrypto_SESSION session,\n");
}
SessionContext* session_ctx = crypto_engine->FindSession(session);
if (!session_ctx || !session_ctx->isValid()) {
LOGE("[OEMCrypto_GenerateNonce(): ERROR_NO_INVALID_SESSION]");
return OEMCrypto_ERROR_INVALID_SESSION;
}
uint32_t nonce_value;
uint8_t* nonce_string = reinterpret_cast<uint8_t*>(&nonce_value);
// Generate 4 bytes of random data
if (!RAND_bytes(nonce_string, 4)) {
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
}
session_ctx->AddNonce(nonce_value);
*nonce = nonce_value;
if (trace_all_calls) {
printf("nonce = %08x\n", nonce_value);
}
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 (trace_all_calls) {
printf("-- OEMCryptoResult OEMCrypto_GenerateDerivedKeys(\n");
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 (NO_ERROR != crypto_engine->ValidateKeybox()) {
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_NO_INVALID_SESSION]");
return OEMCrypto_ERROR_INVALID_SESSION;
}
const std::vector<uint8_t> mac_ctx_str(mac_key_context,
mac_key_context + mac_key_context_length);
const std::vector<uint8_t> 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(mac_ctx_str, enc_ctx_str)) {
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
}
if (trace_all_calls) {
dump_hex("mac_key", &session_ctx->mac_key()[0],
session_ctx->mac_key().size());
dump_hex("enc_key", &session_ctx->encryption_key()[0],
session_ctx->encryption_key().size());
}
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 (trace_all_calls) {
printf("-- OEMCryptoResult OEMCrypto_GenerateSignature(\n");
dump_hex("message", message, message_length);
}
if (NO_ERROR != crypto_engine->ValidateKeybox()) {
LOGE("[OEMCrypto_GenerateSignature(): ERROR_KEYBOX_INVALID]");
return OEMCrypto_ERROR_KEYBOX_INVALID;
}
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_NO_INVALID_SESSION]");
return OEMCrypto_ERROR_INVALID_SESSION;
}
if (session_ctx->GenerateSignature(message,
message_length,
signature,
signature_length)) {
if (trace_all_calls) {
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_key,
size_t num_keys,
const OEMCrypto_KeyObject* key_array) {
if (trace_all_calls) {
printf("-- OEMCryptoResult OEMCrypto_LoadKeys(OEMCrypto_SESSION session,\n");
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_key", enc_mac_key, wvcdm::MAC_KEY_SIZE);
for (size_t i = 0; i < num_keys; i++) {
printf("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, wvcdm::KEY_IV_SIZE);
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 (NO_ERROR != crypto_engine->ValidateKeybox()) {
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_NO_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;
}
// Range check
if (!RangeCheck(message, message_length, enc_mac_key,
wvcdm::MAC_KEY_SIZE, true) ||
!RangeCheck(message, message_length, enc_mac_key_iv,
wvcdm::KEY_IV_SIZE, true)) {
LOGE("[OEMCrypto_LoadKeys(): OEMCrypto_ERROR_SIGNATURE_FAILURE - range check.]");
return OEMCrypto_ERROR_SIGNATURE_FAILURE;
}
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,
wvcdm::KEY_SIZE, 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, true) ||
!RangeCheck(message, message_length, key_array[i].key_control_iv,
wvcdm::KEY_IV_SIZE, true)) {
LOGE("[OEMCrypto_LoadKeys(): OEMCrypto_ERROR_SIGNATURE_FAILURE -range check %d]", i);
return OEMCrypto_ERROR_SIGNATURE_FAILURE;
}
}
// Validate message signature
if (!session_ctx->ValidateMessage(message, message_length, signature, signature_length)) {
return OEMCrypto_ERROR_SIGNATURE_FAILURE;
}
session_ctx->StartTimer();
// Decrypt and install keys in key object
// Each key will have a key control block. They will all have the same nonce.
std::vector<uint8_t> key_id;
std::vector<uint8_t> enc_key_data;
std::vector<uint8_t> key_data_iv;
std::vector<uint8_t> key_control;
std::vector<uint8_t> 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 + wvcdm::KEY_SIZE);
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) {
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
}
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);
if (!session_ctx->InstallKey(key_id, enc_key_data, key_data_iv, key_control,
key_control_iv)) {
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
}
}
// All keys processed. Flush nonce table
session_ctx->FlushNonces();
// enc_mac_key can be NULL if license renewal is not supported
if (enc_mac_key == NULL) return OEMCrypto_SUCCESS;
// V2 license protocol: update mac key after processing license response
const std::vector<uint8_t> enc_mac_key_str = std::vector<uint8_t>(
enc_mac_key, enc_mac_key + wvcdm::MAC_KEY_SIZE);
const std::vector<uint8_t> enc_mac_key_iv_str = std::vector<uint8_t>(
enc_mac_key_iv, enc_mac_key_iv + wvcdm::KEY_IV_SIZE);
if (!session_ctx->UpdateMacKey(enc_mac_key_str, enc_mac_key_iv_str)) {
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
}
return OEMCrypto_SUCCESS;
}
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 (trace_all_calls) {
printf("-- OEMCryptoResult OEMCrypto_RefreshKeys(OEMCrypto_SESSION session,\n");
}
if (NO_ERROR != crypto_engine->ValidateKeybox()) {
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_NO_INVALID_SESSION]");
return OEMCrypto_ERROR_INVALID_SESSION;
}
if (message == NULL || message_length == 0 ||
signature == NULL || signature_length == 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, true) ||
!RangeCheck(message, message_length, key_array[i].key_control_iv,
wvcdm::KEY_IV_SIZE, true)) {
LOGE("[OEMCrypto_RefreshKeys(): OEMCrypto_ERROR_SIGNATURE_FAILURE]");
return OEMCrypto_ERROR_SIGNATURE_FAILURE;
}
}
// Validate message signature
if (!session_ctx->ValidateMessage(message, message_length,
signature, signature_length)) {
return OEMCrypto_ERROR_SIGNATURE_FAILURE;
}
// Decrypt and refresh keys in key refresh object
std::vector<uint8_t> key_id;
std::vector<uint8_t> key_control;
std::vector<uint8_t> key_control_iv;
for (unsigned int i = 0; i < num_keys; i++) {
// TODO(gmorgan): key_id may be null if special control key type (TBS)
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);
} else {
key_id.clear();
}
if (key_array[i].key_control != NULL) {
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);
} else {
key_control.clear();
key_control_iv.clear();
}
if (!session_ctx->RefreshKey(key_id, key_control, key_control_iv)) {
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
}
}
session_ctx->StartTimer();
return OEMCrypto_SUCCESS;
}
extern "C"
OEMCryptoResult OEMCrypto_SelectKey(const OEMCrypto_SESSION session,
const uint8_t* key_id,
size_t key_id_length) {
if (trace_all_calls) {
printf("-- OEMCryptoResult OEMCrypto_SelectKey(const OEMCrypto_SESSION session,\n");
dump_hex("key_id", key_id, key_id_length);
}
if (NO_ERROR != crypto_engine->ValidateKeybox()) {
LOGE("[OEMCrypto_SelectKey(): ERROR_KEYBOX_INVALID]");
return OEMCrypto_ERROR_KEYBOX_INVALID;
}
SessionContext* session_ctx = crypto_engine->FindSession(session);
if (!session_ctx || !session_ctx->isValid()) {
LOGE("[OEMCrypto_SelectKey(): ERROR_NO_INVALID_SESSION]");
return OEMCrypto_ERROR_INVALID_SESSION;
}
const std::vector<uint8_t> key_id_str = std::vector<uint8_t>(key_id, key_id + key_id_length);
if (!session_ctx->SelectContentKey(key_id_str)) {
LOGE("[OEMCrypto_SelectKey(): FAIL]");
return OEMCrypto_ERROR_NO_CONTENT_KEY;
}
return OEMCrypto_SUCCESS;
}
extern "C"
OEMCryptoResult OEMCrypto_DecryptCTR(OEMCrypto_SESSION session,
const uint8_t* data_addr,
size_t data_length,
bool is_encrypted,
const uint8_t* iv,
size_t offset,
const OEMCrypto_DestBufferDesc* out_buffer) {
if (trace_all_calls) {
printf("-- OEMCryptoResult OEMCrypto_DecryptCTR(OEMCrypto_SESSION session,\n");
}
wvoec_mock::BufferType buffer_type = kBufferTypeDirect;
void* destination = NULL;
size_t max_length = 0;
switch (out_buffer->type) {
case OEMCrypto_BufferType_Clear:
buffer_type = kBufferTypeClear;
destination = out_buffer->buffer.clear.address;
max_length = out_buffer->buffer.clear.max_length;
break;
case OEMCrypto_BufferType_Secure:
buffer_type = kBufferTypeSecure;
destination = out_buffer->buffer.secure.handle;
max_length = out_buffer->buffer.secure.max_length;
break;
default:
case OEMCrypto_BufferType_Direct:
buffer_type = kBufferTypeDirect;
destination = NULL;
break;
}
if (buffer_type != kBufferTypeDirect && max_length < data_length) {
LOGE("[OEMCrypto_DecryptCTR(): OEMCrypto_ERROR_SHORT_BUFFER]");
return OEMCrypto_ERROR_SHORT_BUFFER;
}
if (NO_ERROR != crypto_engine->ValidateKeybox()) {
LOGE("[OEMCrypto_DecryptCTR(): ERROR_KEYBOX_INVALID]");
return OEMCrypto_ERROR_KEYBOX_INVALID;
}
SessionContext* session_ctx = crypto_engine->FindSession(session);
if (!session_ctx || !session_ctx->isValid()) {
LOGE("[OEMCrypto_DecryptCTR(): ERROR_NO_INVALID_SESSION]");
return OEMCrypto_ERROR_INVALID_SESSION;
}
if (data_addr == NULL || data_length == 0 ||
iv == NULL || out_buffer == NULL) {
LOGE("[OEMCrypto_DecryptCTR(): OEMCrypto_ERROR_INVALID_CONTEXT]");
return OEMCrypto_ERROR_INVALID_CONTEXT;
}
std::vector<uint8_t> iv_v(iv, iv + 16);
std::vector<uint8_t> content(data_addr, data_addr + data_length);
if (!crypto_engine->DecryptCTR(session_ctx, iv_v, (int)offset,
content, is_encrypted,
destination, buffer_type)) {
LOGE("[OEMCrypto_DecryptCTR(): OEMCrypto_ERROR_INVALID_CONTEXT]");
return OEMCrypto_ERROR_INVALID_CONTEXT;
}
return OEMCrypto_SUCCESS;
}
extern "C"
OEMCryptoResult OEMCrypto_InstallKeybox(const uint8_t* keybox,
size_t keyBoxLength) {
if (trace_all_calls) {
printf("-- OEMCryptoResult OEMCrypto_InstallKeybox(const uint8_t *keybox,\n");
}
if (crypto_engine->keybox().InstallKeybox(keybox, keyBoxLength)) {
return OEMCrypto_SUCCESS;
}
return OEMCrypto_ERROR_WRITE_KEYBOX;
}
extern "C"
OEMCryptoResult OEMCrypto_IsKeyboxValid(void) {
if (trace_all_calls) {
printf("-- OEMCryptoResult OEMCrypto_IsKeyboxValid(void) {\n");
}
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"
OEMCryptoResult OEMCrypto_GetDeviceID(uint8_t* deviceID,
size_t* idLength) {
if (trace_all_calls) {
printf("-- OEMCryptoResult OEMCrypto_GetDeviceID(uint8_t* deviceID,\n");
}
std::vector<uint8_t> dev_id_string = crypto_engine->keybox().device_id();
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;
LOGD("[OEMCrypto_GetDeviceId(): success]");
return OEMCrypto_SUCCESS;
}
extern "C"
OEMCryptoResult OEMCrypto_GetKeyData(uint8_t* keyData,
size_t* keyDataLength) {
if (trace_all_calls) {
printf("-- OEMCryptoResult OEMCrypto_GetKeyData(uint8_t* keyData,\n");
}
size_t length = crypto_engine->keybox().key_data_length();
if (*keyDataLength < length) {
*keyDataLength = length;
LOGE("[OEMCrypto_GetKeyData(): ERROR_SHORT_BUFFER]");
return OEMCrypto_ERROR_SHORT_BUFFER;
}
memset(keyData, 0, *keyDataLength);
memcpy(keyData, crypto_engine->keybox().key_data(), length);
*keyDataLength = length;
LOGD("[OEMCrypto_GetKeyData(): success]");
return OEMCrypto_SUCCESS;
}
extern "C"
OEMCryptoResult OEMCrypto_GetRandom(uint8_t* randomData, size_t dataLength) {
if (trace_all_calls) {
printf("-- OEMCryptoResult OEMCrypto_GetRandom(uint8_t* randomData, size_t dataLength) {\n");
}
if (!randomData) {
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
}
if (RAND_bytes(randomData, dataLength)) {
return OEMCrypto_SUCCESS;
}
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
}
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 (trace_all_calls) {
printf("-- OEMCryptoResult OEMCrypto_WrapKeybox(const uint8_t *keybox,\n");
}
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_RewrapDeviceRSAKey(OEMCrypto_SESSION session,
const uint8_t* message,
size_t message_length,
const uint8_t* signature,
size_t signature_length,
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) {
if (trace_all_calls) {
printf("-- OEMCryptoResult OEMCrypto_RewrapDeviceRSAKey()\n");
dump_hex("message", message, message_length);
dump_hex("signature", signature, signature_length);
printf("nonce = %08X;\n", *nonce);
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 (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, 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) {
LOGW("[OEMCrypto_RewrapDeviceRSAKey(): ERROR_KEYBOX_INVALID]");
*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 (NO_ERROR != crypto_engine->ValidateKeybox()) {
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_NO_INVALID_SESSION]");
return OEMCrypto_ERROR_INVALID_SESSION;
}
if (message == NULL || message_length == 0 || signature == NULL
|| signature_length == 0 || 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<uint8_t*>(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_LoadKeys(): OEMCrypto_ERROR_SIGNATURE_FAILURE - range check.]");
return OEMCrypto_ERROR_SIGNATURE_FAILURE;
}
// Validate nonce
if (!session_ctx->CheckNonce(*nonce)) {
return OEMCrypto_ERROR_INVALID_NONCE;
}
session_ctx->FlushNonces();
// Decrypt key and verify signature.
if (!session_ctx->LoadRSAKey(enc_rsa_key, enc_rsa_key_length,
enc_rsa_key_iv, message, message_length,
signature, signature_length)) {
return OEMCrypto_ERROR_SIGNATURE_FAILURE;
// return OEMCrypto_ERROR_INVALID_RSA_KEY;
}
// Now we generate a wrapped keybox.
WrappedRSAKey* wrapped = reinterpret_cast<WrappedRSAKey*>(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<uint8_t> mac_ctx_str(wrapped->context,
wrapped->context + sizeof(wrapped->context));
// Generate mac and encryption keys for encrypting the signature.
if (!session_ctx->DeriveKeys(mac_ctx_str, mac_ctx_str)) {
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
}
size_t sig_length = sizeof(wrapped->signature);
if (!session_ctx->GenerateSignature(wrapped->context, // start signing here.
buffer_size - sizeof(wrapped->signature),
wrapped->signature,
&sig_length)) {
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
}
// Encrypt rsa key with keybox.
if (!session_ctx->EncryptRSAKey(wrapped->enc_rsa_key,
enc_rsa_key_length,
wrapped->iv)) {
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
}
if (trace_all_calls) {
dump_hex("wrapped_rsa_key", wrapped_rsa_key, *wrapped_rsa_key_length);
}
return OEMCrypto_SUCCESS;
}
extern "C"
OEMCryptoResult OEMCrypto_LoadDeviceRSAKey(OEMCrypto_SESSION session,
const uint8_t* wrapped_rsa_key,
size_t wrapped_rsa_key_length) {
if (trace_all_calls) {
printf("-- OEMCryptoResult OEMCrypto_LoadDeviceRSAKey()\n");
dump_hex("wrapped_rsa_key", wrapped_rsa_key, wrapped_rsa_key_length);
}
if (wrapped_rsa_key == NULL) {
LOGE("[OEMCrypto_LoadDeviceRSAKey(): OEMCrypto_ERROR_INVALID_CONTEXT]");
return OEMCrypto_ERROR_INVALID_CONTEXT;
}
if (NO_ERROR != crypto_engine->ValidateKeybox()) {
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_NO_INVALID_SESSION]");
return OEMCrypto_ERROR_INVALID_SESSION;
}
// Now we generate a wrapped keybox.
const WrappedRSAKey* wrapped
= reinterpret_cast<const WrappedRSAKey*>(wrapped_rsa_key);
const std::vector<uint8_t> mac_ctx_str(wrapped->context,
wrapped->context + sizeof(wrapped->context));
// Generate mac and encryption keys for encrypting the signature.
if (!session_ctx->DeriveKeys(mac_ctx_str, mac_ctx_str)) {
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
}
// Decrypt key and verify signature.
if (!session_ctx->LoadRSAKey(wrapped->enc_rsa_key,
wrapped_rsa_key_length - sizeof(WrappedRSAKey),
wrapped->iv,
wrapped->context,
wrapped_rsa_key_length - sizeof(wrapped->signature),
wrapped->signature,
sizeof(wrapped->signature))) {
return OEMCrypto_ERROR_INVALID_RSA_KEY;
}
return OEMCrypto_SUCCESS;
}
extern "C"
OEMCryptoResult OEMCrypto_GenerateRSASignature(OEMCrypto_SESSION session,
const uint8_t* message,
size_t message_length,
uint8_t* signature,
size_t* signature_length) {
if (trace_all_calls) {
printf("-- OEMCryptoResult OEMCrypto_GenerateRSASignature()\n");
dump_hex("message", message, message_length);
}
if (NO_ERROR != crypto_engine->ValidateKeybox()) {
LOGE("[OEMCrypto_GenerateRSASignature(): ERROR_KEYBOX_INVALID]");
return OEMCrypto_ERROR_KEYBOX_INVALID;
}
if (message == NULL || message_length == 0 ||
signature == NULL || 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_NO_INVALID_SESSION]");
return OEMCrypto_ERROR_INVALID_SESSION;
}
if (session_ctx->GenerateRSASignature(message,
message_length,
signature,
signature_length)) {
if (trace_all_calls) {
dump_hex("signature", signature, *signature_length);
}
return OEMCrypto_SUCCESS;
}
return OEMCrypto_ERROR_UNKNOWN_FAILURE;;
}
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 (trace_all_calls) {
printf("-- OEMCryptoResult OEMCrypto_DeriveKeysFromSessionKey(\n");
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 (NO_ERROR != crypto_engine->ValidateKeybox()) {
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_NO_INVALID_SESSION]");
return OEMCrypto_ERROR_INVALID_SESSION;
}
const std::vector<uint8_t> ssn_key_str(enc_session_key,
enc_session_key + enc_session_key_length);
const std::vector<uint8_t> mac_ctx_str(mac_key_context,
mac_key_context + mac_key_context_length);
const std::vector<uint8_t> 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 (trace_all_calls) {
dump_hex("mac_key", &session_ctx->mac_key()[0],
session_ctx->mac_key().size());
dump_hex("enc_key", &session_ctx->encryption_key()[0],
session_ctx->encryption_key().size());
}
return OEMCrypto_SUCCESS;
}
}; // namespace wvoec_mock

View File

@@ -0,0 +1,91 @@
// Copyright 2013 Google Inc. All Rights Reserved.
#include "string_conversions.h"
#include <ctype.h>
#include <iostream>
#include <vector>
#include "log.h"
namespace wvcdm {
static bool CharToDigit(char ch, unsigned char* digit) {
if (ch >= '0' && ch <= '9') {
*digit = ch - '0';
} else {
ch = tolower(ch);
if ((ch >= 'a') && (ch <= 'f')) {
*digit = ch - 'a' + 10;
} else {
return false;
}
}
return true;
}
// converts an ascii hex string(2 bytes per digit) into a decimal byte string
std::vector<uint8_t> a2b_hex(const std::string& byte) {
std::vector<uint8_t> array(0);
unsigned int count = byte.size();
if (count == 0 || (count % 2) != 0) {
LOGE("Invalid input size %u for string %s", count, byte.c_str());
return array;
}
for (unsigned int i = 0; i < count / 2; ++i) {
unsigned char msb = 0; // most significant 4 bits
unsigned char lsb = 0; // least significant 4 bits
if (!CharToDigit(byte[i * 2], &msb) ||
!CharToDigit(byte[i * 2 + 1], &lsb)) {
LOGE("Invalid hex value %c%c at index %d", byte[i*2], byte[i*2+1], i);
return array;
}
array.push_back((msb << 4) | lsb);
}
return array;
}
std::string b2a_hex(const std::vector<uint8_t>& byte) {
return HexEncode(&byte[0], byte.size());
}
std::string HexEncode(const uint8_t* in_buffer, unsigned int size) {
static const char kHexChars[] = "0123456789ABCDEF";
// Each input byte creates two output hex characters.
std::string out_buffer(size * 2, '\0');
for (unsigned int i = 0; i < size; ++i) {
char byte = in_buffer[i];
out_buffer[(i << 1)] = kHexChars[(byte >> 4) & 0xf];
out_buffer[(i << 1) + 1] = kHexChars[byte & 0xf];
}
return out_buffer;
}
std::string IntToString(int value) {
// log10(2) ~= 0.3 bytes needed per bit or per byte log10(2**8) ~= 2.4.
// So round up to allocate 3 output characters per byte, plus 1 for '-'.
const int kOutputBufSize = 3 * sizeof(int) + 1;
char buffer[kOutputBufSize];
memset(buffer, 0, kOutputBufSize);
snprintf(buffer, kOutputBufSize, "%d", value);
std::string out_string(buffer, sizeof(buffer));
return out_string;
}
std::string UintToString(unsigned int value) {
// log10(2) ~= 0.3 bytes needed per bit or per byte log10(2**8) ~= 2.4.
// So round up to allocate 3 output characters per byte.
const int kOutputBufSize = 3 * sizeof(unsigned int);
char buffer[kOutputBufSize];
memset(buffer, 0, kOutputBufSize);
snprintf(buffer, kOutputBufSize, "%u", value);
std::string out_string(buffer, sizeof(buffer));
return out_string;
}
}; // namespace wvcdm

View File

@@ -0,0 +1,20 @@
// Copyright 2013 Google Inc. All Rights Reserved.
#ifndef OEMCRYPTO_STRING_CONVERSIONS_H_
#define OEMCRYPTO_STRING_CONVERSIONS_H_
#include <string>
#include <vector>
namespace wvcdm {
std::vector<uint8_t> a2b_hex(const std::string& b);
std::string b2a_hex(const std::vector<uint8_t>& b);
std::string HexEncode(const uint8_t* bytes, unsigned size);
std::string IntToString(int value);
std::string UintToString(unsigned int value);
}; // namespace wvcdm
#endif // OEMCRYPTO_STRING_CONVERSIONS_H_

View File

@@ -0,0 +1,14 @@
// Copyright 2013 Google Inc. All Rights Reserved.
#ifndef OEMCRYPTO_WV_CDM_CONSTANTS_H_
#define OEMCRYPTO_WV_CDM_CONSTANTS_H_
namespace wvcdm {
static const size_t KEY_CONTROL_SIZE = 16;
static const size_t KEY_IV_SIZE = 16;
static const size_t KEY_PAD_SIZE = 16;
static const size_t KEY_SIZE = 16;
static const size_t MAC_KEY_SIZE = 32;
} // namespace wvcdm
#endif // OEMCRYPTO_WV_CDM_CONSTANTS_H_

View File

@@ -0,0 +1,49 @@
// Copyright 2013 Google Inc. All Rights Reserved.
#ifndef OEMCRYPTO_WV_CDM_TYPES_H_
#define OEMCRYPTO_WV_CDM_TYPES_H_
#include <map>
#include <stdint.h>
#include <string>
namespace wvcdm {
typedef std::string CdmKeySystem;
typedef std::string CdmInitData;
typedef std::string CdmKeyMessage;
typedef std::string CdmKeyResponse;
typedef std::string KeyId;
typedef std::string CdmSessionId;
typedef std::string RequestId;
typedef uint32_t CryptoResult;
typedef uint32_t CryptoSessionId;
typedef std::string CryptoKeyId;
enum CdmResponseType {
NO_ERROR,
UNKNOWN_ERROR,
KEY_ADDED,
KEY_ERROR,
KEY_MESSAGE,
NEED_KEY,
KEY_CANCELED,
};
#define CORE_DISALLOW_COPY_AND_ASSIGN(TypeName) \
TypeName(const TypeName&); \
void operator=(const TypeName&)
enum CdmEventType {
LICENSE_EXPIRED,
LICENSE_RENEWAL_NEEDED
};
// forward class references
class KeyMessage;
class Request;
class Key;
} // namespace wvcdm
#endif // OEMCRYPTO_WV_CDM_TYPES_H_

View File

@@ -0,0 +1,24 @@
// Copyright 2013 Google Inc. All Rights Reserved.
#ifndef WV_KEYBOX_H_
#define WV_KEYBOX_H_
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;
}
#endif // WV_KEYBOX_H_

View File

@@ -0,0 +1,93 @@
/*********************************************************************
* wvcrc32.cpp
*
* (c) Copyright 2011-2012 Google, Inc.
*
* Compte CRC32 Checksum. Needed for verification of WV Keybox.
*********************************************************************/
#include "wvcrc32.h"
#define INIT_CRC32 0xffffffff
uint32_t wvrunningcrc32(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(uint8_t* p_begin, int i_count) {
return(wvrunningcrc32(p_begin, i_count, INIT_CRC32));
}

View File

@@ -0,0 +1,16 @@
/*********************************************************************
* wvcrc32.h
*
* (c) Copyright 2011-2012 Google, Inc.
*
* Compte CRC32 Checksum. Needed for verification of WV Keybox.
*********************************************************************/
#ifndef WV_CRC_32_H_
#define WV_CRC_32_H_
#include <stdint.h>
uint32_t wvcrc32(uint8_t* p_begin, int i_count);
#endif // WV_CRC_32_H_

View File

@@ -0,0 +1,128 @@
# Copyright 2012 Google Inc. All Rights Reserved.
# Author: rkuroiwa@google.com (Rintaro Kuroiwa)
{
'variables': {
'chromium_code': 1,
'oec_target_type': "",
},
'targets': [
{
'target_name': 'oec_lib',
'type': 'none',
'conditions': [
['target_arch=="arm" and oec_target_type != "CAN_INSTALL_KEYBOX"', {
'dependencies': [
'oec_mrvl',
],
'libraries': [
],
}, {
'dependencies': [
'oec_mock',
],
}],
],
},
{
'target_name': 'oec_client',
'type': 'static_library',
'sources': [
'client/oemcrypto_client.h',
'client/oemcrypto_client.cpp',
],
'dependencies': [
'../../../base/base.gyp:base',
],
'include_dirs': [
'client',
'../include/widevine',
'../core/include',
],
},
{
'target_name': 'oec_mock',
'type': 'static_library',
'conditions': [
[ 'use_openssl==1', {
'sources!': [
'mock/src/encryptor_nss.cpp',
],
}, {
'sources!': [
'mock/src/encryptor_openssl.cpp',
],
},],
],
'sources': [
'mock/src/oemcrypto_mock.cpp',
'mock/src/oemcrypto_engine_mock.cpp',
'mock/src/oemcrypto_engine_mock.h',
'mock/src/oemcrypto_key_mock.cpp',
'mock/src/oemcrypto_key_mock.h',
'mock/src/oemcrypto_keybox_mock.cpp',
'mock/src/oemcrypto_keybox_mock.h',
'mock/src/encryptor.h',
'mock/src/encryptor.cpp',
'mock/src/encryptor_nss.cpp',
'mock/src/encryptor_openssl.cpp',
'mock/src/cmac.h',
'mock/src/cmac.c',
],
'dependencies': [
'../../../base/base.gyp:base',
'../../../crypto/crypto.gyp:crypto',
],
'include_dirs': [
'mock/src',
'../include',
'../core/include',
],
},
{
'target_name': 'oec_mrvl',
'type': 'static_library',
'sources': [
'eureka/src/oemcrypto_mrvl.cpp',
],
'dependencies': [
'../../../base/base.gyp:base',
'../../../crypto/crypto.gyp:crypto',
],
'include_dirs': [
'../include',
'../core/include',
],
'cflags': [
'-Wsign-conversion',
],
'link_settings': {
'libraries': [
'-lOSAL',
'-lPEAgent',
],
},
},
{
'target_name': 'oec_unittest',
'type': '<(gtest_target_type)',
'conditions': [
['target_arch!="arm" or oec_target_type == "CAN_INSTALL_KEYBOX"', {
'defines': [ 'CAN_INSTALL_KEYBOX', ],
},],
],
'sources': [
'test/oemcrypto_test.cpp',
],
'include_dirs': [
'../include',
'../../../testing/gtest/include',
],
'dependencies': [
'oec_lib',
'../../../base/base.gyp:base',
'../../../testing/gtest.gyp:gtest',
],
},
],
}

View File

@@ -0,0 +1,39 @@
LOCAL_PATH:= $(call my-dir)
# THIS IS FOR THE MOCK TESTS:
include $(CLEAR_VARS)
LOCAL_SRC_FILES:= \
oemcrypto_test.cpp \
oemcrypto_keybox_test.cpp
LOCAL_MODULE_TAGS := tests
# Define CAN_INSTALL_KEYBOX and the unit test will install a known keybox and test decryption.
LOCAL_CFLAGS += -DCAN_INSTALL_KEYBOX
LOCAL_C_INCLUDES += \
bionic \
external/gtest/include \
external/openssl/include \
external/stlport/stlport \
$(LOCAL_PATH)/../include \
$(LOCAL_PATH)/../mock/src \
LOCAL_STATIC_LIBRARIES := \
libgtest \
libgtest_main \
LOCAL_SHARED_LIBRARIES := \
libcrypto \
libcutils \
libdl \
liblog \
liboemcrypto \
libstlport \
libutils \
libz \
LOCAL_MODULE:=oemcrypto_test
include $(BUILD_EXECUTABLE)

View File

@@ -0,0 +1,109 @@
#
# Builds oemcrypto_unittests
#
#PROJECTS_ROOT = ~projects
#
ifndef PROJECTS_ROOT
PROJECTS_ROOT = ../../../../..
endif
CDM_ROOT = $(PROJECTS_ROOT)/cdm
CDM_SRC_PATH = $(CDM_ROOT)/cdm
CDM_BASE_INCLUDE_PATH = $(CDM_SRC_PATH)/include
EUREKA_ROOT = $(PROJECTS_ROOT)/eureka/eureka
CHROME_ROOT = $(EUREKA_ROOT)/src/chromium/src
#
# build outputs should go into Chrome repository, such as ../chromium/src/out
# or some local equivalent.
# WARNING: splitting outputs from CHROME_ROOT can lead to build errors
ifndef CHROME_ROOT
CHROME_ROOT = $(CDM_ROOT)/out
endif
# TARGET_PLATFORM from {x86,eureka}
ifndef TARGET_PLATFORM
TARGET_PLATFORM = x86
endif
# TARGET_BUILD from {debug,release}
ifndef TARGET_BUILD
TARGET_BUILD = debug
endif
ifeq ($(TARGET_PLATFORM),x86)
BUILDPLATFORM = out_x86_linux
else ifeq ($(TARGET_PLATFORM),eureka)
BUILDPLATFORM = out_arm_eureka
else
BUILDPLATFORM = UNKNOWN
endif
ifeq ($(TARGET_BUILD),debug)
BUILDTYPE = Debug
else ifeq ($(TARGET_BUILD),release)
BUILDTYPE = Release
else
BUILDTYPE = UNKNOWN
endif
BUILDPATH = $(CHROME_ROOT)/$(BUILDPLATFORM)/$(BUILDTYPE)
OBJPATH = $(BUILDPATH)/obj
CHROME_THIRD_PARTY_LIBS = $(BUILDPATH)/obj/third_party
# target image file name
TARGET_TEST_EXE = oemcrypto_unittests
TARGET_OBJECTS = oemcrypto_test.o
OBJECTDIR = $(OBJPATH)/oemcrypto_unittests
INSTALLDIR = $(BUILDPATH)
LIBGTEST_INCLUDE = $(CDM_SRC_PATH)/prebuilt/gtest/include
LIBGTEST_LIBS = $(CDM_SRC_PATH)/prebuilt/gtest/$(BUILDPLATFORM)/$(BUILDTYPE)/lib
LIBGTEST_LIBNAME = gtest
INCLUDES = \
-I$(LIBGTEST_INCLUDE) \
-I$(CDM_BASE_INCLUDE_PATH)
LIBDIRS = \
-L$(INSTALLDIR) \
-L$(LIBGTEST_LIBS)
OBJECTS := $(patsubst %.o,$(OBJECTDIR)/%.o,$(TARGET_OBJECTS))
CXXFLAGS = -m64 -fPIC -W -Wall -g -DCDM_TEST
LINK = $(CXX)
MKDIR = mkdir -p
$(INSTALLDIR)/$(TARGET_TEST_EXE): $(OBJECTDIR) $(INSTALLDIR) $(OBJECTS)
$(CXX) -v -fPIC -m64 $(OBJECTS) $(LIBDIRS) -loemcrypto_mock \
-lcrypto -ldl -lrt -lpthread -l$(LIBGTEST_LIBNAME) -o $@
@echo "[Unit test image: " $(INSTALLDIR)/$(TARGET_TEST_EXE) "]"
$(OBJECTDIR)/%.o: %.cpp
$(CXX) -c $(CXXFLAGS) $(INCLUDES) $< -o $@
$(OBJECTDIR)/%.o: %.cc
$(CXX) -c $(CXXFLAGS) $(INCLUDES) $< -o $@
clean:
$(RM) -rf $(OBJECTDIR)
$(RM) -rf $(INSTALLDIR)/$(TARGET_TEST_EXE)
$(OBJECTDIR):
@$(MKDIR) $@
$(INSTALLDIR):
@$(MKDIR) $@
.PHONY: $(OBJECTDIR)
.PHONY: $(INSTALLDIR)
.PHONY: clean
.PHONY: test

View File

@@ -0,0 +1,170 @@
// Copyright 2013 Google Inc. All Rights Reserved.
//
// OEMCrypto unit tests
//
#include <gtest/gtest.h>
#include <map>
#include <stdint.h>
#include <string>
#include <sys/types.h>
#include "OEMCryptoCENC.h"
#include "string_conversions.h"
#include "wv_cdm_constants.h"
#include "wv_keybox.h"
using namespace std;
namespace wvoec {
static 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 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,
}
};
// Define CAN_INSTALL_KEYBOX if you are compiling with the reference
// implementation of OEMCrypto, or if your version of OEMCrypto supports
// OEMCrypto_InstallKeybox and OEMCrypto_WrapKeybox.
#if defined(CAN_INSTALL_KEYBOX)
// The Below tests are based on a specific keybox which is installed for testing.
class OEMCryptoKeyboxTest : public ::testing::Test {
protected:
virtual void SetUp() {
ASSERT_EQ(OEMCrypto_SUCCESS, OEMCrypto_Initialize())
<< "OEMCrypto_Initialize failed.";
}
void install_keybox(wvoec_mock::WidevineKeybox& keybox) {
OEMCryptoResult sts;
uint8_t wrapped[sizeof(wvoec_mock::WidevineKeybox)];
size_t length = sizeof(wvoec_mock::WidevineKeybox);
sts = OEMCrypto_WrapKeybox(reinterpret_cast<uint8_t*>(&keybox),
sizeof(keybox),
wrapped,
&length,
NULL, 0);
ASSERT_EQ(OEMCrypto_SUCCESS, sts);
sts = OEMCrypto_InstallKeybox(wrapped, sizeof(keybox));
ASSERT_EQ(OEMCrypto_SUCCESS, sts);
}
virtual void TearDown() {
ASSERT_EQ(OEMCrypto_SUCCESS, OEMCrypto_Terminate())
<< "OEMCrypto_Terminate failed.";
}
public:
};
TEST_F(OEMCryptoKeyboxTest, DefaultKeybox) {
OEMCryptoResult sts;
sts = OEMCrypto_IsKeyboxValid();
ASSERT_EQ(OEMCrypto_SUCCESS, sts);
}
TEST_F(OEMCryptoKeyboxTest, GoodKeybox) {
wvoec_mock::WidevineKeybox keybox = kValidKeybox02;
OEMCryptoResult sts;
install_keybox(keybox);
sts = OEMCrypto_IsKeyboxValid();
ASSERT_EQ(OEMCrypto_SUCCESS, sts);
keybox = kValidKeybox03;
install_keybox(keybox);
sts = OEMCrypto_IsKeyboxValid();
ASSERT_EQ(OEMCrypto_SUCCESS, sts);
}
TEST_F(OEMCryptoKeyboxTest, BadCRCKeybox) {
wvoec_mock::WidevineKeybox keybox = kValidKeybox02;
keybox.crc_[1] = 42;
OEMCryptoResult sts;
install_keybox(keybox);
sts = OEMCrypto_IsKeyboxValid();
ASSERT_EQ(OEMCrypto_ERROR_BAD_CRC, sts);
}
TEST_F(OEMCryptoKeyboxTest, BadMagicKeybox) {
wvoec_mock::WidevineKeybox keybox = kValidKeybox02;
keybox.magic_[1] = 42;
OEMCryptoResult sts;
install_keybox(keybox);
sts = OEMCrypto_IsKeyboxValid();
ASSERT_EQ(OEMCrypto_ERROR_BAD_MAGIC, sts);
}
TEST_F(OEMCryptoKeyboxTest, BadDataKeybox) {
wvoec_mock::WidevineKeybox keybox = kValidKeybox02;
keybox.data_[1] = 42;
OEMCryptoResult sts;
install_keybox(keybox);
sts = OEMCrypto_IsKeyboxValid();
ASSERT_EQ(OEMCrypto_ERROR_BAD_CRC, sts);
}
#endif // CAN_INSTALL_KEYBOX
} // namespace wvoec

File diff suppressed because it is too large Load Diff