You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
803 lines
27 KiB
803 lines
27 KiB
/*
|
|
* Copyright (C) 2012 The Android Open Source Project
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License"); you
|
|
* may not use this file except in compliance with the License. You may
|
|
* obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
|
* implied. See the License for the specific language governing
|
|
* permissions and limitations under the License.
|
|
*/
|
|
|
|
#include <errno.h>
|
|
#include <string.h>
|
|
#include <stdint.h>
|
|
|
|
#include <memory>
|
|
|
|
#include <hardware/hardware.h>
|
|
#include <hardware/keymaster0.h>
|
|
|
|
#include <openssl/evp.h>
|
|
#include <openssl/bio.h>
|
|
#include <openssl/rsa.h>
|
|
#include <openssl/err.h>
|
|
#include <openssl/x509.h>
|
|
|
|
#include <linux/ioctl.h>
|
|
#include <linux/msm_ion.h>
|
|
#include <sys/mman.h>
|
|
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <stddef.h>
|
|
#include <unistd.h>
|
|
#include <dirent.h>
|
|
#include <fcntl.h>
|
|
|
|
#include <sys/types.h>
|
|
#include <sys/stat.h>
|
|
#include <dlfcn.h>
|
|
|
|
#include "QSEEComAPI.h"
|
|
#include "keymaster_qcom.h"
|
|
|
|
// For debugging
|
|
//#define LOG_NDEBUG 0
|
|
|
|
#define LOG_TAG "QCOMKeyMaster"
|
|
#include <cutils/log.h>
|
|
struct qcom_km_ion_info_t {
|
|
int32_t ion_fd;
|
|
int32_t ifd_data_fd;
|
|
struct ion_handle_data ion_alloc_handle;
|
|
unsigned char * ion_sbuffer;
|
|
uint32_t sbuf_len;
|
|
};
|
|
|
|
struct qcom_keymaster_handle {
|
|
struct QSEECom_handle *qseecom;
|
|
void *libhandle;
|
|
int (*QSEECom_start_app)(struct QSEECom_handle ** handle, char* path,
|
|
char* appname, uint32_t size);
|
|
int (*QSEECom_shutdown_app)(struct QSEECom_handle **handle);
|
|
int (*QSEECom_send_cmd)(struct QSEECom_handle* handle, void *cbuf,
|
|
uint32_t clen, void *rbuf, uint32_t rlen);
|
|
int (*QSEECom_send_modified_cmd)(struct QSEECom_handle* handle, void *cbuf,
|
|
uint32_t clen, void *rbuf, uint32_t rlen,
|
|
struct QSEECom_ion_fd_info *ihandle);
|
|
int (*QSEECom_set_bandwidth)(struct QSEECom_handle* handle, bool high);
|
|
};
|
|
typedef struct qcom_keymaster_handle qcom_keymaster_handle_t;
|
|
|
|
struct EVP_PKEY_Delete {
|
|
void operator()(EVP_PKEY* p) const {
|
|
EVP_PKEY_free(p);
|
|
}
|
|
};
|
|
typedef std::unique_ptr<EVP_PKEY, EVP_PKEY_Delete> Unique_EVP_PKEY;
|
|
|
|
struct RSA_Delete {
|
|
void operator()(RSA* p) const {
|
|
RSA_free(p);
|
|
}
|
|
};
|
|
typedef std::unique_ptr<RSA, RSA_Delete> Unique_RSA;
|
|
|
|
typedef std::unique_ptr<keymaster0_device_t> Unique_keymaster_device_t;
|
|
|
|
/**
|
|
* Many OpenSSL APIs take ownership of an argument on success but don't free the argument
|
|
* on failure. This means we need to tell our scoped pointers when we've transferred ownership,
|
|
* without triggering a warning by not using the result of release().
|
|
*/
|
|
#define OWNERSHIP_TRANSFERRED(obj) \
|
|
typeof (obj.release()) _dummy __attribute__((unused)) = obj.release()
|
|
|
|
static int qcom_km_get_keypair_public(const keymaster0_device_t* dev,
|
|
const uint8_t* keyBlob, const size_t keyBlobLength,
|
|
uint8_t** x509_data, size_t* x509_data_length) {
|
|
|
|
struct qcom_km_key_blob * keyblob_ptr = (struct qcom_km_key_blob *)keyBlob;
|
|
|
|
if (x509_data == NULL || x509_data_length == NULL) {
|
|
ALOGE("Output public key buffer == NULL");
|
|
return -1;
|
|
}
|
|
|
|
if (keyBlob == NULL) {
|
|
ALOGE("Supplied key blob was NULL");
|
|
return -1;
|
|
}
|
|
|
|
// Should be large enough for keyblob data:
|
|
if (keyBlobLength < (sizeof(qcom_km_key_blob_t))) {
|
|
ALOGE("key blob appears to be truncated");
|
|
return -1;
|
|
}
|
|
|
|
if (keyblob_ptr->magic_num != KM_MAGIC_NUM) {
|
|
ALOGE("Cannot read key; it was not made by this keymaster");
|
|
return -1;
|
|
}
|
|
|
|
if (keyblob_ptr->public_exponent_size == 0 ) {
|
|
ALOGE("Key blob appears to have incorrect exponent length");
|
|
return -1;
|
|
}
|
|
if (keyblob_ptr->modulus_size == 0 ) {
|
|
ALOGE("Key blob appears to have incorrect modulus length");
|
|
return -1;
|
|
}
|
|
|
|
Unique_RSA rsa(RSA_new());
|
|
if (rsa.get() == NULL) {
|
|
ALOGE("Could not allocate RSA structure");
|
|
return -1;
|
|
}
|
|
|
|
rsa->n = BN_bin2bn(reinterpret_cast<const unsigned char*>(keyblob_ptr->modulus),
|
|
keyblob_ptr->modulus_size, NULL);
|
|
if (rsa->n == NULL) {
|
|
ALOGE("Failed to initialize modulus");
|
|
return -1;
|
|
}
|
|
|
|
rsa->e = BN_bin2bn(reinterpret_cast<const unsigned char*>(&keyblob_ptr->public_exponent),
|
|
keyblob_ptr->public_exponent_size, NULL);
|
|
if (rsa->e == NULL) {
|
|
ALOGE("Failed to initialize public exponent");
|
|
return -1;
|
|
}
|
|
|
|
Unique_EVP_PKEY pkey(EVP_PKEY_new());
|
|
if (pkey.get() == NULL) {
|
|
ALOGE("Could not allocate EVP_PKEY structure");
|
|
return -1;
|
|
}
|
|
if (EVP_PKEY_assign_RSA(pkey.get(), rsa.get()) != 1) {
|
|
ALOGE("Failed to assign rsa parameters \n");
|
|
return -1;
|
|
}
|
|
OWNERSHIP_TRANSFERRED(rsa);
|
|
|
|
int len = i2d_PUBKEY(pkey.get(), NULL);
|
|
if (len <= 0) {
|
|
ALOGE("Len returned is < 0 len = %d", len);
|
|
return -1;
|
|
}
|
|
|
|
std::unique_ptr<unsigned char[]> key(new unsigned char[len]);
|
|
if (key.get() == NULL) {
|
|
ALOGE("Could not allocate memory for public key data");
|
|
return -1;
|
|
}
|
|
|
|
unsigned char* tmp = key.get();
|
|
if (i2d_PUBKEY(pkey.get(), &tmp) != len) {
|
|
ALOGE("Len 2 returned is < 0 len = %d", len);
|
|
return -1;
|
|
}
|
|
*x509_data_length = len;
|
|
*x509_data = key.release();
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int32_t qcom_km_ION_memalloc(struct qcom_km_ion_info_t *handle,
|
|
uint32_t size)
|
|
{
|
|
int32_t ret = 0;
|
|
int32_t iret = 0;
|
|
int32_t fd = 0;
|
|
unsigned char *v_addr;
|
|
struct ion_allocation_data ion_alloc_data;
|
|
int32_t ion_fd;
|
|
int32_t rc;
|
|
struct ion_fd_data ifd_data;
|
|
struct ion_handle_data handle_data;
|
|
|
|
/* open ION device for memory management
|
|
* O_DSYNC -> uncached memory
|
|
*/
|
|
if(handle == NULL){
|
|
ALOGE("Error:: null handle received");
|
|
return -1;
|
|
}
|
|
ion_fd = open("/dev/ion", O_RDONLY | O_DSYNC);
|
|
if (ion_fd < 0) {
|
|
ALOGE("Error::Cannot open ION device");
|
|
return -1;
|
|
}
|
|
handle->ion_sbuffer = NULL;
|
|
handle->ifd_data_fd = 0;
|
|
|
|
/* Size of allocation */
|
|
ion_alloc_data.len = (size + 4095) & (~4095);
|
|
|
|
/* 4K aligned */
|
|
ion_alloc_data.align = 4096;
|
|
|
|
/* memory is allocated from EBI heap */
|
|
ion_alloc_data.ION_HEAP_MASK = ION_HEAP(ION_QSECOM_HEAP_ID);
|
|
|
|
/* Set the memory to be uncached */
|
|
ion_alloc_data.flags = 0;
|
|
|
|
/* IOCTL call to ION for memory request */
|
|
rc = ioctl(ion_fd, ION_IOC_ALLOC, &ion_alloc_data);
|
|
if (rc) {
|
|
ret = -1;
|
|
goto alloc_fail;
|
|
}
|
|
|
|
if (ion_alloc_data.handle != NULL) {
|
|
ifd_data.handle = ion_alloc_data.handle;
|
|
} else {
|
|
ret = -1;
|
|
goto alloc_fail;
|
|
}
|
|
/* Call MAP ioctl to retrieve the ifd_data.fd file descriptor */
|
|
rc = ioctl(ion_fd, ION_IOC_MAP, &ifd_data);
|
|
if (rc) {
|
|
ret = -1;
|
|
goto ioctl_fail;
|
|
}
|
|
|
|
/* Make the ion mmap call */
|
|
v_addr = (unsigned char *)mmap(NULL, ion_alloc_data.len,
|
|
PROT_READ | PROT_WRITE,
|
|
MAP_SHARED, ifd_data.fd, 0);
|
|
if (v_addr == MAP_FAILED) {
|
|
ALOGE("Error::ION MMAP failed");
|
|
ret = -1;
|
|
goto map_fail;
|
|
}
|
|
handle->ion_fd = ion_fd;
|
|
handle->ifd_data_fd = ifd_data.fd;
|
|
handle->ion_sbuffer = v_addr;
|
|
handle->ion_alloc_handle.handle = ion_alloc_data.handle;
|
|
handle->sbuf_len = size;
|
|
return ret;
|
|
|
|
map_fail:
|
|
if (handle->ion_sbuffer != NULL) {
|
|
iret = munmap(handle->ion_sbuffer, ion_alloc_data.len);
|
|
if (iret)
|
|
ALOGE("Error::Failed to unmap memory for load image. ret = %d", ret);
|
|
}
|
|
|
|
ioctl_fail:
|
|
handle_data.handle = ion_alloc_data.handle;
|
|
if (handle->ifd_data_fd)
|
|
close(handle->ifd_data_fd);
|
|
iret = ioctl(ion_fd, ION_IOC_FREE, &handle_data);
|
|
if (iret) {
|
|
ALOGE("Error::ION FREE ioctl returned error = %d",iret);
|
|
}
|
|
|
|
alloc_fail:
|
|
if (ion_fd > 0)
|
|
close(ion_fd);
|
|
return ret;
|
|
}
|
|
|
|
/** @brief: Deallocate ION memory
|
|
*
|
|
*
|
|
*/
|
|
static int32_t qcom_km_ion_dealloc(struct qcom_km_ion_info_t *handle)
|
|
{
|
|
struct ion_handle_data handle_data;
|
|
int32_t ret = 0;
|
|
|
|
/* Deallocate the memory for the listener */
|
|
ret = munmap(handle->ion_sbuffer, (handle->sbuf_len + 4095) & (~4095));
|
|
if (ret) {
|
|
ALOGE("Error::Unmapping ION Buffer failed with ret = %d", ret);
|
|
}
|
|
|
|
handle_data.handle = handle->ion_alloc_handle.handle;
|
|
close(handle->ifd_data_fd);
|
|
ret = ioctl(handle->ion_fd, ION_IOC_FREE, &handle_data);
|
|
if (ret) {
|
|
ALOGE("Error::ION Memory FREE ioctl failed with ret = %d", ret);
|
|
}
|
|
close(handle->ion_fd);
|
|
return ret;
|
|
}
|
|
|
|
static int qcom_km_generate_keypair(const keymaster0_device_t* dev,
|
|
const keymaster_keypair_t key_type, const void* key_params,
|
|
uint8_t** keyBlob, size_t* keyBlobLength) {
|
|
|
|
if (dev->context == NULL) {
|
|
ALOGE("qcom_km_generate_keypair: Context == NULL");
|
|
return -1;
|
|
}
|
|
|
|
if (key_type != TYPE_RSA) {
|
|
ALOGE("Unsupported key type %d", key_type);
|
|
return -1;
|
|
} else if (key_params == NULL) {
|
|
ALOGE("key_params == null");
|
|
return -1;
|
|
}
|
|
if (keyBlob == NULL || keyBlobLength == NULL) {
|
|
ALOGE("output key blob or length == NULL");
|
|
return -1;
|
|
}
|
|
keymaster_rsa_keygen_params_t* rsa_params = (keymaster_rsa_keygen_params_t*) key_params;
|
|
|
|
keymaster_gen_keypair_cmd_t *send_cmd = NULL;
|
|
keymaster_gen_keypair_resp_t *resp = NULL;
|
|
struct QSEECom_handle *handle = NULL;
|
|
struct qcom_keymaster_handle *km_handle =(struct qcom_keymaster_handle *)dev->context;
|
|
int ret = 0;
|
|
|
|
handle = (struct QSEECom_handle *)(km_handle->qseecom);
|
|
send_cmd = (keymaster_gen_keypair_cmd_t *)handle->ion_sbuffer;
|
|
resp = (keymaster_gen_keypair_resp_t *)(handle->ion_sbuffer +
|
|
QSEECOM_ALIGN(sizeof(keymaster_gen_keypair_cmd_t)));
|
|
send_cmd->cmd_id = KEYMASTER_GENERATE_KEYPAIR;
|
|
send_cmd->key_type = key_type;
|
|
send_cmd->rsa_params.modulus_size = rsa_params->modulus_size;
|
|
send_cmd->rsa_params.public_exponent = rsa_params->public_exponent;
|
|
resp->status = KEYMASTER_FAILURE;
|
|
resp->key_blob_len = sizeof(qcom_km_key_blob_t);
|
|
|
|
ret = (*km_handle->QSEECom_set_bandwidth)(handle, true);
|
|
if (ret < 0) {
|
|
ALOGE("Generate key command failed (unable to enable clks) ret =%d", ret);
|
|
return -1;
|
|
}
|
|
|
|
ret = (*km_handle->QSEECom_send_cmd)(handle, send_cmd,
|
|
QSEECOM_ALIGN(sizeof(keymaster_gen_keypair_cmd_t)), resp,
|
|
QSEECOM_ALIGN(sizeof(keymaster_gen_keypair_resp_t)));
|
|
|
|
if((*km_handle->QSEECom_set_bandwidth)(handle, false))
|
|
ALOGE("Import key command: (unable to disable clks)");
|
|
|
|
if ( (ret < 0) || (resp->status < 0)) {
|
|
ALOGE("Generate key command failed resp->status = %d ret =%d", resp->status, ret);
|
|
return -1;
|
|
} else {
|
|
std::unique_ptr<unsigned char[]> keydata(new unsigned char[resp->key_blob_len]);
|
|
if (keydata.get() == NULL) {
|
|
ALOGE("could not allocate memory for key blob");
|
|
return -1;
|
|
}
|
|
unsigned char* p = keydata.get();
|
|
memcpy(p, (unsigned char *)(&resp->key_blob), resp->key_blob_len);
|
|
*keyBlob = keydata.release();
|
|
*keyBlobLength = resp->key_blob_len;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static int qcom_km_import_keypair(const keymaster0_device_t* dev,
|
|
const uint8_t* key, const size_t key_length,
|
|
uint8_t** keyBlob, size_t* keyBlobLength)
|
|
{
|
|
if (dev->context == NULL) {
|
|
ALOGE("qcom_km_import_keypair: Context == NULL");
|
|
return -1;
|
|
}
|
|
|
|
if (key == NULL) {
|
|
ALOGE("Input key == NULL");
|
|
return -1;
|
|
} else if (keyBlob == NULL || keyBlobLength == NULL) {
|
|
ALOGE("Output key blob or length == NULL");
|
|
return -1;
|
|
}
|
|
|
|
struct QSEECom_ion_fd_info ion_fd_info;
|
|
struct qcom_km_ion_info_t ihandle;
|
|
int ret = 0;
|
|
|
|
ihandle.ion_fd = 0;
|
|
ihandle.ion_alloc_handle.handle = NULL;
|
|
if (qcom_km_ION_memalloc(&ihandle, QSEECOM_ALIGN(key_length)) < 0) {
|
|
ALOGE("ION allocation failed");
|
|
return -1;
|
|
}
|
|
memset(&ion_fd_info, 0, sizeof(struct QSEECom_ion_fd_info));
|
|
|
|
/* Populate the send data structure */
|
|
ion_fd_info.data[0].fd = ihandle.ifd_data_fd;
|
|
ion_fd_info.data[0].cmd_buf_offset = sizeof(enum keymaster_cmd_t);
|
|
|
|
|
|
struct QSEECom_handle *handle = NULL;
|
|
keymaster_import_keypair_cmd_t *send_cmd = NULL;
|
|
keymaster_import_keypair_resp_t *resp = NULL;
|
|
struct qcom_keymaster_handle *km_handle =(struct qcom_keymaster_handle *)dev->context;
|
|
|
|
handle = (struct QSEECom_handle *)(km_handle->qseecom);
|
|
send_cmd = (keymaster_import_keypair_cmd_t *)handle->ion_sbuffer;
|
|
resp = (keymaster_import_keypair_resp_t *)(handle->ion_sbuffer +
|
|
QSEECOM_ALIGN(sizeof(keymaster_import_keypair_cmd_t)));
|
|
send_cmd->cmd_id = KEYMASTER_IMPORT_KEYPAIR;
|
|
send_cmd->pkcs8_key = (uint32_t)ihandle.ion_sbuffer;
|
|
|
|
memcpy((unsigned char *)ihandle.ion_sbuffer, key, key_length);
|
|
|
|
send_cmd->pkcs8_key_len = key_length;
|
|
resp->status = KEYMASTER_FAILURE;
|
|
resp->key_blob_len = sizeof(qcom_km_key_blob_t);
|
|
|
|
ret = (*km_handle->QSEECom_set_bandwidth)(handle, true);
|
|
if (ret < 0) {
|
|
ALOGE("Import key command failed (unable to enable clks) ret =%d", ret);
|
|
qcom_km_ion_dealloc(&ihandle);
|
|
return -1;
|
|
}
|
|
ret = (*km_handle->QSEECom_send_modified_cmd)(handle, send_cmd,
|
|
QSEECOM_ALIGN(sizeof(*send_cmd)), resp,
|
|
QSEECOM_ALIGN(sizeof(*resp)), &ion_fd_info);
|
|
|
|
if((*km_handle->QSEECom_set_bandwidth)(handle, false))
|
|
ALOGE("Import key command: (unable to disable clks)");
|
|
|
|
if ( (ret < 0) || (resp->status < 0)) {
|
|
ALOGE("Import key command failed resp->status = %d ret =%d", resp->status, ret);
|
|
qcom_km_ion_dealloc(&ihandle);
|
|
return -1;
|
|
} else {
|
|
std::unique_ptr<unsigned char[]> keydata(new unsigned char[resp->key_blob_len]);
|
|
if (keydata.get() == NULL) {
|
|
ALOGE("could not allocate memory for key blob");
|
|
return -1;
|
|
}
|
|
unsigned char* p = keydata.get();
|
|
memcpy(p, (unsigned char *)(&resp->key_blob), resp->key_blob_len);
|
|
*keyBlob = keydata.release();
|
|
*keyBlobLength = resp->key_blob_len;
|
|
|
|
}
|
|
qcom_km_ion_dealloc(&ihandle);
|
|
return 0;
|
|
}
|
|
|
|
static int qcom_km_sign_data(const keymaster0_device_t* dev,
|
|
const void* params,
|
|
const uint8_t* keyBlob, const size_t keyBlobLength,
|
|
const uint8_t* data, const size_t dataLength,
|
|
uint8_t** signedData, size_t* signedDataLength)
|
|
{
|
|
if (dev->context == NULL) {
|
|
ALOGE("qcom_km_sign_data: Context == NULL");
|
|
return -1;
|
|
}
|
|
if (dataLength > KM_KEY_SIZE_MAX) {
|
|
ALOGE("Input data to be signed is too long %d bytes", dataLength);
|
|
return -1;
|
|
}
|
|
if (data == NULL) {
|
|
ALOGE("input data to sign == NULL");
|
|
return -1;
|
|
} else if (signedData == NULL || signedDataLength == NULL) {
|
|
ALOGE("Output signature buffer == NULL");
|
|
return -1;
|
|
}
|
|
keymaster_rsa_sign_params_t* sign_params = (keymaster_rsa_sign_params_t*) params;
|
|
if (sign_params->digest_type != DIGEST_NONE) {
|
|
ALOGE("Cannot handle digest type %d", sign_params->digest_type);
|
|
return -1;
|
|
} else if (sign_params->padding_type != PADDING_NONE) {
|
|
ALOGE("Cannot handle padding type %d", sign_params->padding_type);
|
|
return -1;
|
|
}
|
|
|
|
struct QSEECom_handle *handle = NULL;
|
|
keymaster_sign_data_cmd_t *send_cmd = NULL;
|
|
keymaster_sign_data_resp_t *resp = NULL;
|
|
struct QSEECom_ion_fd_info ion_fd_info;
|
|
struct qcom_km_ion_info_t ihandle;
|
|
struct qcom_keymaster_handle *km_handle =(struct qcom_keymaster_handle *)dev->context;
|
|
int ret = 0;
|
|
|
|
handle = (struct QSEECom_handle *)(km_handle->qseecom);
|
|
ihandle.ion_fd = 0;
|
|
ihandle.ion_alloc_handle.handle = NULL;
|
|
if (qcom_km_ION_memalloc(&ihandle, dataLength) < 0) {
|
|
ALOGE("ION allocation failed");
|
|
return -1;
|
|
}
|
|
memset(&ion_fd_info, 0, sizeof(struct QSEECom_ion_fd_info));
|
|
|
|
/* Populate the send data structure */
|
|
ion_fd_info.data[0].fd = ihandle.ifd_data_fd;
|
|
ion_fd_info.data[0].cmd_buf_offset = sizeof(enum keymaster_cmd_t) +
|
|
sizeof(qcom_km_key_blob_t) + sizeof(keymaster_rsa_sign_params_t);
|
|
|
|
send_cmd = (keymaster_sign_data_cmd_t *)handle->ion_sbuffer;
|
|
resp = (keymaster_sign_data_resp_t *)(handle->ion_sbuffer +
|
|
QSEECOM_ALIGN(sizeof(keymaster_sign_data_cmd_t)));
|
|
send_cmd->cmd_id = KEYMASTER_SIGN_DATA ;
|
|
send_cmd->sign_param.digest_type = sign_params->digest_type;
|
|
send_cmd->sign_param.padding_type = sign_params->padding_type;
|
|
memcpy((unsigned char *)(&send_cmd->key_blob), keyBlob, keyBlobLength);
|
|
memcpy((unsigned char *)ihandle.ion_sbuffer, data, dataLength);
|
|
|
|
send_cmd->data = (uint32_t)ihandle.ion_sbuffer;
|
|
send_cmd->dlen = dataLength;
|
|
resp->sig_len = KM_KEY_SIZE_MAX;
|
|
resp->status = KEYMASTER_FAILURE;
|
|
|
|
ret = (*km_handle->QSEECom_set_bandwidth)(handle, true);
|
|
if (ret < 0) {
|
|
ALOGE("Sign data command failed (unable to enable clks) ret =%d", ret);
|
|
qcom_km_ion_dealloc(&ihandle);
|
|
return -1;
|
|
}
|
|
|
|
ret = (*km_handle->QSEECom_send_modified_cmd)(handle, send_cmd,
|
|
QSEECOM_ALIGN(sizeof(*send_cmd)), resp,
|
|
QSEECOM_ALIGN(sizeof(*resp)), &ion_fd_info);
|
|
|
|
if((*km_handle->QSEECom_set_bandwidth)(handle, false))
|
|
ALOGE("Sign data command: (unable to disable clks)");
|
|
|
|
if ( (ret < 0) || (resp->status < 0)) {
|
|
ALOGE("Sign data command failed resp->status = %d ret =%d", resp->status, ret);
|
|
qcom_km_ion_dealloc(&ihandle);
|
|
return -1;
|
|
} else {
|
|
std::unique_ptr<uint8_t> signedDataPtr(reinterpret_cast<uint8_t*>(malloc(resp->sig_len)));
|
|
if (signedDataPtr.get() == NULL) {
|
|
ALOGE("Sign data memory allocation failed");
|
|
qcom_km_ion_dealloc(&ihandle);
|
|
return -1;
|
|
}
|
|
unsigned char* p = signedDataPtr.get();
|
|
memcpy(p, (unsigned char *)(&resp->signed_data), resp->sig_len);
|
|
|
|
*signedDataLength = resp->sig_len;
|
|
*signedData = signedDataPtr.release();
|
|
}
|
|
qcom_km_ion_dealloc(&ihandle);
|
|
return 0;
|
|
}
|
|
|
|
static int qcom_km_verify_data(const keymaster0_device_t* dev,
|
|
const void* params,
|
|
const uint8_t* keyBlob, const size_t keyBlobLength,
|
|
const uint8_t* signedData, const size_t signedDataLength,
|
|
const uint8_t* signature, const size_t signatureLength)
|
|
{
|
|
if (dev->context == NULL) {
|
|
ALOGE("qcom_km_verify_data: Context == NULL");
|
|
return -1;
|
|
}
|
|
|
|
if (signedData == NULL || signature == NULL) {
|
|
ALOGE("data or signature buffers == NULL");
|
|
return -1;
|
|
}
|
|
|
|
keymaster_rsa_sign_params_t* sign_params = (keymaster_rsa_sign_params_t*) params;
|
|
if (sign_params->digest_type != DIGEST_NONE) {
|
|
ALOGE("Cannot handle digest type %d", sign_params->digest_type);
|
|
return -1;
|
|
} else if (sign_params->padding_type != PADDING_NONE) {
|
|
ALOGE("Cannot handle padding type %d", sign_params->padding_type);
|
|
return -1;
|
|
} else if (signatureLength != signedDataLength) {
|
|
ALOGE("signed data length must be signature length");
|
|
return -1;
|
|
}
|
|
|
|
struct QSEECom_handle *handle = NULL;
|
|
keymaster_verify_data_cmd_t *send_cmd = NULL;
|
|
keymaster_verify_data_resp_t *resp = NULL;
|
|
|
|
struct QSEECom_ion_fd_info ion_fd_info;
|
|
struct qcom_km_ion_info_t ihandle;
|
|
struct qcom_keymaster_handle *km_handle =(struct qcom_keymaster_handle *)dev->context;
|
|
int ret = 0;
|
|
|
|
handle = (struct QSEECom_handle *)(km_handle->qseecom);
|
|
ihandle.ion_fd = 0;
|
|
ihandle.ion_alloc_handle.handle = NULL;
|
|
if (qcom_km_ION_memalloc(&ihandle, signedDataLength + signatureLength) <0) {
|
|
ALOGE("ION allocation failed");
|
|
return -1;
|
|
}
|
|
memset(&ion_fd_info, 0, sizeof(struct QSEECom_ion_fd_info));
|
|
|
|
/* Populate the send data structure */
|
|
ion_fd_info.data[0].fd = ihandle.ifd_data_fd;
|
|
ion_fd_info.data[0].cmd_buf_offset = sizeof(enum keymaster_cmd_t) +
|
|
sizeof(qcom_km_key_blob_t ) + sizeof(keymaster_rsa_sign_params_t);
|
|
|
|
send_cmd = (keymaster_verify_data_cmd_t *)handle->ion_sbuffer;
|
|
resp = (keymaster_verify_data_resp_t *)((char *)handle->ion_sbuffer +
|
|
sizeof(keymaster_verify_data_cmd_t));
|
|
send_cmd->cmd_id = KEYMASTER_VERIFY_DATA ;
|
|
send_cmd->sign_param.digest_type = sign_params->digest_type;
|
|
send_cmd->sign_param.padding_type = sign_params->padding_type;
|
|
memcpy((unsigned char *)(&send_cmd->key_blob), keyBlob, keyBlobLength);
|
|
|
|
send_cmd->signed_data = (uint32_t)ihandle.ion_sbuffer;
|
|
send_cmd->signed_dlen = signedDataLength;
|
|
memcpy((unsigned char *)ihandle.ion_sbuffer, signedData, signedDataLength);
|
|
|
|
send_cmd->signature = signedDataLength;
|
|
send_cmd->slen = signatureLength;
|
|
memcpy(((unsigned char *)ihandle.ion_sbuffer + signedDataLength),
|
|
signature, signatureLength);
|
|
resp->status = KEYMASTER_FAILURE;
|
|
|
|
ret = (*km_handle->QSEECom_set_bandwidth)(handle, true);
|
|
if (ret < 0) {
|
|
ALOGE("Verify data command failed (unable to enable clks) ret =%d", ret);
|
|
qcom_km_ion_dealloc(&ihandle);
|
|
return -1;
|
|
}
|
|
|
|
ret = (*km_handle->QSEECom_send_modified_cmd)(handle, send_cmd,
|
|
QSEECOM_ALIGN(sizeof(*send_cmd)), resp,
|
|
QSEECOM_ALIGN(sizeof(*resp)), &ion_fd_info);
|
|
|
|
if((*km_handle->QSEECom_set_bandwidth)(handle, false))
|
|
ALOGE("Verify data command: (unable to disable clks)");
|
|
|
|
if ( (ret < 0) || (resp->status < 0)) {
|
|
ALOGE("Verify data command failed resp->status = %d ret =%d", resp->status, ret);
|
|
qcom_km_ion_dealloc(&ihandle);
|
|
return -1;
|
|
}
|
|
qcom_km_ion_dealloc(&ihandle);
|
|
return 0;
|
|
}
|
|
|
|
/* Close an opened OpenSSL instance */
|
|
static int qcom_km_close(hw_device_t *dev)
|
|
{
|
|
keymaster0_device_t* km_dev = (keymaster0_device_t *)dev;
|
|
struct qcom_keymaster_handle *km_handle =(struct qcom_keymaster_handle *)km_dev->context;
|
|
|
|
if (km_handle->qseecom == NULL) {
|
|
ALOGE("Context == NULL");
|
|
return -1;
|
|
}
|
|
(*km_handle->QSEECom_shutdown_app)((struct QSEECom_handle **)&km_handle->qseecom);
|
|
free(km_dev->context);
|
|
free(dev);
|
|
return 0;
|
|
}
|
|
|
|
static int qcom_km_get_lib_sym(qcom_keymaster_handle_t* km_handle)
|
|
{
|
|
km_handle->libhandle = dlopen("libQSEEComAPI.so", RTLD_NOW);
|
|
if ( km_handle->libhandle ) {
|
|
*(void **)(&km_handle->QSEECom_start_app) =
|
|
dlsym(km_handle->libhandle,"QSEECom_start_app");
|
|
if (km_handle->QSEECom_start_app == NULL) {
|
|
ALOGE("dlsym: Error Loading QSEECom_start_app");
|
|
dlclose(km_handle->libhandle );
|
|
km_handle->libhandle = NULL;
|
|
return -1;
|
|
}
|
|
*(void **)(&km_handle->QSEECom_shutdown_app) =
|
|
dlsym(km_handle->libhandle,"QSEECom_shutdown_app");
|
|
if (km_handle->QSEECom_shutdown_app == NULL) {
|
|
ALOGE("dlsym: Error Loading QSEECom_shutdown_app");
|
|
dlclose(km_handle->libhandle );
|
|
km_handle->libhandle = NULL;
|
|
return -1;
|
|
}
|
|
*(void **)(&km_handle->QSEECom_send_cmd) =
|
|
dlsym(km_handle->libhandle,"QSEECom_send_cmd");
|
|
if (km_handle->QSEECom_send_cmd == NULL) {
|
|
ALOGE("dlsym: Error Loading QSEECom_send_cmd");
|
|
dlclose(km_handle->libhandle );
|
|
km_handle->libhandle = NULL;
|
|
return -1;
|
|
}
|
|
*(void **)(&km_handle->QSEECom_send_modified_cmd) =
|
|
dlsym(km_handle->libhandle,"QSEECom_send_modified_cmd");
|
|
if (km_handle->QSEECom_send_modified_cmd == NULL) {
|
|
ALOGE("dlsym: Error Loading QSEECom_send_modified_cmd");
|
|
dlclose(km_handle->libhandle );
|
|
km_handle->libhandle = NULL;
|
|
return -1;
|
|
}
|
|
*(void **)(&km_handle->QSEECom_set_bandwidth) =
|
|
dlsym(km_handle->libhandle,"QSEECom_set_bandwidth");
|
|
if (km_handle->QSEECom_set_bandwidth == NULL) {
|
|
ALOGE("dlsym: Error Loading QSEECom_set_bandwidth");
|
|
dlclose(km_handle->libhandle );
|
|
km_handle->libhandle = NULL;
|
|
return -1;
|
|
}
|
|
|
|
} else {
|
|
ALOGE("failed to load qseecom library");
|
|
return -1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* Generic device handling
|
|
*/
|
|
static int qcom_km_open(const hw_module_t* module, const char* name,
|
|
hw_device_t** device)
|
|
{
|
|
int ret = 0;
|
|
qcom_keymaster_handle_t* km_handle;
|
|
if (strcmp(name, KEYSTORE_KEYMASTER) != 0)
|
|
return -EINVAL;
|
|
|
|
km_handle = (qcom_keymaster_handle_t *)malloc(sizeof(qcom_keymaster_handle_t));
|
|
if (km_handle == NULL) {
|
|
ALOGE("Memalloc for keymaster handle failed");
|
|
return -1;
|
|
}
|
|
km_handle->qseecom = NULL;
|
|
km_handle->libhandle = NULL;
|
|
ret = qcom_km_get_lib_sym(km_handle);
|
|
if (ret) {
|
|
free(km_handle);
|
|
return -1;
|
|
}
|
|
Unique_keymaster_device_t dev(new keymaster0_device_t);
|
|
if (dev.get() == NULL){
|
|
free(km_handle);
|
|
return -ENOMEM;
|
|
}
|
|
dev->context = (void *)km_handle;
|
|
ret = (*km_handle->QSEECom_start_app)((struct QSEECom_handle **)&km_handle->qseecom,
|
|
"/vendor/firmware/keymaster", "keymaster", 4096*2);
|
|
if (ret) {
|
|
ALOGE("Loading keymaster app failed");
|
|
free(km_handle);
|
|
return -1;
|
|
}
|
|
dev->common.tag = HARDWARE_DEVICE_TAG;
|
|
dev->common.version = 1;
|
|
dev->common.module = (struct hw_module_t*) module;
|
|
dev->common.close = qcom_km_close;
|
|
dev->flags = KEYMASTER_BLOBS_ARE_STANDALONE;
|
|
|
|
dev->generate_keypair = qcom_km_generate_keypair;
|
|
dev->import_keypair = qcom_km_import_keypair;
|
|
dev->get_keypair_public = qcom_km_get_keypair_public;
|
|
dev->delete_keypair = NULL;
|
|
dev->delete_all = NULL;
|
|
dev->sign_data = qcom_km_sign_data;
|
|
dev->verify_data = qcom_km_verify_data;
|
|
|
|
*device = reinterpret_cast<hw_device_t*>(dev.release());
|
|
|
|
return 0;
|
|
}
|
|
|
|
static struct hw_module_methods_t keystore_module_methods = {
|
|
.open = qcom_km_open,
|
|
};
|
|
|
|
struct keystore_module HAL_MODULE_INFO_SYM
|
|
__attribute__ ((visibility ("default"))) = {
|
|
.common = {
|
|
.tag = HARDWARE_MODULE_TAG,
|
|
.module_api_version = QCOM_KEYMASTER_API_VERSION,
|
|
.hal_api_version = HARDWARE_HAL_API_VERSION,
|
|
.id = KEYSTORE_HARDWARE_MODULE_ID,
|
|
.name = "Keymaster QCOM HAL",
|
|
.author = "The Android Open Source Project",
|
|
.methods = &keystore_module_methods,
|
|
.dso = 0,
|
|
.reserved = {},
|
|
},
|
|
};
|