summaryrefslogtreecommitdiffstats
path: root/keymaster
diff options
context:
space:
mode:
authorKenny Root <kroot@google.com>2012-01-09 13:59:45 -0800
committerKenny Root <kroot@google.com>2012-03-27 20:08:01 -0700
commit7d863c7f2f5f89205756af35989268a81ca5c581 (patch)
tree397ebcdf939fec683ca5db30f7df3ea7bf169a54 /keymaster
parent01a204140470ffc5fb8428fa80abeb0c89a3e202 (diff)
downloaddevice_samsung_tuna-7d863c7f2f5f89205756af35989268a81ca5c581.zip
device_samsung_tuna-7d863c7f2f5f89205756af35989268a81ca5c581.tar.gz
device_samsung_tuna-7d863c7f2f5f89205756af35989268a81ca5c581.tar.bz2
Add implementation of keymaster for tuna
This implements the hardware crypto for tuna devices based on libtf_crypto_tee which means it should be compatible with all OMAP4 devices. Change-Id: I17667214f7ba132428dee829078d990fde8447d1
Diffstat (limited to 'keymaster')
-rw-r--r--keymaster/Android.mk43
-rw-r--r--keymaster/keymaster_tuna.cpp966
2 files changed, 1009 insertions, 0 deletions
diff --git a/keymaster/Android.mk b/keymaster/Android.mk
new file mode 100644
index 0000000..885812f
--- /dev/null
+++ b/keymaster/Android.mk
@@ -0,0 +1,43 @@
+# Copyright (C) 2011 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.
+
+ifeq ($(TARGET_BOARD_PLATFORM),omap4)
+ifeq ($(BOARD_USES_SECURE_SERVICES),true)
+
+LOCAL_PATH := $(call my-dir)
+
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := keystore.tuna
+
+LOCAL_MODULE_PATH := $(TARGET_OUT_SHARED_LIBRARIES)/hw
+
+LOCAL_SRC_FILES := \
+ keymaster_tuna.cpp
+
+LOCAL_C_INCLUDES := \
+ libcore/include \
+ external/openssl/include \
+ hardware/ti/omap4xxx/security/tf_sdk/include
+
+LOCAL_CFLAGS := -fvisibility=hidden -Wall -Werror
+
+LOCAL_SHARED_LIBRARIES := libcutils liblog libcrypto libtf_crypto_sst
+
+LOCAL_MODULE_TAGS := optional
+
+include $(BUILD_SHARED_LIBRARY)
+
+endif # ifeq ($(BOARD_USES_SECURE_SERVICES),true)
+endif # ifeq ($(TARGET_BOARD_PLATFORM),omap4)
diff --git a/keymaster/keymaster_tuna.cpp b/keymaster/keymaster_tuna.cpp
new file mode 100644
index 0000000..fbc654f
--- /dev/null
+++ b/keymaster/keymaster_tuna.cpp
@@ -0,0 +1,966 @@
+/*
+ * Copyright (C) 2011 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>
+
+// For debugging
+#define LOG_NDEBUG 0
+
+// TEE is the Trusted Execution Environment
+#define LOG_TAG "TEEKeyMaster"
+#include <cutils/log.h>
+
+#include <hardware/hardware.h>
+#include <hardware/keymaster.h>
+
+#include <openssl/bn.h>
+#include <openssl/err.h>
+#include <openssl/evp.h>
+#include <openssl/rand.h>
+#include <openssl/x509.h>
+
+#include <cryptoki.h>
+#include <pkcs11.h>
+
+#include <UniquePtr.h>
+
+
+/** The size of a key ID in bytes */
+#define ID_LENGTH 32
+
+/** The current stored key version. */
+const static uint32_t KEY_VERSION = 1;
+
+
+struct EVP_PKEY_Delete {
+ void operator()(EVP_PKEY* p) const {
+ EVP_PKEY_free(p);
+ }
+};
+typedef UniquePtr<EVP_PKEY, EVP_PKEY_Delete> Unique_EVP_PKEY;
+
+struct RSA_Delete {
+ void operator()(RSA* p) const {
+ RSA_free(p);
+ }
+};
+typedef UniquePtr<RSA, RSA_Delete> Unique_RSA;
+
+struct PKCS8_PRIV_KEY_INFO_Delete {
+ void operator()(PKCS8_PRIV_KEY_INFO* p) const {
+ PKCS8_PRIV_KEY_INFO_free(p);
+ }
+};
+typedef UniquePtr<PKCS8_PRIV_KEY_INFO, PKCS8_PRIV_KEY_INFO_Delete> Unique_PKCS8_PRIV_KEY_INFO;
+
+typedef UniquePtr<keymaster_device_t> Unique_keymaster_device_t;
+
+typedef UniquePtr<CK_BYTE[]> Unique_CK_BYTE;
+
+typedef UniquePtr<CK_ATTRIBUTE[]> Unique_CK_ATTRIBUTE;
+
+class ByteArray {
+public:
+ ByteArray(CK_BYTE* array, size_t len) :
+ mArray(array), mLength(len) {
+ }
+
+ ByteArray(size_t len) :
+ mLength(len) {
+ mArray = new CK_BYTE[len];
+ }
+
+ ~ByteArray() {
+ if (mArray != NULL) {
+ delete[] mArray;
+ }
+ }
+
+ CK_BYTE* get() const {
+ return mArray;
+ }
+
+ size_t length() const {
+ return mLength;
+ }
+
+ CK_BYTE* release() {
+ CK_BYTE* array = mArray;
+ mArray = NULL;
+ return array;
+ }
+
+private:
+ CK_BYTE* mArray;
+ size_t mLength;
+};
+typedef UniquePtr<ByteArray> Unique_ByteArray;
+
+class CryptoSession {
+public:
+ CryptoSession(CK_SESSION_HANDLE masterHandle) :
+ mHandle(masterHandle), mSubsession(CK_INVALID_HANDLE) {
+ CK_SESSION_HANDLE subsessionHandle = mHandle;
+ CK_RV openSessionRV = C_OpenSession(CKV_TOKEN_USER,
+ CKF_SERIAL_SESSION | CKF_RW_SESSION | CKVF_OPEN_SUB_SESSION,
+ NULL,
+ NULL,
+ &subsessionHandle);
+
+ if (openSessionRV != CKR_OK || subsessionHandle == CK_INVALID_HANDLE) {
+ (void) C_Finalize(NULL_PTR);
+ ALOGE("Error opening secondary session with TEE: 0x%x", openSessionRV);
+ } else {
+ ALOGV("Opening subsession 0x%x", subsessionHandle);
+ mSubsession = subsessionHandle;
+ }
+ }
+
+ ~CryptoSession() {
+ if (mSubsession != CK_INVALID_HANDLE) {
+ CK_RV rv = C_CloseSession(mSubsession);
+ ALOGV("Closing subsession 0x%x: 0x%x", mSubsession, rv);
+ mSubsession = CK_INVALID_HANDLE;
+ }
+ }
+
+ CK_SESSION_HANDLE get() const {
+ return mSubsession;
+ }
+
+ CK_SESSION_HANDLE getPrimary() const {
+ return mHandle;
+ }
+
+private:
+ CK_SESSION_HANDLE mHandle;
+ CK_SESSION_HANDLE mSubsession;
+};
+
+class ObjectHandle {
+public:
+ ObjectHandle(const CryptoSession* session, CK_OBJECT_HANDLE handle = CK_INVALID_HANDLE) :
+ mSession(session), mHandle(handle) {
+ }
+
+ ~ObjectHandle() {
+ if (mHandle != CK_INVALID_HANDLE) {
+ CK_RV rv = C_CloseObjectHandle(mSession->getPrimary(), mHandle);
+ if (rv != CKR_OK) {
+ ALOGW("Couldn't close object handle 0x%x: 0x%x", mHandle, rv);
+ } else {
+ ALOGV("Closing object handle 0x%x", mHandle);
+ mHandle = CK_INVALID_HANDLE;
+ }
+ }
+ }
+
+ CK_OBJECT_HANDLE get() const {
+ return mHandle;
+ }
+
+ void reset(CK_OBJECT_HANDLE handle) {
+ mHandle = handle;
+ }
+
+private:
+ const CryptoSession* mSession;
+ CK_OBJECT_HANDLE mHandle;
+};
+
+
+/**
+ * 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()
+
+
+/*
+ * Checks this thread's OpenSSL error queue and logs if
+ * necessary.
+ */
+static void logOpenSSLError(const char* location) {
+ int error = ERR_get_error();
+
+ if (error != 0) {
+ char message[256];
+ ERR_error_string_n(error, message, sizeof(message));
+ ALOGE("OpenSSL error in %s %d: %s", location, error, message);
+ }
+
+ ERR_clear_error();
+ ERR_remove_state(0);
+}
+
+
+/**
+ * Convert from OpenSSL's BIGNUM format to TEE's Big Integer format.
+ */
+static ByteArray* bignum_to_array(const BIGNUM* bn) {
+ const int bignumSize = BN_num_bytes(bn);
+
+ Unique_CK_BYTE bytes(new CK_BYTE[bignumSize]);
+
+ unsigned char* tmp = reinterpret_cast<unsigned char*>(bytes.get());
+ if (BN_bn2bin(bn, tmp) != bignumSize) {
+ ALOGE("public exponent size wasn't what was expected");
+ return NULL;
+ }
+
+ return new ByteArray(bytes.release(), bignumSize);
+}
+
+static void set_attribute(CK_ATTRIBUTE* attrib, CK_ATTRIBUTE_TYPE type, void* pValue,
+ CK_ULONG ulValueLen) {
+ attrib->type = type;
+ attrib->pValue = pValue;
+ attrib->ulValueLen = ulValueLen;
+}
+
+static ByteArray* generate_random_id() {
+ Unique_ByteArray id(new ByteArray(ID_LENGTH));
+ if (RAND_pseudo_bytes(reinterpret_cast<unsigned char*>(id->get()), id->length()) < 0) {
+ return NULL;
+ }
+
+ return id.release();
+}
+
+static int keyblob_save(ByteArray* objId, uint8_t** key_blob, size_t* key_blob_length) {
+ Unique_ByteArray handleBlob(new ByteArray(sizeof(uint32_t) + objId->length()));
+ if (handleBlob.get() == NULL) {
+ ALOGE("Could not allocate key blob");
+ return -1;
+ }
+ uint8_t* tmp = handleBlob->get();
+ for (size_t i = 0; i < sizeof(uint32_t); i++) {
+ *tmp++ = KEY_VERSION >> ((sizeof(uint32_t) - i - 1) * 8);
+ }
+ memcpy(tmp, objId->get(), objId->length());
+
+ *key_blob_length = handleBlob->length();
+ *key_blob = handleBlob->get();
+ ByteArray* unused __attribute__((unused)) = handleBlob.release();
+
+ return 0;
+}
+
+static int find_single_object(const uint8_t* obj_id, const size_t obj_id_length,
+ CK_OBJECT_CLASS obj_class, const CryptoSession* session, ObjectHandle* object) {
+
+ // Note that the CKA_ID attribute is never written, so we can cast away const here.
+ void* obj_id_ptr = reinterpret_cast<void*>(const_cast<uint8_t*>(obj_id));
+ CK_ATTRIBUTE attributes[] = {
+ { CKA_ID, obj_id_ptr, obj_id_length },
+ { CKA_CLASS, &obj_class, sizeof(obj_class) },
+ };
+
+ CK_RV rv = C_FindObjectsInit(session->get(), attributes,
+ sizeof(attributes) / sizeof(CK_ATTRIBUTE));
+ if (rv != CKR_OK) {
+ ALOGE("Error in C_FindObjectsInit: 0x%x", rv);
+ return -1;
+ }
+
+ CK_OBJECT_HANDLE tmpHandle;
+ CK_ULONG tmpCount;
+
+ rv = C_FindObjects(session->get(), &tmpHandle, 1, &tmpCount);
+ ALOGV("Found %d object 0x%x : class 0x%x", tmpCount, tmpHandle, obj_class);
+ if (rv != CKR_OK || tmpCount != 1) {
+ C_FindObjectsFinal(session->get());
+ ALOGE("Couldn't find key!");
+ return -1;
+ }
+ C_FindObjectsFinal(session->get());
+
+ object->reset(tmpHandle);
+ return 0;
+}
+
+static int keyblob_restore(const CryptoSession* session, const uint8_t* keyBlob,
+ const size_t keyBlobLength, ObjectHandle* public_key, ObjectHandle* private_key) {
+ if (keyBlob == NULL) {
+ ALOGE("key blob was null");
+ return -1;
+ }
+
+ if (keyBlobLength < (sizeof(KEY_VERSION) + ID_LENGTH)) {
+ ALOGE("key blob is not correct size");
+ return -1;
+ }
+
+ uint32_t keyVersion = 0;
+
+ const uint8_t* p = keyBlob;
+ for (size_t i = 0; i < sizeof(keyVersion); i++) {
+ keyVersion = (keyVersion << 8) | *p++;
+ }
+
+ if (keyVersion != 1) {
+ ALOGE("Invalid key version %d", keyVersion);
+ return -1;
+ }
+
+ return find_single_object(p, ID_LENGTH, CKO_PUBLIC_KEY, session, public_key)
+ || find_single_object(p, ID_LENGTH, CKO_PRIVATE_KEY, session, private_key);
+}
+
+static int tee_generate_keypair(const keymaster_device_t* dev,
+ const keymaster_keypair_t type, const void* key_params,
+ uint8_t** key_blob, size_t* key_blob_length) {
+ CK_BBOOL bTRUE = CK_TRUE;
+
+ if (type != TYPE_RSA) {
+ ALOGW("Unknown key type %d", type);
+ return -1;
+ }
+
+ if (key_params == NULL) {
+ ALOGW("generate_keypair params were NULL");
+ return -1;
+ }
+
+ keymaster_rsa_keygen_params_t* rsa_params = (keymaster_rsa_keygen_params_t*) key_params;
+
+ CK_MECHANISM mechanism = {
+ CKM_RSA_PKCS_KEY_PAIR_GEN, NULL, 0,
+ };
+ CK_ULONG modulusBits = (CK_ULONG) rsa_params->modulus_size;
+
+ /**
+ * Convert our unsigned 64-bit integer to the TEE Big Integer class. It's
+ * an unsigned array of bytes with MSB first.
+ */
+ CK_BYTE publicExponent[sizeof(uint64_t)];
+ const uint64_t exp = rsa_params->public_exponent;
+ size_t offset = sizeof(publicExponent) - 1;
+ for (size_t i = 0; i < sizeof(publicExponent); i++) {
+ publicExponent[offset--] = (exp >> (i * CHAR_BIT)) & 0xFF;
+ }
+
+ Unique_ByteArray objId(generate_random_id());
+ if (objId.get() == NULL) {
+ ALOGE("Couldn't generate random key ID");
+ return -1;
+ }
+
+ CK_ATTRIBUTE publicKeyTemplate[] = {
+ {CKA_ID, objId->get(), objId->length()},
+ {CKA_TOKEN, &bTRUE, sizeof(bTRUE)},
+ {CKA_ENCRYPT, &bTRUE, sizeof(bTRUE)},
+ {CKA_VERIFY, &bTRUE, sizeof(bTRUE)},
+ {CKA_MODULUS_BITS, &modulusBits, sizeof(modulusBits)},
+ {CKA_PUBLIC_EXPONENT, publicExponent, sizeof(publicExponent)},
+ };
+
+ CK_ATTRIBUTE privateKeyTemplate[] = {
+ {CKA_ID, objId->get(), objId->length()},
+ {CKA_TOKEN, &bTRUE, sizeof(bTRUE)},
+ {CKA_DECRYPT, &bTRUE, sizeof(bTRUE)},
+ {CKA_SIGN, &bTRUE, sizeof(bTRUE)},
+ };
+
+ CryptoSession session(reinterpret_cast<CK_SESSION_HANDLE>(dev->context));
+
+ CK_OBJECT_HANDLE hPublicKey, hPrivateKey;
+ CK_RV rv = C_GenerateKeyPair(session.get(),
+ &mechanism,
+ publicKeyTemplate,
+ sizeof(publicKeyTemplate)/sizeof(CK_ATTRIBUTE),
+ privateKeyTemplate,
+ sizeof(privateKeyTemplate)/sizeof(CK_ATTRIBUTE),
+ &hPublicKey,
+ &hPrivateKey);
+
+ if (rv != CKR_OK) {
+ ALOGE("Generate keypair failed: 0x%x", rv);
+ return -1;
+ }
+
+ ObjectHandle publicKey(&session, hPublicKey);
+ ObjectHandle privateKey(&session, hPrivateKey);
+ ALOGV("public handle = 0x%x, private handle = 0x%x", publicKey.get(), privateKey.get());
+
+ return keyblob_save(objId.get(), key_blob, key_blob_length);
+}
+
+static int tee_import_keypair(const keymaster_device_t* dev,
+ const uint8_t* key, const size_t key_length,
+ uint8_t** key_blob, size_t* key_blob_length) {
+ CK_RV rv;
+ CK_BBOOL bTRUE = CK_TRUE;
+
+ if (key == NULL) {
+ ALOGW("provided key is null");
+ return -1;
+ }
+
+ Unique_PKCS8_PRIV_KEY_INFO pkcs8(d2i_PKCS8_PRIV_KEY_INFO(NULL, &key, key_length));
+ if (pkcs8.get() == NULL) {
+ logOpenSSLError("tee_import_keypair");
+ return -1;
+ }
+
+ /* assign to EVP */
+ Unique_EVP_PKEY pkey(EVP_PKCS82PKEY(pkcs8.get()));
+ if (pkey.get() == NULL) {
+ logOpenSSLError("tee_import_keypair");
+ return -1;
+ }
+
+ if (EVP_PKEY_type(pkey->type) != EVP_PKEY_RSA) {
+ ALOGE("Unsupported key type: %d", EVP_PKEY_type(pkey->type));
+ return -1;
+ }
+
+ Unique_RSA rsa(EVP_PKEY_get1_RSA(pkey.get()));
+ if (rsa.get() == NULL) {
+ logOpenSSLError("tee_import_keypair");
+ return -1;
+ }
+
+ Unique_ByteArray modulus(bignum_to_array(rsa->n));
+ if (modulus.get() == NULL) {
+ ALOGW("Could not convert modulus to array");
+ return -1;
+ }
+
+ Unique_ByteArray publicExponent(bignum_to_array(rsa->e));
+ if (publicExponent.get() == NULL) {
+ ALOGW("Could not convert publicExponent to array");
+ return -1;
+ }
+
+ CK_KEY_TYPE rsaType = CKK_RSA;
+
+ CK_OBJECT_CLASS pubClass = CKO_PUBLIC_KEY;
+
+ Unique_ByteArray objId(generate_random_id());
+ if (objId.get() == NULL) {
+ ALOGE("Couldn't generate random key ID");
+ return -1;
+ }
+
+ CK_ATTRIBUTE publicKeyTemplate[] = {
+ {CKA_ID, objId->get(), objId->length()},
+ {CKA_TOKEN, &bTRUE, sizeof(bTRUE)},
+ {CKA_CLASS, &pubClass, sizeof(pubClass)},
+ {CKA_KEY_TYPE, &rsaType, sizeof(rsaType)},
+ {CKA_ENCRYPT, &bTRUE, sizeof(bTRUE)},
+ {CKA_VERIFY, &bTRUE, sizeof(bTRUE)},
+ {CKA_MODULUS, modulus->get(), modulus->length()},
+ {CKA_PUBLIC_EXPONENT, publicExponent->get(), publicExponent->length()},
+ };
+
+ CryptoSession session(reinterpret_cast<CK_SESSION_HANDLE>(dev->context));
+
+ CK_OBJECT_HANDLE hPublicKey;
+ rv = C_CreateObject(session.get(),
+ publicKeyTemplate,
+ sizeof(publicKeyTemplate)/sizeof(CK_ATTRIBUTE),
+ &hPublicKey);
+ if (rv != CKR_OK) {
+ ALOGE("Creation of public key failed: 0x%x", rv);
+ return -1;
+ }
+ ObjectHandle publicKey(&session, hPublicKey);
+
+ Unique_ByteArray privateExponent(bignum_to_array(rsa->d));
+ if (privateExponent.get() == NULL) {
+ ALOGW("Could not convert private exponent");
+ return -1;
+ }
+
+
+ /*
+ * Normally we need:
+ * CKA_ID
+ * CKA_TOKEN
+ * CKA_CLASS
+ * CKA_KEY_TYPE
+ *
+ * CKA_DECRYPT
+ * CKA_SIGN
+ *
+ * CKA_MODULUS
+ * CKA_PUBLIC_EXPONENT
+ * CKA_PRIVATE_EXPONENT
+ */
+#define PRIV_ATTRIB_NORMAL_NUM (4 + 2 + 3)
+
+ /*
+ * For additional private key values:
+ * CKA_PRIME_1
+ * CKA_PRIME_2
+ *
+ * CKA_EXPONENT_1
+ * CKA_EXPONENT_2
+ *
+ * CKA_COEFFICIENT
+ */
+#define PRIV_ATTRIB_EXTENDED_NUM (PRIV_ATTRIB_NORMAL_NUM + 5)
+
+ /*
+ * If we have the prime, prime exponents, and coefficient, we can
+ * copy them in.
+ */
+ bool has_extra_data = (rsa->p != NULL) && (rsa->q != NULL) && (rsa->dmp1 != NULL) &&
+ (rsa->dmq1 != NULL) && (rsa->iqmp != NULL);
+
+ Unique_CK_ATTRIBUTE privateKeyTemplate(new CK_ATTRIBUTE[
+ has_extra_data ? PRIV_ATTRIB_EXTENDED_NUM : PRIV_ATTRIB_NORMAL_NUM]);
+
+ CK_OBJECT_CLASS privClass = CKO_PRIVATE_KEY;
+
+ size_t templateOffset = 0;
+
+ set_attribute(&privateKeyTemplate[templateOffset++], CKA_ID, objId->get(), objId->length());
+ set_attribute(&privateKeyTemplate[templateOffset++], CKA_TOKEN, &bTRUE, sizeof(bTRUE));
+ set_attribute(&privateKeyTemplate[templateOffset++], CKA_CLASS, &privClass, sizeof(privClass));
+ set_attribute(&privateKeyTemplate[templateOffset++], CKA_KEY_TYPE, &rsaType, sizeof(rsaType));
+
+ set_attribute(&privateKeyTemplate[templateOffset++], CKA_DECRYPT, &bTRUE, sizeof(bTRUE));
+ set_attribute(&privateKeyTemplate[templateOffset++], CKA_SIGN, &bTRUE, sizeof(bTRUE));
+
+ set_attribute(&privateKeyTemplate[templateOffset++], CKA_MODULUS, modulus->get(),
+ modulus->length());
+ set_attribute(&privateKeyTemplate[templateOffset++], CKA_PUBLIC_EXPONENT,
+ publicExponent->get(), publicExponent->length());
+ set_attribute(&privateKeyTemplate[templateOffset++], CKA_PRIVATE_EXPONENT,
+ privateExponent->get(), privateExponent->length());
+
+ Unique_ByteArray prime1, prime2, exp1, exp2, coeff;
+ if (has_extra_data) {
+ prime1.reset(bignum_to_array(rsa->p));
+ if (prime1->get() == NULL) {
+ ALOGW("Could not convert prime1");
+ return -1;
+ }
+ set_attribute(&privateKeyTemplate[templateOffset++], CKA_PRIME_1, prime1->get(),
+ prime1->length());
+
+ prime2.reset(bignum_to_array(rsa->q));
+ if (prime2->get() == NULL) {
+ ALOGW("Could not convert prime2");
+ return -1;
+ }
+ set_attribute(&privateKeyTemplate[templateOffset++], CKA_PRIME_2, prime2->get(),
+ prime2->length());
+
+ exp1.reset(bignum_to_array(rsa->dmp1));
+ if (exp1->get() == NULL) {
+ ALOGW("Could not convert exponent 1");
+ return -1;
+ }
+ set_attribute(&privateKeyTemplate[templateOffset++], CKA_EXPONENT_1, exp1->get(),
+ exp1->length());
+
+ exp2.reset(bignum_to_array(rsa->dmq1));
+ if (exp2->get() == NULL) {
+ ALOGW("Could not convert exponent 2");
+ return -1;
+ }
+ set_attribute(&privateKeyTemplate[templateOffset++], CKA_EXPONENT_2, exp2->get(),
+ exp2->length());
+
+ coeff.reset(bignum_to_array(rsa->iqmp));
+ if (coeff->get() == NULL) {
+ ALOGW("Could not convert coefficient");
+ return -1;
+ }
+ set_attribute(&privateKeyTemplate[templateOffset++], CKA_COEFFICIENT, coeff->get(),
+ coeff->length());
+ }
+
+ CK_OBJECT_HANDLE hPrivateKey;
+ rv = C_CreateObject(session.get(),
+ privateKeyTemplate.get(),
+ templateOffset,
+ &hPrivateKey);
+ if (rv != CKR_OK) {
+ ALOGE("Creation of private key failed: 0x%x", rv);
+ return -1;
+ }
+ ObjectHandle privateKey(&session, hPrivateKey);
+
+ ALOGV("public handle = 0x%x, private handle = 0x%x", publicKey.get(), privateKey.get());
+
+ return keyblob_save(objId.get(), key_blob, key_blob_length);
+}
+
+static int tee_get_keypair_public(const struct keymaster_device* dev,
+ const uint8_t* key_blob, const size_t key_blob_length,
+ uint8_t** x509_data, size_t* x509_data_length) {
+
+ CryptoSession session(reinterpret_cast<CK_SESSION_HANDLE>(dev->context));
+
+ ObjectHandle publicKey(&session);
+ ObjectHandle privateKey(&session);
+
+ if (keyblob_restore(&session, key_blob, key_blob_length, &publicKey, &privateKey)) {
+ return -1;
+ }
+
+ if (x509_data == NULL || x509_data_length == NULL) {
+ ALOGW("Provided destination variables were null");
+ return -1;
+ }
+
+ CK_ATTRIBUTE attributes[] = {
+ {CKA_MODULUS, NULL, 0},
+ {CKA_PUBLIC_EXPONENT, NULL, 0},
+ };
+
+ // Call first to get the sizes of the values.
+ CK_RV rv = C_GetAttributeValue(session.get(), publicKey.get(), attributes,
+ sizeof(attributes)/sizeof(CK_ATTRIBUTE));
+ if (rv != CKR_OK) {
+ ALOGW("Could not query attribute value sizes: 0x%02x", rv);
+ return -1;
+ }
+
+ ByteArray modulus(new CK_BYTE[attributes[0].ulValueLen], attributes[0].ulValueLen);
+ ByteArray exponent(new CK_BYTE[attributes[1].ulValueLen], attributes[1].ulValueLen);
+
+ attributes[0].pValue = modulus.get();
+ attributes[1].pValue = exponent.get();
+
+ rv = C_GetAttributeValue(session.get(), publicKey.get(), attributes,
+ sizeof(attributes) / sizeof(CK_ATTRIBUTE));
+ if (rv != CKR_OK) {
+ ALOGW("Could not query attribute values: 0x%02x", rv);
+ return -1;
+ }
+
+ ALOGV("modulus is %d, exponent is %d", modulus.length(), exponent.length());
+
+ 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*>(modulus.get()), modulus.length(),
+ NULL);
+ if (rsa->n == NULL) {
+ logOpenSSLError("tee_get_keypair_public");
+ return -1;
+ }
+
+ rsa->e = BN_bin2bn(reinterpret_cast<const unsigned char*>(exponent.get()), exponent.length(),
+ NULL);
+ if (rsa->e == NULL) {
+ logOpenSSLError("tee_get_keypair_public");
+ 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) {
+ logOpenSSLError("tee_get_keypair_public");
+ return -1;
+ }
+ OWNERSHIP_TRANSFERRED(rsa);
+
+ int len = i2d_PUBKEY(pkey.get(), NULL);
+ if (len <= 0) {
+ logOpenSSLError("tee_get_keypair_public");
+ return -1;
+ }
+
+ UniquePtr<uint8_t> key(static_cast<uint8_t*>(malloc(len)));
+ if (key.get() == NULL) {
+ ALOGE("Could not allocate memory for public key data");
+ return -1;
+ }
+
+ unsigned char* tmp = reinterpret_cast<unsigned char*>(key.get());
+ if (i2d_PUBKEY(pkey.get(), &tmp) != len) {
+ logOpenSSLError("tee_get_keypair_public");
+ return -1;
+ }
+
+ ALOGV("Length of x509 data is %d", len);
+ *x509_data_length = len;
+ *x509_data = key.release();
+
+ return 0;
+}
+
+static int tee_delete_keypair(const struct keymaster_device* dev,
+ const uint8_t* key_blob, const size_t key_blob_length) {
+
+ CryptoSession session(reinterpret_cast<CK_SESSION_HANDLE>(dev->context));
+
+ ObjectHandle publicKey(&session);
+ ObjectHandle privateKey(&session);
+
+ if (keyblob_restore(&session, key_blob, key_blob_length, &publicKey, &privateKey)) {
+ return -1;
+ }
+
+ // Delete the private key.
+ CK_RV rv = C_DestroyObject(session.get(), privateKey.get());
+ if (rv != CKR_OK) {
+ ALOGW("Could destroy private key object: 0x%02x", rv);
+ return -1;
+ }
+
+ // Delete the public key.
+ rv = C_DestroyObject(session.get(), publicKey.get());
+ if (rv != CKR_OK) {
+ ALOGW("Could destroy public key object: 0x%02x", rv);
+ return -1;
+ }
+
+ return 0;
+}
+
+static int tee_sign_data(const keymaster_device_t* dev,
+ const void* params,
+ const uint8_t* key_blob, const size_t key_blob_length,
+ const uint8_t* data, const size_t dataLength,
+ uint8_t** signedData, size_t* signedDataLength) {
+ ALOGV("tee_sign_data(%p, %p, %llu, %p, %llu, %p, %p)", dev, key_blob,
+ (unsigned long long) key_blob_length, data, (unsigned long long) dataLength, signedData,
+ signedDataLength);
+
+ if (params == NULL) {
+ ALOGW("Signing params were null");
+ return -1;
+ }
+
+ CryptoSession session(reinterpret_cast<CK_SESSION_HANDLE>(dev->context));
+
+ ObjectHandle publicKey(&session);
+ ObjectHandle privateKey(&session);
+
+ if (keyblob_restore(&session, key_blob, key_blob_length, &publicKey, &privateKey)) {
+ return -1;
+ }
+ ALOGV("public handle = 0x%x, private handle = 0x%x", publicKey.get(), privateKey.get());
+
+ keymaster_rsa_sign_params_t* sign_params = (keymaster_rsa_sign_params_t*) params;
+ if (sign_params->digest_type != DIGEST_NONE) {
+ ALOGW("Cannot handle digest type %d", sign_params->digest_type);
+ return -1;
+ } else if (sign_params->padding_type != PADDING_NONE) {
+ ALOGW("Cannot handle padding type %d", sign_params->padding_type);
+ return -1;
+ }
+
+ CK_MECHANISM rawRsaMechanism = {
+ CKM_RSA_X_509, NULL, 0
+ };
+
+ CK_RV rv = C_SignInit(session.get(), &rawRsaMechanism, privateKey.get());
+ if (rv != CKR_OK) {
+ ALOGV("C_SignInit failed: 0x%x", rv);
+ return -1;
+ }
+
+ CK_BYTE signature[1024];
+ CK_ULONG signatureLength = 1024;
+
+ rv = C_Sign(session.get(), data, dataLength, signature, &signatureLength);
+ if (rv != CKR_OK) {
+ ALOGV("C_SignFinal failed: 0x%x", rv);
+ return -1;
+ }
+
+ UniquePtr<uint8_t[]> finalSignature(new uint8_t[signatureLength]);
+ if (finalSignature.get() == NULL) {
+ ALOGE("Couldn't allocate memory to copy signature");
+ return -1;
+ }
+
+ memcpy(finalSignature.get(), signature, signatureLength);
+
+ *signedData = finalSignature.release();
+ *signedDataLength = static_cast<size_t>(signatureLength);
+
+ ALOGV("tee_sign_data(%p, %p, %llu, %p, %llu, %p, %p) => %p size %llu", dev, key_blob,
+ (unsigned long long) key_blob_length, data, (unsigned long long) dataLength, signedData,
+ signedDataLength, *signedData, (unsigned long long) *signedDataLength);
+
+ return 0;
+}
+
+static int tee_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) {
+ ALOGV("tee_verify_data(%p, %p, %llu, %p, %llu, %p, %llu)", dev, keyBlob,
+ (unsigned long long) keyBlobLength, signedData, (unsigned long long) signedDataLength,
+ signature, (unsigned long long) signatureLength);
+
+ if (params == NULL) {
+ ALOGW("Verification params were null");
+ return -1;
+ }
+
+ CryptoSession session(reinterpret_cast<CK_SESSION_HANDLE>(dev->context));
+
+ ObjectHandle publicKey(&session);
+ ObjectHandle privateKey(&session);
+
+ if (keyblob_restore(&session, keyBlob, keyBlobLength, &publicKey, &privateKey)) {
+ return -1;
+ }
+ ALOGV("public handle = 0x%x, private handle = 0x%x", publicKey.get(), privateKey.get());
+
+ keymaster_rsa_sign_params_t* sign_params = (keymaster_rsa_sign_params_t*) params;
+ if (sign_params->digest_type != DIGEST_NONE) {
+ ALOGW("Cannot handle digest type %d", sign_params->digest_type);
+ return -1;
+ } else if (sign_params->padding_type != PADDING_NONE) {
+ ALOGW("Cannot handle padding type %d", sign_params->padding_type);
+ return -1;
+ }
+
+ CK_MECHANISM rawRsaMechanism = {
+ CKM_RSA_X_509, NULL, 0
+ };
+
+ CK_RV rv = C_VerifyInit(session.get(), &rawRsaMechanism, publicKey.get());
+ if (rv != CKR_OK) {
+ ALOGV("C_VerifyInit failed: 0x%x", rv);
+ return -1;
+ }
+
+ // This is a bad prototype for this function. C_Verify should have only const args.
+ rv = C_Verify(session.get(), signedData, signedDataLength,
+ const_cast<unsigned char*>(signature), signatureLength);
+ if (rv != CKR_OK) {
+ ALOGV("C_Verify failed: 0x%x", rv);
+ return -1;
+ }
+
+ return 0;
+}
+
+/* Close an opened OpenSSL instance */
+static int tee_close(hw_device_t *dev) {
+ keymaster_device_t *keymaster_dev = (keymaster_device_t *) dev;
+ if (keymaster_dev != NULL) {
+ CK_SESSION_HANDLE handle = reinterpret_cast<CK_SESSION_HANDLE>(keymaster_dev->context);
+ if (handle != CK_INVALID_HANDLE) {
+ C_CloseSession(handle);
+ }
+ }
+
+ CK_RV finalizeRV = C_Finalize(NULL_PTR);
+ if (finalizeRV != CKR_OK) {
+ ALOGE("Error closing the TEE");
+ }
+ free(dev);
+
+ return 0;
+}
+
+/*
+ * Generic device handling
+ */
+static int tee_open(const hw_module_t* module, const char* name,
+ hw_device_t** device) {
+ if (strcmp(name, KEYSTORE_KEYMASTER) != 0)
+ return -EINVAL;
+
+ Unique_keymaster_device_t dev(new keymaster_device_t);
+ if (dev.get() == NULL)
+ return -ENOMEM;
+
+ dev->common.tag = HARDWARE_DEVICE_TAG;
+ dev->common.version = 1;
+ dev->common.module = (struct hw_module_t*) module;
+ dev->common.close = tee_close;
+
+ dev->generate_keypair = tee_generate_keypair;
+ dev->import_keypair = tee_import_keypair;
+ dev->get_keypair_public = tee_get_keypair_public;
+ dev->delete_keypair = tee_delete_keypair;
+ dev->sign_data = tee_sign_data;
+ dev->verify_data = tee_verify_data;
+
+ CK_RV initializeRV = C_Initialize(NULL);
+ if (initializeRV != CKR_OK) {
+ ALOGE("Error initializing TEE: 0x%x", initializeRV);
+ return -ENODEV;
+ }
+
+ CK_INFO info;
+ CK_RV infoRV = C_GetInfo(&info);
+ if (infoRV != CKR_OK) {
+ (void) C_Finalize(NULL_PTR);
+ ALOGE("Error getting information about TEE during initialization: 0x%x", infoRV);
+ return -ENODEV;
+ }
+
+ ALOGI("C_GetInfo cryptokiVer=%d.%d manufID=%s flags=%d libDesc=%s libVer=%d.%d\n",
+ info.cryptokiVersion.major, info.cryptokiVersion.minor,
+ info.manufacturerID, info.flags, info.libraryDescription,
+ info.libraryVersion.major, info.libraryVersion.minor);
+
+ CK_SESSION_HANDLE sessionHandle = CK_INVALID_HANDLE;
+
+ CK_RV openSessionRV = C_OpenSession(CKV_TOKEN_USER,
+ CKF_SERIAL_SESSION | CKF_RW_SESSION,
+ NULL,
+ NULL,
+ &sessionHandle);
+
+ if (openSessionRV != CKR_OK || sessionHandle == CK_INVALID_HANDLE) {
+ (void) C_Finalize(NULL_PTR);
+ ALOGE("Error opening primary session with TEE: 0x%x", openSessionRV);
+ return -1;
+ }
+
+ ERR_load_crypto_strings();
+ ERR_load_BIO_strings();
+
+ dev->context = reinterpret_cast<void*>(sessionHandle);
+ *device = reinterpret_cast<hw_device_t*>(dev.release());
+
+ return 0;
+}
+
+static struct hw_module_methods_t keystore_module_methods = {
+ open: tee_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 TEE HAL",
+ author: "The Android Open Source Project",
+ methods: &keystore_module_methods,
+ dso: 0,
+ reserved: {},
+ },
+};