| /* |
| * 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 <utils/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; |
| dev->delete_all = NULL; |
| |
| 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: {}, |
| }, |
| }; |