diff options
author | Kenny Root <kroot@google.com> | 2012-01-09 13:59:45 -0800 |
---|---|---|
committer | Kenny Root <kroot@google.com> | 2012-03-27 20:08:01 -0700 |
commit | 41612a3cfef340bf8da31eee1737c1d03428a2dd (patch) | |
tree | cdb4910192773ab8c38d3330cfe5a491b861822d /keymaster | |
parent | 72ddb75c0d7c28314c95fbd484008b1993203dcf (diff) | |
download | device_samsung_tuna-41612a3cfef340bf8da31eee1737c1d03428a2dd.zip device_samsung_tuna-41612a3cfef340bf8da31eee1737c1d03428a2dd.tar.gz device_samsung_tuna-41612a3cfef340bf8da31eee1737c1d03428a2dd.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.mk | 43 | ||||
-rw-r--r-- | keymaster/keymaster_tuna.cpp | 966 |
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: {}, + }, +}; |