/******************************************************************************* * * Copyright 2013 Google Inc. All Rights Reserved. * * mock implementation of OEMCrypto APIs * ******************************************************************************/ #include "L3CryptoCENC.h" #include #include #include #include #include "log.h" #include "l3crypto_engine_mock.h" #include "openssl/rand.h" #include "wv_cdm_constants.h" namespace wvoec_obfs { static CryptoEngine* crypto_engine = NULL; // Set this to true when you are generating test vectors. const bool trace_all_calls = false; 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[%d].%s = NULL;\n", array.c_str(), index, name.c_str()); return; } printf("std::string s%d_", index); dump_hex(name, vector, length); printf("%s[%d].%s = message_ptr + message.find(s%d_%s.data());\n", array.c_str(), index, name.c_str(), index, name.c_str()); } extern "C" OEMCryptoResult L3Crypto_Initialize(void) { if (trace_all_calls) { printf("------------------------- L3Crypto_Initialize(void)\n"); } crypto_engine = new CryptoEngine; if (!crypto_engine || !crypto_engine->Initialized()) { LOGE("[L3Crypto_Initialize(): failed]"); return OEMCrypto_ERROR_INIT_FAILED; } LOGD("[L3Crypto_Initialize(): success]"); return OEMCrypto_SUCCESS; } extern "C" OEMCryptoResult L3Crypto_Terminate(void) { if (trace_all_calls) { printf("----------------- OEMCryptoResult L3Crypto_Terminate(void)\n"); } if (!crypto_engine) { LOGE("[L3Crypto_Terminate(): failed]"); return OEMCrypto_ERROR_TERMINATE_FAILED; } if (crypto_engine->Initialized()) { crypto_engine->Terminate(); } delete crypto_engine; crypto_engine = NULL; LOGD("[L3Crypto_Terminate(): success]"); return OEMCrypto_SUCCESS; } extern "C" OEMCryptoResult L3Crypto_OpenSession(OEMCrypto_SESSION* session) { if (trace_all_calls) { printf("-- OEMCryptoResult L3Crypto_OpenSession(OEMCrypto_SESSION *session)\n"); } SessionId sid = crypto_engine->CreateSession(); *session = (OEMCrypto_SESSION)sid; LOGD("[L3Crypto_OpenSession(): SID=%08x]", sid); return OEMCrypto_SUCCESS; } extern "C" OEMCryptoResult L3Crypto_CloseSession(OEMCrypto_SESSION session) { if (trace_all_calls) { printf("-- OEMCryptoResult L3Crypto_CloseSession(OEMCrypto_SESSION session)\n"); } if (!crypto_engine->DestroySession((SessionId)session)) { LOGD("[L3Crypto_CloseSession(SID=%08X): failed]", session); return OEMCrypto_ERROR_CLOSE_SESSION_FAILED; } else { LOGD("[L3Crypto_CloseSession(SID=%08X): success]", session); return OEMCrypto_SUCCESS; } } extern "C" OEMCryptoResult L3Crypto_GenerateNonce(OEMCrypto_SESSION session, uint32_t* nonce) { if (trace_all_calls) { printf("-- OEMCryptoResult L3Crypto_GenerateNonce(OEMCrypto_SESSION session,\n"); } SessionContext* session_ctx = crypto_engine->FindSession(session); if (!session_ctx || !session_ctx->isValid()) { LOGE("[L3Crypto_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; if (trace_all_calls) { printf("nonce = %08x\n", nonce_value); } return OEMCrypto_SUCCESS; } extern "C" 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) { if (trace_all_calls) { printf("-- OEMCryptoResult L3Crypto_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("[L3Crypto_GenerateDerivedKeys(): ERROR_KEYBOX_INVALID]"); return OEMCrypto_ERROR_KEYBOX_INVALID; } SessionContext* session_ctx = crypto_engine->FindSession(session); if (!session_ctx || !session_ctx->isValid()) { LOGE("[L3Crypto_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 L3Crypto_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 L3Crypto_GenerateSignature(\n"); dump_hex("message", message, message_length); } if (NO_ERROR != crypto_engine->ValidateKeybox()) { LOGE("[L3Crypto_GenerateSignature(): ERROR_KEYBOX_INVALID]"); return OEMCrypto_ERROR_KEYBOX_INVALID; } if (message == NULL || message_length == 0 || signature == NULL || signature_length == 0) { LOGE("[L3Crypto_GenerateSignature(): OEMCrypto_ERROR_INVALID_CONTEXT]"); return OEMCrypto_ERROR_INVALID_CONTEXT; } SessionContext* session_ctx = crypto_engine->FindSession(session); if (!session_ctx || !session_ctx->isValid()) { LOGE("[L3Crypto_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 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) { if (trace_all_calls) { printf("-- OEMCryptoResult L3Crypto_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[%d].key_id_length=%d;\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("[L3Crypto_LoadKeys(): ERROR_KEYBOX_INVALID]"); return OEMCrypto_ERROR_KEYBOX_INVALID; } SessionContext* session_ctx = crypto_engine->FindSession(session); if (!session_ctx || !session_ctx->isValid()) { LOGE("[L3Crypto_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("[L3Crypto_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("[L3Crypto_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("[L3Crypto_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; } // Decrypt and install keys in key object 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; } } // 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 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) { if (trace_all_calls) { printf("-- OEMCryptoResult L3Crypto_RefreshKeys(OEMCrypto_SESSION session,\n"); } if (NO_ERROR != crypto_engine->ValidateKeybox()) { LOGE("[L3Crypto_RefreshKeys(): ERROR_KEYBOX_INVALID]"); return OEMCrypto_ERROR_KEYBOX_INVALID; } SessionContext* session_ctx = crypto_engine->FindSession(session); if (!session_ctx || !session_ctx->isValid()) { LOGE("[L3Crypto_RefreshKeys(): ERROR_NO_INVALID_SESSION]"); return OEMCrypto_ERROR_INVALID_SESSION; } if (message == NULL || message_length == 0 || signature == NULL || signature_length == 0) { LOGE("[L3Crypto_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("[L3Crypto_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; } } return OEMCrypto_SUCCESS; } extern "C" OEMCryptoResult L3Crypto_SelectKey(const OEMCrypto_SESSION session, const uint8_t* key_id, size_t key_id_length) { if (trace_all_calls) { printf("-- OEMCryptoResult L3Crypto_SelectKey(const OEMCrypto_SESSION session,\n"); dump_hex("key_id", key_id, key_id_length); } if (NO_ERROR != crypto_engine->ValidateKeybox()) { LOGE("[L3Crypto_SelectKey(): ERROR_KEYBOX_INVALID]"); return OEMCrypto_ERROR_KEYBOX_INVALID; } SessionContext* session_ctx = crypto_engine->FindSession(session); if (!session_ctx || !session_ctx->isValid()) { LOGE("[L3Crypto_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("[L3Crypto_SelectKey(): FAIL]"); return OEMCrypto_ERROR_NO_CONTENT_KEY; } return OEMCrypto_SUCCESS; } extern "C" 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) { if (trace_all_calls) { printf("-- OEMCryptoResult L3Crypto_DecryptCTR(OEMCrypto_SESSION session,\n"); } wvoec_obfs::BufferType buffer_type = BUFFER_TYPE_DIRECT; void *destination = NULL; size_t max_length = 0; switch (out_buffer->type) { case OEMCrypto_BufferType_Clear: buffer_type = BUFFER_TYPE_CLEAR; destination = out_buffer->buffer.clear.address; max_length = out_buffer->buffer.clear.max_length; break; case OEMCrypto_BufferType_Secure: buffer_type = BUFFER_TYPE_SECURE; destination = out_buffer->buffer.secure.handle; max_length = out_buffer->buffer.secure.max_length; break; default: case OEMCrypto_BufferType_Direct: buffer_type = BUFFER_TYPE_DIRECT; destination = NULL; break; } if (buffer_type != BUFFER_TYPE_DIRECT && max_length < data_length) { LOGE("[L3Crypto_DecryptCTR(): OEMCrypto_ERROR_SHORT_BUFFER]"); return OEMCrypto_ERROR_SHORT_BUFFER; } if (NO_ERROR != crypto_engine->ValidateKeybox()) { LOGE("[L3Crypto_DecryptCTR(): ERROR_KEYBOX_INVALID]"); return OEMCrypto_ERROR_KEYBOX_INVALID; } SessionContext* session_ctx = crypto_engine->FindSession(session); if (!session_ctx || !session_ctx->isValid()) { LOGE("[L3Crypto_DecryptCTR(): ERROR_NO_INVALID_SESSION]"); return OEMCrypto_ERROR_INVALID_SESSION; } if (data_addr == NULL || data_length == 0 || iv == NULL || out_buffer == NULL) { LOGE("[L3Crypto_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("[L3Crypto_DecryptCTR(): OEMCrypto_ERROR_INVALID_CONTEXT]"); return OEMCrypto_ERROR_INVALID_CONTEXT; } return OEMCrypto_SUCCESS; } extern "C" OEMCryptoResult L3Crypto_InstallKeybox(const uint8_t* keybox, size_t keyBoxLength) { if (trace_all_calls) { printf("-- OEMCryptoResult L3Crypto_InstallKeybox(const uint8_t *keybox,\n"); } if (crypto_engine->keybox().InstallKeybox(keybox, keyBoxLength)) { return OEMCrypto_SUCCESS; } return OEMCrypto_ERROR_WRITE_KEYBOX; } extern "C" OEMCryptoResult L3Crypto_IsKeyboxValid(void) { if (trace_all_calls) { printf("-- OEMCryptoResult L3Crypto_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 L3Crypto_GetDeviceID(uint8_t* deviceID, size_t* idLength) { if (trace_all_calls) { printf("-- OEMCryptoResult L3Crypto_GetDeviceID(uint8_t* deviceID,\n"); } std::vector dev_id_string = crypto_engine->keybox().device_id(); if (dev_id_string.empty()) { LOGE("[L3Crypto_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("[L3Crypto_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("[L3Crypto_GetDeviceId(): success]"); return OEMCrypto_SUCCESS; } extern "C" OEMCryptoResult L3Crypto_GetKeyData(uint8_t* keyData, size_t* keyDataLength) { if (trace_all_calls) { printf("-- OEMCryptoResult L3Crypto_GetKeyData(uint8_t* keyData,\n"); } size_t length = crypto_engine->keybox().key_data_length(); if (*keyDataLength < length) { *keyDataLength = length; LOGE("[L3Crypto_GetKeyData(): ERROR_SHORT_BUFFER]"); return OEMCrypto_ERROR_SHORT_BUFFER; } memset(keyData, 0, *keyDataLength); memcpy(keyData, crypto_engine->keybox().key_data(), length); *keyDataLength = length; LOGD("[L3Crypto_GetKeyData(): success]"); return OEMCrypto_SUCCESS; } extern "C" OEMCryptoResult L3Crypto_GetRandom(uint8_t* randomData, size_t dataLength) { if (trace_all_calls) { printf("-- OEMCryptoResult L3Crypto_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 L3Crypto_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 L3Crypto_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; } }; // namespace wvoec_obfs