From fb5cdde80ba39d571e3281f3619717a3a5361cf0 Mon Sep 17 00:00:00 2001 From: Ajay Dudani Date: Thu, 20 Sep 2012 14:57:01 -0700 Subject: keymaster: Add keymaster implementation Initial implementation of keymaster HAL APIs published in keymaster.h Bug: 7106634 Change-Id: I1b1f611f1988212ce5f6f7f03b9a59ca652d92cf Signed-off-by: Iliyan Malchev --- Android.mk | 30 +++ QSEEComAPI.h | 299 +++++++++++++++++++++ keymaster_qcom.cpp | 751 +++++++++++++++++++++++++++++++++++++++++++++++++++++ keymaster_qcom.h | 216 +++++++++++++++ 4 files changed, 1296 insertions(+) create mode 100644 Android.mk create mode 100644 QSEEComAPI.h create mode 100644 keymaster_qcom.cpp create mode 100644 keymaster_qcom.h diff --git a/Android.mk b/Android.mk new file mode 100644 index 0000000..467d7a0 --- /dev/null +++ b/Android.mk @@ -0,0 +1,30 @@ +LOCAL_PATH := $(call my-dir) + +ifneq ($(filter msm8960,$(TARGET_BOARD_PLATFORM)),) + +include $(CLEAR_VARS) + +LOCAL_MODULE := keystore.msm8960 + +LOCAL_MODULE_PATH := $(TARGET_OUT_SHARED_LIBRARIES)/hw + +LOCAL_SRC_FILES := keymaster_qcom.cpp + +LOCAL_C_INCLUDES := $(TARGET_OUT_HEADERS)/common/inc \ + external/openssl/include + +LOCAL_C_FLAGS = -fvisibility=hidden -Wall -Werror + +LOCAL_SHARED_LIBRARIES := \ + libcrypto \ + liblog \ + libc \ + libdl + +LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.mk + +LOCAL_MODULE_TAGS := optional + +include $(BUILD_SHARED_LIBRARY) + +endif # msm8960 == TARGET_BOARD_PLATFORM diff --git a/QSEEComAPI.h b/QSEEComAPI.h new file mode 100644 index 0000000..a96cda3 --- /dev/null +++ b/QSEEComAPI.h @@ -0,0 +1,299 @@ +/* + * 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. + */ + +#ifndef __QSEECOMAPI_H_ +#define __QSEECOMAPI_H_ + + +/*---------------------------------------------------------------------------- + * Include Files +* -------------------------------------------------------------------------*/ +#include +#include + +#define QSEECOM_ALIGN_SIZE 0x40 +#define QSEECOM_ALIGN_MASK (QSEECOM_ALIGN_SIZE - 1) +#define QSEECOM_ALIGN(x) \ + ((x + QSEECOM_ALIGN_SIZE) & (~QSEECOM_ALIGN_MASK)) + +#ifdef __cplusplus +extern "C" { +#endif + +/*---------------------------------------------------------------------------- + * Preprocessor Definitions and Constants + * -------------------------------------------------------------------------*/ +/** The memory is locked and non-pageable */ +#define MEM_LOCKED 0x00000001 +/** The memory is marked non-cacheable */ +#define MEM_NON_CACHED 0x00000002 + +#define QSEECOM_APP_QUERY_FAILED -6 +#define QSEECOM_APP_NOT_LOADED -5 +#define QSEECOM_APP_ALREADY_LOADED -4 +#define QSEECOM_LISTENER_UNREGISTERED -3 +#define QSEECOM_LISTENER_ALREADY_REGISTERED -2 +#define QSEECOM_LISTENER_REGISTER_FAIL -1 + +/*---------------------------------------------------------------------------- + * Type Declarations + * -------------------------------------------------------------------------*/ +struct QSEECom_handle { + unsigned char *ion_sbuffer; +}; + +struct QSEECom_ion_fd_data { + int32_t fd; + uint32_t cmd_buf_offset; +}; + +struct QSEECom_ion_fd_info { + struct QSEECom_ion_fd_data data[4]; +}; + +/*---------------------------------------------------------------------------- + * Function Declarations and Documentation + * -------------------------------------------------------------------------*/ +/** + * @brief Open a handle to the QSEECom device. + * + * - Load a secure application. The application will be verified that it is + * secure by digital signature verification. + * Allocate memory for sending requests to the QSAPP + * + * Note/Comments: + * There is a one-to-one relation for a HLOS client and a QSAPP; + * meaning that only one app can communicate to a QSAPP at a time. + * + * Please note that there is difference between an application and a listener + * service. A QSAPP must be loaded at the request of the HLOS, + * and all requests are orginated by the HLOS client. + * A listener service on the otherhand is started during start-up by a + * daemon, qseecomd. + * + * A HLOS application may create mutiple handles to the QSAPP + * + * @param[in/out] handle The device handle + * @param[in] fname The directory and filename to load. + * @param[in] sb_size Size of the shared buffer memory for sending requests. + * @return Zero on success, negative on failure. errno will be set on + * error. + */ +int QSEECom_start_app(struct QSEECom_handle **clnt_handle, const char *path, + const char *fname, uint32_t sb_size); + +/** + * @brief Close the application associated with the handle. + * + * - Unload a secure application. The driver will verify if there exists + * any other applications that are communicating with the QSAPP to which + * the "handle" is tied. + * - De-allocate memory for sending requests to QSAPP. + * + * @param[in] handle The device handle + * @return Zero on success, negative on failure. errno will be set on + * error. + */ +int QSEECom_shutdown_app(struct QSEECom_handle **handle); + +/** + * @brief Open a handle to the QSEECom device. + * + * - Load an external elf. The elf will be verified that it is + * secure by digital signature verification. + * + * A HLOS application may create mutiple opens (only one is permitted for the + * app, but each listener service can open a unique device in the same HLOS app + * /executable. + * @param[in/out] handle The device handle + * @param[in] fname The directory and filename to load. + * @return Zero on success, negative on failure. errno will be set on + * error. + */ +int QSEECom_load_external_elf(struct QSEECom_handle **clnt_handle, const char *path, + const char *fname); + +/** + * @brief Close the external elf + * + * - Unload an external elf. + * + * @param[in] handle The device handle + * + * @return Zero on success, negative on failure. errno will be set on + * error. + */ +int QSEECom_unload_external_elf(struct QSEECom_handle **handle); + +/** + * @brief Register an HLOS listener service. This allows messages from QSAPP + * to be received. + * + * @param[in] handle The device handle + * @param[in] lstnr_id The listener service identifier. This ID must be uniquely + * assigned to avoid any collisions. + * @param[in] sb_length Shared memory buffer between OS and QSE. + * @param[in] flags Provide the shared memory flags attributes. + * + * @return Zero on success, negative on failure. errno will be set on + * error. + * + */ +int QSEECom_register_listener(struct QSEECom_handle **handle, + uint32_t lstnr_id, uint32_t sb_length, uint32_t flags); + +/** + * @brief Unregister a listener service. + * + * @param[in] handle The device handle + * + * @return Zero on success, negative on failure. errno will be set on + * error. + */ +int QSEECom_unregister_listener(struct QSEECom_handle *handle); + + +/** + * @brief Send QSAPP a "user" defined buffer (may contain some message/ + * command request) and receives a response from QSAPP in receive buffer. + * The HLOS client writes to the send_buf, where QSAPP writes to the rcv_buf. + * This is a blocking call. + * + * @param[in] handle The device handle + * @param[in] send_buf The buffer to be sent. + * If using ion_sbuffer, ensure this + * QSEECOM_BUFFER_ALIGN'ed. + * @param[in] sbuf_len The send buffer length + * If using ion_sbuffer, ensure length is + * multiple of QSEECOM_BUFFER_ALIGN. + * @param[in] rcv_buf The QSEOS returned buffer. + * If using ion_sbuffer, ensure this is + * QSEECOM_BUFFER_ALIGN'ed. + * @param[in] rbuf_len The returned buffer length. + * If using ion_sbuffer, ensure length is + * multiple of QSEECOM_BUFFER_ALIGN. + * @param[in] rbuf_len The returned buffer length. + * + * @return Zero on success, negative on failure. errno will be set on + * error. + */ +int QSEECom_send_cmd(struct QSEECom_handle *handle, void *send_buf, + uint32_t sbuf_len, void *rcv_buf, uint32_t rbuf_len); + + +/** + * @brief Send QSAPP a "user" defined buffer (may contain some message/ + * command request) and receives a response from QSAPP in receive buffer. + * This API is same as send_cmd except it takes in addition parameter, + * "ifd_data". This "ifd_data" holds information (ion fd handle and + * cmd_buf_offset) used for modifying data in the message in send_buf + * at an offset. Essentailly, it has the ion fd handle information to + * retrieve physical address and modify the message in send_buf at the + * mentioned offset. + * + * The HLOS client writes to the send_buf, where QSAPP writes to the rcv_buf. + * This is a blocking call. + * + * @param[in] handle The device handle + * @param[in] send_buf The buffer to be sent. + * If using ion_sbuffer, ensure this + * QSEECOM_BUFFER_ALIGN'ed. + * @param[in] sbuf_len The send buffer length + * If using ion_sbuffer, ensure length is + * multiple of QSEECOM_BUFFER_ALIGN. + * @param[in] rcv_buf The QSEOS returned buffer. + * If using ion_sbuffer, ensure this is + * QSEECOM_BUFFER_ALIGN'ed. + * @param[in] rbuf_len The returned buffer length. + * If using ion_sbuffer, ensure length is + * multiple of QSEECOM_BUFFER_ALIGN. + * @param[in] QSEECom_ion_fd_info data related to memory allocated by ion. + * + * @return Zero on success, negative on failure. errno will be set on + * error. + */ +int QSEECom_send_modified_cmd(struct QSEECom_handle *handle, void *send_buf, + uint32_t sbuf_len, void *resp_buf, uint32_t rbuf_len, + struct QSEECom_ion_fd_info *ifd_data); + +/** + * @brief Receive a service defined buffer. + * + * @param[in] handle The device handle + * @param[out] buf The buffer that is received + * @param[in] len The receive buffer length + * + * @return Zero on success, negative on failure. errno will be set on + * error. + */ +int QSEECom_receive_req(struct QSEECom_handle *handle, + void *buf, uint32_t len); + +/** + * @brief Send a response based on the previous QSEECom_receive_req. + * + * This allows a listener service to receive a command (e.g. read file abc). + * The service can then handle the request from QSEECom_receive_req, and provide + * that information back to QSAPP. + * + * This allows the HLOS to act as the server and QSAPP to behave as the client. + * + * @param[in] handle The device handle + * @param[out] send_buf The buffer to be returned back to QSAPP + * @param[in] len The send buffer length + * + * @return Zero on success, negative on failure. errno will be set on + * error. + */ +int QSEECom_send_resp(struct QSEECom_handle *handle, + void *send_buf, uint32_t len); + +/** + * @brief Set the bandwidth for QSEE. + * + * This API resulst in improving the performance on the Crypto hardware + * in QSEE. It should be called before issuing send_cmd/send_modified_cmd + * for commands that requires using the crypto hardware on the QSEE. + * Typically this API should be called before issuing the send request to + * enable high performance mode and after completion of the send_cmd to + * resume to low performance and hence to low power mode. + * + * This allows the clients of QSEECom to set the QSEE cyptpo HW bus + * bandwidth to high/low. + * + * @param[in] high Set to 1 to enable bandwidth. + * + * @return Zero on success, negative on failure. errno will be set on + * error. + */ +int QSEECom_set_bandwidth(struct QSEECom_handle *handle, bool high); + +/** + * @brief Query QSEE to check if app is loaded. + * + * This API queries QSEE to see if the app is loaded or not. + * + * @param[in] app_name Name of the app. + * + * @return QSEECOM_APP_QUERY_FAILED/QSEECOM_APP_NOT_LOADED/QSEECOM_APP_LOADED. + */ +int QSEECom_app_load_query(struct QSEECom_handle *handle, char *app_name); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/keymaster_qcom.cpp b/keymaster_qcom.cpp new file mode 100644 index 0000000..4f64c8e --- /dev/null +++ b/keymaster_qcom.cpp @@ -0,0 +1,751 @@ +/* + * 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 +#include +#include + +#include +#include + +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "QSEEComAPI.h" +#include "keymaster_qcom.h" + +// For debugging +//#define LOG_NDEBUG 0 + +#define LOG_TAG "QCOMKeyMaster" +#include +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, const char* path, + const 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); +}; +typedef struct qcom_keymaster_handle qcom_keymaster_handle_t; + +struct EVP_PKEY_Delete { + void operator()(EVP_PKEY* p) const { + EVP_PKEY_free(p); + } +}; +typedef UniquePtr Unique_EVP_PKEY; + +struct RSA_Delete { + void operator()(RSA* p) const { + RSA_free(p); + } +}; +typedef UniquePtr Unique_RSA; + +typedef UniquePtr 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 keymaster_device* 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(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(&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; + } + + UniquePtr key(static_cast(malloc(len))); + if (key.get() == NULL) { + ALOGE("Could not allocate memory for public key data"); + return -1; + } + + unsigned char* tmp = reinterpret_cast(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.heap_mask = ION_HEAP_CARVEOUT_MASK; + + /* Set the memory to be uncached */ + ion_alloc_data.flags = ION_HEAP(ION_QSECOM_HEAP_ID); + + /* 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 keymaster_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_send_cmd)(handle, send_cmd, + QSEECOM_ALIGN(sizeof(keymaster_gen_keypair_cmd_t)), resp, + QSEECOM_ALIGN(sizeof(keymaster_gen_keypair_resp_t))); + + if ( (ret < 0) || (resp->status < 0)) { + ALOGE("Generate key command failed resp->status = %d ret =%d", resp->status, ret); + return -1; + } else { + UniquePtr 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 keymaster_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_send_modified_cmd)(handle, send_cmd, + QSEECOM_ALIGN(sizeof(*send_cmd)), resp, + QSEECOM_ALIGN(sizeof(*resp)), &ion_fd_info); + + 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 { + UniquePtr 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 keymaster_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_send_modified_cmd)(handle, send_cmd, + QSEECOM_ALIGN(sizeof(*send_cmd)), resp, + QSEECOM_ALIGN(sizeof(*resp)), &ion_fd_info); + 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 { + UniquePtr signedDataPtr(reinterpret_cast(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 keymaster_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_send_modified_cmd)(handle, send_cmd, + QSEECOM_ALIGN(sizeof(*send_cmd)), resp, + QSEECOM_ALIGN(sizeof(*resp)), &ion_fd_info); + + 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) +{ + keymaster_device_t* km_dev = (keymaster_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("/system/lib/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; + } + } 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 keymaster_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); + if (ret) { + ALOGE("Loading keymaster app failied"); + 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 = 0; + + 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(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, + version_major: 1, + version_minor: 0, + id: KEYSTORE_HARDWARE_MODULE_ID, + name: "Keymaster QCOM HAL", + author: "The Android Open Source Project", + methods: &keystore_module_methods, + dso: 0, + reserved: {}, + }, +}; diff --git a/keymaster_qcom.h b/keymaster_qcom.h new file mode 100644 index 0000000..14aa497 --- /dev/null +++ b/keymaster_qcom.h @@ -0,0 +1,216 @@ +/* + * 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. + */ + +#ifndef ANDROID_HARDWARE_QCOM_KEYMASTER_H +#define ANDROID_HARDWARE_QCOM_KEYMASTER_H + +#include +#include +#include + +__BEGIN_DECLS + +/** + * The id of this module + */ +#define QCOM_KEYSTORE_KEYMASTER "qcom_keymaster" +/** + * Operation result + */ +#define KEYMATER_SUCCESS 0 +#define KEYMASTER_FAILURE -1 + +/** + * The API level of this version of the header. The allows the implementing + * module to recognize which API level of the client it is dealing with in + * the case of pre-compiled binary clients. + */ +#define QCOM_KEYMASTER_API_VERSION 1 + +#define KM_MAGIC_NUM (0x4B4D4B42) /* "KMKB" Key Master Key Blob in hex */ +#define KM_KEY_SIZE_MAX (256) /* 2048 bits */ +#define KM_IV_LENGTH (16) /* AES128 CBC IV */ +#define KM_HMAC_LENGTH (32) /* SHA2 will be used for HMAC */ + +struct qcom_km_key_blob { + uint32_t magic_num; + uint32_t version_num; + uint8_t modulus[KM_KEY_SIZE_MAX]; + uint32_t modulus_size; + uint8_t public_exponent[KM_KEY_SIZE_MAX]; + uint32_t public_exponent_size; + uint8_t iv[KM_IV_LENGTH]; + uint8_t encrypted_private_exponent[KM_KEY_SIZE_MAX]; + uint32_t encrypted_private_exponent_size; + uint8_t hmac[KM_HMAC_LENGTH]; +}; +typedef struct qcom_km_key_blob qcom_km_key_blob_t; +/** + * Commands supported + */ +enum keymaster_cmd_t { + /* + * List the commands supportedin by the hardware. + */ + KEYMASTER_GENERATE_KEYPAIR = 0x00000001, + KEYMASTER_IMPORT_KEYPAIR = 0x00000002, + KEYMASTER_SIGN_DATA = 0x00000003, + KEYMASTER_VERIFY_DATA = 0x00000004, +}; + + +/** + * Command to Generate a public and private key. The key data returned + * (by secure app) is in shared buffer at offset of "key_blob" and is opaque + * + * cmd_id : Command issue to secure app + * key_type : Currently on RSA_TYPE is supported + * rsa_params : Parameters needed to generate an RSA key + */ + struct keymaster_gen_keypair_cmd { + keymaster_cmd_t cmd_id; + keymaster_keypair_t key_type; + keymaster_rsa_keygen_params_t rsa_params; +}; +typedef struct keymaster_gen_keypair_cmd keymaster_gen_keypair_cmd_t; + +/** + * Response to Generate a public and private key. The key data returned + * (by secure app) is in shared buffer at offset of "key_blob" and is opaque + * + * cmd_id : Command issue to secure app + * key_blob : key blob data + * key_blob_len : Total length of key blob information + * status : Result (success 0, or failure -1) + */ +struct keymaster_gen_keypair_resp { + keymaster_cmd_t cmd_id; + qcom_km_key_blob_t key_blob; + size_t key_blob_len; + int32_t status; +}; +typedef struct keymaster_gen_keypair_resp keymaster_gen_keypair_resp_t; + + +/** + * Command to import a public and private key pair. The imported keys + * will be in PKCS#8 format with DER encoding (Java standard). The key + * data returned (by secure app) is in shared buffer at offset of + * "key_blob" and is opaque + * + * cmd_id : Command issue to secure app + * pkcs8_key : Pointer to pkcs8 formatted key information + * pkcs8_key_len: PKCS8 formatted key length + */ +struct keymaster_import_keypair_cmd { + keymaster_cmd_t cmd_id; + uint32_t pkcs8_key; + size_t pkcs8_key_len; +}; +typedef struct keymaster_import_keypair_cmd keymaster_import_keypair_cmd_t; + +/** + * Response to import a public and private key. The key data returned + * (by secure app) is in shared buffer at offset of "key_blob" and is opaque + * + * cmd_id : Command issue to secure app + * key_blob : key blob data + * key_blob_len : Total length of key blob information + * status : Result (success 0, or failure -1) + */ +struct keymaster_import_keypair_resp { + keymaster_cmd_t cmd_id; + qcom_km_key_blob_t key_blob; + size_t key_blob_len; + int32_t status; +}; +typedef struct keymaster_import_keypair_resp keymaster_import_keypair_resp_t; + +/** + * Command to sign data using a key info generated before. This can use either + * an asymmetric key or a secret key. + * The signed data is returned (by secure app) at offset of data + dlen. + * + * cmd_id : Command issue to secure app + * sign_param : + * key_blob : Key data information (in shared buffer) + * data : Pointer to plain data buffer + * dlen : Plain data length + */ +struct keymaster_sign_data_cmd { + keymaster_cmd_t cmd_id; + keymaster_rsa_sign_params_t sign_param; + qcom_km_key_blob_t key_blob; + uint32_t data; + size_t dlen; +}; +typedef struct keymaster_sign_data_cmd keymaster_sign_data_cmd_t; + +/** + * Response to sign data response + * + * cmd_id : Command issue to secure app + * signed_data : signature + * sig_len : Signed data length + * status : Result (success 0, or failure -1) + */ +struct keymaster_sign_data_resp { + keymaster_cmd_t cmd_id; + uint8_t signed_data[KM_KEY_SIZE_MAX]; + size_t sig_len; + int32_t status; +}; + +typedef struct keymaster_sign_data_resp keymaster_sign_data_resp_t; + +/** + * Command to verify data using a key info generated before. This can use either + * an asymmetric key or a secret key. + * + * cmd_id : Command issue to secure app + * sign_param : + * key_blob : Key data information (in shared buffer) + * key_blob_len: Total key length + * signed_data : Pointer to signed data buffer + * signed_dlen : Signed data length + * signature : Offset to the signature data buffer (from signed data buffer) + * slen : Signature data length + */ +struct keymaster_verify_data_cmd { + keymaster_cmd_t cmd_id; + keymaster_rsa_sign_params_t sign_param; + qcom_km_key_blob_t key_blob; + uint32_t signed_data; + size_t signed_dlen; + uint32_t signature; + size_t slen; +}; +typedef struct keymaster_verify_data_cmd keymaster_verify_data_cmd_t; +/** + * Response to verify data + * + * cmd_id : Command issue to secure app + * status : Result (success 0, or failure -1) + */ +struct keymaster_verify_data_resp { + keymaster_cmd_t cmd_id; + int32_t status; +}; +typedef struct keymaster_verify_data_resp keymaster_verify_data_resp_t; + +__END_DECLS + +#endif // ANDROID_HARDWARE_QCOM_KEYMASTER_H -- cgit v1.2.3