Upgrade to version 2.1 of license protocol in OEMCrypto. related-to-bug: 8621521 Merge of https://widevine-internal-review.googlesource.com/#/c/4952/ from Widevine CDM repository to android repository. Change-Id: I0d85dae1981b7525ab17aec5f21cf668d078bf47
1309 lines
54 KiB
C++
1309 lines
54 KiB
C++
/*******************************************************************************
|
|
*
|
|
* Copyright 2013 Google Inc. All Rights Reserved.
|
|
*
|
|
* Wrapper for Level 1 OEMCrypto or Level 3 Fallback APIs
|
|
*
|
|
******************************************************************************/
|
|
|
|
#include "OEMCryptoCENC.h"
|
|
|
|
#include <dlfcn.h>
|
|
#include <stdio.h>
|
|
#include <iostream>
|
|
#include <cstring>
|
|
#include <string>
|
|
|
|
#include "log.h"
|
|
#include "oemcrypto_engine_mock.h"
|
|
#include "openssl/cmac.h"
|
|
#include "openssl/evp.h"
|
|
#include "openssl/hmac.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,
|
|
uint8_t subsample_flags);
|
|
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<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;
|
|
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<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(crypto_engine->keybox().device_key().value(),
|
|
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_keys,
|
|
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_keys,
|
|
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_keys,
|
|
2*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,
|
|
key_array[i].key_data_length, false) ||
|
|
!RangeCheck(message, message_length, key_array[i].key_data_iv,
|
|
wvcdm::KEY_IV_SIZE, false) ||
|
|
!RangeCheck(message, message_length, key_array[i].key_control,
|
|
wvcdm::KEY_CONTROL_SIZE, false) ||
|
|
!RangeCheck(message, message_length, key_array[i].key_control_iv,
|
|
wvcdm::KEY_IV_SIZE, false)) {
|
|
LOGE("[OEMCrypto_LoadKeys(): OEMCrypto_ERROR_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.
|
|
bool status = true;
|
|
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 + key_array[i].key_data_length);
|
|
key_data_iv.assign(key_array[i].key_data_iv,
|
|
key_array[i].key_data_iv + wvcdm::KEY_IV_SIZE);
|
|
if (key_array[i].key_control == NULL) {
|
|
status = false;
|
|
break;
|
|
}
|
|
key_control.assign(key_array[i].key_control,
|
|
key_array[i].key_control + wvcdm::KEY_CONTROL_SIZE);
|
|
key_control_iv.assign(key_array[i].key_control_iv,
|
|
key_array[i].key_control_iv + wvcdm::KEY_IV_SIZE);
|
|
|
|
if (!session_ctx->InstallKey(key_id, enc_key_data, key_data_iv, key_control,
|
|
key_control_iv)) {
|
|
status = false;
|
|
break;
|
|
}
|
|
}
|
|
|
|
session_ctx->FlushNonces();
|
|
if (!status) return OEMCrypto_ERROR_UNKNOWN_FAILURE;
|
|
|
|
// enc_mac_key can be NULL if license renewal is not supported
|
|
if (enc_mac_keys == NULL) return OEMCrypto_SUCCESS;
|
|
|
|
// V2.1 license protocol: update mac keys after processing license response
|
|
const std::vector<uint8_t> enc_mac_keys_str = std::vector<uint8_t>(
|
|
enc_mac_keys, enc_mac_keys + 2*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->UpdateMacKeys(enc_mac_keys_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 ||
|
|
num_keys == 0) {
|
|
LOGE("[OEMCrypto_RefreshKeys(): OEMCrypto_ERROR_INVALID_CONTEXT]");
|
|
return OEMCrypto_ERROR_INVALID_CONTEXT;
|
|
}
|
|
|
|
// Range check
|
|
for (unsigned int i = 0; i < num_keys; i++) {
|
|
if (!RangeCheck(message, message_length, key_array[i].key_id,
|
|
key_array[i].key_id_length, true) ||
|
|
!RangeCheck(message, message_length, key_array[i].key_control,
|
|
wvcdm::KEY_CONTROL_SIZE, false) ||
|
|
!RangeCheck(message, message_length, key_array[i].key_control_iv,
|
|
wvcdm::KEY_IV_SIZE, true)) {
|
|
LOGE("[OEMCrypto_RefreshKeys(): Range Check %d]", i);
|
|
return OEMCrypto_ERROR_SIGNATURE_FAILURE;
|
|
}
|
|
}
|
|
|
|
// Validate message signature
|
|
if (!session_ctx->ValidateMessage(message, message_length,
|
|
signature, signature_length)) {
|
|
LOGE("[OEMCrypto_RefreshKeys(): signature was invalid]");
|
|
return OEMCrypto_ERROR_SIGNATURE_FAILURE;
|
|
}
|
|
|
|
// Decrypt and refresh keys in key refresh object
|
|
bool status = true;
|
|
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++) {
|
|
if (key_array[i].key_id != NULL) {
|
|
key_id.assign(key_array[i].key_id,
|
|
key_array[i].key_id + key_array[i].key_id_length);
|
|
key_control.assign(key_array[i].key_control,
|
|
key_array[i].key_control + wvcdm::KEY_CONTROL_SIZE);
|
|
if (key_array[i].key_control_iv == NULL ) {
|
|
key_control_iv.clear();
|
|
} else {
|
|
key_control_iv.assign(key_array[i].key_control_iv,
|
|
key_array[i].key_control_iv + wvcdm::KEY_IV_SIZE);
|
|
}
|
|
} else {
|
|
// key_id could be null if special control key type
|
|
// key_control is not encrypted in this case
|
|
key_id.clear();
|
|
key_control_iv.clear();
|
|
key_control.assign(key_array[i].key_control,
|
|
key_array[i].key_control + wvcdm::KEY_CONTROL_SIZE);
|
|
}
|
|
|
|
if (!session_ctx->RefreshKey(key_id, key_control, key_control_iv)) {
|
|
LOGE("[OEMCrypto_RefreshKeys(): error in key %i]", i);
|
|
status = false;
|
|
break;
|
|
}
|
|
}
|
|
|
|
session_ctx->FlushNonces();
|
|
if (!status) 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<uint8_t> key_id_vec = std::vector<uint8_t>(key_id, key_id + key_id_length);
|
|
if (!session_ctx->SelectContentKey(key_id_vec)) {
|
|
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 block_offset,
|
|
const OEMCrypto_DestBufferDesc* out_buffer,
|
|
uint8_t subsample_flags) {
|
|
if (level1.library) {
|
|
return level1.OEMCrypto_DecryptCTR( session, data_addr, data_length,
|
|
is_encrypted, iv, block_offset,
|
|
out_buffer, subsample_flags);
|
|
}
|
|
wvoec_mock::BufferType buffer_type = kBufferTypeDirect;
|
|
uint8_t* 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 = ((uint8_t*)out_buffer->buffer.secure.handle
|
|
+ out_buffer->buffer.secure.offset);
|
|
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;
|
|
}
|
|
|
|
#ifndef NDEBUG
|
|
if (NO_ERROR != crypto_engine->ValidateKeybox()) {
|
|
LOGE("[OEMCrypto_DecryptCTR(): ERROR_KEYBOX_INVALID]");
|
|
return OEMCrypto_ERROR_KEYBOX_INVALID;
|
|
}
|
|
#endif
|
|
|
|
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;
|
|
}
|
|
|
|
if (!crypto_engine->DecryptCTR(session_ctx, iv, (int)block_offset,
|
|
data_addr, data_length, is_encrypted,
|
|
destination, buffer_type)) {
|
|
LOGE("[OEMCrypto_DecryptCTR(): OEMCrypto_ERROR_DECRYPT_FAILED]");
|
|
return OEMCrypto_ERROR_DECRYPT_FAILED;
|
|
}
|
|
|
|
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<uint8_t> dev_id_vec = crypto_engine->keybox().device_id();
|
|
if (dev_id_vec.empty()) {
|
|
LOGE("[OEMCrypto_GetDeviceId(): Keybox Invalid]");
|
|
return OEMCrypto_ERROR_KEYBOX_INVALID;
|
|
}
|
|
|
|
size_t dev_id_len = dev_id_vec.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_vec[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<const 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_RewrapDeviceRSAKey(): - range check.]");
|
|
return OEMCrypto_ERROR_SIGNATURE_FAILURE;
|
|
}
|
|
|
|
|
|
// Validate nonce
|
|
if (!session_ctx->CheckNonce(*nonce)) {
|
|
return OEMCrypto_ERROR_INVALID_NONCE;
|
|
}
|
|
session_ctx->FlushNonces();
|
|
|
|
// Decrypt RSA key.
|
|
uint8_t* pkcs8_rsa_key = new uint8_t[enc_rsa_key_length];
|
|
OEMCryptoResult result = OEMCrypto_SUCCESS;
|
|
if (!session_ctx->DecryptRSAKey(enc_rsa_key, enc_rsa_key_length,
|
|
enc_rsa_key_iv, pkcs8_rsa_key)) {
|
|
result = OEMCrypto_ERROR_INVALID_RSA_KEY;
|
|
}
|
|
size_t padding = pkcs8_rsa_key[enc_rsa_key_length - 1];
|
|
if( result == OEMCrypto_SUCCESS) {
|
|
if (padding > 16) {
|
|
LOGE("[RewrapRSAKey(): Encrypted RSA has bad padding: %d]", padding);
|
|
result = OEMCrypto_ERROR_INVALID_RSA_KEY;
|
|
}
|
|
}
|
|
size_t rsa_key_length = enc_rsa_key_length - padding;
|
|
// verify signature, verify RSA key, and load it.
|
|
if( result == OEMCrypto_SUCCESS) {
|
|
if (!session_ctx->LoadRSAKey(pkcs8_rsa_key, rsa_key_length,
|
|
message, message_length,
|
|
signature, signature_length)) {
|
|
result = 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( result == OEMCrypto_SUCCESS) {
|
|
if (!RAND_bytes(wrapped->context, sizeof(wrapped->context))) {
|
|
result = OEMCrypto_ERROR_UNKNOWN_FAILURE;
|
|
}
|
|
if (!RAND_bytes(wrapped->iv, sizeof(wrapped->iv))) {
|
|
result = OEMCrypto_ERROR_UNKNOWN_FAILURE;
|
|
}
|
|
}
|
|
const std::vector<uint8_t> context(wrapped->context,
|
|
wrapped->context + sizeof(wrapped->context));
|
|
// Generate mac and encryption keys for encrypting the signature.
|
|
if( result == OEMCrypto_SUCCESS) {
|
|
if (!session_ctx->DeriveKeys(crypto_engine->keybox().device_key().value(),
|
|
context, context)) {
|
|
result = OEMCrypto_ERROR_UNKNOWN_FAILURE;
|
|
}
|
|
}
|
|
|
|
// Encrypt rsa key with keybox.
|
|
if( result == OEMCrypto_SUCCESS) {
|
|
if (!session_ctx->EncryptRSAKey(pkcs8_rsa_key, enc_rsa_key_length,
|
|
wrapped->iv, wrapped->enc_rsa_key)) {
|
|
result = OEMCrypto_ERROR_UNKNOWN_FAILURE;
|
|
}
|
|
}
|
|
delete[] pkcs8_rsa_key;
|
|
|
|
// The wrapped keybox must be signed with the same key we verify with. I'll
|
|
// pick the server key, so I don't have to modify LoadRSAKey.
|
|
if( result == OEMCrypto_SUCCESS) {
|
|
size_t sig_length = sizeof(wrapped->signature);
|
|
if (!HMAC(EVP_sha256(), &session_ctx->mac_key_server()[0],
|
|
SHA256_DIGEST_LENGTH, wrapped->context,
|
|
buffer_size - sizeof(wrapped->signature), wrapped->signature,
|
|
&sig_length)) {
|
|
result = OEMCrypto_ERROR_UNKNOWN_FAILURE;
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
|
|
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);
|
|
}
|
|
if (wrapped_rsa_key == NULL) {
|
|
LOGE("[OEMCrypto_LoadDeviceRSAKey(): OEMCrypto_ERROR_INVALID_CONTEXT]");
|
|
return OEMCrypto_ERROR_INVALID_CONTEXT;
|
|
}
|
|
const WrappedRSAKey* wrapped
|
|
= reinterpret_cast<const WrappedRSAKey*>(wrapped_rsa_key);
|
|
|
|
if (NO_ERROR != crypto_engine->ValidateKeybox()) {
|
|
LOGE("[OEMCrypto_LoadDeviceRSAKey(): ERROR_KEYBOX_INVALID]");
|
|
return OEMCrypto_ERROR_KEYBOX_INVALID;
|
|
}
|
|
|
|
SessionContext* session_ctx = crypto_engine->FindSession(session);
|
|
if (!session_ctx || !session_ctx->isValid()) {
|
|
LOGE("[OEMCrypto_LoadDeviceRSAKey(): ERROR_NO_INVALID_SESSION]");
|
|
return OEMCrypto_ERROR_INVALID_SESSION;
|
|
}
|
|
const std::vector<uint8_t> context(wrapped->context,
|
|
wrapped->context + sizeof(wrapped->context));
|
|
// Generate mac and encryption keys for encrypting the signature.
|
|
if (!session_ctx->DeriveKeys(crypto_engine->keybox().device_key().value(),
|
|
context, context)) {
|
|
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
|
|
}
|
|
// Decrypt RSA key.
|
|
uint8_t* pkcs8_rsa_key = new uint8_t[wrapped_rsa_key_length
|
|
- sizeof(wrapped->signature)];
|
|
size_t enc_rsa_key_length = wrapped_rsa_key_length - sizeof(WrappedRSAKey);
|
|
OEMCryptoResult result = OEMCrypto_SUCCESS;
|
|
if (!session_ctx->DecryptRSAKey(wrapped->enc_rsa_key, enc_rsa_key_length,
|
|
wrapped->iv, pkcs8_rsa_key)) {
|
|
result = OEMCrypto_ERROR_INVALID_RSA_KEY;
|
|
}
|
|
size_t padding = pkcs8_rsa_key[enc_rsa_key_length - 1];
|
|
if( result == OEMCrypto_SUCCESS) {
|
|
if (padding > 16) {
|
|
LOGE("[LoadDeviceRSAKey(): Encrypted RSA has bad padding: %d]", padding);
|
|
result = OEMCrypto_ERROR_INVALID_RSA_KEY;
|
|
}
|
|
}
|
|
size_t rsa_key_length = enc_rsa_key_length - padding;
|
|
// verify signature.
|
|
if( result == OEMCrypto_SUCCESS) {
|
|
if (!session_ctx->LoadRSAKey(pkcs8_rsa_key, rsa_key_length,
|
|
wrapped->context,
|
|
wrapped_rsa_key_length - sizeof(wrapped->signature),
|
|
wrapped->signature,
|
|
sizeof(wrapped->signature))) {
|
|
result = OEMCrypto_ERROR_SIGNATURE_FAILURE;
|
|
// return OEMCrypto_ERROR_INVALID_RSA_KEY;
|
|
}
|
|
}
|
|
delete[] pkcs8_rsa_key;
|
|
return result;
|
|
}
|
|
|
|
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<uint8_t> ssn_key_vec(enc_session_key,
|
|
enc_session_key + enc_session_key_length);
|
|
const std::vector<uint8_t> mac_ctx_vec(mac_key_context,
|
|
mac_key_context + mac_key_context_length);
|
|
const std::vector<uint8_t> enc_ctx_vec(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_vec, mac_ctx_vec, enc_ctx_vec)) {
|
|
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);
|
|
}
|
|
if (NO_ERROR != crypto_engine->ValidateKeybox()) {
|
|
LOGE("[OEMCrypto_Generic_Enrypt(): ERROR_KEYBOX_INVALID]");
|
|
return OEMCrypto_ERROR_KEYBOX_INVALID;
|
|
}
|
|
SessionContext* session_ctx = crypto_engine->FindSession(session);
|
|
if (!session_ctx || !session_ctx->isValid()) {
|
|
LOGE("[OEMCrypto_Generic_Enrypt(): ERROR_NO_INVALID_SESSION]");
|
|
return OEMCrypto_ERROR_INVALID_SESSION;
|
|
}
|
|
if (in_buffer == NULL || buffer_length == 0 ||
|
|
iv == NULL || out_buffer == NULL) {
|
|
LOGE("[OEMCrypto_Generic_Enrypt(): OEMCrypto_ERROR_INVALID_CONTEXT]");
|
|
return OEMCrypto_ERROR_INVALID_CONTEXT;
|
|
}
|
|
if (!session_ctx->Generic_Encrypt(in_buffer, buffer_length, iv, algorithm,
|
|
out_buffer)) {
|
|
LOGE("[OEMCrypto_Generic_Enrypt(): OEMCrypto_ERROR_UNKNOWN_FAILURE]");
|
|
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
|
|
}
|
|
return OEMCrypto_SUCCESS;
|
|
}
|
|
|
|
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);
|
|
}
|
|
if (NO_ERROR != crypto_engine->ValidateKeybox()) {
|
|
LOGE("[OEMCrypto_Generic_Decrypt(): ERROR_KEYBOX_INVALID]");
|
|
return OEMCrypto_ERROR_KEYBOX_INVALID;
|
|
}
|
|
SessionContext* session_ctx = crypto_engine->FindSession(session);
|
|
if (!session_ctx || !session_ctx->isValid()) {
|
|
LOGE("[OEMCrypto_Generic_Decrypt(): ERROR_NO_INVALID_SESSION]");
|
|
return OEMCrypto_ERROR_INVALID_SESSION;
|
|
}
|
|
if (!session_ctx->Generic_Decrypt(in_buffer, buffer_length, iv, algorithm,
|
|
out_buffer)) {
|
|
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
|
|
}
|
|
if (in_buffer == NULL || buffer_length == 0 ||
|
|
iv == NULL || out_buffer == NULL) {
|
|
LOGE("[OEMCrypto_Generic_Decrypt(): OEMCrypto_ERROR_INVALID_CONTEXT]");
|
|
return OEMCrypto_ERROR_INVALID_CONTEXT;
|
|
}
|
|
return OEMCrypto_SUCCESS;
|
|
}
|
|
|
|
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);
|
|
}
|
|
if (NO_ERROR != crypto_engine->ValidateKeybox()) {
|
|
LOGE("[OEMCrypto_Generic_Sign(): ERROR_KEYBOX_INVALID]");
|
|
return OEMCrypto_ERROR_KEYBOX_INVALID;
|
|
}
|
|
SessionContext* session_ctx = crypto_engine->FindSession(session);
|
|
if (!session_ctx || !session_ctx->isValid()) {
|
|
LOGE("[OEMCrypto_Generic_Sign(): ERROR_NO_INVALID_SESSION]");
|
|
return OEMCrypto_ERROR_INVALID_SESSION;
|
|
}
|
|
if (*signature_length < SHA256_DIGEST_LENGTH) {
|
|
*signature_length = SHA256_DIGEST_LENGTH;
|
|
return OEMCrypto_ERROR_SHORT_BUFFER;
|
|
}
|
|
if (in_buffer == NULL || buffer_length == 0 || signature == NULL) {
|
|
LOGE("[OEMCrypto_Generic_Sign(): OEMCrypto_ERROR_INVALID_CONTEXT]");
|
|
return OEMCrypto_ERROR_INVALID_CONTEXT;
|
|
}
|
|
if (!session_ctx->Generic_Sign(in_buffer, buffer_length, algorithm,
|
|
signature, signature_length)) {
|
|
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
|
|
}
|
|
return OEMCrypto_SUCCESS;
|
|
}
|
|
|
|
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);
|
|
}
|
|
if (NO_ERROR != crypto_engine->ValidateKeybox()) {
|
|
LOGE("[OEMCrypto_Generic_Verify(): ERROR_KEYBOX_INVALID]");
|
|
return OEMCrypto_ERROR_KEYBOX_INVALID;
|
|
}
|
|
SessionContext* session_ctx = crypto_engine->FindSession(session);
|
|
if (!session_ctx || !session_ctx->isValid()) {
|
|
LOGE("[OEMCrypto_Generic_Verify(): ERROR_NO_INVALID_SESSION]");
|
|
return OEMCrypto_ERROR_INVALID_SESSION;
|
|
}
|
|
if (signature_length != SHA256_DIGEST_LENGTH) {
|
|
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
|
|
}
|
|
if (in_buffer == NULL || buffer_length == 0 || signature == NULL) {
|
|
LOGE("[OEMCrypto_Generic_Verify(): OEMCrypto_ERROR_INVALID_CONTEXT]");
|
|
return OEMCrypto_ERROR_INVALID_CONTEXT;
|
|
}
|
|
if (!session_ctx->Generic_Verify(in_buffer, buffer_length, algorithm,
|
|
signature, signature_length)) {
|
|
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
|
|
}
|
|
return OEMCrypto_SUCCESS;
|
|
}
|
|
|
|
}; // namespace wvoec_mock
|