/******************************************************************************* * * Copyright 2013 Google Inc. All Rights Reserved. * * mock implementation of OEMCrypto APIs * ******************************************************************************/ #include "OEMCryptoCENC.h" #include #include #include #include #include #include "log.h" #include "oemcrypto_engine_mock.h" #include "openssl/rand.h" #include "openssl/sha.h" #include "wv_cdm_constants.h" namespace wvoec_mock { // TODO(fredgc): These are in the level 3 directory, but they are entry points for // both level 3 and level 1. That is confusing. Fix it. typedef OEMCryptoResult (*L1_Initialize_t)(void); typedef OEMCryptoResult (*L1_Terminate_t)(void); typedef OEMCryptoResult (*L1_OpenSession_t)(OEMCrypto_SESSION *session); typedef OEMCryptoResult (*L1_CloseSession_t)(OEMCrypto_SESSION session); typedef OEMCryptoResult (*L1_GenerateDerivedKeys_t)(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); typedef OEMCryptoResult (*L1_GenerateNonce_t)(OEMCrypto_SESSION session, uint32_t* nonce); typedef OEMCryptoResult (*L1_GenerateSignature_t)(OEMCrypto_SESSION session, const uint8_t* message, size_t message_length, uint8_t* signature, size_t* signature_length); typedef OEMCryptoResult (*L1_LoadKeys_t)(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); typedef OEMCryptoResult (*L1_RefreshKeys_t)(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); typedef OEMCryptoResult (*L1_SelectKey_t)(const OEMCrypto_SESSION session, const uint8_t* key_id, size_t key_id_length); typedef OEMCryptoResult (*L1_DecryptCTR_t)(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); typedef OEMCryptoResult (*L1_InstallKeybox_t)(const uint8_t *keybox, size_t keyBoxLength); typedef OEMCryptoResult (*L1_IsKeyboxValid_t)(void); typedef OEMCryptoResult (*L1_GetDeviceID_t)(uint8_t* deviceID, size_t *idLength); typedef OEMCryptoResult (*L1_GetKeyData_t)(uint8_t* keyData, size_t *keyDataLength); typedef OEMCryptoResult (*L1_GetRandom_t)(uint8_t* randomData, size_t dataLength); typedef OEMCryptoResult (*L1_WrapKeybox_t)(const uint8_t *keybox, size_t keyBoxLength, uint8_t *wrappedKeybox, size_t *wrappedKeyBoxLength, const uint8_t *transportKey, size_t transportKeyLength); typedef OEMCryptoResult (*L1_RewrapDeviceRSAKey_t)(OEMCrypto_SESSION session, const uint8_t* message, size_t message_length, const uint8_t* signature, size_t signature_length, const uint32_t *nonce, const uint8_t* enc_rsa_key, size_t enc_rsa_key_length, const uint8_t* enc_rsa_key_iv, uint8_t* wrapped_rsa_key, size_t *wrapped_rsa_key_length); typedef OEMCryptoResult (*L1_LoadDeviceRSAKey_t)(OEMCrypto_SESSION session, const uint8_t* wrapped_rsa_key, size_t wrapped_rsa_key_length); typedef OEMCryptoResult (*L1_GenerateRSASignature_t)(OEMCrypto_SESSION session, const uint8_t* message, size_t message_length, uint8_t* signature, size_t *signature_length); typedef OEMCryptoResult (*L1_DeriveKeysFromSessionKey_t)(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); typedef OEMCryptoResult (*L1_Generic_Encrypt_t)(OEMCrypto_SESSION session, const uint8_t* in_buffer, size_t buffer_length, const uint8_t* iv, OEMCrypto_Algorithm algorithm, uint8_t* out_buffer); typedef OEMCryptoResult (*L1_Generic_Decrypt_t)(OEMCrypto_SESSION session, const uint8_t* in_buffer, size_t buffer_length, const uint8_t* iv, OEMCrypto_Algorithm algorithm, uint8_t* out_buffer); typedef OEMCryptoResult (*L1_Generic_Sign_t)(OEMCrypto_SESSION session, const uint8_t* in_buffer, size_t buffer_length, OEMCrypto_Algorithm algorithm, uint8_t* signature, size_t* signature_length); typedef OEMCryptoResult (*L1_Generic_Verify_t)(OEMCrypto_SESSION session, const uint8_t* in_buffer, size_t buffer_length, OEMCrypto_Algorithm algorithm, const uint8_t* signature, size_t signature_length); typedef uint8_t (*L1_APIVersion_t)(); typedef const char* (*L1_SecurityLevel_t)(); struct FunctionPointers { void* library; L1_Initialize_t OEMCrypto_Initialize; L1_Terminate_t OEMCrypto_Terminate; L1_OpenSession_t OEMCrypto_OpenSession; L1_CloseSession_t OEMCrypto_CloseSession; L1_GenerateDerivedKeys_t OEMCrypto_GenerateDerivedKeys; L1_GenerateNonce_t OEMCrypto_GenerateNonce; L1_GenerateSignature_t OEMCrypto_GenerateSignature; L1_LoadKeys_t OEMCrypto_LoadKeys; L1_RefreshKeys_t OEMCrypto_RefreshKeys; L1_SelectKey_t OEMCrypto_SelectKey; L1_DecryptCTR_t OEMCrypto_DecryptCTR; L1_InstallKeybox_t OEMCrypto_InstallKeybox; L1_IsKeyboxValid_t OEMCrypto_IsKeyboxValid; L1_GetDeviceID_t OEMCrypto_GetDeviceID; L1_GetKeyData_t OEMCrypto_GetKeyData; L1_GetRandom_t OEMCrypto_GetRandom; L1_WrapKeybox_t OEMCrypto_WrapKeybox; L1_RewrapDeviceRSAKey_t OEMCrypto_RewrapDeviceRSAKey; L1_LoadDeviceRSAKey_t OEMCrypto_LoadDeviceRSAKey; L1_GenerateRSASignature_t OEMCrypto_GenerateRSASignature; L1_DeriveKeysFromSessionKey_t OEMCrypto_DeriveKeysFromSessionKey; L1_APIVersion_t OEMCrypto_APIVersion; L1_SecurityLevel_t OEMCrypto_SecurityLevel; L1_Generic_Encrypt_t OEMCrypto_Generic_Encrypt; L1_Generic_Decrypt_t OEMCrypto_Generic_Decrypt; L1_Generic_Sign_t OEMCrypto_Generic_Sign; L1_Generic_Verify_t OEMCrypto_Generic_Verify; }; static struct FunctionPointers level1; #define QUOTE_DEFINE(A) #A #define QUOTE(A) QUOTE_DEFINE(A) #define LOOKUP(Type, Name) \ level1.Name = (Type)dlsym(level1.library, QUOTE(Name)); \ if (!level1.Name) { \ dll_valid = 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 CryptoEngine* crypto_engine = NULL; extern "C" OEMCryptoResult OEMCrypto_Initialize(void) { crypto_engine = new CryptoEngine; if (!crypto_engine || !crypto_engine->Initialized()) { LOGE("[OEMCrypto_Initialize(): failed]"); return OEMCrypto_ERROR_INIT_FAILED; } LOGD("OEMCrypto_Initialize Level 3 success. Now I will try to load Level 1"); level1.library = dlopen("liboemcrypto.so", RTLD_NOW); if (level1.library == NULL) { LOGW("Could not load liboemcrypto.so. Falling Back to L3."); return OEMCrypto_SUCCESS; } bool dll_valid = true; LOOKUP(L1_Initialize_t, OEMCrypto_Initialize); LOOKUP(L1_Terminate_t, OEMCrypto_Terminate); LOOKUP(L1_OpenSession_t, OEMCrypto_OpenSession); LOOKUP(L1_CloseSession_t, OEMCrypto_CloseSession); LOOKUP(L1_GenerateDerivedKeys_t, OEMCrypto_GenerateDerivedKeys); LOOKUP(L1_GenerateNonce_t, OEMCrypto_GenerateNonce); LOOKUP(L1_GenerateSignature_t, OEMCrypto_GenerateSignature); LOOKUP(L1_LoadKeys_t, OEMCrypto_LoadKeys); LOOKUP(L1_RefreshKeys_t, OEMCrypto_RefreshKeys); LOOKUP(L1_SelectKey_t, OEMCrypto_SelectKey); LOOKUP(L1_DecryptCTR_t, OEMCrypto_DecryptCTR); LOOKUP(L1_InstallKeybox_t, OEMCrypto_InstallKeybox); LOOKUP(L1_IsKeyboxValid_t, OEMCrypto_IsKeyboxValid); LOOKUP(L1_GetDeviceID_t, OEMCrypto_GetDeviceID); LOOKUP(L1_GetKeyData_t, OEMCrypto_GetKeyData); LOOKUP(L1_GetRandom_t, OEMCrypto_GetRandom); LOOKUP(L1_WrapKeybox_t, OEMCrypto_WrapKeybox); // TODO(fredgc): Move the validity check from here to below after we have // an L1 library that matches current version. if (!dll_valid) { dlclose(level1.library); level1.library = NULL; LOGW("Could not load functions from liboemcrypto.so. Falling Back to L3."); return OEMCrypto_SUCCESS; } LOOKUP(L1_RewrapDeviceRSAKey_t, OEMCrypto_RewrapDeviceRSAKey); LOOKUP(L1_LoadDeviceRSAKey_t, OEMCrypto_LoadDeviceRSAKey); LOOKUP(L1_GenerateRSASignature_t, OEMCrypto_GenerateRSASignature); LOOKUP(L1_DeriveKeysFromSessionKey_t, OEMCrypto_DeriveKeysFromSessionKey); LOOKUP(L1_APIVersion_t, OEMCrypto_APIVersion); LOOKUP(L1_SecurityLevel_t, OEMCrypto_SecurityLevel); LOOKUP(L1_Generic_Decrypt_t, OEMCrypto_Generic_Decrypt); LOOKUP(L1_Generic_Encrypt_t, OEMCrypto_Generic_Encrypt); LOOKUP(L1_Generic_Sign_t, OEMCrypto_Generic_Sign); LOOKUP(L1_Generic_Verify_t, OEMCrypto_Generic_Verify); // TODO(fredgc): Move the validity check from above to here after we have // a current L1 library. OEMCryptoResult st = level1.OEMCrypto_Initialize(); if (st != OEMCrypto_SUCCESS) { LOGW("Could not initialize liboemcrypto.so. Falling Back to L3."); dlclose(level1.library); level1.library = NULL; return OEMCrypto_SUCCESS; } if (level1.OEMCrypto_APIVersion ) { uint32_t level1_version = level1.OEMCrypto_APIVersion(); if( level1_version > oec_latest_version ) { // Check for foward jump. LOGW("liboemcrypto.so is version %d, not %d. Falling Back to L3.", level1_version, oec_latest_version); dlclose(level1.library); level1.library = NULL; return OEMCrypto_SUCCESS; } } LOGD("OEMCrypto_Initialize Level 1 success. I will use level 1."); return OEMCrypto_SUCCESS; } extern "C" OEMCryptoResult OEMCrypto_Terminate(void) { if (level1.library) { OEMCryptoResult st = level1.OEMCrypto_Terminate(); dlclose(level1.library); level1.library = NULL; return st; } 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 (level1.library) { return level1.OEMCrypto_OpenSession(session); } 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 (level1.library) { return level1.OEMCrypto_CloseSession(session); } 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 (level1.library) { return level1.OEMCrypto_GenerateNonce(session, nonce); } 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(&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; 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 (level1.library) { return level1.OEMCrypto_GenerateDerivedKeys(session, mac_key_context, mac_key_context_length, enc_key_context, 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 mac_ctx_str(mac_key_context, mac_key_context + mac_key_context_length); const std::vector enc_ctx_str(enc_key_context, enc_key_context + enc_key_context_length); // Generate mac and encryption keys for current session context if (!session_ctx->DeriveKeys(mac_ctx_str, enc_ctx_str)) { return OEMCrypto_ERROR_UNKNOWN_FAILURE; } 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 (level1.library) { return level1.OEMCrypto_GenerateSignature( session, message, message_length, signature, signature_length); } if (NO_ERROR != crypto_engine->ValidateKeybox()) { LOGE("[OEMCrypto_GenerateSignature(): ERROR_KEYBOX_INVALID]"); return OEMCrypto_ERROR_KEYBOX_INVALID; } if (*signature_length < SHA256_DIGEST_LENGTH) { *signature_length = SHA256_DIGEST_LENGTH; return OEMCrypto_ERROR_SHORT_BUFFER; } if (message == NULL || message_length == 0 || signature == NULL || signature_length == 0) { LOGE("[OEMCrypto_GenerateSignature(): OEMCrypto_ERROR_INVALID_CONTEXT]"); return OEMCrypto_ERROR_INVALID_CONTEXT; } SessionContext* session_ctx = crypto_engine->FindSession(session); if (!session_ctx || !session_ctx->isValid()) { LOGE("[OEMCrypto_GenerateSignature(): ERROR_NO_INVALID_SESSION]"); return OEMCrypto_ERROR_INVALID_SESSION; } if (session_ctx->GenerateSignature(message, message_length, 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 (level1.library) { return level1.OEMCrypto_LoadKeys(session, message, message_length, signature, signature_length, enc_mac_key_iv, enc_mac_key, num_keys, key_array); } 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 key_id; std::vector enc_key_data; std::vector key_data_iv; std::vector key_control; std::vector key_control_iv; for (unsigned int i = 0; i < num_keys; i++) { key_id.assign(key_array[i].key_id, key_array[i].key_id + key_array[i].key_id_length); enc_key_data.assign(key_array[i].key_data, key_array[i].key_data + 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 enc_mac_key_str = std::vector( enc_mac_key, enc_mac_key + wvcdm::MAC_KEY_SIZE); const std::vector enc_mac_key_iv_str = std::vector( enc_mac_key_iv, enc_mac_key_iv + wvcdm::KEY_IV_SIZE); if (!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 (level1.library) { return level1.OEMCrypto_RefreshKeys(session, message, message_length, signature, signature_length, num_keys, key_array); } 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 key_id; std::vector key_control; std::vector 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 (level1.library) { return level1.OEMCrypto_SelectKey(session, 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 key_id_str = std::vector(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 (level1.library) { return level1.OEMCrypto_DecryptCTR( session, data_addr, data_length, is_encrypted, iv, offset, out_buffer); } 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 iv_v(iv, iv + 16); std::vector 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 (level1.library) { return level1.OEMCrypto_InstallKeybox(keybox, keyBoxLength); } if (crypto_engine->keybox().InstallKeybox(keybox, keyBoxLength)) { return OEMCrypto_SUCCESS; } return OEMCrypto_ERROR_WRITE_KEYBOX; } extern "C" OEMCryptoResult OEMCrypto_IsKeyboxValid(void) { if (level1.library) { return level1.OEMCrypto_IsKeyboxValid(); } 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 (level1.library) { return level1.OEMCrypto_GetDeviceID(deviceID, idLength); } std::vector 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 (level1.library) { return level1.OEMCrypto_GetKeyData(keyData, keyDataLength); } 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 (level1.library) { return level1.OEMCrypto_GetRandom(randomData, dataLength); } 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 (level1.library) { return level1.OEMCrypto_WrapKeybox(keybox, keyBoxLength, wrappedKeybox, wrappedKeyBoxLength, transportKey, transportKeyLength); } 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, const uint32_t* nonce, const uint8_t* enc_rsa_key, size_t enc_rsa_key_length, const uint8_t* enc_rsa_key_iv, uint8_t* wrapped_rsa_key, size_t* wrapped_rsa_key_length) { if (level1.library) { if (!level1.OEMCrypto_RewrapDeviceRSAKey ) { return OEMCrypto_ERROR_NOT_IMPLEMENTED; } return level1.OEMCrypto_RewrapDeviceRSAKey( session, message, message_length, signature, signature_length, nonce, enc_rsa_key, enc_rsa_key_length, enc_rsa_key_iv, wrapped_rsa_key, wrapped_rsa_key_length); } if (wrapped_rsa_key_length == NULL) { LOGE("[OEMCrypto_RewrapDeviceRSAKey(): OEMCrypto_ERROR_INVALID_CONTEXT]"); return OEMCrypto_ERROR_INVALID_CONTEXT; } // For the reference implementation, the wrapped key and the encrypted // key are the same size -- just encrypted with different keys. // We add 32 bytes for a context, 32 for iv, and 32 bytes for a signature. // Important: This layout must match OEMCrypto_LoadDeviceRSAKey below. size_t buffer_size = enc_rsa_key_length + sizeof(WrappedRSAKey); if (wrapped_rsa_key == NULL || *wrapped_rsa_key_length < buffer_size) { LOGW("[OEMCrypto_RewrapDeviceRSAKey(): Wrapped Keybox Short Buffer]"); *wrapped_rsa_key_length = buffer_size; return OEMCrypto_ERROR_SHORT_BUFFER; } *wrapped_rsa_key_length = buffer_size; // Tell caller how much space we used. if (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(nonce), sizeof(uint32_t), true) || !RangeCheck(message, message_length, enc_rsa_key, enc_rsa_key_length, true) || !RangeCheck(message, message_length, enc_rsa_key_iv, wvcdm::KEY_IV_SIZE, true)) { LOGE("[OEMCrypto_RewrapDeviceRSAKey(): - range check.]"); return OEMCrypto_ERROR_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(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 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; } // Encrypt rsa key with keybox. if (!session_ctx->EncryptRSAKey(wrapped->enc_rsa_key, enc_rsa_key_length, wrapped->iv)) { 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; } return OEMCrypto_SUCCESS; } extern "C" OEMCryptoResult OEMCrypto_LoadDeviceRSAKey(OEMCrypto_SESSION session, const uint8_t* wrapped_rsa_key, size_t wrapped_rsa_key_length) { if (level1.library) { if (!level1.OEMCrypto_LoadDeviceRSAKey ) { return OEMCrypto_ERROR_NOT_IMPLEMENTED; } return level1.OEMCrypto_LoadDeviceRSAKey( session, wrapped_rsa_key, wrapped_rsa_key_length); } const WrappedRSAKey* wrapped = reinterpret_cast(wrapped_rsa_key); 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; } const std::vector 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 (level1.library) { if (!level1.OEMCrypto_GenerateRSASignature ) { return OEMCrypto_ERROR_NOT_IMPLEMENTED; } return level1.OEMCrypto_GenerateRSASignature(session, message, message_length, signature, signature_length); } if (NO_ERROR != crypto_engine->ValidateKeybox()) { LOGE("[OEMCrypto_GenerateRSASignature(): ERROR_KEYBOX_INVALID]"); return OEMCrypto_ERROR_KEYBOX_INVALID; } if (signature_length == 0) { LOGE("[OEMCrypto_GenerateRSASignature(): OEMCrypto_ERROR_INVALID_CONTEXT]"); return OEMCrypto_ERROR_INVALID_CONTEXT; } SessionContext* session_ctx = crypto_engine->FindSession(session); if (!session_ctx || !session_ctx->isValid()) { LOGE("[OEMCrypto_GenerateRSASignature(): ERROR_NO_INVALID_SESSION]"); return OEMCrypto_ERROR_INVALID_SESSION; } size_t required_size = session_ctx->RSASignatureSize(); if (*signature_length < required_size) { *signature_length = required_size; return OEMCrypto_ERROR_SHORT_BUFFER; } if (message == NULL || message_length == 0 || signature == NULL || signature_length == 0) { LOGE("[OEMCrypto_GenerateRSASignature(): OEMCrypto_ERROR_INVALID_CONTEXT]"); return OEMCrypto_ERROR_INVALID_CONTEXT; } if (session_ctx->GenerateRSASignature(message, message_length, 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 (level1.library) { if (!level1.OEMCrypto_DeriveKeysFromSessionKey ) { return OEMCrypto_ERROR_NOT_IMPLEMENTED; } return level1.OEMCrypto_DeriveKeysFromSessionKey(session, enc_session_key, enc_session_key_length, mac_key_context, mac_key_context_length, enc_key_context, 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 ssn_key_str(enc_session_key, enc_session_key + enc_session_key_length); const std::vector mac_ctx_str(mac_key_context, mac_key_context + mac_key_context_length); const std::vector enc_ctx_str(enc_key_context, enc_key_context + enc_key_context_length); // Generate mac and encryption keys for current session context if (!session_ctx->RSADeriveKeys(ssn_key_str, mac_ctx_str, enc_ctx_str)) { return OEMCrypto_ERROR_UNKNOWN_FAILURE; } return OEMCrypto_SUCCESS; } extern "C" uint32_t OEMCrypto_APIVersion() { if (level1.library) { if (!level1.OEMCrypto_APIVersion ) { return 5; } return level1.OEMCrypto_APIVersion(); } return oec_latest_version; } extern "C" const char* OEMCrypto_SecurityLevel() { if (level1.library) { if (!level1.OEMCrypto_SecurityLevel ) { return "Unknown"; } return level1.OEMCrypto_SecurityLevel(); } return "L3"; } extern "C" OEMCryptoResult OEMCrypto_Generic_Encrypt(OEMCrypto_SESSION session, const uint8_t* in_buffer, size_t buffer_length, const uint8_t* iv, OEMCrypto_Algorithm algorithm, uint8_t* out_buffer) { if (level1.library) { if (!level1.OEMCrypto_Generic_Encrypt ) { return OEMCrypto_ERROR_NOT_IMPLEMENTED; } return level1.OEMCrypto_Generic_Encrypt(session, in_buffer, buffer_length, iv, algorithm, out_buffer); } return OEMCrypto_ERROR_NOT_IMPLEMENTED; } extern "C" OEMCryptoResult OEMCrypto_Generic_Decrypt(OEMCrypto_SESSION session, const uint8_t* in_buffer, size_t buffer_length, const uint8_t* iv, OEMCrypto_Algorithm algorithm, uint8_t* out_buffer) { if (level1.library) { if (!level1.OEMCrypto_Generic_Decrypt ) { return OEMCrypto_ERROR_NOT_IMPLEMENTED; } return level1.OEMCrypto_Generic_Decrypt(session, in_buffer, buffer_length, iv, algorithm, out_buffer); } return OEMCrypto_ERROR_NOT_IMPLEMENTED; } extern "C" OEMCryptoResult OEMCrypto_Generic_Sign(OEMCrypto_SESSION session, const uint8_t* in_buffer, size_t buffer_length, OEMCrypto_Algorithm algorithm, uint8_t* signature, size_t* signature_length) { if (level1.library) { if (!level1.OEMCrypto_Generic_Sign ) { return OEMCrypto_ERROR_NOT_IMPLEMENTED; } return level1.OEMCrypto_Generic_Sign(session, in_buffer, buffer_length, algorithm, signature, signature_length); } return OEMCrypto_ERROR_NOT_IMPLEMENTED; } extern "C" OEMCryptoResult OEMCrypto_Generic_Verify(OEMCrypto_SESSION session, const uint8_t* in_buffer, size_t buffer_length, OEMCrypto_Algorithm algorithm, const uint8_t* signature, size_t signature_length) { if (level1.library) { if (!level1.OEMCrypto_Generic_Verify ) { return OEMCrypto_ERROR_NOT_IMPLEMENTED; } return level1.OEMCrypto_Generic_Verify(session, in_buffer, buffer_length, algorithm, signature, signature_length); } return OEMCrypto_ERROR_NOT_IMPLEMENTED; } }; // namespace wvoec_mock